Merge branch 'maint'
[kdbg.git] / kdbg / sourcewnd.cpp
blobed52bfe167d08ab45fa00911758e40d93461b4cb
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 (QTextEdit::find(text, caseSensitive, false, dir > 0))
302 return;
303 // not found; wrap around
304 int para = dir > 0 ? 0 : paragraphs(), index = 0;
305 QTextEdit::find(text, caseSensitive, false, dir > 0, &para, &index);
308 void SourceWindow::mousePressEvent(QMouseEvent* ev)
310 // we handle left and middle button
311 if (ev->button() != LeftButton && ev->button() != MidButton)
313 QTextEdit::mousePressEvent(ev);
314 return;
317 // get row
318 QPoint p = viewportToContents(QPoint(0, ev->y() - viewport()->y()));
319 int row = paragraphAt(p);
320 if (row < 0)
321 return;
323 if (ev->x() > m_widthItems+frameWidth())
325 if (isRowExpanded(row)) {
326 actionCollapseRow(row);
327 } else {
328 actionExpandRow(row);
330 return;
333 int sourceRow;
334 int line = rowToLine(row, &sourceRow);
336 // find address if row is disassembled code
337 DbgAddr address;
338 if (row > sourceRow) {
339 // get offset from source code line
340 int off = row - sourceRow;
341 address = m_sourceCode[line].disassAddr[off-1];
344 switch (ev->button()) {
345 case LeftButton:
346 TRACE(QString().sprintf("left-clicked line %d", line));
347 emit clickedLeft(m_fileName, line, address,
348 (ev->state() & ShiftButton) != 0);
349 break;
350 case MidButton:
351 TRACE(QString().sprintf("mid-clicked row %d", line));
352 emit clickedMid(m_fileName, line, address);
353 break;
354 default:;
358 void SourceWindow::keyPressEvent(QKeyEvent* ev)
360 int top1, top2;
361 QPoint top;
362 switch (ev->key()) {
363 case Key_Plus:
364 actionExpandRow(m_curRow);
365 return;
366 case Key_Minus:
367 actionCollapseRow(m_curRow);
368 return;
369 case Key_Up:
370 if (m_curRow > 0) {
371 setCursorPosition(m_curRow-1, 0);
373 return;
374 case Key_Down:
375 if (m_curRow < paragraphs()-1) {
376 setCursorPosition(m_curRow+1, 0);
378 return;
379 case Key_Home:
380 setCursorPosition(0, 0);
381 return;
382 case Key_End:
383 setCursorPosition(paragraphs()-1, 0);
384 return;
385 case Key_Next:
386 case Key_Prior:
387 top = viewportToContents(QPoint(0,0));
388 top1 = paragraphAt(top);
391 QTextEdit::keyPressEvent(ev);
393 switch (ev->key()) {
394 case Key_Next:
395 case Key_Prior:
396 top = viewportToContents(QPoint(0,0));
397 top2 = paragraphAt(top);
398 setCursorPosition(m_curRow+(top2-top1), 0);
402 static inline bool isident(QChar c)
404 return c.isLetterOrNumber() || c.latin1() == '_';
407 bool SourceWindow::wordAtPoint(const QPoint& p, QString& word, QRect& r)
409 QPoint pv = viewportToContents(p - viewport()->pos());
410 int row, col = charAt(pv, &row);
411 if (row < 0 || col < 0)
412 return false;
414 // isolate the word at row, col
415 QString line = text(row);
416 if (!isident(line[col]))
417 return false;
419 int begin = col;
420 while (begin > 0 && isident(line[begin-1]))
421 --begin;
423 ++col;
424 while (col < int(line.length()) && isident(line[col]));
426 r = QRect(p, p);
427 r.addCoords(-5,-5,5,5);
428 word = line.mid(begin, col-begin);
429 return true;
432 void SourceWindow::paletteChange(const QPalette& oldPal)
434 setFont(KGlobalSettings::fixedFont());
435 QTextEdit::paletteChange(oldPal);
439 * Two file names (possibly full paths) match if the last parts - the file
440 * names - match.
442 bool SourceWindow::fileNameMatches(const QString& other)
444 const QString& me = fileName();
446 // check for null file names first
447 if (me.isNull() || other.isNull()) {
448 return me.isNull() && other.isNull();
452 * Get file names. Note: Either there is a slash, then skip it, or
453 * there is no slash, then -1 + 1 = 0!
455 int sme = me.findRev('/') + 1;
456 int sother = other.findRev('/') + 1;
457 return strcmp(me.data() + sme, other.data() + sother) == 0;
460 void SourceWindow::disassembled(int lineNo, const QList<DisassembledCode>& disass)
462 TRACE("disassembled line " + QString().setNum(lineNo));
463 if (lineNo < 0 || lineNo >= int(m_sourceCode.size()))
464 return;
466 SourceLine& sl = m_sourceCode[lineNo];
468 // copy disassembled code and its addresses
469 sl.disass.resize(disass.count());
470 sl.disassAddr.resize(disass.count());
471 sl.canDisass = disass.count() > 0;
472 for (uint i = 0; i < disass.count(); i++) {
473 const DisassembledCode* c =
474 const_cast<QList<DisassembledCode>&>(disass).at(i);
475 QString code = c->code;
476 while (code.endsWith("\n"))
477 code.truncate(code.length()-1);
478 sl.disass[i] = c->address.asString() + ' ' + code;
479 sl.disassAddr[i] = c->address;
482 int row = lineToRow(lineNo);
483 if (sl.canDisass) {
484 expandRow(row);
485 } else {
486 // clear expansion marker
487 update();
491 int SourceWindow::rowToLine(int row, int* sourceRow)
493 int line = row >= 0 ? m_rowToLine[row] : -1;
494 if (sourceRow != 0) {
495 // search back until we hit the first entry with the current line number
496 while (row > 0 && m_rowToLine[row-1] == line)
497 row--;
498 *sourceRow = row;
500 return line;
504 * Rows showing diassembled code have the same line number as the
505 * corresponding source code line number. Therefore, the line numbers in
506 * m_rowToLine are monotonically increasing with blocks of equal line
507 * numbers for a source line and its disassembled code that follows it.
509 * Hence, m_rowToLine always obeys the following condition:
511 * m_rowToLine[i] <= i
514 int SourceWindow::lineToRow(int line)
516 // line is zero-based!
518 assert(line < int(m_rowToLine.size()));
520 // quick test for common case
521 if (line < 0 || m_rowToLine[line] == line)
522 return line;
524 assert(m_rowToLine[line] < line);
527 * Binary search between row == line and end of list. In the loop below
528 * we use the fact that the line numbers m_rowToLine do not contain
529 * holes.
531 int l = line;
532 int h = m_rowToLine.size();
533 while (l < h && m_rowToLine[l] != line)
535 assert(h == int(m_rowToLine.size()) || m_rowToLine[l] < m_rowToLine[h]);
538 * We want to round down the midpoint so that we find the
539 * lowest row that belongs to the line we seek.
541 int mid = (l+h)/2;
542 if (m_rowToLine[mid] <= line)
543 l = mid;
544 else
545 h = mid;
547 // Found! Result is in l:
548 assert(m_rowToLine[l] == line);
551 * We might not have hit the lowest index for the line.
553 while (l > 0 && m_rowToLine[l-1] == line)
554 --l;
556 return l;
559 int SourceWindow::lineToRow(int line, const DbgAddr& address)
561 int row = lineToRow(line);
562 if (isRowExpanded(row)) {
563 row += m_sourceCode[line].findAddressRowOffset(address);
565 return row;
568 bool SourceWindow::isRowExpanded(int row)
570 assert(row >= 0);
571 return row < int(m_rowToLine.size())-1 &&
572 m_rowToLine[row] == m_rowToLine[row+1];
575 bool SourceWindow::isRowDisassCode(int row)
577 return row > 0 && row < int(m_rowToLine.size()) &&
578 m_rowToLine[row] == m_rowToLine[row-1];
581 void SourceWindow::expandRow(int row)
583 TRACE("expanding row " + QString().setNum(row));
584 // get disassembled code
585 int line = rowToLine(row);
586 const std::vector<QString>& disass = m_sourceCode[line].disass;
588 // remove PC (must be set again in slot of signal expanded())
589 m_lineItems[row] &= ~(liPC|liPCup);
591 // adjust current row
592 if (m_curRow > row) {
593 m_curRow += disass.size();
594 // highlight is moved automatically
597 // insert new lines
598 setUpdatesEnabled(false);
599 ++row;
600 for (size_t i = 0; i < disass.size(); i++) {
601 m_rowToLine.insert(m_rowToLine.begin()+row, line);
602 m_lineItems.insert(m_lineItems.begin()+row, 0);
603 insertParagraph(disass[i], row++);
605 setUpdatesEnabled(true);
606 viewport()->update();
607 update(); // line items
609 emit expanded(line); /* must set PC */
612 void SourceWindow::collapseRow(int row)
614 TRACE("collapsing row " + QString().setNum(row));
615 int line = rowToLine(row);
617 // find end of this block
618 int end = row+1;
619 while (end < int(m_rowToLine.size()) && m_rowToLine[end] == m_rowToLine[row]) {
620 end++;
622 ++row;
623 // adjust current row
624 if (m_curRow >= row) {
625 m_curRow -= end-row;
626 if (m_curRow < row) // was m_curRow in disassembled code?
627 m_curRow = -1;
629 setUpdatesEnabled(false);
630 while (--end >= row) {
631 m_rowToLine.erase(m_rowToLine.begin()+end);
632 m_lineItems.erase(m_lineItems.begin()+end);
633 removeParagraph(end);
635 setUpdatesEnabled(true);
636 viewport()->update();
637 update(); // line items
639 emit collapsed(line);
642 void SourceWindow::activeLine(int& line, DbgAddr& address)
644 int row = m_curRow;
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(currentFont());
713 QString s;
714 int w = fm.width(s.fill('x', numChars));
715 setTabStopWidth(w);
718 void SourceWindow::cursorChanged(int row)
720 if (row == m_curRow)
721 return;
723 if (m_curRow >= 0 && m_curRow < paragraphs())
724 clearParagraphBackground(m_curRow);
725 m_curRow = row;
726 setParagraphBackgroundColor(row, colorGroup().background());
728 emit lineChanged();
732 * We must override the context menu handling because QTextEdit's handling
733 * requires that it receives ownership of the popup menu; but the popup menu
734 * returned from the GUI factory is owned by the factory.
737 void SourceWindow::contextMenuEvent(QContextMenuEvent* e)
739 // get the context menu from the GUI factory
740 QWidget* top = this;
742 top = top->parentWidget();
743 while (!top->isTopLevel());
744 KMainWindow* mw = static_cast<KMainWindow*>(top);
745 QPopupMenu* m =
746 static_cast<QPopupMenu*>(mw->factory()->container("popup_files", mw));
747 m->exec(e->globalPos());
750 bool SourceWindow::eventFilter(QObject* watched, QEvent* e)
752 if (e->type() == QEvent::ContextMenu && watched == viewport())
754 contextMenuEvent(static_cast<QContextMenuEvent*>(e));
755 return true;
757 return QTextEdit::eventFilter(watched, e);
760 HighlightCpp::HighlightCpp(SourceWindow* srcWnd) :
761 QSyntaxHighlighter(srcWnd),
762 m_srcWnd(srcWnd)
766 enum HLState {
767 hlCommentLine = 1,
768 hlCommentBlock,
769 hlIdent,
770 hlString
773 static const QString ckw[] =
775 "and",
776 "and_eq",
777 "asm",
778 "auto",
779 "bitand",
780 "bitor",
781 "bool",
782 "break",
783 "case",
784 "catch",
785 "char",
786 "class",
787 "compl",
788 "const",
789 "const_cast",
790 "continue",
791 "default",
792 "delete",
793 "do",
794 "double",
795 "dynamic_cast",
796 "else",
797 "enum",
798 "explicit",
799 "export",
800 "extern",
801 "false",
802 "float",
803 "for",
804 "friend",
805 "goto",
806 "if",
807 "inline",
808 "int",
809 "long",
810 "mutable",
811 "namespace",
812 "new",
813 "not",
814 "not_eq",
815 "operator",
816 "or",
817 "or_eq",
818 "private",
819 "protected",
820 "public",
821 "reinterpret_cast",
822 "register",
823 "return",
824 "short",
825 "signed",
826 "sizeof",
827 "static",
828 "static_cast",
829 "struct",
830 "switch",
831 "template",
832 "this",
833 "throw",
834 "true",
835 "try",
836 "typedef",
837 "typeid",
838 "typename",
839 "using",
840 "union",
841 "unsigned",
842 "virtual",
843 "void",
844 "volatile",
845 "wchar_t",
846 "while",
847 "xor",
848 "xor_eq"
851 int HighlightCpp::highlightParagraph(const QString& text, int state)
853 int row = currentParagraph();
854 // highlight assembly lines
855 if (m_srcWnd->isRowDisassCode(row))
857 setFormat(0, text.length(), blue);
858 return state;
861 if (state == -2) // initial state
862 state = 0;
864 // check for preprocessor line
865 if (state == 0 && text.stripWhiteSpace().startsWith("#"))
867 setFormat(0, text.length(), QColor("dark green"));
868 return 0;
871 // a font for keywords
872 QFont identFont = textEdit()->currentFont();
873 identFont.setBold(!identFont.bold());
875 unsigned start = 0;
876 while (start < text.length())
878 int end;
879 switch (state) {
880 case hlCommentLine:
881 end = text.length();
882 state = 0;
883 setFormat(start, end-start, QColor("gray50"));
884 break;
885 case hlCommentBlock:
886 end = text.find("*/", start);
887 if (end >= 0)
888 end += 2, state = 0;
889 else
890 end = text.length();
891 setFormat(start, end-start, QColor("gray50"));
892 break;
893 case hlString:
894 for (end = start+1; end < int(text.length()); end++) {
895 if (text[end] == '\\') {
896 if (end < int(text.length()))
897 ++end;
898 } else if (text[end] == text[start]) {
899 ++end;
900 break;
903 state = 0;
904 setFormat(start, end-start, QColor("dark red"));
905 break;
906 case hlIdent:
907 for (end = start+1; end < int(text.length()); end++) {
908 if (!text[end].isLetterOrNumber() && text[end] != '_')
909 break;
911 state = 0;
912 if (std::binary_search(ckw, ckw + sizeof(ckw)/sizeof(ckw[0]),
913 text.mid(start, end-start)))
915 setFormat(start, end-start, identFont);
916 } else {
917 setFormat(start, end-start, m_srcWnd->colorGroup().text());
919 break;
920 default:
921 for (end = start; end < int(text.length()); end++)
923 if (text[end] == '/')
925 if (end+1 < int(text.length())) {
926 if (text[end+1] == '/') {
927 state = hlCommentLine;
928 break;
929 } else if (text[end+1] == '*') {
930 state = hlCommentBlock;
931 break;
935 else if (text[end] == '"' || text[end] == '\'')
937 state = hlString;
938 break;
940 else if (text[end] >= 'A' && text[end] <= 'Z' ||
941 text[end] >= 'a' && text[end] <= 'z' ||
942 text[end] == '_')
944 state = hlIdent;
945 break;
948 setFormat(start, end-start, m_srcWnd->colorGroup().text());
950 start = end;
952 return state;
955 #include "sourcewnd.moc"