guiDebugger.cc 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2013 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #include "console/console.h"
  23. #include "graphics/dgl.h"
  24. #include "gui/guiCanvas.h"
  25. #include "gui/editor/guiDebugger.h"
  26. #include "io/stream.h"
  27. IMPLEMENT_CONOBJECT(DbgFileView);
  28. static const char* itoa(S32 i)
  29. {
  30. static char buf[32];
  31. dSprintf(buf, sizeof(buf), "%d", i);
  32. return buf;
  33. }
  34. static const char* itoa2(S32 i)
  35. {
  36. static char buf[32];
  37. dSprintf(buf, sizeof(buf), "%d", i);
  38. return buf;
  39. }
  40. DbgFileView::~DbgFileView()
  41. {
  42. clear();
  43. }
  44. DbgFileView::DbgFileView()
  45. {
  46. VECTOR_SET_ASSOCIATION(mFileView);
  47. mFileName = NULL;
  48. mPCFileName = NULL;
  49. mPCCurrentLine = -1;
  50. mBlockStart = -1;
  51. mBlockEnd = -1;
  52. mFindString[0] = '\0';
  53. mFindLineNumber = -1;
  54. mSize.set(1, 0);
  55. }
  56. ConsoleMethod(DbgFileView, setCurrentLine, void, 4, 4, "(int line, bool selected)"
  57. "Set the current highlighted line.\n"
  58. "@return No return value.")
  59. {
  60. object->setCurrentLine(dAtoi(argv[2]), dAtob(argv[3]));
  61. }
  62. ConsoleMethod(DbgFileView, getCurrentLine, const char *, 2, 2, "()"
  63. "Get the currently executing file and line, if any.\n\n"
  64. "@returns A string containing the file, a tab, and then the line number."
  65. " Use getField() with this.")
  66. {
  67. S32 lineNum;
  68. const char *file = object->getCurrentLine(lineNum);
  69. char* ret = Con::getReturnBuffer(256);
  70. dSprintf(ret, 256 * sizeof(char), "%s\t%d", file, lineNum);
  71. return ret;
  72. }
  73. ConsoleMethod(DbgFileView, open, bool, 3, 3, "(string filename)"
  74. "Open a file for viewing.\n\n"
  75. "@return Returns true on success.\n"
  76. "@note This loads the file from the local system.")
  77. {
  78. return object->openFile(argv[2]);
  79. }
  80. ConsoleMethod(DbgFileView, clearBreakPositions, void, 2, 2, "()"
  81. "Clear all break points in the current file.\n"
  82. "@return No return value.")
  83. {
  84. object->clearBreakPositions();
  85. }
  86. ConsoleMethod(DbgFileView, setBreakPosition, void, 3, 3, "(int line)"
  87. "Set a breakpoint at the specified line.\n"
  88. "@return No return value.")
  89. {
  90. object->setBreakPosition(dAtoi(argv[2]));
  91. }
  92. ConsoleMethod(DbgFileView, setBreak, void, 3, 3, "(int line)"
  93. "Set a breakpoint at the specified line.\n"
  94. "@return No return value.")
  95. {
  96. object->setBreakPointStatus(dAtoi(argv[2]), true);
  97. }
  98. ConsoleMethod(DbgFileView, removeBreak, void, 3, 3, "(int line)"
  99. "Remove a breakpoint from the specified line.\n"
  100. "@return No return value.")
  101. {
  102. object->setBreakPointStatus(dAtoi(argv[2]), false);
  103. }
  104. ConsoleMethod(DbgFileView, findString, bool, 3, 3, "(string findThis)"
  105. "Find the specified string in the currently viewed file and "
  106. "scroll it into view.\n"
  107. "@return Returns true on success\n")
  108. {
  109. return object->findString(argv[2]);
  110. }
  111. //this value is the offset used in the ::onRender() method...
  112. static S32 gFileXOffset = 44;
  113. void DbgFileView::AdjustCellSize()
  114. {
  115. if (! bool(mFont))
  116. return;
  117. S32 maxWidth = 1;
  118. for (U32 i = 0; i < (U32)mFileView.size(); i++)
  119. {
  120. S32 cellWidth = gFileXOffset + mFont->getStrWidth((const UTF8 *)mFileView[i].text);
  121. maxWidth = getMax(maxWidth, cellWidth);
  122. }
  123. mCellSize.set(maxWidth, mFont->getHeight() + 2);
  124. setSize(mSize);
  125. }
  126. bool DbgFileView::onWake()
  127. {
  128. if (! Parent::onWake())
  129. return false;
  130. //clear the mouse over var
  131. mMouseOverVariable[0] = '\0';
  132. mbMouseDragging = false;
  133. //adjust the cellwidth to the maximum line length
  134. AdjustCellSize();
  135. mSize.set(1, mFileView.size());
  136. return true;
  137. }
  138. void DbgFileView::addLine(const char *string, U32 strLen)
  139. {
  140. // first compute the size
  141. U32 size = 1; // for null
  142. for(U32 i = 0; i < strLen; i++)
  143. {
  144. if(string[i] == '\t')
  145. size += 3;
  146. else if(string[i] != '\r')
  147. size++;
  148. }
  149. FileLine fl;
  150. fl.breakPosition = false;
  151. fl.breakOnLine = false;
  152. if(size)
  153. {
  154. fl.text = (char *) dMalloc(size);
  155. U32 dstIndex = 0;
  156. for(U32 i = 0; i < strLen; i++)
  157. {
  158. if(string[i] == '\t')
  159. {
  160. fl.text[dstIndex] = ' ';
  161. fl.text[dstIndex + 1] = ' ';
  162. fl.text[dstIndex + 2] = ' ';
  163. dstIndex += 3;
  164. }
  165. else if(string[i] != '\r')
  166. fl.text[dstIndex++] = string[i];
  167. }
  168. fl.text[dstIndex] = 0;
  169. }
  170. else
  171. fl.text = NULL;
  172. mFileView.push_back(fl);
  173. }
  174. void DbgFileView::clear()
  175. {
  176. for(Vector<FileLine>::iterator i = mFileView.begin(); i != mFileView.end(); i++)
  177. dFree(i->text);
  178. mFileView.clear();
  179. }
  180. bool DbgFileView::findString(const char *text)
  181. {
  182. //make sure we have a valid string to find
  183. if (!text || !text[0])
  184. return false;
  185. //see which line we start searching from
  186. S32 curLine = 0;
  187. bool searchAgain = false;
  188. if (mFindLineNumber >= 0 && !dStricmp(mFindString, text))
  189. {
  190. searchAgain = true;
  191. curLine = mFindLineNumber;
  192. }
  193. else
  194. mFindLineNumber = -1;
  195. //copy the search text
  196. dStrncpy(mFindString, text, 255);
  197. S32 length = dStrlen(mFindString);
  198. //loop through looking for the next instance
  199. while (curLine < mFileView.size())
  200. {
  201. char *curText;
  202. if (curLine == mFindLineNumber && mBlockStart >= 0)
  203. curText = &mFileView[curLine].text[mBlockStart + 1];
  204. else
  205. curText = &mFileView[curLine].text[0];
  206. //search for the string (the hard way... - apparently dStrupr is broken...
  207. char *found = NULL;
  208. char *curTextPtr = curText;
  209. while (*curTextPtr != '\0')
  210. {
  211. if (!dStrnicmp(mFindString, curTextPtr, length))
  212. {
  213. found = curTextPtr;
  214. break;
  215. }
  216. else
  217. curTextPtr++;
  218. }
  219. //did we find it?
  220. if (found)
  221. {
  222. //scroll first
  223. mFindLineNumber = curLine;
  224. scrollToLine(mFindLineNumber + 1);
  225. //then hilite
  226. mBlockStart = (S32)(found - &mFileView[curLine].text[0]);
  227. mBlockEnd = mBlockStart + length;
  228. return true;
  229. }
  230. else
  231. curLine++;
  232. }
  233. //didn't find anything - reset the vars for the next search
  234. mBlockStart = -1;
  235. mBlockEnd = -1;
  236. mFindLineNumber = -1;
  237. setSelectedCell(Point2I(-1, -1));
  238. return false;
  239. }
  240. void DbgFileView::setCurrentLine(S32 lineNumber, bool setCurrentLine)
  241. {
  242. //update the line number
  243. if (setCurrentLine)
  244. {
  245. mPCFileName = mFileName;
  246. mPCCurrentLine = lineNumber;
  247. mBlockStart = -1;
  248. mBlockEnd = -1;
  249. if (lineNumber >= 0)
  250. scrollToLine(mPCCurrentLine);
  251. }
  252. else
  253. {
  254. scrollToLine(lineNumber);
  255. }
  256. }
  257. const char* DbgFileView::getCurrentLine(S32 &lineNumber)
  258. {
  259. lineNumber = mPCCurrentLine;
  260. return mPCFileName;
  261. }
  262. bool DbgFileView::openFile(const char *fileName)
  263. {
  264. if ((! fileName) || (! fileName[0]))
  265. return false;
  266. StringTableEntry newFile = StringTable->insert(fileName);
  267. if (mFileName == newFile)
  268. return true;
  269. U32 fileSize = ResourceManager->getSize(fileName);
  270. char *fileBuf;
  271. if (fileSize)
  272. {
  273. fileBuf = new char [fileSize+1];
  274. Stream *s = ResourceManager->openStream(fileName);
  275. if (s)
  276. {
  277. s->read(fileSize, fileBuf);
  278. ResourceManager->closeStream(s);
  279. fileBuf[fileSize] = '\0';
  280. }
  281. else
  282. {
  283. delete [] fileBuf;
  284. fileBuf = NULL;
  285. }
  286. }
  287. if (!fileSize || !fileBuf)
  288. {
  289. Con::printf("DbgFileView: unable to open file %s.", fileName);
  290. return false;
  291. }
  292. //copy the file name
  293. mFileName = newFile;
  294. //clear the old mFileView
  295. clear();
  296. setSize(Point2I(1, 0));
  297. //begin reading and parsing at each '\n'
  298. char *parsePtr = fileBuf;
  299. for(;;) {
  300. char *tempPtr = dStrchr(parsePtr, '\n');
  301. if(tempPtr)
  302. addLine(parsePtr, (U32)(tempPtr - parsePtr));
  303. else if(parsePtr[0])
  304. addLine(parsePtr, dStrlen(parsePtr));
  305. if(!tempPtr)
  306. break;
  307. parsePtr = tempPtr + 1;
  308. }
  309. //delete the buffer
  310. delete [] fileBuf;
  311. //set the file size
  312. AdjustCellSize();
  313. setSize(Point2I(1, mFileView.size()));
  314. return true;
  315. }
  316. void DbgFileView::scrollToLine(S32 lineNumber)
  317. {
  318. GuiControl *parent = getParent();
  319. if (! parent)
  320. return;
  321. S32 yOffset = (lineNumber - 1) * mCellSize.y;
  322. //see if the line is already visible
  323. if (! (yOffset + mBounds.point.y >= 0 && yOffset + mBounds.point.y < parent->mBounds.extent.y - mCellSize.y))
  324. {
  325. //reposition the control
  326. S32 newYOffset = getMin(0, getMax(parent->mBounds.extent.y - mBounds.extent.y, (mCellSize.y * 4) - yOffset));
  327. resize(Point2I(mBounds.point.x, newYOffset), mBounds.extent);
  328. }
  329. //hilite the line
  330. cellSelected(Point2I(0, lineNumber - 1));
  331. }
  332. S32 DbgFileView::findMouseOverChar(const char *text, S32 stringPosition)
  333. {
  334. static char tempBuf[512];
  335. char *bufPtr = &tempBuf[1];
  336. // Handle the empty string correctly.
  337. if (text[0] == '\0') {
  338. return -1;
  339. }
  340. // Copy the line's text into the scratch buffer.
  341. dStrncpy(tempBuf, text, 512);
  342. // Make sure we have a null terminator.
  343. tempBuf[511] = '\0';
  344. // Loop over the characters...
  345. bool found = false;
  346. bool finished = false;
  347. do {
  348. // Note the current character, then replace it with EOL.
  349. char c = *bufPtr;
  350. *bufPtr = '\0';
  351. // Is the resulting string long enough to include the current
  352. // mouse position?
  353. if ((S32)mFont->getStrWidth((const UTF8 *)tempBuf) > stringPosition) {
  354. // Yep.
  355. // Restore the character.
  356. *bufPtr = c;
  357. // Set bufPtr to point to the char under the mouse.
  358. bufPtr--;
  359. // Bail.
  360. found = true;
  361. finished = true;
  362. }
  363. else {
  364. // Nope.
  365. // Restore the character.
  366. *bufPtr = c;
  367. // Move on to the next character.
  368. bufPtr++;
  369. // Bail if EOL.
  370. if (*bufPtr == '\0') finished = true;
  371. }
  372. } while (!finished);
  373. // Did we find a character under the mouse?
  374. if (found) {
  375. // If so, return its position.
  376. return (S32)(bufPtr - tempBuf);
  377. }
  378. // If not, return -1.
  379. else return -1;
  380. }
  381. bool DbgFileView::findMouseOverVariable()
  382. {
  383. GuiCanvas *root = getRoot();
  384. AssertFatal(root, "Unable to get the root Canvas.");
  385. Point2I curMouse = root->getCursorPos();
  386. Point2I pt = globalToLocalCoord(curMouse);
  387. //find out which cell was hit
  388. Point2I cell((pt.x < 0 ? -1 : pt.x / mCellSize.x), (pt.y < 0 ? -1 : pt.y / mCellSize.y));
  389. if(cell.x >= 0 && cell.x < mSize.x && cell.y >= 0 && cell.y < mSize.y)
  390. {
  391. S32 stringPosition = pt.x - gFileXOffset;
  392. char tempBuf[256], *varNamePtr = &tempBuf[1];
  393. dStrcpy(tempBuf, mFileView[cell.y].text);
  394. //find the current mouse over char
  395. S32 charNum = findMouseOverChar(mFileView[cell.y].text, stringPosition);
  396. if (charNum >= 0)
  397. {
  398. varNamePtr = &tempBuf[charNum];
  399. }
  400. else
  401. {
  402. mMouseOverVariable[0] = '\0';
  403. mMouseOverValue[0] = '\0';
  404. return false;
  405. }
  406. //now make sure we can go from the current cursor mPosition to the beginning of a var name
  407. bool found = false;
  408. while (varNamePtr >= &tempBuf[0])
  409. {
  410. if (*varNamePtr == '%' || *varNamePtr == '$')
  411. {
  412. found = true;
  413. break;
  414. }
  415. else if ((dToupper(*varNamePtr) >= 'A' && dToupper(*varNamePtr) <= 'Z') ||
  416. (*varNamePtr >= '0' && *varNamePtr <= '9') || *varNamePtr == '_' || *varNamePtr == ':')
  417. {
  418. varNamePtr--;
  419. }
  420. else
  421. {
  422. break;
  423. }
  424. }
  425. //mouse wasn't over a possible variable name
  426. if (! found)
  427. {
  428. mMouseOverVariable[0] = '\0';
  429. mMouseOverValue[0] = '\0';
  430. return false;
  431. }
  432. //set the var char start positions
  433. mMouseVarStart = (S32)(varNamePtr - tempBuf);
  434. //now copy the (possible) var name into the buf
  435. char *tempPtr = &mMouseOverVariable[0];
  436. //copy the leading '%' or '$'
  437. *tempPtr++ = *varNamePtr++;
  438. //now copy letters and numbers until the end of the name
  439. while ((dToupper(*varNamePtr) >= 'A' && dToupper(*varNamePtr) <= 'Z') ||
  440. (*varNamePtr >= '0' && *varNamePtr <= '9') || *varNamePtr == '_' || *varNamePtr == ':')
  441. {
  442. *tempPtr++ = *varNamePtr++;
  443. }
  444. *tempPtr = '\0';
  445. //set the var char end positions
  446. mMouseVarEnd = (S32)(varNamePtr - tempBuf);
  447. return true;
  448. }
  449. return false;
  450. }
  451. void DbgFileView::clearBreakPositions()
  452. {
  453. for(Vector<FileLine>::iterator i = mFileView.begin(); i != mFileView.end(); i++)
  454. {
  455. i->breakPosition = false;
  456. i->breakOnLine = false;
  457. }
  458. }
  459. void DbgFileView::setBreakPosition(U32 line)
  460. {
  461. if(line > (U32)mFileView.size())
  462. return;
  463. mFileView[line-1].breakPosition = true;
  464. }
  465. void DbgFileView::setBreakPointStatus(U32 line, bool set)
  466. {
  467. if(line > (U32)mFileView.size())
  468. return;
  469. mFileView[line-1].breakOnLine = set;
  470. }
  471. void DbgFileView::onPreRender()
  472. {
  473. setUpdate();
  474. char oldVar[256];
  475. dStrcpy(oldVar, mMouseOverVariable);
  476. bool found = findMouseOverVariable();
  477. if (found && mPCCurrentLine >= 0)
  478. {
  479. //send the query only when the var changes
  480. if (dStricmp(oldVar, mMouseOverVariable))
  481. Con::executef(2, "DbgSetCursorWatch", mMouseOverVariable);
  482. }
  483. else
  484. Con::executef(2, "DbgSetCursorWatch", "");
  485. }
  486. void DbgFileView::onTouchDown(const GuiEvent &event)
  487. {
  488. if (! mActive)
  489. {
  490. Parent::onTouchDown(event);
  491. return;
  492. }
  493. Point2I pt = globalToLocalCoord(event.mousePoint);
  494. bool doubleClick = (event.mouseClickCount > 1);
  495. //find out which cell was hit
  496. Point2I cell((pt.x < 0 ? -1 : pt.x / mCellSize.x), (pt.y < 0 ? -1 : pt.y / mCellSize.y));
  497. if(cell.x >= 0 && cell.x < mSize.x && cell.y >= 0 && cell.y < mSize.y)
  498. {
  499. //if we clicked on the breakpoint mark
  500. if (pt.x >= 0 && pt.x <= 12)
  501. {
  502. //toggle the break point
  503. if (mFileView[cell.y].breakPosition)
  504. {
  505. if (mFileView[cell.y].breakOnLine)
  506. Con::executef(this, 2, "onRemoveBreakPoint", itoa(cell.y + 1));
  507. else
  508. Con::executef(this, 2, "onSetBreakPoint", itoa(cell.y + 1));
  509. }
  510. }
  511. else
  512. {
  513. Point2I prevSelected = mSelectedCell;
  514. Parent::onTouchDown(event);
  515. mBlockStart= -1;
  516. mBlockEnd = -1;
  517. //open the file view
  518. if (mSelectedCell.y == prevSelected.y && doubleClick && mMouseOverVariable[0])
  519. {
  520. Con::executef(this, 2, "onSetWatch", mMouseOverVariable);
  521. mBlockStart = mMouseVarStart;
  522. mBlockEnd = mMouseVarEnd;
  523. }
  524. else
  525. {
  526. S32 stringPosition = pt.x - gFileXOffset;
  527. //find which character we're over
  528. S32 charNum = findMouseOverChar(mFileView[mSelectedCell.y].text, stringPosition);
  529. if (charNum >= 0)
  530. {
  531. //lock the mouse
  532. mouseLock();
  533. setFirstResponder();
  534. //set the block hilite start and end
  535. mbMouseDragging = true;
  536. mMouseDownChar = charNum;
  537. }
  538. }
  539. }
  540. }
  541. else
  542. {
  543. Parent::onTouchDown(event);
  544. }
  545. }
  546. void DbgFileView::onTouchDragged(const GuiEvent &event)
  547. {
  548. if (mbMouseDragging)
  549. {
  550. Point2I pt = globalToLocalCoord(event.mousePoint);
  551. S32 stringPosition = pt.x - gFileXOffset;
  552. //find which character we're over
  553. S32 charNum = findMouseOverChar(mFileView[mSelectedCell.y].text, stringPosition);
  554. if (charNum >= 0)
  555. {
  556. if (charNum < mMouseDownChar)
  557. {
  558. mBlockEnd = mMouseDownChar + 1;
  559. mBlockStart = charNum;
  560. }
  561. else
  562. {
  563. mBlockEnd = charNum + 1;
  564. mBlockStart = mMouseDownChar;
  565. }
  566. }
  567. //otherwise, the cursor is past the end of the string
  568. else
  569. {
  570. mBlockStart = mMouseDownChar;
  571. mBlockEnd = dStrlen(mFileView[mSelectedCell.y].text) + 1;
  572. }
  573. }
  574. }
  575. void DbgFileView::onTouchUp(const GuiEvent &)
  576. {
  577. //unlock the mouse
  578. mouseUnlock();
  579. mbMouseDragging = false;
  580. }
  581. void DbgFileView::onRenderCell(Point2I offset, Point2I cell, bool selected, bool)
  582. {
  583. Point2I cellOffset = offset;
  584. cellOffset.x += 4;
  585. //draw the break point marks
  586. if (mFileView[cell.y].breakOnLine)
  587. {
  588. dglSetBitmapModulation(mProfile->mFontColorHL);
  589. dglDrawText(mFont, cellOffset, "#");
  590. }
  591. else if (mFileView[cell.y].breakPosition)
  592. {
  593. dglSetBitmapModulation(mProfile->mFontColor);
  594. dglDrawText(mFont, cellOffset, "-");
  595. }
  596. cellOffset.x += 8;
  597. //draw in the "current line" indicator
  598. if (mFileName == mPCFileName && (cell.y + 1 == mPCCurrentLine))
  599. {
  600. dglSetBitmapModulation(mProfile->mFontColorHL);
  601. dglDrawText(mFont, cellOffset, "=>");
  602. }
  603. //by this time, the cellOffset has been incremented by 44 - the value of gFileXOffset
  604. cellOffset.x += 32;
  605. //hilite the line if selected
  606. if (selected)
  607. {
  608. if (mBlockStart == -1)
  609. {
  610. dglDrawRectFill(RectI(cellOffset.x - 2, cellOffset.y - 3,
  611. mCellSize.x + 4, mCellSize.y + 6), mProfile->mFillColorHL);
  612. }
  613. else if (mBlockStart >= 0 && mBlockEnd > mBlockStart && mBlockEnd <= S32(dStrlen(mFileView[cell.y].text) + 1))
  614. {
  615. S32 startPos, endPos;
  616. char tempBuf[256];
  617. dStrcpy(tempBuf, mFileView[cell.y].text);
  618. //get the end coord
  619. tempBuf[mBlockEnd] = '\0';
  620. endPos = mFont->getStrWidth((const UTF8 *)tempBuf);
  621. //get the start coord
  622. tempBuf[mBlockStart] = '\0';
  623. startPos = mFont->getStrWidth((const UTF8 *)tempBuf);
  624. //draw the hilite
  625. dglDrawRectFill(RectI(cellOffset.x + startPos, cellOffset.y - 3, endPos - startPos + 2, mCellSize.y + 6), mProfile->mFillColorHL);
  626. }
  627. }
  628. //draw the line of text
  629. dglSetBitmapModulation(mFileView[cell.y].breakOnLine ? mProfile->mFontColorHL : mProfile->mFontColor);
  630. dglDrawText(mFont, cellOffset, mFileView[cell.y].text);
  631. }