guiDebugger.cpp 18 KB

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