You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
968 lines
22 KiB
968 lines
22 KiB
/*
|
|
* Copyright Johannes Sixt
|
|
* This file is licensed under the GNU General Public License Version 2.
|
|
* See the file COPYING in the toplevel directory of the source directory.
|
|
*/
|
|
|
|
#include "debugger.h"
|
|
#include "sourcewnd.h"
|
|
#include <tqtextstream.h>
|
|
#include <tqpainter.h>
|
|
#include <tqbrush.h>
|
|
#include <tqfile.h>
|
|
#include <tqfileinfo.h>
|
|
#include <tqkeycode.h>
|
|
#include <tqpopupmenu.h>
|
|
#include <tdeapplication.h>
|
|
#include <kiconloader.h>
|
|
#include <tdeglobalsettings.h>
|
|
#include <tdemainwindow.h>
|
|
#include <algorithm>
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
#include "mydebug.h"
|
|
|
|
|
|
SourceWindow::SourceWindow(const TQString& fileName, TQWidget* parent, const char* name) :
|
|
TQTextEdit(parent, name),
|
|
m_fileName(fileName),
|
|
m_curRow(-1),
|
|
m_widthItems(16),
|
|
m_widthPlus(12),
|
|
m_widthLineNo(30)
|
|
{
|
|
// load pixmaps
|
|
m_pcinner = UserIcon("pcinner");
|
|
m_pcup = UserIcon("pcup");
|
|
m_brkena = UserIcon("brkena");
|
|
m_brkdis = UserIcon("brkdis");
|
|
m_brktmp = UserIcon("brktmp");
|
|
m_brkcond = UserIcon("brkcond");
|
|
m_brkorph = UserIcon("brkorph");
|
|
setFont(TDEGlobalSettings::fixedFont());
|
|
setReadOnly(true);
|
|
setMargins(m_widthItems+m_widthPlus+m_widthLineNo, 0, 0 ,0);
|
|
setAutoFormatting(AutoNone);
|
|
setTextFormat(PlainText);
|
|
setWordWrap(NoWrap);
|
|
connect(verticalScrollBar(), SIGNAL(valueChanged(int)),
|
|
this, SLOT(update()));
|
|
connect(this, SIGNAL(cursorPositionChanged(int,int)), this, SLOT(cursorChanged(int)));
|
|
viewport()->installEventFilter(this);
|
|
|
|
// add a syntax highlighter
|
|
if (TQRegExp("\\.(c(pp|c|\\+\\+)?|CC?|h(\\+\\+|h)?|HH?)$").search(m_fileName) >= 0)
|
|
{
|
|
new HighlightCpp(this);
|
|
}
|
|
}
|
|
|
|
SourceWindow::~SourceWindow()
|
|
{
|
|
delete syntaxHighlighter();
|
|
}
|
|
|
|
bool SourceWindow::loadFile()
|
|
{
|
|
// first we load the code into TQTextEdit
|
|
TQFile f(m_fileName);
|
|
if (!f.open(IO_ReadOnly)) {
|
|
return false;
|
|
}
|
|
|
|
TQTextStream t(&f);
|
|
setText(t.read());
|
|
f.close();
|
|
|
|
// then we copy it into our own m_sourceCode
|
|
int n = paragraphs();
|
|
m_sourceCode.resize(n);
|
|
m_rowToLine.resize(n);
|
|
for (int i = 0; i < n; i++) {
|
|
m_sourceCode[i].code = text(i);
|
|
m_rowToLine[i] = i;
|
|
}
|
|
m_lineItems.resize(n, 0);
|
|
|
|
// set a font for line numbers
|
|
m_lineNoFont = currentFont();
|
|
m_lineNoFont.setPixelSize(11);
|
|
|
|
return true;
|
|
}
|
|
|
|
void SourceWindow::reloadFile()
|
|
{
|
|
TQFile f(m_fileName);
|
|
if (!f.open(IO_ReadOnly)) {
|
|
// open failed; leave alone
|
|
return;
|
|
}
|
|
|
|
// read text into m_sourceCode
|
|
m_sourceCode.clear(); /* clear old text */
|
|
|
|
TQTextStream t(&f);
|
|
setText(t.read());
|
|
f.close();
|
|
|
|
m_sourceCode.resize(paragraphs());
|
|
for (size_t i = 0; i < m_sourceCode.size(); i++) {
|
|
m_sourceCode[i].code = text(i);
|
|
}
|
|
// expanded lines are collapsed: move existing line items up
|
|
for (size_t i = 0; i < m_lineItems.size(); i++) {
|
|
if (m_rowToLine[i] != i) {
|
|
m_lineItems[m_rowToLine[i]] |= m_lineItems[i];
|
|
m_lineItems[i] = 0;
|
|
}
|
|
}
|
|
// allocate line items
|
|
m_lineItems.resize(m_sourceCode.size(), 0);
|
|
|
|
m_rowToLine.resize(m_sourceCode.size());
|
|
for (size_t i = 0; i < m_sourceCode.size(); i++)
|
|
m_rowToLine[i] = i;
|
|
|
|
// Highlighting was applied above when the text was inserted into widget,
|
|
// but at that time m_rowToLine was not corrected, yet, so that lines
|
|
// that previously were assembly were painted incorrectly.
|
|
if (syntaxHighlighter())
|
|
syntaxHighlighter()->rehighlight();
|
|
update(); // line numbers
|
|
}
|
|
|
|
void SourceWindow::scrollTo(int lineNo, const DbgAddr& address)
|
|
{
|
|
if (lineNo < 0 || lineNo >= int(m_sourceCode.size()))
|
|
return;
|
|
|
|
int row = lineToRow(lineNo, address);
|
|
scrollToRow(row);
|
|
}
|
|
|
|
void SourceWindow::scrollToRow(int row)
|
|
{
|
|
setCursorPosition(row, 0);
|
|
ensureCursorVisible();
|
|
}
|
|
|
|
void SourceWindow::drawFrame(TQPainter* p)
|
|
{
|
|
TQTextEdit::drawFrame(p);
|
|
|
|
// and paragraph at the top is...
|
|
int top = paragraphAt(TQPoint(0,contentsY()));
|
|
int bot = paragraphAt(TQPoint(0,contentsY()+visibleHeight()-1));
|
|
if (bot < 0)
|
|
bot = paragraphs()-1;
|
|
|
|
p->save();
|
|
|
|
// set a clip rectangle
|
|
int fw = frameWidth();
|
|
TQRect inside = rect();
|
|
inside.addCoords(fw,fw,-fw,-fw);
|
|
TQRegion clip = p->clipRegion();
|
|
clip &= TQRegion(inside);
|
|
p->setClipRegion(clip);
|
|
|
|
p->setFont(m_lineNoFont);
|
|
p->setPen(colorGroup().text());
|
|
p->eraseRect(inside);
|
|
|
|
for (int row = top; row <= bot; row++)
|
|
{
|
|
uchar item = m_lineItems[row];
|
|
p->save();
|
|
|
|
TQRect r = paragraphRect(row);
|
|
TQPoint pt = contentsToViewport(r.topLeft());
|
|
int h = r.height();
|
|
p->translate(fw, pt.y()+viewport()->y());
|
|
|
|
if (item & liBP) {
|
|
// enabled breakpoint
|
|
int y = (h - m_brkena.height())/2;
|
|
if (y < 0) y = 0;
|
|
p->drawPixmap(0,y,m_brkena);
|
|
}
|
|
if (item & liBPdisabled) {
|
|
// disabled breakpoint
|
|
int y = (h - m_brkdis.height())/2;
|
|
if (y < 0) y = 0;
|
|
p->drawPixmap(0,y,m_brkdis);
|
|
}
|
|
if (item & liBPtemporary) {
|
|
// temporary breakpoint marker
|
|
int y = (h - m_brktmp.height())/2;
|
|
if (y < 0) y = 0;
|
|
p->drawPixmap(0,y,m_brktmp);
|
|
}
|
|
if (item & liBPconditional) {
|
|
// conditional breakpoint marker
|
|
int y = (h - m_brkcond.height())/2;
|
|
if (y < 0) y = 0;
|
|
p->drawPixmap(0,y,m_brkcond);
|
|
}
|
|
if (item & liBPorphan) {
|
|
// orphaned breakpoint marker
|
|
int y = (h - m_brkcond.height())/2;
|
|
if (y < 0) y = 0;
|
|
p->drawPixmap(0,y,m_brkorph);
|
|
}
|
|
if (item & liPC) {
|
|
// program counter in innermost frame
|
|
int y = (h - m_pcinner.height())/2;
|
|
if (y < 0) y = 0;
|
|
p->drawPixmap(0,y,m_pcinner);
|
|
}
|
|
if (item & liPCup) {
|
|
// program counter somewhere up the stack
|
|
int y = (h - m_pcup.height())/2;
|
|
if (y < 0) y = 0;
|
|
p->drawPixmap(0,y,m_pcup);
|
|
}
|
|
p->translate(m_widthItems, 0);
|
|
if (!isRowDisassCode(row) && m_sourceCode[rowToLine(row)].canDisass) {
|
|
int w = m_widthPlus;
|
|
int x = w/2;
|
|
int y = h/2;
|
|
p->drawLine(x-2, y, x+2, y);
|
|
if (!isRowExpanded(row)) {
|
|
p->drawLine(x, y-2, x, y+2);
|
|
}
|
|
}
|
|
p->translate(m_widthPlus, 0);
|
|
if (!isRowDisassCode(row)) {
|
|
p->drawText(0, 0, m_widthLineNo, h, AlignRight|AlignVCenter,
|
|
TQString().setNum(rowToLine(row)+1));
|
|
}
|
|
p->restore();
|
|
}
|
|
p->restore();
|
|
}
|
|
|
|
void SourceWindow::updateLineItems(const KDebugger* dbg)
|
|
{
|
|
// clear outdated breakpoints
|
|
for (int i = m_lineItems.size()-1; i >= 0; i--) {
|
|
if (m_lineItems[i] & liBPany) {
|
|
// check if this breakpoint still exists
|
|
int line = rowToLine(i);
|
|
TRACE(TQString("checking for bp at %1").arg(line));
|
|
KDebugger::BrkptROIterator bp = dbg->breakpointsBegin();
|
|
for (; bp != dbg->breakpointsEnd(); ++bp)
|
|
{
|
|
if (bp->lineNo == line &&
|
|
fileNameMatches(bp->fileName) &&
|
|
lineToRow(line, bp->address) == i)
|
|
{
|
|
// yes it exists; mode is changed below
|
|
break;
|
|
}
|
|
}
|
|
if (bp == dbg->breakpointsEnd()) {
|
|
/* doesn't exist anymore, remove it */
|
|
m_lineItems[i] &= ~liBPany;
|
|
update();
|
|
}
|
|
}
|
|
}
|
|
|
|
// add new breakpoints
|
|
for (KDebugger::BrkptROIterator bp = dbg->breakpointsBegin(); bp != dbg->breakpointsEnd(); ++bp)
|
|
{
|
|
if (fileNameMatches(bp->fileName)) {
|
|
TRACE(TQString("updating %1:%2").arg(bp->fileName).arg(bp->lineNo));
|
|
int i = bp->lineNo;
|
|
if (i < 0 || i >= int(m_sourceCode.size()))
|
|
continue;
|
|
// compute new line item flags for breakpoint
|
|
uchar flags = bp->enabled ? liBP : liBPdisabled;
|
|
if (bp->temporary)
|
|
flags |= liBPtemporary;
|
|
if (!bp->condition.isEmpty() || bp->ignoreCount != 0)
|
|
flags |= liBPconditional;
|
|
if (bp->isOrphaned())
|
|
flags |= liBPorphan;
|
|
// update if changed
|
|
int row = lineToRow(i, bp->address);
|
|
if ((m_lineItems[row] & liBPany) != flags) {
|
|
m_lineItems[row] &= ~liBPany;
|
|
m_lineItems[row] |= flags;
|
|
update();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void SourceWindow::setPC(bool set, int lineNo, const DbgAddr& address, int frameNo)
|
|
{
|
|
if (lineNo < 0 || lineNo >= int(m_sourceCode.size())) {
|
|
return;
|
|
}
|
|
|
|
int row = lineToRow(lineNo, address);
|
|
|
|
uchar flag = frameNo == 0 ? liPC : liPCup;
|
|
if (set) {
|
|
// set only if not already set
|
|
if ((m_lineItems[row] & flag) == 0) {
|
|
m_lineItems[row] |= flag;
|
|
update();
|
|
}
|
|
} else {
|
|
// clear only if not set
|
|
if ((m_lineItems[row] & flag) != 0) {
|
|
m_lineItems[row] &= ~flag;
|
|
update();
|
|
}
|
|
}
|
|
}
|
|
|
|
void SourceWindow::find(const TQString& text, bool caseSensitive, FindDirection dir)
|
|
{
|
|
ASSERT(dir == 1 || dir == -1);
|
|
if (TQTextEdit::find(text, caseSensitive, false, dir > 0))
|
|
return;
|
|
// not found; wrap around
|
|
int para = dir > 0 ? 0 : paragraphs(), index = 0;
|
|
TQTextEdit::find(text, caseSensitive, false, dir > 0, ¶, &index);
|
|
}
|
|
|
|
void SourceWindow::mousePressEvent(TQMouseEvent* ev)
|
|
{
|
|
// we handle left and middle button
|
|
if (ev->button() != LeftButton && ev->button() != MidButton)
|
|
{
|
|
TQTextEdit::mousePressEvent(ev);
|
|
return;
|
|
}
|
|
|
|
// get row
|
|
TQPoint p = viewportToContents(TQPoint(0, ev->y() - viewport()->y()));
|
|
int row = paragraphAt(p);
|
|
if (row < 0)
|
|
return;
|
|
|
|
if (ev->x() > m_widthItems+frameWidth())
|
|
{
|
|
if (isRowExpanded(row)) {
|
|
actionCollapseRow(row);
|
|
} else {
|
|
actionExpandRow(row);
|
|
}
|
|
return;
|
|
}
|
|
|
|
int sourceRow;
|
|
int line = rowToLine(row, &sourceRow);
|
|
|
|
// find address if row is disassembled code
|
|
DbgAddr address;
|
|
if (row > sourceRow) {
|
|
// get offset from source code line
|
|
int off = row - sourceRow;
|
|
address = m_sourceCode[line].disassAddr[off-1];
|
|
}
|
|
|
|
switch (ev->button()) {
|
|
case LeftButton:
|
|
TRACE(TQString("left-clicked line %1").arg(line));
|
|
emit clickedLeft(m_fileName, line, address,
|
|
(ev->state() & ShiftButton) != 0);
|
|
break;
|
|
case MidButton:
|
|
TRACE(TQString("mid-clicked row %1").arg(line));
|
|
emit clickedMid(m_fileName, line, address);
|
|
break;
|
|
default:;
|
|
}
|
|
}
|
|
|
|
void SourceWindow::keyPressEvent(TQKeyEvent* ev)
|
|
{
|
|
int top1, top2;
|
|
TQPoint top;
|
|
switch (ev->key()) {
|
|
case Key_Plus:
|
|
actionExpandRow(m_curRow);
|
|
return;
|
|
case Key_Minus:
|
|
actionCollapseRow(m_curRow);
|
|
return;
|
|
case Key_Up:
|
|
if (m_curRow > 0) {
|
|
setCursorPosition(m_curRow-1, 0);
|
|
}
|
|
return;
|
|
case Key_Down:
|
|
if (m_curRow < paragraphs()-1) {
|
|
setCursorPosition(m_curRow+1, 0);
|
|
}
|
|
return;
|
|
case Key_Home:
|
|
setCursorPosition(0, 0);
|
|
return;
|
|
case Key_End:
|
|
setCursorPosition(paragraphs()-1, 0);
|
|
return;
|
|
case Key_Next:
|
|
case Key_Prior:
|
|
top = viewportToContents(TQPoint(0,0));
|
|
top1 = paragraphAt(top);
|
|
}
|
|
|
|
TQTextEdit::keyPressEvent(ev);
|
|
|
|
switch (ev->key()) {
|
|
case Key_Next:
|
|
case Key_Prior:
|
|
top = viewportToContents(TQPoint(0,0));
|
|
top2 = paragraphAt(top);
|
|
setCursorPosition(m_curRow+(top2-top1), 0);
|
|
}
|
|
}
|
|
|
|
static inline bool isident(TQChar c)
|
|
{
|
|
return c.isLetterOrNumber() || c.latin1() == '_';
|
|
}
|
|
|
|
bool SourceWindow::wordAtPoint(const TQPoint& p, TQString& word, TQRect& r)
|
|
{
|
|
TQPoint pv = viewportToContents(p - viewport()->pos());
|
|
int row, col = charAt(pv, &row);
|
|
if (row < 0 || col < 0)
|
|
return false;
|
|
|
|
// isolate the word at row, col
|
|
TQString line = text(row);
|
|
if (!isident(line[col]))
|
|
return false;
|
|
|
|
int begin = col;
|
|
while (begin > 0 && isident(line[begin-1]))
|
|
--begin;
|
|
do
|
|
++col;
|
|
while (col < int(line.length()) && isident(line[col]));
|
|
|
|
r = TQRect(p, p);
|
|
r.addCoords(-5,-5,5,5);
|
|
word = line.mid(begin, col-begin);
|
|
return true;
|
|
}
|
|
|
|
void SourceWindow::paletteChange(const TQPalette& oldPal)
|
|
{
|
|
setFont(TDEGlobalSettings::fixedFont());
|
|
TQTextEdit::paletteChange(oldPal);
|
|
}
|
|
|
|
/*
|
|
* Two file names (possibly full paths) match if the last parts - the file
|
|
* names - match.
|
|
*/
|
|
bool SourceWindow::fileNameMatches(const TQString& other)
|
|
{
|
|
return TQFileInfo(other).fileName() == TQFileInfo(m_fileName).fileName();
|
|
}
|
|
|
|
void SourceWindow::disassembled(int lineNo, const std::list<DisassembledCode>& disass)
|
|
{
|
|
TRACE("disassembled line " + TQString().setNum(lineNo));
|
|
if (lineNo < 0 || lineNo >= int(m_sourceCode.size()))
|
|
return;
|
|
|
|
SourceLine& sl = m_sourceCode[lineNo];
|
|
|
|
// copy disassembled code and its addresses
|
|
sl.disass.resize(disass.size());
|
|
sl.disassAddr.resize(disass.size());
|
|
sl.canDisass = !disass.empty();
|
|
int i = 0;
|
|
for (std::list<DisassembledCode>::const_iterator c = disass.begin(); c != disass.end(); ++c, ++i)
|
|
{
|
|
TQString code = c->code;
|
|
while (code.endsWith("\n"))
|
|
code.truncate(code.length()-1);
|
|
sl.disass[i] = c->address.asString() + ' ' + code;
|
|
sl.disassAddr[i] = c->address;
|
|
}
|
|
|
|
int row = lineToRow(lineNo);
|
|
if (sl.canDisass) {
|
|
expandRow(row);
|
|
} else {
|
|
// clear expansion marker
|
|
update();
|
|
}
|
|
}
|
|
|
|
int SourceWindow::rowToLine(int row, int* sourceRow)
|
|
{
|
|
int line = row >= 0 ? m_rowToLine[row] : -1;
|
|
if (sourceRow != 0) {
|
|
// search back until we hit the first entry with the current line number
|
|
while (row > 0 && m_rowToLine[row-1] == line)
|
|
row--;
|
|
*sourceRow = row;
|
|
}
|
|
return line;
|
|
}
|
|
|
|
/*
|
|
* Rows showing diassembled code have the same line number as the
|
|
* corresponding source code line number. Therefore, the line numbers in
|
|
* m_rowToLine are monotonically increasing with blocks of equal line
|
|
* numbers for a source line and its disassembled code that follows it.
|
|
*
|
|
* Hence, m_rowToLine always obeys the following condition:
|
|
*
|
|
* m_rowToLine[i] <= i
|
|
*/
|
|
|
|
int SourceWindow::lineToRow(int line)
|
|
{
|
|
// line is zero-based!
|
|
|
|
assert(line < int(m_rowToLine.size()));
|
|
|
|
// quick test for common case
|
|
if (line < 0 || m_rowToLine[line] == line)
|
|
return line;
|
|
|
|
assert(m_rowToLine[line] < line);
|
|
|
|
/*
|
|
* Binary search between row == line and end of list. In the loop below
|
|
* we use the fact that the line numbers m_rowToLine do not contain
|
|
* holes.
|
|
*/
|
|
int l = line;
|
|
int h = m_rowToLine.size();
|
|
while (l < h && m_rowToLine[l] != line)
|
|
{
|
|
assert(h == int(m_rowToLine.size()) || m_rowToLine[l] < m_rowToLine[h]);
|
|
|
|
/*
|
|
* We want to round down the midpoint so that we find the
|
|
* lowest row that belongs to the line we seek.
|
|
*/
|
|
int mid = (l+h)/2;
|
|
if (m_rowToLine[mid] <= line)
|
|
l = mid;
|
|
else
|
|
h = mid;
|
|
}
|
|
// Found! Result is in l:
|
|
assert(m_rowToLine[l] == line);
|
|
|
|
/*
|
|
* We might not have hit the lowest index for the line.
|
|
*/
|
|
while (l > 0 && m_rowToLine[l-1] == line)
|
|
--l;
|
|
|
|
return l;
|
|
}
|
|
|
|
int SourceWindow::lineToRow(int line, const DbgAddr& address)
|
|
{
|
|
int row = lineToRow(line);
|
|
if (isRowExpanded(row)) {
|
|
row += m_sourceCode[line].findAddressRowOffset(address);
|
|
}
|
|
return row;
|
|
}
|
|
|
|
bool SourceWindow::isRowExpanded(int row)
|
|
{
|
|
assert(row >= 0);
|
|
return row < int(m_rowToLine.size())-1 &&
|
|
m_rowToLine[row] == m_rowToLine[row+1];
|
|
}
|
|
|
|
bool SourceWindow::isRowDisassCode(int row)
|
|
{
|
|
return row > 0 && row < int(m_rowToLine.size()) &&
|
|
m_rowToLine[row] == m_rowToLine[row-1];
|
|
}
|
|
|
|
void SourceWindow::expandRow(int row)
|
|
{
|
|
TRACE("expanding row " + TQString().setNum(row));
|
|
// get disassembled code
|
|
int line = rowToLine(row);
|
|
const std::vector<TQString>& disass = m_sourceCode[line].disass;
|
|
|
|
// remove PC (must be set again in slot of signal expanded())
|
|
m_lineItems[row] &= ~(liPC|liPCup);
|
|
|
|
// adjust current row
|
|
if (m_curRow > row) {
|
|
m_curRow += disass.size();
|
|
// highlight is moved automatically
|
|
}
|
|
|
|
// insert new lines
|
|
setUpdatesEnabled(false);
|
|
++row;
|
|
for (size_t i = 0; i < disass.size(); i++) {
|
|
m_rowToLine.insert(m_rowToLine.begin()+row, line);
|
|
m_lineItems.insert(m_lineItems.begin()+row, 0);
|
|
insertParagraph(disass[i], row++);
|
|
}
|
|
setUpdatesEnabled(true);
|
|
viewport()->update();
|
|
update(); // line items
|
|
|
|
emit expanded(line); /* must set PC */
|
|
}
|
|
|
|
void SourceWindow::collapseRow(int row)
|
|
{
|
|
TRACE("collapsing row " + TQString().setNum(row));
|
|
int line = rowToLine(row);
|
|
|
|
// find end of this block
|
|
int end = row+1;
|
|
while (end < int(m_rowToLine.size()) && m_rowToLine[end] == m_rowToLine[row]) {
|
|
end++;
|
|
}
|
|
++row;
|
|
// adjust current row
|
|
if (m_curRow >= row) {
|
|
m_curRow -= end-row;
|
|
if (m_curRow < row) // was m_curRow in disassembled code?
|
|
m_curRow = -1;
|
|
}
|
|
setUpdatesEnabled(false);
|
|
while (--end >= row) {
|
|
m_rowToLine.erase(m_rowToLine.begin()+end);
|
|
m_lineItems.erase(m_lineItems.begin()+end);
|
|
removeParagraph(end);
|
|
}
|
|
setUpdatesEnabled(true);
|
|
viewport()->update();
|
|
update(); // line items
|
|
|
|
emit collapsed(line);
|
|
}
|
|
|
|
void SourceWindow::activeLine(int& line, DbgAddr& address)
|
|
{
|
|
int row = m_curRow;
|
|
|
|
int sourceRow;
|
|
line = rowToLine(row, &sourceRow);
|
|
if (row > sourceRow) {
|
|
int off = row - sourceRow; /* offset from source line */
|
|
address = m_sourceCode[line].disassAddr[off-1];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the offset from the line displaying the source code to
|
|
* the line containing the specified address. If the address is not
|
|
* found, 0 is returned.
|
|
*/
|
|
int SourceWindow::SourceLine::findAddressRowOffset(const DbgAddr& address) const
|
|
{
|
|
if (address.isEmpty())
|
|
return 0;
|
|
|
|
for (size_t i = 0; i < disassAddr.size(); i++) {
|
|
if (disassAddr[i] == address) {
|
|
// found exact address
|
|
return i+1;
|
|
}
|
|
if (disassAddr[i] > address) {
|
|
/*
|
|
* We have already advanced too far; the address is before this
|
|
* index, but obviously we haven't found an exact match
|
|
* earlier. address is somewhere between the displayed
|
|
* addresses. We return the previous line.
|
|
*/
|
|
return i;
|
|
}
|
|
}
|
|
// not found
|
|
return 0;
|
|
}
|
|
|
|
void SourceWindow::actionExpandRow(int row)
|
|
{
|
|
if (row < 0 || isRowExpanded(row) || isRowDisassCode(row))
|
|
return;
|
|
|
|
// disassemble
|
|
int line = rowToLine(row);
|
|
const SourceLine& sl = m_sourceCode[line];
|
|
if (!sl.canDisass)
|
|
return;
|
|
if (sl.disass.size() == 0) {
|
|
emit disassemble(m_fileName, line);
|
|
} else {
|
|
expandRow(row);
|
|
}
|
|
}
|
|
|
|
void SourceWindow::actionCollapseRow(int row)
|
|
{
|
|
if (row < 0 || !isRowExpanded(row) || isRowDisassCode(row))
|
|
return;
|
|
|
|
collapseRow(row);
|
|
}
|
|
|
|
void SourceWindow::setTabWidth(int numChars)
|
|
{
|
|
if (numChars <= 0)
|
|
numChars = 8;
|
|
TQFontMetrics fm(currentFont());
|
|
TQString s;
|
|
int w = fm.width(s.fill('x', numChars));
|
|
setTabStopWidth(w);
|
|
}
|
|
|
|
void SourceWindow::cursorChanged(int row)
|
|
{
|
|
if (row == m_curRow)
|
|
return;
|
|
|
|
if (m_curRow >= 0 && m_curRow < paragraphs())
|
|
clearParagraphBackground(m_curRow);
|
|
m_curRow = row;
|
|
setParagraphBackgroundColor(row, colorGroup().background());
|
|
}
|
|
|
|
/*
|
|
* We must override the context menu handling because TQTextEdit's handling
|
|
* requires that it receives ownership of the popup menu; but the popup menu
|
|
* returned from the GUI factory is owned by the factory.
|
|
*/
|
|
|
|
void SourceWindow::contextMenuEvent(TQContextMenuEvent* e)
|
|
{
|
|
// get the context menu from the GUI factory
|
|
TQWidget* top = this;
|
|
do
|
|
top = top->parentWidget();
|
|
while (!top->isTopLevel());
|
|
TDEMainWindow* mw = static_cast<TDEMainWindow*>(top);
|
|
TQPopupMenu* m =
|
|
static_cast<TQPopupMenu*>(mw->factory()->container("popup_files", mw));
|
|
m->exec(e->globalPos());
|
|
}
|
|
|
|
bool SourceWindow::eventFilter(TQObject* watched, TQEvent* e)
|
|
{
|
|
if (e->type() == TQEvent::ContextMenu && watched == viewport())
|
|
{
|
|
contextMenuEvent(static_cast<TQContextMenuEvent*>(e));
|
|
return true;
|
|
}
|
|
return TQTextEdit::eventFilter(watched, e);
|
|
}
|
|
|
|
HighlightCpp::HighlightCpp(SourceWindow* srcWnd) :
|
|
TQSyntaxHighlighter(srcWnd),
|
|
m_srcWnd(srcWnd)
|
|
{
|
|
}
|
|
|
|
enum HLState {
|
|
hlCommentLine = 1,
|
|
hlCommentBlock,
|
|
hlIdent,
|
|
hlString
|
|
};
|
|
|
|
static const TQString ckw[] =
|
|
{
|
|
"and",
|
|
"and_eq",
|
|
"asm",
|
|
"auto",
|
|
"bitand",
|
|
"bitor",
|
|
"bool",
|
|
"break",
|
|
"case",
|
|
"catch",
|
|
"char",
|
|
"class",
|
|
"compl",
|
|
"const",
|
|
"const_cast",
|
|
"continue",
|
|
"default",
|
|
"delete",
|
|
"do",
|
|
"double",
|
|
"dynamic_cast",
|
|
"else",
|
|
"enum",
|
|
"explicit",
|
|
"export",
|
|
"extern",
|
|
"false",
|
|
"float",
|
|
"for",
|
|
"friend",
|
|
"goto",
|
|
"if",
|
|
"inline",
|
|
"int",
|
|
"long",
|
|
"mutable",
|
|
"namespace",
|
|
"new",
|
|
"not",
|
|
"not_eq",
|
|
"operator",
|
|
"or",
|
|
"or_eq",
|
|
"private",
|
|
"protected",
|
|
"public",
|
|
"reinterpret_cast",
|
|
"register",
|
|
"return",
|
|
"short",
|
|
"signed",
|
|
"sizeof",
|
|
"static",
|
|
"static_cast",
|
|
"struct",
|
|
"switch",
|
|
"template",
|
|
"this",
|
|
"throw",
|
|
"true",
|
|
"try",
|
|
"typedef",
|
|
"typeid",
|
|
"typename",
|
|
"using",
|
|
"union",
|
|
"unsigned",
|
|
"virtual",
|
|
"void",
|
|
"volatile",
|
|
"wchar_t",
|
|
"while",
|
|
"xor",
|
|
"xor_eq"
|
|
};
|
|
|
|
int HighlightCpp::highlightParagraph(const TQString& text, int state)
|
|
{
|
|
int row = currentParagraph();
|
|
// highlight assembly lines
|
|
if (m_srcWnd->isRowDisassCode(row))
|
|
{
|
|
setFormat(0, text.length(), blue);
|
|
return state;
|
|
}
|
|
|
|
if (state == -2) // initial state
|
|
state = 0;
|
|
|
|
// check for preprocessor line
|
|
if (state == 0 && text.stripWhiteSpace().startsWith("#"))
|
|
{
|
|
setFormat(0, text.length(), TQColor("dark green"));
|
|
return 0;
|
|
}
|
|
|
|
// a font for keywords
|
|
TQFont identFont = textEdit()->currentFont();
|
|
identFont.setBold(!identFont.bold());
|
|
|
|
unsigned start = 0;
|
|
while (start < text.length())
|
|
{
|
|
int end;
|
|
switch (state) {
|
|
case hlCommentLine:
|
|
end = text.length();
|
|
state = 0;
|
|
setFormat(start, end-start, TQColor("gray50"));
|
|
break;
|
|
case hlCommentBlock:
|
|
end = text.find("*/", start);
|
|
if (end >= 0)
|
|
end += 2, state = 0;
|
|
else
|
|
end = text.length();
|
|
setFormat(start, end-start, TQColor("gray50"));
|
|
break;
|
|
case hlString:
|
|
for (end = start+1; end < int(text.length()); end++) {
|
|
if (text[end] == '\\') {
|
|
if (end < int(text.length()))
|
|
++end;
|
|
} else if (text[end] == text[start]) {
|
|
++end;
|
|
break;
|
|
}
|
|
}
|
|
state = 0;
|
|
setFormat(start, end-start, TQColor("dark red"));
|
|
break;
|
|
case hlIdent:
|
|
for (end = start+1; end < int(text.length()); end++) {
|
|
if (!text[end].isLetterOrNumber() && text[end] != '_')
|
|
break;
|
|
}
|
|
state = 0;
|
|
if (std::binary_search(ckw, ckw + sizeof(ckw)/sizeof(ckw[0]),
|
|
text.mid(start, end-start)))
|
|
{
|
|
setFormat(start, end-start, identFont);
|
|
} else {
|
|
setFormat(start, end-start, m_srcWnd->colorGroup().text());
|
|
}
|
|
break;
|
|
default:
|
|
for (end = start; end < int(text.length()); end++)
|
|
{
|
|
if (text[end] == '/')
|
|
{
|
|
if (end+1 < int(text.length())) {
|
|
if (text[end+1] == '/') {
|
|
state = hlCommentLine;
|
|
break;
|
|
} else if (text[end+1] == '*') {
|
|
state = hlCommentBlock;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else if (text[end] == '"' || text[end] == '\'')
|
|
{
|
|
state = hlString;
|
|
break;
|
|
}
|
|
else if (text[end] >= 'A' && text[end] <= 'Z' ||
|
|
text[end] >= 'a' && text[end] <= 'z' ||
|
|
text[end] == '_')
|
|
{
|
|
state = hlIdent;
|
|
break;
|
|
}
|
|
}
|
|
setFormat(start, end-start, m_srcWnd->colorGroup().text());
|
|
}
|
|
start = end;
|
|
}
|
|
return state;
|
|
}
|
|
|
|
#include "sourcewnd.moc"
|