703 lines
18 KiB
C++
Executable File
703 lines
18 KiB
C++
Executable File
//-----------------------------------------------------------------------------
|
|
// Torque Game Engine
|
|
// Copyright (C) GarageGames.com, Inc.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#include "console/console.h"
|
|
#include "dgl/dgl.h"
|
|
#include "gui/core/guiCanvas.h"
|
|
#include "gui/editor/guiDebugger.h"
|
|
#include "core/stream.h"
|
|
|
|
IMPLEMENT_CONOBJECT(DbgFileView);
|
|
|
|
static const char* itoa(S32 i)
|
|
{
|
|
static char buf[32];
|
|
dSprintf(buf, sizeof(buf), "%d", i);
|
|
return buf;
|
|
}
|
|
|
|
static const char* itoa2(S32 i)
|
|
{
|
|
static char buf[32];
|
|
dSprintf(buf, sizeof(buf), "%d", i);
|
|
return buf;
|
|
}
|
|
|
|
DbgFileView::~DbgFileView()
|
|
{
|
|
clear();
|
|
}
|
|
|
|
DbgFileView::DbgFileView()
|
|
{
|
|
VECTOR_SET_ASSOCIATION(mFileView);
|
|
|
|
mFileName = NULL;
|
|
mPCFileName = NULL;
|
|
mPCCurrentLine = -1;
|
|
|
|
mBlockStart = -1;
|
|
mBlockEnd = -1;
|
|
|
|
mFindString[0] = '\0';
|
|
mFindLineNumber = -1;
|
|
|
|
mSize.set(1, 0);
|
|
}
|
|
|
|
ConsoleMethod(DbgFileView, setCurrentLine, void, 4, 4, "(int line, bool selected)"
|
|
"Set the current highlighted line.")
|
|
{
|
|
object->setCurrentLine(dAtoi(argv[2]), dAtob(argv[3]));
|
|
}
|
|
|
|
ConsoleMethod(DbgFileView, getCurrentLine, const char *, 2, 2, "()"
|
|
"Get the currently executing file and line, if any.\n\n"
|
|
"@returns A string containing the file, a tab, and then the line number."
|
|
" Use getField() with this.")
|
|
{
|
|
S32 lineNum;
|
|
const char *file = object->getCurrentLine(lineNum);
|
|
char* ret = Con::getReturnBuffer(256);
|
|
dSprintf(ret, sizeof(ret), "%s\t%d", file, lineNum);
|
|
return ret;
|
|
}
|
|
|
|
ConsoleMethod(DbgFileView, open, bool, 3, 3, "(string filename)"
|
|
"Open a file for viewing.\n\n"
|
|
"@note This loads the file from the local system.")
|
|
{
|
|
return object->openFile(argv[2]);
|
|
}
|
|
|
|
ConsoleMethod(DbgFileView, clearBreakPositions, void, 2, 2, "()"
|
|
"Clear all break points in the current file.")
|
|
{
|
|
object->clearBreakPositions();
|
|
}
|
|
|
|
ConsoleMethod(DbgFileView, setBreakPosition, void, 3, 3, "(int line)"
|
|
"Set a breakpoint at the specified line.")
|
|
{
|
|
object->setBreakPosition(dAtoi(argv[2]));
|
|
}
|
|
|
|
ConsoleMethod(DbgFileView, setBreak, void, 3, 3, "(int line)"
|
|
"Set a breakpoint at the specified line.")
|
|
{
|
|
object->setBreakPointStatus(dAtoi(argv[2]), true);
|
|
}
|
|
|
|
ConsoleMethod(DbgFileView, removeBreak, void, 3, 3, "(int line)"
|
|
"Remove a breakpoint from the specified line.")
|
|
{
|
|
object->setBreakPointStatus(dAtoi(argv[2]), false);
|
|
}
|
|
|
|
ConsoleMethod(DbgFileView, findString, bool, 3, 3, "(string findThis)"
|
|
"Find the specified string in the currently viewed file and "
|
|
"scroll it into view.")
|
|
{
|
|
return object->findString(argv[2]);
|
|
}
|
|
|
|
//this value is the offset used in the ::onRender() method...
|
|
static S32 gFileXOffset = 44;
|
|
|
|
void DbgFileView::AdjustCellSize()
|
|
{
|
|
if (! bool(mFont))
|
|
return;
|
|
S32 maxWidth = 1;
|
|
for (U32 i = 0; i < mFileView.size(); i++)
|
|
{
|
|
S32 cellWidth = gFileXOffset + mFont->getStrWidth((const UTF8 *)mFileView[i].text);
|
|
maxWidth = getMax(maxWidth, cellWidth);
|
|
}
|
|
|
|
mCellSize.set(maxWidth, mFont->getHeight() + 2);
|
|
setSize(mSize);
|
|
}
|
|
|
|
bool DbgFileView::onWake()
|
|
{
|
|
if (! Parent::onWake())
|
|
return false;
|
|
|
|
//clear the mouse over var
|
|
mMouseOverVariable[0] = '\0';
|
|
mbMouseDragging = false;
|
|
|
|
//adjust the cellwidth to the maximum line length
|
|
AdjustCellSize();
|
|
mSize.set(1, mFileView.size());
|
|
|
|
return true;
|
|
}
|
|
|
|
void DbgFileView::addLine(const char *string, U32 strLen)
|
|
{
|
|
// first compute the size
|
|
U32 size = 1; // for null
|
|
for(U32 i = 0; i < strLen; i++)
|
|
{
|
|
if(string[i] == '\t')
|
|
size += 3;
|
|
else if(string[i] != '\r')
|
|
size++;
|
|
}
|
|
FileLine fl;
|
|
fl.breakPosition = false;
|
|
fl.breakOnLine = false;
|
|
if(size)
|
|
{
|
|
fl.text = (char *) dMalloc(size);
|
|
|
|
U32 dstIndex = 0;
|
|
for(U32 i = 0; i < strLen; i++)
|
|
{
|
|
if(string[i] == '\t')
|
|
{
|
|
fl.text[dstIndex] = ' ';
|
|
fl.text[dstIndex + 1] = ' ';
|
|
fl.text[dstIndex + 2] = ' ';
|
|
dstIndex += 3;
|
|
}
|
|
else if(string[i] != '\r')
|
|
fl.text[dstIndex++] = string[i];
|
|
}
|
|
fl.text[dstIndex] = 0;
|
|
}
|
|
else
|
|
fl.text = NULL;
|
|
mFileView.push_back(fl);
|
|
}
|
|
|
|
void DbgFileView::clear()
|
|
{
|
|
for(Vector<FileLine>::iterator i = mFileView.begin(); i != mFileView.end(); i++)
|
|
dFree(i->text);
|
|
mFileView.clear();
|
|
}
|
|
|
|
bool DbgFileView::findString(const char *text)
|
|
{
|
|
//make sure we have a valid string to find
|
|
if (!text || !text[0])
|
|
return false;
|
|
|
|
//see which line we start searching from
|
|
S32 curLine = 0;
|
|
bool searchAgain = false;
|
|
if (mFindLineNumber >= 0 && !dStricmp(mFindString, text))
|
|
{
|
|
searchAgain = true;
|
|
curLine = mFindLineNumber;
|
|
}
|
|
else
|
|
mFindLineNumber = -1;
|
|
|
|
//copy the search text
|
|
dStrncpy(mFindString, text, 255);
|
|
S32 length = dStrlen(mFindString);
|
|
|
|
//loop through looking for the next instance
|
|
while (curLine < mFileView.size())
|
|
{
|
|
char *curText;
|
|
if (curLine == mFindLineNumber && mBlockStart >= 0)
|
|
curText = &mFileView[curLine].text[mBlockStart + 1];
|
|
else
|
|
curText = &mFileView[curLine].text[0];
|
|
|
|
//search for the string (the hard way... - apparently dStrupr is broken...
|
|
char *found = NULL;
|
|
char *curTextPtr = curText;
|
|
while (*curTextPtr != '\0')
|
|
{
|
|
if (!dStrnicmp(mFindString, curTextPtr, length))
|
|
{
|
|
found = curTextPtr;
|
|
break;
|
|
}
|
|
else
|
|
curTextPtr++;
|
|
}
|
|
|
|
//did we find it?
|
|
if (found)
|
|
{
|
|
//scroll first
|
|
mFindLineNumber = curLine;
|
|
scrollToLine(mFindLineNumber + 1);
|
|
|
|
//then hilite
|
|
mBlockStart = (S32)(found - &mFileView[curLine].text[0]);
|
|
mBlockEnd = mBlockStart + length;
|
|
|
|
return true;
|
|
}
|
|
else
|
|
curLine++;
|
|
}
|
|
|
|
//didn't find anything - reset the vars for the next search
|
|
mBlockStart = -1;
|
|
mBlockEnd = -1;
|
|
mFindLineNumber = -1;
|
|
|
|
setSelectedCell(Point2I(-1, -1));
|
|
return false;
|
|
}
|
|
|
|
void DbgFileView::setCurrentLine(S32 lineNumber, bool setCurrentLine)
|
|
{
|
|
//update the line number
|
|
if (setCurrentLine)
|
|
{
|
|
mPCFileName = mFileName;
|
|
mPCCurrentLine = lineNumber;
|
|
mBlockStart = -1;
|
|
mBlockEnd = -1;
|
|
if (lineNumber >= 0)
|
|
scrollToLine(mPCCurrentLine);
|
|
}
|
|
else
|
|
{
|
|
scrollToLine(lineNumber);
|
|
}
|
|
}
|
|
|
|
const char* DbgFileView::getCurrentLine(S32 &lineNumber)
|
|
{
|
|
lineNumber = mPCCurrentLine;
|
|
return mPCFileName;
|
|
}
|
|
|
|
bool DbgFileView::openFile(const char *fileName)
|
|
{
|
|
if ((! fileName) || (! fileName[0]))
|
|
return false;
|
|
|
|
StringTableEntry newFile = StringTable->insert(fileName);
|
|
if (mFileName == newFile)
|
|
return true;
|
|
|
|
U32 fileSize = ResourceManager->getSize(fileName);
|
|
char *fileBuf;
|
|
if (fileSize)
|
|
{
|
|
fileBuf = new char [fileSize+1];
|
|
Stream *s = ResourceManager->openStream(fileName);
|
|
if (s)
|
|
{
|
|
s->read(fileSize, fileBuf);
|
|
ResourceManager->closeStream(s);
|
|
fileBuf[fileSize] = '\0';
|
|
}
|
|
else
|
|
{
|
|
delete [] fileBuf;
|
|
fileBuf = NULL;
|
|
}
|
|
}
|
|
if (!fileSize || !fileBuf)
|
|
{
|
|
Con::printf("DbgFileView: unable to open file %s.", fileName);
|
|
return false;
|
|
}
|
|
|
|
//copy the file name
|
|
mFileName = newFile;
|
|
|
|
//clear the old mFileView
|
|
clear();
|
|
setSize(Point2I(1, 0));
|
|
|
|
//begin reading and parsing at each '\n'
|
|
char *parsePtr = fileBuf;
|
|
for(;;) {
|
|
char *tempPtr = dStrchr(parsePtr, '\n');
|
|
if(tempPtr)
|
|
addLine(parsePtr, tempPtr - parsePtr);
|
|
else if(parsePtr[0])
|
|
addLine(parsePtr, dStrlen(parsePtr));
|
|
if(!tempPtr)
|
|
break;
|
|
parsePtr = tempPtr + 1;
|
|
}
|
|
//delete the buffer
|
|
delete [] fileBuf;
|
|
|
|
//set the file size
|
|
AdjustCellSize();
|
|
setSize(Point2I(1, mFileView.size()));
|
|
|
|
return true;
|
|
}
|
|
|
|
void DbgFileView::scrollToLine(S32 lineNumber)
|
|
{
|
|
GuiControl *parent = getParent();
|
|
if (! parent)
|
|
return;
|
|
|
|
S32 yOffset = (lineNumber - 1) * mCellSize.y;
|
|
|
|
//see if the line is already visible
|
|
if (! (yOffset + mBounds.point.y >= 0 && yOffset + mBounds.point.y < parent->mBounds.extent.y - mCellSize.y))
|
|
{
|
|
//reposition the control
|
|
S32 newYOffset = getMin(0, getMax(parent->mBounds.extent.y - mBounds.extent.y, (mCellSize.y * 4) - yOffset));
|
|
resize(Point2I(mBounds.point.x, newYOffset), mBounds.extent);
|
|
}
|
|
|
|
//hilite the line
|
|
cellSelected(Point2I(0, lineNumber - 1));
|
|
}
|
|
|
|
S32 DbgFileView::findMouseOverChar(const char *text, S32 stringPosition)
|
|
{
|
|
static char tempBuf[512];
|
|
char *bufPtr = &tempBuf[1];
|
|
|
|
// Handle the empty string correctly.
|
|
if (text[0] == '\0') {
|
|
return -1;
|
|
}
|
|
|
|
// Copy the line's text into the scratch buffer.
|
|
dStrncpy(tempBuf, text, 512);
|
|
|
|
// Make sure we have a null terminator.
|
|
tempBuf[511] = '\0';
|
|
|
|
// Loop over the characters...
|
|
bool found = false;
|
|
bool finished = false;
|
|
do {
|
|
// Note the current character, then replace it with EOL.
|
|
char c = *bufPtr;
|
|
*bufPtr = '\0';
|
|
// Is the resulting string long enough to include the current
|
|
// mouse position?
|
|
if ((S32)mFont->getStrWidth((const UTF8 *)tempBuf) > stringPosition) {
|
|
// Yep.
|
|
// Restore the character.
|
|
*bufPtr = c;
|
|
// Set bufPtr to point to the char under the mouse.
|
|
bufPtr--;
|
|
// Bail.
|
|
found = true;
|
|
finished = true;
|
|
}
|
|
else {
|
|
// Nope.
|
|
// Restore the character.
|
|
*bufPtr = c;
|
|
// Move on to the next character.
|
|
bufPtr++;
|
|
// Bail if EOL.
|
|
if (*bufPtr == '\0') finished = true;
|
|
}
|
|
} while (!finished);
|
|
|
|
// Did we find a character under the mouse?
|
|
if (found) {
|
|
// If so, return its position.
|
|
return bufPtr - tempBuf;
|
|
}
|
|
// If not, return -1.
|
|
else return -1;
|
|
}
|
|
|
|
bool DbgFileView::findMouseOverVariable()
|
|
{
|
|
GuiCanvas *root = getRoot();
|
|
AssertFatal(root, "Unable to get the root Canvas.");
|
|
|
|
Point2I curMouse = root->getCursorPos();
|
|
Point2I pt = globalToLocalCoord(curMouse);
|
|
|
|
//find out which cell was hit
|
|
Point2I cell((pt.x < 0 ? -1 : pt.x / mCellSize.x), (pt.y < 0 ? -1 : pt.y / mCellSize.y));
|
|
if(cell.x >= 0 && cell.x < mSize.x && cell.y >= 0 && cell.y < mSize.y)
|
|
{
|
|
S32 stringPosition = pt.x - gFileXOffset;
|
|
char tempBuf[256], *varNamePtr = &tempBuf[1];
|
|
dStrcpy(tempBuf, mFileView[cell.y].text);
|
|
|
|
//find the current mouse over char
|
|
S32 charNum = findMouseOverChar(mFileView[cell.y].text, stringPosition);
|
|
if (charNum >= 0)
|
|
{
|
|
varNamePtr = &tempBuf[charNum];
|
|
}
|
|
else
|
|
{
|
|
mMouseOverVariable[0] = '\0';
|
|
mMouseOverValue[0] = '\0';
|
|
return false;
|
|
}
|
|
|
|
//now make sure we can go from the current cursor mPosition to the beginning of a var name
|
|
bool found = false;
|
|
while (varNamePtr >= &tempBuf[0])
|
|
{
|
|
if (*varNamePtr == '%' || *varNamePtr == '$')
|
|
{
|
|
found = true;
|
|
break;
|
|
}
|
|
else if ((dToupper(*varNamePtr) >= 'A' && dToupper(*varNamePtr) <= 'Z') ||
|
|
(*varNamePtr >= '0' && *varNamePtr <= '9') || *varNamePtr == '_' || *varNamePtr == ':')
|
|
{
|
|
varNamePtr--;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
//mouse wasn't over a possible variable name
|
|
if (! found)
|
|
{
|
|
mMouseOverVariable[0] = '\0';
|
|
mMouseOverValue[0] = '\0';
|
|
return false;
|
|
}
|
|
|
|
//set the var char start positions
|
|
mMouseVarStart = varNamePtr - tempBuf;
|
|
|
|
//now copy the (possible) var name into the buf
|
|
char *tempPtr = &mMouseOverVariable[0];
|
|
|
|
//copy the leading '%' or '$'
|
|
*tempPtr++ = *varNamePtr++;
|
|
|
|
//now copy letters and numbers until the end of the name
|
|
while ((dToupper(*varNamePtr) >= 'A' && dToupper(*varNamePtr) <= 'Z') ||
|
|
(*varNamePtr >= '0' && *varNamePtr <= '9') || *varNamePtr == '_' || *varNamePtr == ':')
|
|
{
|
|
*tempPtr++ = *varNamePtr++;
|
|
}
|
|
*tempPtr = '\0';
|
|
|
|
//set the var char end positions
|
|
mMouseVarEnd = varNamePtr - tempBuf;
|
|
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void DbgFileView::clearBreakPositions()
|
|
{
|
|
for(Vector<FileLine>::iterator i = mFileView.begin(); i != mFileView.end(); i++)
|
|
{
|
|
i->breakPosition = false;
|
|
i->breakOnLine = false;
|
|
}
|
|
}
|
|
|
|
void DbgFileView::setBreakPosition(U32 line)
|
|
{
|
|
if(line > mFileView.size())
|
|
return;
|
|
mFileView[line-1].breakPosition = true;
|
|
}
|
|
|
|
void DbgFileView::setBreakPointStatus(U32 line, bool set)
|
|
{
|
|
if(line > mFileView.size())
|
|
return;
|
|
mFileView[line-1].breakOnLine = set;
|
|
}
|
|
|
|
void DbgFileView::onPreRender()
|
|
{
|
|
setUpdate();
|
|
char oldVar[256];
|
|
dStrcpy(oldVar, mMouseOverVariable);
|
|
bool found = findMouseOverVariable();
|
|
if (found && mPCCurrentLine >= 0)
|
|
{
|
|
//send the query only when the var changes
|
|
if (dStricmp(oldVar, mMouseOverVariable))
|
|
Con::executef(2, "DbgSetCursorWatch", mMouseOverVariable);
|
|
}
|
|
else
|
|
Con::executef(2, "DbgSetCursorWatch", "");
|
|
}
|
|
|
|
void DbgFileView::onMouseDown(const GuiEvent &event)
|
|
{
|
|
if (! mActive)
|
|
{
|
|
Parent::onMouseDown(event);
|
|
return;
|
|
}
|
|
|
|
Point2I pt = globalToLocalCoord(event.mousePoint);
|
|
bool doubleClick = (event.mouseClickCount > 1);
|
|
|
|
//find out which cell was hit
|
|
Point2I cell((pt.x < 0 ? -1 : pt.x / mCellSize.x), (pt.y < 0 ? -1 : pt.y / mCellSize.y));
|
|
if(cell.x >= 0 && cell.x < mSize.x && cell.y >= 0 && cell.y < mSize.y)
|
|
{
|
|
//if we clicked on the breakpoint mark
|
|
if (pt.x >= 0 && pt.x <= 12)
|
|
{
|
|
//toggle the break point
|
|
if (mFileView[cell.y].breakPosition)
|
|
{
|
|
if (mFileView[cell.y].breakOnLine)
|
|
Con::executef(this, 2, "onRemoveBreakPoint", itoa(cell.y + 1));
|
|
else
|
|
Con::executef(this, 2, "onSetBreakPoint", itoa(cell.y + 1));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Point2I prevSelected = mSelectedCell;
|
|
Parent::onMouseDown(event);
|
|
mBlockStart= -1;
|
|
mBlockEnd = -1;
|
|
|
|
//open the file view
|
|
if (mSelectedCell.y == prevSelected.y && doubleClick && mMouseOverVariable[0])
|
|
{
|
|
Con::executef(this, 2, "onSetWatch", mMouseOverVariable);
|
|
mBlockStart = mMouseVarStart;
|
|
mBlockEnd = mMouseVarEnd;
|
|
}
|
|
else
|
|
{
|
|
S32 stringPosition = pt.x - gFileXOffset;
|
|
|
|
//find which character we're over
|
|
S32 charNum = findMouseOverChar(mFileView[mSelectedCell.y].text, stringPosition);
|
|
if (charNum >= 0)
|
|
{
|
|
//lock the mouse
|
|
mouseLock();
|
|
setFirstResponder();
|
|
|
|
//set the block hilite start and end
|
|
mbMouseDragging = true;
|
|
mMouseDownChar = charNum;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Parent::onMouseDown(event);
|
|
}
|
|
}
|
|
|
|
void DbgFileView::onMouseDragged(const GuiEvent &event)
|
|
{
|
|
if (mbMouseDragging)
|
|
{
|
|
Point2I pt = globalToLocalCoord(event.mousePoint);
|
|
S32 stringPosition = pt.x - gFileXOffset;
|
|
|
|
//find which character we're over
|
|
S32 charNum = findMouseOverChar(mFileView[mSelectedCell.y].text, stringPosition);
|
|
if (charNum >= 0)
|
|
{
|
|
if (charNum < mMouseDownChar)
|
|
{
|
|
|
|
mBlockEnd = mMouseDownChar + 1;
|
|
mBlockStart = charNum;
|
|
}
|
|
else
|
|
{
|
|
mBlockEnd = charNum + 1;
|
|
mBlockStart = mMouseDownChar;
|
|
}
|
|
}
|
|
|
|
//otherwise, the cursor is past the end of the string
|
|
else
|
|
{
|
|
mBlockStart = mMouseDownChar;
|
|
mBlockEnd = dStrlen(mFileView[mSelectedCell.y].text) + 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
void DbgFileView::onMouseUp(const GuiEvent &)
|
|
{
|
|
//unlock the mouse
|
|
mouseUnlock();
|
|
|
|
mbMouseDragging = false;
|
|
}
|
|
|
|
void DbgFileView::onRenderCell(Point2I offset, Point2I cell, bool selected, bool)
|
|
{
|
|
Point2I cellOffset = offset;
|
|
cellOffset.x += 4;
|
|
|
|
//draw the break point marks
|
|
if (mFileView[cell.y].breakOnLine)
|
|
{
|
|
dglSetBitmapModulation(mProfile->mFontColorHL);
|
|
dglDrawText(mFont, cellOffset, "#");
|
|
}
|
|
else if (mFileView[cell.y].breakPosition)
|
|
{
|
|
dglSetBitmapModulation(mProfile->mFontColor);
|
|
dglDrawText(mFont, cellOffset, "-");
|
|
}
|
|
cellOffset.x += 8;
|
|
|
|
//draw in the "current line" indicator
|
|
if (mFileName == mPCFileName && (cell.y + 1 == mPCCurrentLine))
|
|
{
|
|
dglSetBitmapModulation(mProfile->mFontColorHL);
|
|
dglDrawText(mFont, cellOffset, "=>");
|
|
}
|
|
|
|
//by this time, the cellOffset has been incremented by 44 - the value of gFileXOffset
|
|
cellOffset.x += 32;
|
|
|
|
//hilite the line if selected
|
|
if (selected)
|
|
{
|
|
if (mBlockStart == -1)
|
|
{
|
|
dglDrawRectFill(RectI(cellOffset.x - 2, cellOffset.y - 3,
|
|
mCellSize.x + 4, mCellSize.y + 6), mProfile->mFillColorHL);
|
|
}
|
|
else if (mBlockStart >= 0 && mBlockEnd > mBlockStart && mBlockEnd <= S32(dStrlen(mFileView[cell.y].text) + 1))
|
|
{
|
|
S32 startPos, endPos;
|
|
char tempBuf[256];
|
|
dStrcpy(tempBuf, mFileView[cell.y].text);
|
|
|
|
//get the end coord
|
|
tempBuf[mBlockEnd] = '\0';
|
|
endPos = mFont->getStrWidth((const UTF8 *)tempBuf);
|
|
|
|
//get the start coord
|
|
tempBuf[mBlockStart] = '\0';
|
|
startPos = mFont->getStrWidth((const UTF8 *)tempBuf);
|
|
|
|
//draw the hilite
|
|
dglDrawRectFill(RectI(cellOffset.x + startPos, cellOffset.y - 3, endPos - startPos + 2, mCellSize.y + 6), mProfile->mFillColorHL);
|
|
}
|
|
}
|
|
|
|
//draw the line of text
|
|
dglSetBitmapModulation(mFileView[cell.y].breakOnLine ? mProfile->mFontColorHL : mProfile->mFontColor);
|
|
dglDrawText(mFont, cellOffset, mFileView[cell.y].text);
|
|
}
|