Speed up display of disassembly.
[kdbg.git] / kdbg / sourcewnd.cpp
blob97995ee2ee58b2b73504585cca7a3949870e9f5a
1 // $Id$
3 // Copyright by Johannes Sixt
4 // This file is under GPL, the GNU General Public Licence
6 #include "debugger.h"
7 #include "sourcewnd.h"
8 #include <qtextstream.h>
9 #include <qpainter.h>
10 #include <qbrush.h>
11 #include <qfile.h>
12 #include <qkeycode.h>
13 #include <qpopupmenu.h>
14 #include <kapp.h>
15 #include <kiconloader.h>
16 #include <kglobalsettings.h>
17 #include <kmainwindow.h>
18 #include <algorithm>
19 #ifdef HAVE_CONFIG_H
20 #include "config.h"
21 #endif
22 #include "mydebug.h"
25 SourceWindow::SourceWindow(const char* fileName, QWidget* parent, const char* name) :
26 QTextEdit(parent, name),
27 m_fileName(fileName),
28 m_curRow(-1),
29 m_widthItems(16),
30 m_widthPlus(12)
32 // load pixmaps
33 m_pcinner = UserIcon("pcinner");
34 m_pcup = UserIcon("pcup");
35 m_brkena = UserIcon("brkena");
36 m_brkdis = UserIcon("brkdis");
37 m_brktmp = UserIcon("brktmp");
38 m_brkcond = UserIcon("brkcond");
39 m_brkorph = UserIcon("brkorph");
40 setFont(KGlobalSettings::fixedFont());
41 setReadOnly(true);
42 setMargins(m_widthItems+m_widthPlus, 0, 0 ,0);
43 setAutoFormatting(AutoNone);
44 setTextFormat(PlainText);
45 setWordWrap(NoWrap);
46 connect(verticalScrollBar(), SIGNAL(valueChanged(int)),
47 this, SLOT(update()));
48 connect(this, SIGNAL(cursorPositionChanged(int,int)), this, SLOT(cursorChanged(int)));
49 viewport()->installEventFilter(this);
51 // add a syntax highlighter
52 if (QRegExp("\\.(c(pp|c|\\+\\+)?|CC?|h(\\+\\+|h)?|HH?)$").search(m_fileName))
54 new HighlightCpp(this);
58 SourceWindow::~SourceWindow()
60 delete syntaxHighlighter();
63 bool SourceWindow::loadFile()
65 // first we load the code into QTextEdit
66 QFile f(m_fileName);
67 if (!f.open(IO_ReadOnly)) {
68 return false;
71 QTextStream t(&f);
72 setText(t.read());
73 f.close();
75 // then we copy it into our own m_sourceCode
76 int n = paragraphs();
77 m_sourceCode.resize(n);
78 m_rowToLine.resize(n);
79 for (int i = 0; i < n; i++) {
80 m_sourceCode[i].code = text(i);
81 m_rowToLine[i] = i;
83 m_lineItems.resize(n, 0);
85 return true;
88 void SourceWindow::reloadFile()
90 QFile f(m_fileName);
91 if (!f.open(IO_ReadOnly)) {
92 // open failed; leave alone
93 return;
96 // read text into m_sourceCode
97 m_sourceCode.clear(); /* clear old text */
99 QTextStream t(&f);
100 setText(t.read());
101 f.close();
103 m_sourceCode.resize(paragraphs());
104 for (size_t i = 0; i < m_sourceCode.size(); i++) {
105 m_sourceCode[i].code = text(i);
107 // allocate line items
108 m_lineItems.resize(m_sourceCode.size(), 0);
110 m_rowToLine.resize(m_sourceCode.size());
111 for (size_t i = 0; i < m_sourceCode.size(); i++)
112 m_rowToLine[i] = i;
115 void SourceWindow::scrollTo(int lineNo, const DbgAddr& address)
117 if (lineNo < 0 || lineNo >= int(m_sourceCode.size()))
118 return;
120 int row = lineToRow(lineNo, address);
121 scrollToRow(row);
124 void SourceWindow::scrollToRow(int row)
126 setCursorPosition(row, 0);
127 ensureCursorVisible();
130 void SourceWindow::drawFrame(QPainter* p)
132 QTextEdit::drawFrame(p);
134 // and paragraph at the top is...
135 int top = paragraphAt(QPoint(0,contentsY()));
136 int bot = paragraphAt(QPoint(0,contentsY()+visibleHeight()-1));
137 if (bot < 0)
138 bot = paragraphs()-1;
140 p->save();
142 // set a clip rectangle
143 int fw = frameWidth();
144 QRect inside = rect();
145 inside.addCoords(fw,fw,-fw,-fw);
146 QRegion clip = p->clipRegion();
147 clip &= QRegion(inside);
148 p->setClipRegion(clip);
150 p->setPen(colorGroup().text());
151 p->eraseRect(inside);
153 for (int row = top; row <= bot; row++)
155 uchar item = m_lineItems[row];
156 p->save();
158 QRect r = paragraphRect(row);
159 QPoint pt = contentsToViewport(r.topLeft());
160 int h = r.height();
161 p->translate(fw, pt.y()+viewport()->y());
163 if (item & liBP) {
164 // enabled breakpoint
165 int y = (h - m_brkena.height())/2;
166 if (y < 0) y = 0;
167 p->drawPixmap(0,y,m_brkena);
169 if (item & liBPdisabled) {
170 // disabled breakpoint
171 int y = (h - m_brkdis.height())/2;
172 if (y < 0) y = 0;
173 p->drawPixmap(0,y,m_brkdis);
175 if (item & liBPtemporary) {
176 // temporary breakpoint marker
177 int y = (h - m_brktmp.height())/2;
178 if (y < 0) y = 0;
179 p->drawPixmap(0,y,m_brktmp);
181 if (item & liBPconditional) {
182 // conditional breakpoint marker
183 int y = (h - m_brkcond.height())/2;
184 if (y < 0) y = 0;
185 p->drawPixmap(0,y,m_brkcond);
187 if (item & liBPorphan) {
188 // orphaned breakpoint marker
189 int y = (h - m_brkcond.height())/2;
190 if (y < 0) y = 0;
191 p->drawPixmap(0,y,m_brkorph);
193 if (item & liPC) {
194 // program counter in innermost frame
195 int y = (h - m_pcinner.height())/2;
196 if (y < 0) y = 0;
197 p->drawPixmap(0,y,m_pcinner);
199 if (item & liPCup) {
200 // program counter somewhere up the stack
201 int y = (h - m_pcup.height())/2;
202 if (y < 0) y = 0;
203 p->drawPixmap(0,y,m_pcup);
205 if (!isRowDisassCode(row) && m_sourceCode[rowToLine(row)].canDisass) {
206 int w = m_widthPlus;
207 p->translate(m_widthItems, 0);
208 int x = w/2;
209 int y = h/2;
210 p->drawLine(x-2, y, x+2, y);
211 if (!isRowExpanded(row)) {
212 p->drawLine(x, y-2, x, y+2);
215 p->restore();
217 p->restore();
220 void SourceWindow::updateLineItems(const KDebugger* dbg)
222 // clear outdated breakpoints
223 for (int i = m_lineItems.size()-1; i >= 0; i--) {
224 if (m_lineItems[i] & liBPany) {
225 // check if this breakpoint still exists
226 int line = rowToLine(i);
227 TRACE(QString().sprintf("checking for bp at %d", line));
228 int j;
229 for (j = dbg->numBreakpoints()-1; j >= 0; j--) {
230 const Breakpoint* bp = dbg->breakpoint(j);
231 if (bp->lineNo == line &&
232 fileNameMatches(bp->fileName) &&
233 lineToRow(line, bp->address) == i)
235 // yes it exists; mode is changed below
236 break;
239 if (j < 0) {
240 /* doesn't exist anymore, remove it */
241 m_lineItems[i] &= ~liBPany;
242 update();
247 // add new breakpoints
248 for (int j = dbg->numBreakpoints()-1; j >= 0; j--) {
249 const Breakpoint* bp = dbg->breakpoint(j);
250 if (fileNameMatches(bp->fileName)) {
251 TRACE(QString().sprintf("updating %s:%d", bp->fileName.data(), bp->lineNo));
252 int i = bp->lineNo;
253 if (i < 0 || i >= int(m_sourceCode.size()))
254 continue;
255 // compute new line item flags for breakpoint
256 uchar flags = bp->enabled ? liBP : liBPdisabled;
257 if (bp->temporary)
258 flags |= liBPtemporary;
259 if (!bp->condition.isEmpty() || bp->ignoreCount != 0)
260 flags |= liBPconditional;
261 if (bp->isOrphaned())
262 flags |= liBPorphan;
263 // update if changed
264 int row = lineToRow(i, bp->address);
265 if ((m_lineItems[row] & liBPany) != flags) {
266 m_lineItems[row] &= ~liBPany;
267 m_lineItems[row] |= flags;
268 update();
274 void SourceWindow::setPC(bool set, int lineNo, const DbgAddr& address, int frameNo)
276 if (lineNo < 0 || lineNo >= int(m_sourceCode.size())) {
277 return;
280 int row = lineToRow(lineNo, address);
282 uchar flag = frameNo == 0 ? liPC : liPCup;
283 if (set) {
284 // set only if not already set
285 if ((m_lineItems[row] & flag) == 0) {
286 m_lineItems[row] |= flag;
287 update();
289 } else {
290 // clear only if not set
291 if ((m_lineItems[row] & flag) != 0) {
292 m_lineItems[row] &= ~flag;
293 update();
298 void SourceWindow::find(const QString& text, bool caseSensitive, FindDirection dir)
300 ASSERT(dir == 1 || dir == -1);
301 if (m_sourceCode.size() == 0 || text.isEmpty())
302 return;
304 int line;
305 DbgAddr dummyAddr;
306 activeLine(line, dummyAddr);
307 if (line < 0)
308 line = 0;
309 int curLine = line; /* remember where we started */
310 bool found = false;
311 do {
312 // advance and wrap around
313 line += dir;
314 if (line < 0)
315 line = m_sourceCode.size()-1;
316 else if (line >= int(m_sourceCode.size()))
317 line = 0;
318 // search text
319 found = m_sourceCode[line].code.find(text, 0, caseSensitive) >= 0;
320 } while (!found && line != curLine);
322 scrollTo(line, DbgAddr());
325 void SourceWindow::mousePressEvent(QMouseEvent* ev)
327 // we handle left and middle button
328 if (ev->button() != LeftButton && ev->button() != MidButton)
330 QTextEdit::mousePressEvent(ev);
331 return;
334 // get row
335 QPoint p = viewportToContents(QPoint(0, ev->y() - viewport()->y()));
336 int row = paragraphAt(p);
337 if (row < 0)
338 return;
340 if (ev->x() > m_widthItems+frameWidth())
342 if (isRowExpanded(row)) {
343 actionCollapseRow(row);
344 } else {
345 actionExpandRow(row);
347 return;
350 int sourceRow;
351 int line = rowToLine(row, &sourceRow);
353 // find address if row is disassembled code
354 DbgAddr address;
355 if (row > sourceRow) {
356 // get offset from source code line
357 int off = row - sourceRow;
358 address = m_sourceCode[line].disassAddr[off-1];
361 switch (ev->button()) {
362 case LeftButton:
363 TRACE(QString().sprintf("left-clicked line %d", line));
364 emit clickedLeft(m_fileName, line, address,
365 (ev->state() & ShiftButton) != 0);
366 break;
367 case MidButton:
368 TRACE(QString().sprintf("mid-clicked row %d", line));
369 emit clickedMid(m_fileName, line, address);
370 break;
371 default:;
375 void SourceWindow::keyPressEvent(QKeyEvent* ev)
377 int top1, top2;
378 QPoint top;
379 switch (ev->key()) {
380 case Key_Plus:
381 actionExpandRow(m_curRow);
382 return;
383 case Key_Minus:
384 actionCollapseRow(m_curRow);
385 return;
386 case Key_Up:
387 if (m_curRow > 0) {
388 setCursorPosition(m_curRow-1, 0);
390 return;
391 case Key_Down:
392 if (m_curRow < paragraphs()-1) {
393 setCursorPosition(m_curRow+1, 0);
395 return;
396 case Key_Home:
397 setCursorPosition(0, 0);
398 return;
399 case Key_End:
400 setCursorPosition(paragraphs()-1, 0);
401 return;
402 case Key_Next:
403 case Key_Prior:
404 top = viewportToContents(QPoint(0,0));
405 top1 = paragraphAt(top);
408 QTextEdit::keyPressEvent(ev);
410 switch (ev->key()) {
411 case Key_Next:
412 case Key_Prior:
413 top = viewportToContents(QPoint(0,0));
414 top2 = paragraphAt(top);
415 setCursorPosition(m_curRow+(top2-top1), 0);
419 static inline bool isident(QChar c)
421 return c.isLetterOrNumber() || c.latin1() == '_';
424 bool SourceWindow::wordAtPoint(const QPoint& p, QString& word, QRect& r)
426 QPoint pv = viewportToContents(p - viewport()->pos());
427 int row, col = charAt(pv, &row);
428 if (row < 0 || col < 0)
429 return false;
431 // isolate the word at row, col
432 QString line = text(row);
433 if (!isident(line[col]))
434 return false;
436 int begin = col;
437 while (begin > 0 && isident(line[begin-1]))
438 --begin;
440 ++col;
441 while (col < int(line.length()) && isident(line[col]));
443 r = QRect(p, p);
444 r.addCoords(-5,-5,5,5);
445 word = line.mid(begin, col-begin);
446 return true;
449 void SourceWindow::paletteChange(const QPalette& oldPal)
451 setFont(KGlobalSettings::fixedFont());
452 QTextEdit::paletteChange(oldPal);
456 * Two file names (possibly full paths) match if the last parts - the file
457 * names - match.
459 bool SourceWindow::fileNameMatches(const QString& other)
461 const QString& me = fileName();
463 // check for null file names first
464 if (me.isNull() || other.isNull()) {
465 return me.isNull() && other.isNull();
469 * Get file names. Note: Either there is a slash, then skip it, or
470 * there is no slash, then -1 + 1 = 0!
472 int sme = me.findRev('/') + 1;
473 int sother = other.findRev('/') + 1;
474 return strcmp(me.data() + sme, other.data() + sother) == 0;
477 void SourceWindow::disassembled(int lineNo, const QList<DisassembledCode>& disass)
479 TRACE("disassembled line " + QString().setNum(lineNo));
480 if (lineNo < 0 || lineNo >= int(m_sourceCode.size()))
481 return;
483 SourceLine& sl = m_sourceCode[lineNo];
485 // copy disassembled code and its addresses
486 sl.disass.resize(disass.count());
487 sl.disassAddr.resize(disass.count());
488 sl.canDisass = disass.count() > 0;
489 for (uint i = 0; i < disass.count(); i++) {
490 const DisassembledCode* c =
491 const_cast<QList<DisassembledCode>&>(disass).at(i);
492 QString code = c->code;
493 while (code.endsWith("\n"))
494 code.truncate(code.length()-1);
495 sl.disass[i] = c->address.asString() + ' ' + code;
496 sl.disassAddr[i] = c->address;
499 int row = lineToRow(lineNo);
500 if (sl.canDisass) {
501 expandRow(row);
502 } else {
503 // clear expansion marker
504 update();
508 int SourceWindow::rowToLine(int row, int* sourceRow)
510 int line = row >= 0 ? m_rowToLine[row] : -1;
511 if (sourceRow != 0) {
512 // search back until we hit the first entry with the current line number
513 while (row > 0 && m_rowToLine[row-1] == line)
514 row--;
515 *sourceRow = row;
517 return line;
521 * Rows showing diassembled code have the same line number as the
522 * corresponding source code line number. Therefore, the line numbers in
523 * m_rowToLine are monotonically increasing with blocks of equal line
524 * numbers for a source line and its disassembled code that follows it.
526 * Hence, m_rowToLine always obeys the following condition:
528 * m_rowToLine[i] <= i
531 int SourceWindow::lineToRow(int line)
533 // line is zero-based!
535 assert(line < int(m_rowToLine.size()));
537 // quick test for common case
538 if (line < 0 || m_rowToLine[line] == line)
539 return line;
541 assert(m_rowToLine[line] < line);
544 * Binary search between row == line and end of list. In the loop below
545 * we use the fact that the line numbers m_rowToLine do not contain
546 * holes.
548 int l = line;
549 int h = m_rowToLine.size();
550 while (l < h && m_rowToLine[l] != line)
552 assert(h == int(m_rowToLine.size()) || m_rowToLine[l] < m_rowToLine[h]);
555 * We want to round down the midpoint so that we find the
556 * lowest row that belongs to the line we seek.
558 int mid = (l+h)/2;
559 if (m_rowToLine[mid] <= line)
560 l = mid;
561 else
562 h = mid;
564 // Found! Result is in l:
565 assert(m_rowToLine[l] == line);
568 * We might not have hit the lowest index for the line.
570 while (l > 0 && m_rowToLine[l-1] == line)
571 --l;
573 return l;
576 int SourceWindow::lineToRow(int line, const DbgAddr& address)
578 int row = lineToRow(line);
579 if (isRowExpanded(row)) {
580 row += m_sourceCode[line].findAddressRowOffset(address);
582 return row;
585 bool SourceWindow::isRowExpanded(int row)
587 assert(row >= 0);
588 return row < int(m_rowToLine.size())-1 &&
589 m_rowToLine[row] == m_rowToLine[row+1];
592 bool SourceWindow::isRowDisassCode(int row)
594 return row > 0 && row < int(m_rowToLine.size()) &&
595 m_rowToLine[row] == m_rowToLine[row-1];
598 void SourceWindow::expandRow(int row)
600 TRACE("expanding row " + QString().setNum(row));
601 // get disassembled code
602 int line = rowToLine(row);
603 const std::vector<QString>& disass = m_sourceCode[line].disass;
605 // remove PC (must be set again in slot of signal expanded())
606 m_lineItems[row] &= ~(liPC|liPCup);
608 // adjust current row
609 if (m_curRow > row) {
610 m_curRow += disass.size();
611 // highlight is moved automatically
614 // insert new lines
615 setUpdatesEnabled(false);
616 ++row;
617 for (size_t i = 0; i < disass.size(); i++) {
618 m_rowToLine.insert(m_rowToLine.begin()+row, line);
619 m_lineItems.insert(m_lineItems.begin()+row, 0);
620 insertParagraph(disass[i], row++);
622 setUpdatesEnabled(true);
623 viewport()->update();
624 update(); // line items
626 emit expanded(line); /* must set PC */
629 void SourceWindow::collapseRow(int row)
631 TRACE("collapsing row " + QString().setNum(row));
632 int line = rowToLine(row);
634 // find end of this block
635 int end = row+1;
636 while (end < int(m_rowToLine.size()) && m_rowToLine[end] == m_rowToLine[row]) {
637 end++;
639 ++row;
640 // adjust current row
641 if (m_curRow >= row) {
642 m_curRow -= end-row;
643 if (m_curRow < row) // was m_curRow in disassembled code?
644 m_curRow = -1;
646 setUpdatesEnabled(false);
647 while (--end >= row) {
648 m_rowToLine.erase(m_rowToLine.begin()+end);
649 m_lineItems.erase(m_lineItems.begin()+end);
650 removeParagraph(end);
652 setUpdatesEnabled(true);
653 viewport()->update();
654 update(); // line items
656 emit collapsed(line);
659 void SourceWindow::activeLine(int& line, DbgAddr& address)
661 int row = m_curRow;
663 int sourceRow;
664 line = rowToLine(row, &sourceRow);
665 if (row > sourceRow) {
666 int off = row - sourceRow; /* offset from source line */
667 address = m_sourceCode[line].disassAddr[off-1];
672 * Returns the offset from the line displaying the source code to
673 * the line containing the specified address. If the address is not
674 * found, 0 is returned.
676 int SourceWindow::SourceLine::findAddressRowOffset(const DbgAddr& address) const
678 if (address.isEmpty())
679 return 0;
681 for (size_t i = 0; i < disassAddr.size(); i++) {
682 if (disassAddr[i] == address) {
683 // found exact address
684 return i+1;
686 if (disassAddr[i] > address) {
688 * We have already advanced too far; the address is before this
689 * index, but obviously we haven't found an exact match
690 * earlier. address is somewhere between the displayed
691 * addresses. We return the previous line.
693 return i;
696 // not found
697 return 0;
700 void SourceWindow::actionExpandRow(int row)
702 if (row < 0 || isRowExpanded(row) || isRowDisassCode(row))
703 return;
705 // disassemble
706 int line = rowToLine(row);
707 const SourceLine& sl = m_sourceCode[line];
708 if (!sl.canDisass)
709 return;
710 if (sl.disass.size() == 0) {
711 emit disassemble(m_fileName, line);
712 } else {
713 expandRow(row);
717 void SourceWindow::actionCollapseRow(int row)
719 if (row < 0 || !isRowExpanded(row) || isRowDisassCode(row))
720 return;
722 collapseRow(row);
725 void SourceWindow::setTabWidth(int numChars)
727 if (numChars <= 0)
728 numChars = 8;
729 QFontMetrics fm(currentFont());
730 QString s;
731 int w = fm.width(s.fill('x', numChars));
732 setTabStopWidth(w);
735 void SourceWindow::cursorChanged(int row)
737 if (row == m_curRow)
738 return;
740 if (m_curRow >= 0 && m_curRow < paragraphs())
741 clearParagraphBackground(m_curRow);
742 m_curRow = row;
743 setParagraphBackgroundColor(row, colorGroup().background());
745 emit lineChanged();
749 * We must override the context menu handling because QTextEdit's handling
750 * requires that it receives ownership of the popup menu; but the popup menu
751 * returned from the GUI factory is owned by the factory.
754 void SourceWindow::contextMenuEvent(QContextMenuEvent* e)
756 // get the context menu from the GUI factory
757 QWidget* top = this;
759 top = top->parentWidget();
760 while (!top->isTopLevel());
761 KMainWindow* mw = static_cast<KMainWindow*>(top);
762 QPopupMenu* m =
763 static_cast<QPopupMenu*>(mw->factory()->container("popup_files", mw));
764 m->exec(e->globalPos());
767 bool SourceWindow::eventFilter(QObject* watched, QEvent* e)
769 if (e->type() == QEvent::ContextMenu && watched == viewport())
771 contextMenuEvent(static_cast<QContextMenuEvent*>(e));
772 return true;
774 return QTextEdit::eventFilter(watched, e);
777 HighlightCpp::HighlightCpp(SourceWindow* srcWnd) :
778 QSyntaxHighlighter(srcWnd),
779 m_srcWnd(srcWnd)
783 enum HLState {
784 hlCommentLine = 1,
785 hlCommentBlock,
786 hlIdent,
787 hlString
790 static const QString ckw[] =
792 "and",
793 "and_eq",
794 "asm",
795 "auto",
796 "bitand",
797 "bitor",
798 "bool",
799 "break",
800 "case",
801 "catch",
802 "char",
803 "class",
804 "compl",
805 "const",
806 "const_cast",
807 "continue",
808 "default",
809 "delete",
810 "do",
811 "double",
812 "dynamic_cast",
813 "else",
814 "enum",
815 "explicit",
816 "export",
817 "extern",
818 "false",
819 "float",
820 "for",
821 "friend",
822 "goto",
823 "if",
824 "inline",
825 "int",
826 "long",
827 "mutable",
828 "namespace",
829 "new",
830 "not",
831 "not_eq",
832 "operator",
833 "or",
834 "or_eq",
835 "private",
836 "protected",
837 "public",
838 "reinterpret_cast",
839 "register",
840 "return",
841 "short",
842 "signed",
843 "sizeof",
844 "static",
845 "static_cast",
846 "struct",
847 "switch",
848 "template",
849 "this",
850 "throw",
851 "true",
852 "try",
853 "typedef",
854 "typeid",
855 "typename",
856 "using",
857 "union",
858 "unsigned",
859 "virtual",
860 "void",
861 "volatile",
862 "wchar_t",
863 "while",
864 "xor",
865 "xor_eq"
868 int HighlightCpp::highlightParagraph(const QString& text, int state)
870 int row = currentParagraph();
871 // highlight assembly lines
872 if (m_srcWnd->isRowDisassCode(row))
874 setFormat(0, text.length(), blue);
875 return state;
878 if (state == -2) // initial state
879 state = 0;
881 // check for preprocessor line
882 if (state == 0 && text.stripWhiteSpace().startsWith("#"))
884 setFormat(0, text.length(), QColor("dark green"));
885 return 0;
888 // a font for keywords
889 QFont identFont = textEdit()->currentFont();
890 identFont.setBold(!identFont.bold());
892 unsigned start = 0;
893 while (start < text.length())
895 int end;
896 switch (state) {
897 case hlCommentLine:
898 end = text.length();
899 state = 0;
900 setFormat(start, end-start, QColor("gray50"));
901 break;
902 case hlCommentBlock:
903 end = text.find("*/", start);
904 if (end >= 0)
905 end += 2, state = 0;
906 else
907 end = text.length();
908 setFormat(start, end-start, QColor("gray50"));
909 break;
910 case hlString:
911 for (end = start+1; end < int(text.length()); end++) {
912 if (text[end] == '\\') {
913 if (end < int(text.length()))
914 ++end;
915 } else if (text[end] == text[start]) {
916 ++end;
917 break;
920 state = 0;
921 setFormat(start, end-start, QColor("dark red"));
922 break;
923 case hlIdent:
924 for (end = start+1; end < int(text.length()); end++) {
925 if (!text[end].isLetterOrNumber() && text[end] != '_')
926 break;
928 state = 0;
929 if (std::binary_search(ckw, ckw + sizeof(ckw)/sizeof(ckw[0]),
930 text.mid(start, end-start)))
932 setFormat(start, end-start, identFont);
933 } else {
934 setFormat(start, end-start, m_srcWnd->colorGroup().text());
936 break;
937 default:
938 for (end = start; end < int(text.length()); end++)
940 if (text[end] == '/')
942 if (end+1 < int(text.length())) {
943 if (text[end+1] == '/') {
944 state = hlCommentLine;
945 break;
946 } else if (text[end+1] == '*') {
947 state = hlCommentBlock;
948 break;
952 else if (text[end] == '"' || text[end] == '\'')
954 state = hlString;
955 break;
957 else if (text[end] >= 'A' && text[end] <= 'Z' ||
958 text[end] >= 'a' && text[end] <= 'z' ||
959 text[end] == '_')
961 state = hlIdent;
962 break;
965 setFormat(start, end-start, m_srcWnd->colorGroup().text());
967 start = end;
969 return state;
972 #include "sourcewnd.moc"