KDbg 2.5.6.
[kdbg.git] / kdbg / sourcewnd.cpp
blob08e414b6a7e710e14e2fdd465f6406d5bbf56bc0
1 /*
2 * Copyright Johannes Sixt
3 * This file is licensed under the GNU General Public License Version 2.
4 * See the file COPYING in the toplevel directory of the source directory.
5 */
7 #include "debugger.h"
8 #include "sourcewnd.h"
9 #include "dbgdriver.h"
10 #include <QTextStream>
11 #include <QPainter>
12 #include <QFile>
13 #include <QFileInfo>
14 #include <QMenu>
15 #include <QContextMenuEvent>
16 #include <QKeyEvent>
17 #include <QMouseEvent>
18 #include <kiconloader.h>
19 #include <kglobalsettings.h>
20 #include <kxmlguiwindow.h>
21 #include <kxmlguifactory.h>
22 #include <algorithm>
23 #include "mydebug.h"
26 SourceWindow::SourceWindow(const QString& fileName, QWidget* parent) :
27 QPlainTextEdit(parent),
28 m_fileName(fileName),
29 m_highlighter(0),
30 m_widthItems(16),
31 m_widthPlus(12),
32 m_widthLineNo(30),
33 m_lineInfoArea(new LineInfoArea(this))
35 // load pixmaps
36 m_pcinner = UserIcon("pcinner");
37 m_pcup = UserIcon("pcup");
38 m_brkena = UserIcon("brkena");
39 m_brkdis = UserIcon("brkdis");
40 m_brktmp = UserIcon("brktmp");
41 m_brkcond = UserIcon("brkcond");
42 m_brkorph = UserIcon("brkorph");
43 setFont(KGlobalSettings::fixedFont());
44 setReadOnly(true);
45 setViewportMargins(lineInfoAreaWidth(), 0, 0 ,0);
46 setWordWrapMode(QTextOption::NoWrap);
47 connect(this, SIGNAL(updateRequest(const QRect&, int)),
48 m_lineInfoArea, SLOT(update()));
49 connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(cursorChanged()));
51 // add a syntax highlighter
52 if (QRegExp("\\.(c(pp|c|\\+\\+)?|CC?|h(\\+\\+|h|pp)?|HH?)$").indexIn(m_fileName) >= 0)
54 m_highlighter = new HighlightCpp(this);
58 SourceWindow::~SourceWindow()
60 delete m_highlighter;
63 int SourceWindow::lineInfoAreaWidth() const
65 return 3 + m_widthItems + m_widthPlus + m_widthLineNo;
68 bool SourceWindow::loadFile()
70 QFile f(m_fileName);
71 if (!f.open(QIODevice::ReadOnly)) {
72 return false;
75 QTextStream t(&f);
76 setPlainText(t.readAll());
77 f.close();
79 int n = blockCount();
80 m_sourceCode.resize(n);
81 m_rowToLine.resize(n);
82 for (int i = 0; i < n; i++) {
83 m_rowToLine[i] = i;
85 m_lineItems.resize(n, 0);
87 // set a font for line numbers
88 m_lineNoFont = currentCharFormat().font();
89 m_lineNoFont.setPixelSize(11);
91 return true;
94 void SourceWindow::reloadFile()
96 QFile f(m_fileName);
97 if (!f.open(QIODevice::ReadOnly)) {
98 // open failed; leave alone
99 return;
102 m_sourceCode.clear(); /* clear old text */
104 QTextStream t(&f);
105 setPlainText(t.readAll());
106 f.close();
108 m_sourceCode.resize(blockCount());
109 // expanded lines are collapsed: move existing line items up
110 for (size_t i = 0; i < m_lineItems.size(); i++) {
111 if (m_rowToLine[i] != int(i)) {
112 m_lineItems[m_rowToLine[i]] |= m_lineItems[i];
113 m_lineItems[i] = 0;
116 // allocate line items
117 m_lineItems.resize(m_sourceCode.size(), 0);
119 m_rowToLine.resize(m_sourceCode.size());
120 for (size_t i = 0; i < m_sourceCode.size(); i++)
121 m_rowToLine[i] = i;
123 // Highlighting was applied above when the text was inserted into widget,
124 // but at that time m_rowToLine was not corrected, yet, so that lines
125 // that previously were assembly were painted incorrectly.
126 if (m_highlighter)
127 m_highlighter->rehighlight();
130 void SourceWindow::scrollTo(int lineNo, const DbgAddr& address)
132 if (lineNo < 0 || lineNo >= int(m_sourceCode.size()))
133 return;
135 int row = lineToRow(lineNo, address);
136 scrollToRow(row);
139 void SourceWindow::scrollToRow(int row)
141 QTextCursor cursor(document()->findBlockByNumber(row));
142 setTextCursor(cursor);
143 ensureCursorVisible();
146 void SourceWindow::resizeEvent(QResizeEvent* e)
148 QPlainTextEdit::resizeEvent(e);
150 QRect cr = contentsRect();
151 cr.setRight(lineInfoAreaWidth());
152 m_lineInfoArea->setGeometry(cr);
155 void SourceWindow::drawLineInfoArea(QPainter* p, QPaintEvent* event)
157 QTextBlock block = firstVisibleBlock();
159 p->setFont(m_lineNoFont);
161 for (; block.isValid(); block = block.next())
163 if (!block.isVisible())
164 continue;
166 QRect r = blockBoundingGeometry(block).translated(contentOffset()).toRect();
167 if (r.bottom() < event->rect().top())
168 continue; // skip blocks that are higher than the region being updated
169 else if (r.top() > event->rect().bottom())
170 break; // all the following blocks are lower then the region being updated
172 int row = block.blockNumber();
173 uchar item = m_lineItems[row];
175 int h = r.height();
176 p->save();
177 p->translate(0, r.top());
179 if (item & liBP) {
180 // enabled breakpoint
181 int y = (h - m_brkena.height())/2;
182 if (y < 0) y = 0;
183 p->drawPixmap(0,y,m_brkena);
185 if (item & liBPdisabled) {
186 // disabled breakpoint
187 int y = (h - m_brkdis.height())/2;
188 if (y < 0) y = 0;
189 p->drawPixmap(0,y,m_brkdis);
191 if (item & liBPtemporary) {
192 // temporary breakpoint marker
193 int y = (h - m_brktmp.height())/2;
194 if (y < 0) y = 0;
195 p->drawPixmap(0,y,m_brktmp);
197 if (item & liBPconditional) {
198 // conditional breakpoint marker
199 int y = (h - m_brkcond.height())/2;
200 if (y < 0) y = 0;
201 p->drawPixmap(0,y,m_brkcond);
203 if (item & liBPorphan) {
204 // orphaned breakpoint marker
205 int y = (h - m_brkcond.height())/2;
206 if (y < 0) y = 0;
207 p->drawPixmap(0,y,m_brkorph);
209 if (item & liPC) {
210 // program counter in innermost frame
211 int y = (h - m_pcinner.height())/2;
212 if (y < 0) y = 0;
213 p->drawPixmap(0,y,m_pcinner);
215 if (item & liPCup) {
216 // program counter somewhere up the stack
217 int y = (h - m_pcup.height())/2;
218 if (y < 0) y = 0;
219 p->drawPixmap(0,y,m_pcup);
221 p->translate(m_widthItems, 0);
222 if (!isRowDisassCode(row) && m_sourceCode[rowToLine(row)].canDisass) {
223 int w = m_widthPlus;
224 int x = w/2;
225 int y = h/2;
226 p->drawLine(x-2, y, x+2, y);
227 if (!isRowExpanded(row)) {
228 p->drawLine(x, y-2, x, y+2);
231 p->translate(m_widthPlus, 0);
232 if (!isRowDisassCode(row)) {
233 p->drawText(0, 0, m_widthLineNo, h, Qt::AlignRight|Qt::AlignVCenter,
234 QString().setNum(rowToLine(row)+1));
236 p->restore();
240 void SourceWindow::updateLineItems(const KDebugger* dbg)
242 // clear outdated breakpoints
243 for (int i = m_lineItems.size()-1; i >= 0; i--) {
244 if (m_lineItems[i] & liBPany) {
245 // check if this breakpoint still exists
246 int line = rowToLine(i);
247 TRACE(QString().sprintf("checking for bp at %d", line));
248 KDebugger::BrkptROIterator bp = dbg->breakpointsBegin();
249 for (; bp != dbg->breakpointsEnd(); ++bp)
251 if (bp->lineNo == line &&
252 fileNameMatches(bp->fileName) &&
253 lineToRow(line, bp->address) == i)
255 // yes it exists; mode is changed below
256 break;
259 if (bp == dbg->breakpointsEnd()) {
260 /* doesn't exist anymore, remove it */
261 m_lineItems[i] &= ~liBPany;
266 // add new breakpoints
267 for (KDebugger::BrkptROIterator bp = dbg->breakpointsBegin(); bp != dbg->breakpointsEnd(); ++bp)
269 if (fileNameMatches(bp->fileName)) {
270 TRACE(QString("updating %2:%1").arg(bp->lineNo).arg(bp->fileName));
271 int i = bp->lineNo;
272 if (i < 0 || i >= int(m_sourceCode.size()))
273 continue;
274 // compute new line item flags for breakpoint
275 uchar flags = bp->enabled ? liBP : liBPdisabled;
276 if (bp->temporary)
277 flags |= liBPtemporary;
278 if (!bp->condition.isEmpty() || bp->ignoreCount != 0)
279 flags |= liBPconditional;
280 if (bp->isOrphaned())
281 flags |= liBPorphan;
282 // update if changed
283 int row = lineToRow(i, bp->address);
284 if ((m_lineItems[row] & liBPany) != flags) {
285 m_lineItems[row] &= ~liBPany;
286 m_lineItems[row] |= flags;
290 m_lineInfoArea->update();
293 void SourceWindow::setPC(bool set, int lineNo, const DbgAddr& address, int frameNo)
295 if (lineNo < 0 || lineNo >= int(m_sourceCode.size())) {
296 return;
299 int row = lineToRow(lineNo, address);
301 uchar flag = frameNo == 0 ? liPC : liPCup;
302 if (set) {
303 // set only if not already set
304 if ((m_lineItems[row] & flag) == 0) {
305 m_lineItems[row] |= flag;
306 m_lineInfoArea->update();
308 } else {
309 // clear only if not set
310 if ((m_lineItems[row] & flag) != 0) {
311 m_lineItems[row] &= ~flag;
312 m_lineInfoArea->update();
317 void SourceWindow::find(const QString& text, bool caseSensitive, FindDirection dir)
319 ASSERT(dir == 1 || dir == -1);
320 QTextDocument::FindFlags flags = 0;
321 if (caseSensitive)
322 flags |= QTextDocument::FindCaseSensitively;
323 if (dir < 0)
324 flags |= QTextDocument::FindBackward;
325 if (QPlainTextEdit::find(text, flags))
326 return;
327 // not found; wrap around
328 QTextCursor cursor(document());
329 if (dir < 0)
330 cursor.movePosition(QTextCursor::End);
331 cursor = document()->find(text, cursor, flags);
332 if (!cursor.isNull())
333 setTextCursor(cursor);
336 void SourceWindow::infoMousePress(QMouseEvent* ev)
338 // we handle left and middle button
339 if (ev->button() != Qt::LeftButton && ev->button() != Qt::MidButton)
341 return;
344 // get row
345 int row = cursorForPosition(QPoint(0, ev->y())).blockNumber();
346 if (row < 0)
347 return;
349 if (ev->x() > m_widthItems)
351 if (isRowExpanded(row)) {
352 actionCollapseRow(row);
353 } else {
354 actionExpandRow(row);
356 return;
359 int sourceRow;
360 int line = rowToLine(row, &sourceRow);
362 // find address if row is disassembled code
363 DbgAddr address;
364 if (row > sourceRow) {
365 // get offset from source code line
366 int off = row - sourceRow;
367 address = m_sourceCode[line].disassAddr[off-1];
370 switch (ev->button()) {
371 case Qt::LeftButton:
372 TRACE(QString().sprintf("left-clicked line %d", line));
373 emit clickedLeft(m_fileName, line, address,
374 (ev->modifiers() & Qt::ShiftModifier) != 0);
375 break;
376 case Qt::MidButton:
377 TRACE(QString().sprintf("mid-clicked row %d", line));
378 emit clickedMid(m_fileName, line, address);
379 break;
380 default:;
384 void SourceWindow::keyPressEvent(QKeyEvent* ev)
386 int top1 = 0, top2;
387 switch (ev->key()) {
388 case Qt::Key_Plus:
389 actionExpandRow(textCursor().blockNumber());
390 return;
391 case Qt::Key_Minus:
392 actionCollapseRow(textCursor().blockNumber());
393 return;
394 case Qt::Key_Up:
395 case Qt::Key_K:
396 moveCursor(QTextCursor::PreviousBlock);
397 return;
398 case Qt::Key_Down:
399 case Qt::Key_J:
400 moveCursor(QTextCursor::NextBlock);
401 return;
402 case Qt::Key_Home:
403 moveCursor(QTextCursor::Start);
404 return;
405 case Qt::Key_End:
406 moveCursor(QTextCursor::End);
407 return;
408 case Qt::Key_PageUp:
409 case Qt::Key_PageDown:
410 top1 = firstVisibleBlock().blockNumber();
413 QPlainTextEdit::keyPressEvent(ev);
415 switch (ev->key()) {
416 case Qt::Key_PageUp:
417 case Qt::Key_PageDown:
418 top2 = firstVisibleBlock().blockNumber();
420 QTextCursor cursor = textCursor();
421 if (top1 < top2)
422 cursor.movePosition(QTextCursor::NextBlock,
423 QTextCursor::MoveAnchor, top2-top1);
424 else
425 cursor.movePosition(QTextCursor::PreviousBlock,
426 QTextCursor::MoveAnchor, top1-top2);
427 setTextCursor(cursor);
432 bool SourceWindow::wordAtPoint(const QPoint& p, QString& word, QRect& r)
434 QTextCursor cursor = cursorForPosition(viewport()->mapFrom(this, p));
435 if (cursor.isNull())
436 return false;
438 cursor.select(QTextCursor::WordUnderCursor);
439 word = cursor.selectedText();
441 if (word.isEmpty())
442 return false;
444 // keep only letters and digits
445 QRegExp w("[\\dA-Za-z_]+");
446 if (w.indexIn(word) < 0)
447 return false;
448 word = w.cap();
450 r = QRect(p, p);
451 r.adjust(-5,-5,5,5);
452 return true;
455 void SourceWindow::paletteChange(const QPalette& oldPal)
457 setFont(KGlobalSettings::fixedFont());
458 QPlainTextEdit::paletteChange(oldPal);
462 * Two file names (possibly full paths) match if the last parts - the file
463 * names - match.
465 bool SourceWindow::fileNameMatches(const QString& other)
467 return QFileInfo(other).fileName() == QFileInfo(m_fileName).fileName();
470 void SourceWindow::disassembled(int lineNo, const std::list<DisassembledCode>& disass)
472 TRACE("disassembled line " + QString().setNum(lineNo));
473 if (lineNo < 0 || lineNo >= int(m_sourceCode.size()))
474 return;
476 SourceLine& sl = m_sourceCode[lineNo];
478 // copy disassembled code and its addresses
479 sl.disass.resize(disass.size());
480 sl.disassAddr.resize(disass.size());
481 sl.canDisass = !disass.empty();
482 int i = 0;
483 for (std::list<DisassembledCode>::const_iterator c = disass.begin(); c != disass.end(); ++c, ++i)
485 QString code = c->code;
486 while (code.endsWith("\n"))
487 code.truncate(code.length()-1);
488 sl.disass[i] = c->address.asString() + ' ' + code;
489 sl.disassAddr[i] = c->address;
492 int row = lineToRow(lineNo);
493 if (sl.canDisass) {
494 expandRow(row);
495 } else {
496 // clear expansion marker
497 m_lineInfoArea->update();
501 int SourceWindow::rowToLine(int row, int* sourceRow)
503 int line = row >= 0 ? m_rowToLine[row] : -1;
504 if (sourceRow != 0) {
505 // search back until we hit the first entry with the current line number
506 while (row > 0 && m_rowToLine[row-1] == line)
507 row--;
508 *sourceRow = row;
510 return line;
514 * Rows showing diassembled code have the same line number as the
515 * corresponding source code line number. Therefore, the line numbers in
516 * m_rowToLine are monotonically increasing with blocks of equal line
517 * numbers for a source line and its disassembled code that follows it.
519 * Hence, m_rowToLine always obeys the following condition:
521 * m_rowToLine[i] <= i
524 int SourceWindow::lineToRow(int line)
526 // line is zero-based!
528 assert(line < int(m_rowToLine.size()));
530 // quick test for common case
531 if (line < 0 || m_rowToLine[line] == line)
532 return line;
534 assert(m_rowToLine[line] < line);
537 * Binary search between row == line and end of list. In the loop below
538 * we use the fact that the line numbers m_rowToLine do not contain
539 * holes.
541 int l = line;
542 int h = m_rowToLine.size();
543 while (l < h && m_rowToLine[l] != line)
545 assert(h == int(m_rowToLine.size()) || m_rowToLine[l] < m_rowToLine[h]);
548 * We want to round down the midpoint so that we find the
549 * lowest row that belongs to the line we seek.
551 int mid = (l+h)/2;
552 if (m_rowToLine[mid] <= line)
553 l = mid;
554 else
555 h = mid;
557 // Found! Result is in l:
558 assert(m_rowToLine[l] == line);
561 * We might not have hit the lowest index for the line.
563 while (l > 0 && m_rowToLine[l-1] == line)
564 --l;
566 return l;
569 int SourceWindow::lineToRow(int line, const DbgAddr& address)
571 int row = lineToRow(line);
572 if (isRowExpanded(row)) {
573 row += m_sourceCode[line].findAddressRowOffset(address);
575 return row;
578 bool SourceWindow::isRowExpanded(int row)
580 assert(row >= 0);
581 return row < int(m_rowToLine.size())-1 &&
582 m_rowToLine[row] == m_rowToLine[row+1];
585 bool SourceWindow::isRowDisassCode(int row)
587 return row > 0 && row < int(m_rowToLine.size()) &&
588 m_rowToLine[row] == m_rowToLine[row-1];
591 void SourceWindow::expandRow(int row)
593 TRACE("expanding row " + QString().setNum(row));
594 // get disassembled code
595 int line = rowToLine(row);
596 const std::vector<QString>& disass = m_sourceCode[line].disass;
598 // remove PC (must be set again in slot of signal expanded())
599 m_lineItems[row] &= ~(liPC|liPCup);
601 // insert new lines
602 setUpdatesEnabled(false);
603 ++row;
604 m_rowToLine.insert(m_rowToLine.begin()+row, disass.size(), line);
605 m_lineItems.insert(m_lineItems.begin()+row, disass.size(), 0);
607 QTextCursor cursor(document()->findBlockByNumber(row));
608 for (size_t i = 0; i < disass.size(); i++) {
609 cursor.insertText(disass[i]);
610 cursor.insertBlock();
612 setUpdatesEnabled(true);
614 emit expanded(line); /* must set PC */
617 void SourceWindow::collapseRow(int row)
619 TRACE("collapsing row " + QString().setNum(row));
620 int line = rowToLine(row);
622 // find end of this block
623 int end = row+1;
624 while (end < int(m_rowToLine.size()) && m_rowToLine[end] == m_rowToLine[row]) {
625 end++;
627 ++row;
628 setUpdatesEnabled(false);
629 QTextCursor cursor(document()->findBlockByNumber(end-1));
630 while (--end >= row) {
631 m_rowToLine.erase(m_rowToLine.begin()+end);
632 m_lineItems.erase(m_lineItems.begin()+end);
633 cursor.select(QTextCursor::BlockUnderCursor);
634 cursor.removeSelectedText();
636 setUpdatesEnabled(true);
638 emit collapsed(line);
641 void SourceWindow::activeLine(int& line, DbgAddr& address)
643 int row = textCursor().blockNumber();
645 int sourceRow;
646 line = rowToLine(row, &sourceRow);
647 if (row > sourceRow) {
648 int off = row - sourceRow; /* offset from source line */
649 address = m_sourceCode[line].disassAddr[off-1];
654 * Returns the offset from the line displaying the source code to
655 * the line containing the specified address. If the address is not
656 * found, 0 is returned.
658 int SourceWindow::SourceLine::findAddressRowOffset(const DbgAddr& address) const
660 if (address.isEmpty())
661 return 0;
663 for (size_t i = 0; i < disassAddr.size(); i++) {
664 if (disassAddr[i] == address) {
665 // found exact address
666 return i+1;
668 if (disassAddr[i] > address) {
670 * We have already advanced too far; the address is before this
671 * index, but obviously we haven't found an exact match
672 * earlier. address is somewhere between the displayed
673 * addresses. We return the previous line.
675 return i;
678 // not found
679 return 0;
682 void SourceWindow::actionExpandRow(int row)
684 if (row < 0 || isRowExpanded(row) || isRowDisassCode(row))
685 return;
687 // disassemble
688 int line = rowToLine(row);
689 const SourceLine& sl = m_sourceCode[line];
690 if (!sl.canDisass)
691 return;
692 if (sl.disass.size() == 0) {
693 emit disassemble(m_fileName, line);
694 } else {
695 expandRow(row);
699 void SourceWindow::actionCollapseRow(int row)
701 if (row < 0 || !isRowExpanded(row) || isRowDisassCode(row))
702 return;
704 collapseRow(row);
707 void SourceWindow::setTabWidth(int numChars)
709 if (numChars <= 0)
710 numChars = 8;
711 QFontMetrics fm(document()->defaultFont());
712 QString s;
713 int w = fm.width(s.fill('x', numChars));
714 setTabStopWidth(w);
717 void SourceWindow::cursorChanged()
719 QList<QTextEdit::ExtraSelection> extraSelections;
720 QTextEdit::ExtraSelection selection;
722 selection.format.setBackground(QColor("#E7E7E7"));
723 selection.format.setProperty(QTextFormat::FullWidthSelection, true);
724 selection.cursor = textCursor();
725 selection.cursor.clearSelection();
726 extraSelections.append(selection);
727 setExtraSelections(extraSelections);
731 * Show our own context menu.
733 void SourceWindow::contextMenuEvent(QContextMenuEvent* e)
735 // get the context menu from the GUI factory
736 QWidget* top = this;
738 top = top->parentWidget();
739 while (!top->isTopLevel());
740 KXmlGuiWindow* mw = static_cast<KXmlGuiWindow*>(top);
741 QMenu* m = static_cast<QMenu*>(mw->factory()->container("popup_files", mw));
742 if (m)
743 m->exec(e->globalPos());
746 void LineInfoArea::paintEvent(QPaintEvent* e)
748 QPainter p(this);
749 static_cast<SourceWindow*>(parent())->drawLineInfoArea(&p, e);
752 void LineInfoArea::mousePressEvent(QMouseEvent* ev)
754 static_cast<SourceWindow*>(parent())->infoMousePress(ev);
757 void LineInfoArea::contextMenuEvent(QContextMenuEvent* e)
759 static_cast<SourceWindow*>(parent())->contextMenuEvent(e);
762 HighlightCpp::HighlightCpp(SourceWindow* srcWnd) :
763 QSyntaxHighlighter(srcWnd->document()),
764 m_srcWnd(srcWnd)
768 enum HLState {
769 hlCommentLine = 1,
770 hlCommentBlock,
771 hlIdent,
772 hlString
775 static const QString ckw[] =
777 "and",
778 "and_eq",
779 "asm",
780 "auto",
781 "bitand",
782 "bitor",
783 "bool",
784 "break",
785 "case",
786 "catch",
787 "char",
788 "class",
789 "compl",
790 "const",
791 "const_cast",
792 "continue",
793 "default",
794 "delete",
795 "do",
796 "double",
797 "dynamic_cast",
798 "else",
799 "enum",
800 "explicit",
801 "export",
802 "extern",
803 "false",
804 "float",
805 "for",
806 "friend",
807 "goto",
808 "if",
809 "inline",
810 "int",
811 "long",
812 "mutable",
813 "namespace",
814 "new",
815 "not",
816 "not_eq",
817 "operator",
818 "or",
819 "or_eq",
820 "private",
821 "protected",
822 "public",
823 "register",
824 "reinterpret_cast",
825 "return",
826 "short",
827 "signed",
828 "sizeof",
829 "static",
830 "static_cast",
831 "struct",
832 "switch",
833 "template",
834 "this",
835 "throw",
836 "true",
837 "try",
838 "typedef",
839 "typeid",
840 "typename",
841 "union",
842 "unsigned",
843 "using",
844 "virtual",
845 "void",
846 "volatile",
847 "wchar_t",
848 "while",
849 "xor",
850 "xor_eq"
853 void HighlightCpp::highlightBlock(const QString& text)
855 int state = previousBlockState();
856 state = highlight(text, state);
857 setCurrentBlockState(state);
860 int HighlightCpp::highlight(const QString& text, int state)
862 // highlight assembly lines
863 if (m_srcWnd->isRowDisassCode(currentBlock().blockNumber()))
865 setFormat(0, text.length(), Qt::blue);
866 return state;
869 if (state == -2) // initial state
870 state = 0;
872 // check for preprocessor line
873 if (state == 0 && text.trimmed().startsWith("#"))
875 setFormat(0, text.length(), QColor("darkgreen"));
876 return 0;
879 // a font for keywords
880 QTextCharFormat identFont;
881 identFont.setFontWeight(QFont::Bold);
883 int start = 0;
884 while (start < text.length())
886 int end;
887 switch (state) {
888 case hlCommentLine:
889 end = text.length();
890 state = 0;
891 setFormat(start, end-start, QColor("gray"));
892 break;
893 case hlCommentBlock:
894 end = text.indexOf("*/", start);
895 if (end >= 0)
896 end += 2, state = 0;
897 else
898 end = text.length();
899 setFormat(start, end-start, QColor("gray"));
900 break;
901 case hlString:
902 for (end = start+1; end < int(text.length()); end++) {
903 if (text[end] == '\\') {
904 if (end < int(text.length()))
905 ++end;
906 } else if (text[end] == text[start]) {
907 ++end;
908 break;
911 state = 0;
912 setFormat(start, end-start, QColor("darkred"));
913 break;
914 case hlIdent:
915 for (end = start+1; end < int(text.length()); end++) {
916 if (!text[end].isLetterOrNumber() && text[end] != '_')
917 break;
919 state = 0;
920 if (std::binary_search(ckw, ckw + sizeof(ckw)/sizeof(ckw[0]),
921 text.mid(start, end-start)))
923 setFormat(start, end-start, identFont);
924 } else {
925 setFormat(start, end-start, m_srcWnd->palette().color(QPalette::WindowText));
927 break;
928 default:
929 for (end = start; end < int(text.length()); end++)
931 if (text[end] == '/')
933 if (end+1 < int(text.length())) {
934 if (text[end+1] == '/') {
935 state = hlCommentLine;
936 break;
937 } else if (text[end+1] == '*') {
938 state = hlCommentBlock;
939 break;
943 else if (text[end] == '"' || text[end] == '\'')
945 state = hlString;
946 break;
948 else if ((text[end] >= 'A' && text[end] <= 'Z') ||
949 (text[end] >= 'a' && text[end] <= 'z') ||
950 text[end] == '_')
952 state = hlIdent;
953 break;
956 setFormat(start, end-start, m_srcWnd->palette().color(QPalette::WindowText));
958 start = end;
960 return state;
963 #include "sourcewnd.moc"