guiDebugger.cpp 19 KB

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