Optimize drawLineInfoArea()
[kdbg.git] / kdbg / sourcewnd.cpp
blob502e4dfac5c1986e680c2e5beb00e712b75b36a7
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 QPoint pt = r.topLeft();
176 int h = r.height();
177 p->save();
178 p->translate(0, r.top());
180 if (item & liBP) {
181 // enabled breakpoint
182 int y = (h - m_brkena.height())/2;
183 if (y < 0) y = 0;
184 p->drawPixmap(0,y,m_brkena);
186 if (item & liBPdisabled) {
187 // disabled breakpoint
188 int y = (h - m_brkdis.height())/2;
189 if (y < 0) y = 0;
190 p->drawPixmap(0,y,m_brkdis);
192 if (item & liBPtemporary) {
193 // temporary breakpoint marker
194 int y = (h - m_brktmp.height())/2;
195 if (y < 0) y = 0;
196 p->drawPixmap(0,y,m_brktmp);
198 if (item & liBPconditional) {
199 // conditional breakpoint marker
200 int y = (h - m_brkcond.height())/2;
201 if (y < 0) y = 0;
202 p->drawPixmap(0,y,m_brkcond);
204 if (item & liBPorphan) {
205 // orphaned breakpoint marker
206 int y = (h - m_brkcond.height())/2;
207 if (y < 0) y = 0;
208 p->drawPixmap(0,y,m_brkorph);
210 if (item & liPC) {
211 // program counter in innermost frame
212 int y = (h - m_pcinner.height())/2;
213 if (y < 0) y = 0;
214 p->drawPixmap(0,y,m_pcinner);
216 if (item & liPCup) {
217 // program counter somewhere up the stack
218 int y = (h - m_pcup.height())/2;
219 if (y < 0) y = 0;
220 p->drawPixmap(0,y,m_pcup);
222 p->translate(m_widthItems, 0);
223 if (!isRowDisassCode(row) && m_sourceCode[rowToLine(row)].canDisass) {
224 int w = m_widthPlus;
225 int x = w/2;
226 int y = h/2;
227 p->drawLine(x-2, y, x+2, y);
228 if (!isRowExpanded(row)) {
229 p->drawLine(x, y-2, x, y+2);
232 p->translate(m_widthPlus, 0);
233 if (!isRowDisassCode(row)) {
234 p->drawText(0, 0, m_widthLineNo, h, Qt::AlignRight|Qt::AlignVCenter,
235 QString().setNum(rowToLine(row)+1));
237 p->restore();
241 void SourceWindow::updateLineItems(const KDebugger* dbg)
243 // clear outdated breakpoints
244 for (int i = m_lineItems.size()-1; i >= 0; i--) {
245 if (m_lineItems[i] & liBPany) {
246 // check if this breakpoint still exists
247 int line = rowToLine(i);
248 TRACE(QString().sprintf("checking for bp at %d", line));
249 KDebugger::BrkptROIterator bp = dbg->breakpointsBegin();
250 for (; bp != dbg->breakpointsEnd(); ++bp)
252 if (bp->lineNo == line &&
253 fileNameMatches(bp->fileName) &&
254 lineToRow(line, bp->address) == i)
256 // yes it exists; mode is changed below
257 break;
260 if (bp == dbg->breakpointsEnd()) {
261 /* doesn't exist anymore, remove it */
262 m_lineItems[i] &= ~liBPany;
267 // add new breakpoints
268 for (KDebugger::BrkptROIterator bp = dbg->breakpointsBegin(); bp != dbg->breakpointsEnd(); ++bp)
270 if (fileNameMatches(bp->fileName)) {
271 TRACE(QString("updating %2:%1").arg(bp->lineNo).arg(bp->fileName));
272 int i = bp->lineNo;
273 if (i < 0 || i >= int(m_sourceCode.size()))
274 continue;
275 // compute new line item flags for breakpoint
276 uchar flags = bp->enabled ? liBP : liBPdisabled;
277 if (bp->temporary)
278 flags |= liBPtemporary;
279 if (!bp->condition.isEmpty() || bp->ignoreCount != 0)
280 flags |= liBPconditional;
281 if (bp->isOrphaned())
282 flags |= liBPorphan;
283 // update if changed
284 int row = lineToRow(i, bp->address);
285 if ((m_lineItems[row] & liBPany) != flags) {
286 m_lineItems[row] &= ~liBPany;
287 m_lineItems[row] |= flags;
291 m_lineInfoArea->update();
294 void SourceWindow::setPC(bool set, int lineNo, const DbgAddr& address, int frameNo)
296 if (lineNo < 0 || lineNo >= int(m_sourceCode.size())) {
297 return;
300 int row = lineToRow(lineNo, address);
302 uchar flag = frameNo == 0 ? liPC : liPCup;
303 if (set) {
304 // set only if not already set
305 if ((m_lineItems[row] & flag) == 0) {
306 m_lineItems[row] |= flag;
307 m_lineInfoArea->update();
309 } else {
310 // clear only if not set
311 if ((m_lineItems[row] & flag) != 0) {
312 m_lineItems[row] &= ~flag;
313 m_lineInfoArea->update();
318 void SourceWindow::find(const QString& text, bool caseSensitive, FindDirection dir)
320 ASSERT(dir == 1 || dir == -1);
321 QTextDocument::FindFlags flags = 0;
322 if (caseSensitive)
323 flags |= QTextDocument::FindCaseSensitively;
324 if (dir < 0)
325 flags |= QTextDocument::FindBackward;
326 if (QPlainTextEdit::find(text, flags))
327 return;
328 // not found; wrap around
329 QTextCursor cursor(document());
330 if (dir < 0)
331 cursor.movePosition(QTextCursor::End);
332 cursor = document()->find(text, cursor, flags);
333 if (!cursor.isNull())
334 setTextCursor(cursor);
337 void SourceWindow::infoMousePress(QMouseEvent* ev)
339 // we handle left and middle button
340 if (ev->button() != Qt::LeftButton && ev->button() != Qt::MidButton)
342 return;
345 // get row
346 int row = cursorForPosition(QPoint(0, ev->y())).blockNumber();
347 if (row < 0)
348 return;
350 if (ev->x() > m_widthItems)
352 if (isRowExpanded(row)) {
353 actionCollapseRow(row);
354 } else {
355 actionExpandRow(row);
357 return;
360 int sourceRow;
361 int line = rowToLine(row, &sourceRow);
363 // find address if row is disassembled code
364 DbgAddr address;
365 if (row > sourceRow) {
366 // get offset from source code line
367 int off = row - sourceRow;
368 address = m_sourceCode[line].disassAddr[off-1];
371 switch (ev->button()) {
372 case Qt::LeftButton:
373 TRACE(QString().sprintf("left-clicked line %d", line));
374 emit clickedLeft(m_fileName, line, address,
375 (ev->modifiers() & Qt::ShiftModifier) != 0);
376 break;
377 case Qt::MidButton:
378 TRACE(QString().sprintf("mid-clicked row %d", line));
379 emit clickedMid(m_fileName, line, address);
380 break;
381 default:;
385 void SourceWindow::keyPressEvent(QKeyEvent* ev)
387 int top1 = 0, top2;
388 switch (ev->key()) {
389 case Qt::Key_Plus:
390 actionExpandRow(textCursor().blockNumber());
391 return;
392 case Qt::Key_Minus:
393 actionCollapseRow(textCursor().blockNumber());
394 return;
395 case Qt::Key_Up:
396 case Qt::Key_K:
397 moveCursor(QTextCursor::PreviousBlock);
398 return;
399 case Qt::Key_Down:
400 case Qt::Key_J:
401 moveCursor(QTextCursor::NextBlock);
402 return;
403 case Qt::Key_Home:
404 moveCursor(QTextCursor::Start);
405 return;
406 case Qt::Key_End:
407 moveCursor(QTextCursor::End);
408 return;
409 case Qt::Key_PageUp:
410 case Qt::Key_PageDown:
411 top1 = firstVisibleBlock().blockNumber();
414 QPlainTextEdit::keyPressEvent(ev);
416 switch (ev->key()) {
417 case Qt::Key_PageUp:
418 case Qt::Key_PageDown:
419 top2 = firstVisibleBlock().blockNumber();
421 QTextCursor cursor = textCursor();
422 if (top1 < top2)
423 cursor.movePosition(QTextCursor::NextBlock,
424 QTextCursor::MoveAnchor, top2-top1);
425 else
426 cursor.movePosition(QTextCursor::PreviousBlock,
427 QTextCursor::MoveAnchor, top1-top2);
428 setTextCursor(cursor);
433 bool SourceWindow::wordAtPoint(const QPoint& p, QString& word, QRect& r)
435 QTextCursor cursor = cursorForPosition(viewport()->mapFrom(this, p));
436 if (cursor.isNull())
437 return false;
439 cursor.select(QTextCursor::WordUnderCursor);
440 word = cursor.selectedText();
442 if (word.isEmpty())
443 return false;
445 // keep only letters and digits
446 QRegExp w("[\\dA-Za-z_]+");
447 if (w.indexIn(word) < 0)
448 return false;
449 word = w.cap();
451 r = QRect(p, p);
452 r.adjust(-5,-5,5,5);
453 return true;
456 void SourceWindow::paletteChange(const QPalette& oldPal)
458 setFont(KGlobalSettings::fixedFont());
459 QPlainTextEdit::paletteChange(oldPal);
463 * Two file names (possibly full paths) match if the last parts - the file
464 * names - match.
466 bool SourceWindow::fileNameMatches(const QString& other)
468 return QFileInfo(other).fileName() == QFileInfo(m_fileName).fileName();
471 void SourceWindow::disassembled(int lineNo, const std::list<DisassembledCode>& disass)
473 TRACE("disassembled line " + QString().setNum(lineNo));
474 if (lineNo < 0 || lineNo >= int(m_sourceCode.size()))
475 return;
477 SourceLine& sl = m_sourceCode[lineNo];
479 // copy disassembled code and its addresses
480 sl.disass.resize(disass.size());
481 sl.disassAddr.resize(disass.size());
482 sl.canDisass = !disass.empty();
483 int i = 0;
484 for (std::list<DisassembledCode>::const_iterator c = disass.begin(); c != disass.end(); ++c, ++i)
486 QString code = c->code;
487 while (code.endsWith("\n"))
488 code.truncate(code.length()-1);
489 sl.disass[i] = c->address.asString() + ' ' + code;
490 sl.disassAddr[i] = c->address;
493 int row = lineToRow(lineNo);
494 if (sl.canDisass) {
495 expandRow(row);
496 } else {
497 // clear expansion marker
498 m_lineInfoArea->update();
502 int SourceWindow::rowToLine(int row, int* sourceRow)
504 int line = row >= 0 ? m_rowToLine[row] : -1;
505 if (sourceRow != 0) {
506 // search back until we hit the first entry with the current line number
507 while (row > 0 && m_rowToLine[row-1] == line)
508 row--;
509 *sourceRow = row;
511 return line;
515 * Rows showing diassembled code have the same line number as the
516 * corresponding source code line number. Therefore, the line numbers in
517 * m_rowToLine are monotonically increasing with blocks of equal line
518 * numbers for a source line and its disassembled code that follows it.
520 * Hence, m_rowToLine always obeys the following condition:
522 * m_rowToLine[i] <= i
525 int SourceWindow::lineToRow(int line)
527 // line is zero-based!
529 assert(line < int(m_rowToLine.size()));
531 // quick test for common case
532 if (line < 0 || m_rowToLine[line] == line)
533 return line;
535 assert(m_rowToLine[line] < line);
538 * Binary search between row == line and end of list. In the loop below
539 * we use the fact that the line numbers m_rowToLine do not contain
540 * holes.
542 int l = line;
543 int h = m_rowToLine.size();
544 while (l < h && m_rowToLine[l] != line)
546 assert(h == int(m_rowToLine.size()) || m_rowToLine[l] < m_rowToLine[h]);
549 * We want to round down the midpoint so that we find the
550 * lowest row that belongs to the line we seek.
552 int mid = (l+h)/2;
553 if (m_rowToLine[mid] <= line)
554 l = mid;
555 else
556 h = mid;
558 // Found! Result is in l:
559 assert(m_rowToLine[l] == line);
562 * We might not have hit the lowest index for the line.
564 while (l > 0 && m_rowToLine[l-1] == line)
565 --l;
567 return l;
570 int SourceWindow::lineToRow(int line, const DbgAddr& address)
572 int row = lineToRow(line);
573 if (isRowExpanded(row)) {
574 row += m_sourceCode[line].findAddressRowOffset(address);
576 return row;
579 bool SourceWindow::isRowExpanded(int row)
581 assert(row >= 0);
582 return row < int(m_rowToLine.size())-1 &&
583 m_rowToLine[row] == m_rowToLine[row+1];
586 bool SourceWindow::isRowDisassCode(int row)
588 return row > 0 && row < int(m_rowToLine.size()) &&
589 m_rowToLine[row] == m_rowToLine[row-1];
592 void SourceWindow::expandRow(int row)
594 TRACE("expanding row " + QString().setNum(row));
595 // get disassembled code
596 int line = rowToLine(row);
597 const std::vector<QString>& disass = m_sourceCode[line].disass;
599 // remove PC (must be set again in slot of signal expanded())
600 m_lineItems[row] &= ~(liPC|liPCup);
602 // insert new lines
603 setUpdatesEnabled(false);
604 ++row;
605 m_rowToLine.insert(m_rowToLine.begin()+row, disass.size(), line);
606 m_lineItems.insert(m_lineItems.begin()+row, disass.size(), 0);
608 QTextCursor cursor(document()->findBlockByNumber(row));
609 for (size_t i = 0; i < disass.size(); i++) {
610 cursor.insertText(disass[i]);
611 cursor.insertBlock();
613 setUpdatesEnabled(true);
615 emit expanded(line); /* must set PC */
618 void SourceWindow::collapseRow(int row)
620 TRACE("collapsing row " + QString().setNum(row));
621 int line = rowToLine(row);
623 // find end of this block
624 int end = row+1;
625 while (end < int(m_rowToLine.size()) && m_rowToLine[end] == m_rowToLine[row]) {
626 end++;
628 ++row;
629 setUpdatesEnabled(false);
630 QTextCursor cursor(document()->findBlockByNumber(end-1));
631 while (--end >= row) {
632 m_rowToLine.erase(m_rowToLine.begin()+end);
633 m_lineItems.erase(m_lineItems.begin()+end);
634 cursor.select(QTextCursor::BlockUnderCursor);
635 cursor.removeSelectedText();
637 setUpdatesEnabled(true);
639 emit collapsed(line);
642 void SourceWindow::activeLine(int& line, DbgAddr& address)
644 int row = textCursor().blockNumber();
646 int sourceRow;
647 line = rowToLine(row, &sourceRow);
648 if (row > sourceRow) {
649 int off = row - sourceRow; /* offset from source line */
650 address = m_sourceCode[line].disassAddr[off-1];
655 * Returns the offset from the line displaying the source code to
656 * the line containing the specified address. If the address is not
657 * found, 0 is returned.
659 int SourceWindow::SourceLine::findAddressRowOffset(const DbgAddr& address) const
661 if (address.isEmpty())
662 return 0;
664 for (size_t i = 0; i < disassAddr.size(); i++) {
665 if (disassAddr[i] == address) {
666 // found exact address
667 return i+1;
669 if (disassAddr[i] > address) {
671 * We have already advanced too far; the address is before this
672 * index, but obviously we haven't found an exact match
673 * earlier. address is somewhere between the displayed
674 * addresses. We return the previous line.
676 return i;
679 // not found
680 return 0;
683 void SourceWindow::actionExpandRow(int row)
685 if (row < 0 || isRowExpanded(row) || isRowDisassCode(row))
686 return;
688 // disassemble
689 int line = rowToLine(row);
690 const SourceLine& sl = m_sourceCode[line];
691 if (!sl.canDisass)
692 return;
693 if (sl.disass.size() == 0) {
694 emit disassemble(m_fileName, line);
695 } else {
696 expandRow(row);
700 void SourceWindow::actionCollapseRow(int row)
702 if (row < 0 || !isRowExpanded(row) || isRowDisassCode(row))
703 return;
705 collapseRow(row);
708 void SourceWindow::setTabWidth(int numChars)
710 if (numChars <= 0)
711 numChars = 8;
712 QFontMetrics fm(document()->defaultFont());
713 QString s;
714 int w = fm.width(s.fill('x', numChars));
715 setTabStopWidth(w);
718 void SourceWindow::cursorChanged()
720 QList<QTextEdit::ExtraSelection> extraSelections;
721 QTextEdit::ExtraSelection selection;
723 selection.format.setBackground(QColor("#E7E7E7"));
724 selection.format.setProperty(QTextFormat::FullWidthSelection, true);
725 selection.cursor = textCursor();
726 selection.cursor.clearSelection();
727 extraSelections.append(selection);
728 setExtraSelections(extraSelections);
732 * Show our own context menu.
734 void SourceWindow::contextMenuEvent(QContextMenuEvent* e)
736 // get the context menu from the GUI factory
737 QWidget* top = this;
739 top = top->parentWidget();
740 while (!top->isTopLevel());
741 KXmlGuiWindow* mw = static_cast<KXmlGuiWindow*>(top);
742 QMenu* m = static_cast<QMenu*>(mw->factory()->container("popup_files", mw));
743 if (m)
744 m->exec(e->globalPos());
747 void LineInfoArea::paintEvent(QPaintEvent* e)
749 QPainter p(this);
750 static_cast<SourceWindow*>(parent())->drawLineInfoArea(&p, e);
753 void LineInfoArea::mousePressEvent(QMouseEvent* ev)
755 static_cast<SourceWindow*>(parent())->infoMousePress(ev);
758 void LineInfoArea::contextMenuEvent(QContextMenuEvent* e)
760 static_cast<SourceWindow*>(parent())->contextMenuEvent(e);
763 HighlightCpp::HighlightCpp(SourceWindow* srcWnd) :
764 QSyntaxHighlighter(srcWnd->document()),
765 m_srcWnd(srcWnd)
769 enum HLState {
770 hlCommentLine = 1,
771 hlCommentBlock,
772 hlIdent,
773 hlString
776 static const QString ckw[] =
778 "and",
779 "and_eq",
780 "asm",
781 "auto",
782 "bitand",
783 "bitor",
784 "bool",
785 "break",
786 "case",
787 "catch",
788 "char",
789 "class",
790 "compl",
791 "const",
792 "const_cast",
793 "continue",
794 "default",
795 "delete",
796 "do",
797 "double",
798 "dynamic_cast",
799 "else",
800 "enum",
801 "explicit",
802 "export",
803 "extern",
804 "false",
805 "float",
806 "for",
807 "friend",
808 "goto",
809 "if",
810 "inline",
811 "int",
812 "long",
813 "mutable",
814 "namespace",
815 "new",
816 "not",
817 "not_eq",
818 "operator",
819 "or",
820 "or_eq",
821 "private",
822 "protected",
823 "public",
824 "reinterpret_cast",
825 "register",
826 "return",
827 "short",
828 "signed",
829 "sizeof",
830 "static",
831 "static_cast",
832 "struct",
833 "switch",
834 "template",
835 "this",
836 "throw",
837 "true",
838 "try",
839 "typedef",
840 "typeid",
841 "typename",
842 "using",
843 "union",
844 "unsigned",
845 "virtual",
846 "void",
847 "volatile",
848 "wchar_t",
849 "while",
850 "xor",
851 "xor_eq"
854 void HighlightCpp::highlightBlock(const QString& text)
856 int state = previousBlockState();
857 state = highlight(text, state);
858 setCurrentBlockState(state);
861 int HighlightCpp::highlight(const QString& text, int state)
863 // highlight assembly lines
864 if (m_srcWnd->isRowDisassCode(currentBlock().blockNumber()))
866 setFormat(0, text.length(), Qt::blue);
867 return state;
870 if (state == -2) // initial state
871 state = 0;
873 // check for preprocessor line
874 if (state == 0 && text.trimmed().startsWith("#"))
876 setFormat(0, text.length(), QColor("darkgreen"));
877 return 0;
880 // a font for keywords
881 QTextCharFormat identFont;
882 identFont.setFontWeight(QFont::Bold);
884 int start = 0;
885 while (start < text.length())
887 int end;
888 switch (state) {
889 case hlCommentLine:
890 end = text.length();
891 state = 0;
892 setFormat(start, end-start, QColor("gray"));
893 break;
894 case hlCommentBlock:
895 end = text.indexOf("*/", start);
896 if (end >= 0)
897 end += 2, state = 0;
898 else
899 end = text.length();
900 setFormat(start, end-start, QColor("gray"));
901 break;
902 case hlString:
903 for (end = start+1; end < int(text.length()); end++) {
904 if (text[end] == '\\') {
905 if (end < int(text.length()))
906 ++end;
907 } else if (text[end] == text[start]) {
908 ++end;
909 break;
912 state = 0;
913 setFormat(start, end-start, QColor("darkred"));
914 break;
915 case hlIdent:
916 for (end = start+1; end < int(text.length()); end++) {
917 if (!text[end].isLetterOrNumber() && text[end] != '_')
918 break;
920 state = 0;
921 if (std::binary_search(ckw, ckw + sizeof(ckw)/sizeof(ckw[0]),
922 text.mid(start, end-start)))
924 setFormat(start, end-start, identFont);
925 } else {
926 setFormat(start, end-start, m_srcWnd->palette().color(QPalette::WindowText));
928 break;
929 default:
930 for (end = start; end < int(text.length()); end++)
932 if (text[end] == '/')
934 if (end+1 < int(text.length())) {
935 if (text[end+1] == '/') {
936 state = hlCommentLine;
937 break;
938 } else if (text[end+1] == '*') {
939 state = hlCommentBlock;
940 break;
944 else if (text[end] == '"' || text[end] == '\'')
946 state = hlString;
947 break;
949 else if ((text[end] >= 'A' && text[end] <= 'Z') ||
950 (text[end] >= 'a' && text[end] <= 'z') ||
951 text[end] == '_')
953 state = hlIdent;
954 break;
957 setFormat(start, end-start, m_srcWnd->palette().color(QPalette::WindowText));
959 start = end;
961 return state;
964 #include "sourcewnd.moc"