guiDebugger.cpp 19 KB

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