Insert a license note at the top of source files.
[kdbg.git] / kdbg / sourcewnd.cpp
blob86b8318a351d4b5b5e329472c473c22472aa2cae
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 <qtextstream.h>
10 #include <qpainter.h>
11 #include <qbrush.h>
12 #include <qfile.h>
13 #include <qkeycode.h>
14 #include <qpopupmenu.h>
15 #include <kapp.h>
16 #include <kiconloader.h>
17 #include <kglobalsettings.h>
18 #include <kmainwindow.h>
19 #include <algorithm>
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23 #include "mydebug.h"
26 SourceWindow::SourceWindow(const char* fileName, QWidget* parent, const char* name) :
27 QTextEdit(parent, name),
28 m_fileName(fileName),
29 m_curRow(-1),
30 m_widthItems(16),
31 m_widthPlus(12)
33 // load pixmaps
34 m_pcinner = UserIcon("pcinner");
35 m_pcup = UserIcon("pcup");
36 m_brkena = UserIcon("brkena");
37 m_brkdis = UserIcon("brkdis");
38 m_brktmp = UserIcon("brktmp");
39 m_brkcond = UserIcon("brkcond");
40 m_brkorph = UserIcon("brkorph");
41 setFont(KGlobalSettings::fixedFont());
42 setReadOnly(true);
43 setMargins(m_widthItems+m_widthPlus, 0, 0 ,0);
44 setAutoFormatting(AutoNone);
45 setTextFormat(PlainText);
46 setWordWrap(NoWrap);
47 connect(verticalScrollBar(), SIGNAL(valueChanged(int)),
48 this, SLOT(update()));
49 connect(this, SIGNAL(cursorPositionChanged(int,int)), this, SLOT(cursorChanged(int)));
50 viewport()->installEventFilter(this);
52 // add a syntax highlighter
53 if (QRegExp("\\.(c(pp|c|\\+\\+)?|CC?|h(\\+\\+|h)?|HH?)$").search(m_fileName))
55 new HighlightCpp(this);
59 SourceWindow::~SourceWindow()
61 delete syntaxHighlighter();
64 bool SourceWindow::loadFile()
66 // first we load the code into QTextEdit
67 QFile f(m_fileName);
68 if (!f.open(IO_ReadOnly)) {
69 return false;
72 QTextStream t(&f);
73 setText(t.read());
74 f.close();
76 // then we copy it into our own m_sourceCode
77 int n = paragraphs();
78 m_sourceCode.resize(n);
79 m_rowToLine.resize(n);
80 for (int i = 0; i < n; i++) {
81 m_sourceCode[i].code = text(i);
82 m_rowToLine[i] = i;
84 m_lineItems.resize(n, 0);
86 return true;
89 void SourceWindow::reloadFile()
91 QFile f(m_fileName);
92 if (!f.open(IO_ReadOnly)) {
93 // open failed; leave alone
94 return;
97 // read text into m_sourceCode
98 m_sourceCode.clear(); /* clear old text */
100 QTextStream t(&f);
101 setText(t.read());
102 f.close();
104 m_sourceCode.resize(paragraphs());
105 for (size_t i = 0; i < m_sourceCode.size(); i++) {
106 m_sourceCode[i].code = text(i);
108 // allocate line items
109 m_lineItems.resize(m_sourceCode.size(), 0);
111 m_rowToLine.resize(m_sourceCode.size());
112 for (size_t i = 0; i < m_sourceCode.size(); i++)
113 m_rowToLine[i] = i;
116 void SourceWindow::scrollTo(int lineNo, const DbgAddr& address)
118 if (lineNo < 0 || lineNo >= int(m_sourceCode.size()))
119 return;
121 int row = lineToRow(lineNo, address);
122 scrollToRow(row);
125 void SourceWindow::scrollToRow(int row)
127 setCursorPosition(row, 0);
128 ensureCursorVisible();
131 void SourceWindow::drawFrame(QPainter* p)
133 QTextEdit::drawFrame(p);
135 // and paragraph at the top is...
136 int top = paragraphAt(QPoint(0,contentsY()));
137 int bot = paragraphAt(QPoint(0,contentsY()+visibleHeight()-1));
138 if (bot < 0)
139 bot = paragraphs()-1;
141 p->save();
143 // set a clip rectangle
144 int fw = frameWidth();
145 QRect inside = rect();
146 inside.addCoords(fw,fw,-fw,-fw);
147 QRegion clip = p->clipRegion();
148 clip &= QRegion(inside);
149 p->setClipRegion(clip);
151 p->setPen(colorGroup().text());
152 p->eraseRect(inside);
154 for (int row = top; row <= bot; row++)
156 uchar item = m_lineItems[row];
157 p->save();
159 QRect r = paragraphRect(row);
160 QPoint pt = contentsToViewport(r.topLeft());
161 int h = r.height();
162 p->translate(fw, pt.y()+viewport()->y());
164 if (item & liBP) {
165 // enabled breakpoint
166 int y = (h - m_brkena.height())/2;
167 if (y < 0) y = 0;
168 p->drawPixmap(0,y,m_brkena);
170 if (item & liBPdisabled) {
171 // disabled breakpoint
172 int y = (h - m_brkdis.height())/2;
173 if (y < 0) y = 0;
174 p->drawPixmap(0,y,m_brkdis);
176 if (item & liBPtemporary) {
177 // temporary breakpoint marker
178 int y = (h - m_brktmp.height())/2;
179 if (y < 0) y = 0;
180 p->drawPixmap(0,y,m_brktmp);
182 if (item & liBPconditional) {
183 // conditional breakpoint marker
184 int y = (h - m_brkcond.height())/2;
185 if (y < 0) y = 0;
186 p->drawPixmap(0,y,m_brkcond);
188 if (item & liBPorphan) {
189 // orphaned breakpoint marker
190 int y = (h - m_brkcond.height())/2;
191 if (y < 0) y = 0;
192 p->drawPixmap(0,y,m_brkorph);
194 if (item & liPC) {
195 // program counter in innermost frame
196 int y = (h - m_pcinner.height())/2;
197 if (y < 0) y = 0;
198 p->drawPixmap(0,y,m_pcinner);
200 if (item & liPCup) {
201 // program counter somewhere up the stack
202 int y = (h - m_pcup.height())/2;
203 if (y < 0) y = 0;
204 p->drawPixmap(0,y,m_pcup);
206 if (!isRowDisassCode(row) && m_sourceCode[rowToLine(row)].canDisass) {
207 int w = m_widthPlus;
208 p->translate(m_widthItems, 0);
209 int x = w/2;
210 int y = h/2;
211 p->drawLine(x-2, y, x+2, y);
212 if (!isRowExpanded(row)) {
213 p->drawLine(x, y-2, x, y+2);
216 p->restore();
218 p->restore();
221 void SourceWindow::updateLineItems(const KDebugger* dbg)
223 // clear outdated breakpoints
224 for (int i = m_lineItems.size()-1; i >= 0; i--) {
225 if (m_lineItems[i] & liBPany) {
226 // check if this breakpoint still exists
227 int line = rowToLine(i);
228 TRACE(QString().sprintf("checking for bp at %d", line));
229 int j;
230 for (j = dbg->numBreakpoints()-1; j >= 0; j--) {
231 const Breakpoint* bp = dbg->breakpoint(j);
232 if (bp->lineNo == line &&
233 fileNameMatches(bp->fileName) &&
234 lineToRow(line, bp->address) == i)
236 // yes it exists; mode is changed below
237 break;
240 if (j < 0) {
241 /* doesn't exist anymore, remove it */
242 m_lineItems[i] &= ~liBPany;
243 update();
248 // add new breakpoints
249 for (int j = dbg->numBreakpoints()-1; j >= 0; j--) {
250 const Breakpoint* bp = dbg->breakpoint(j);
251 if (fileNameMatches(bp->fileName)) {
252 TRACE(QString().sprintf("updating %s:%d", bp->fileName.data(), bp->lineNo));
253 int i = bp->lineNo;
254 if (i < 0 || i >= int(m_sourceCode.size()))
255 continue;
256 // compute new line item flags for breakpoint
257 uchar flags = bp->enabled ? liBP : liBPdisabled;
258 if (bp->temporary)
259 flags |= liBPtemporary;
260 if (!bp->condition.isEmpty() || bp->ignoreCount != 0)
261 flags |= liBPconditional;
262 if (bp->isOrphaned())
263 flags |= liBPorphan;
264 // update if changed
265 int row = lineToRow(i, bp->address);
266 if ((m_lineItems[row] & liBPany) != flags) {
267 m_lineItems[row] &= ~liBPany;
268 m_lineItems[row] |= flags;
269 update();
275 void SourceWindow::setPC(bool set, int lineNo, const DbgAddr& address, int frameNo)
277 if (lineNo < 0 || lineNo >= int(m_sourceCode.size())) {
278 return;
281 int row = lineToRow(lineNo, address);
283 uchar flag = frameNo == 0 ? liPC : liPCup;
284 if (set) {
285 // set only if not already set
286 if ((m_lineItems[row] & flag) == 0) {
287 m_lineItems[row] |= flag;
288 update();
290 } else {
291 // clear only if not set
292 if ((m_lineItems[row] & flag) != 0) {
293 m_lineItems[row] &= ~flag;
294 update();
299 void SourceWindow::find(const QString& text, bool caseSensitive, FindDirection dir)
301 ASSERT(dir == 1 || dir == -1);
302 if (QTextEdit::find(text, caseSensitive, false, dir > 0))
303 return;
304 // not found; wrap around
305 int para = dir > 0 ? 0 : paragraphs(), index = 0;
306 QTextEdit::find(text, caseSensitive, false, dir > 0, &para, &index);
309 void SourceWindow::mousePressEvent(QMouseEvent* ev)
311 // we handle left and middle button
312 if (ev->button() != LeftButton && ev->button() != MidButton)
314 QTextEdit::mousePressEvent(ev);
315 return;
318 // get row
319 QPoint p = viewportToContents(QPoint(0, ev->y() - viewport()->y()));
320 int row = paragraphAt(p);
321 if (row < 0)
322 return;
324 if (ev->x() > m_widthItems+frameWidth())
326 if (isRowExpanded(row)) {
327 actionCollapseRow(row);
328 } else {
329 actionExpandRow(row);
331 return;
334 int sourceRow;
335 int line = rowToLine(row, &sourceRow);
337 // find address if row is disassembled code
338 DbgAddr address;
339 if (row > sourceRow) {
340 // get offset from source code line
341 int off = row - sourceRow;
342 address = m_sourceCode[line].disassAddr[off-1];
345 switch (ev->button()) {
346 case LeftButton:
347 TRACE(QString().sprintf("left-clicked line %d", line));
348 emit clickedLeft(m_fileName, line, address,
349 (ev->state() & ShiftButton) != 0);
350 break;
351 case MidButton:
352 TRACE(QString().sprintf("mid-clicked row %d", line));
353 emit clickedMid(m_fileName, line, address);
354 break;
355 default:;
359 void SourceWindow::keyPressEvent(QKeyEvent* ev)
361 int top1, top2;
362 QPoint top;
363 switch (ev->key()) {
364 case Key_Plus:
365 actionExpandRow(m_curRow);
366 return;
367 case Key_Minus:
368 actionCollapseRow(m_curRow);
369 return;
370 case Key_Up:
371 if (m_curRow > 0) {
372 setCursorPosition(m_curRow-1, 0);
374 return;
375 case Key_Down:
376 if (m_curRow < paragraphs()-1) {
377 setCursorPosition(m_curRow+1, 0);
379 return;
380 case Key_Home:
381 setCursorPosition(0, 0);
382 return;
383 case Key_End:
384 setCursorPosition(paragraphs()-1, 0);
385 return;
386 case Key_Next:
387 case Key_Prior:
388 top = viewportToContents(QPoint(0,0));
389 top1 = paragraphAt(top);
392 QTextEdit::keyPressEvent(ev);
394 switch (ev->key()) {
395 case Key_Next:
396 case Key_Prior:
397 top = viewportToContents(QPoint(0,0));
398 top2 = paragraphAt(top);
399 setCursorPosition(m_curRow+(top2-top1), 0);
403 static inline bool isident(QChar c)
405 return c.isLetterOrNumber() || c.latin1() == '_';
408 bool SourceWindow::wordAtPoint(const QPoint& p, QString& word, QRect& r)
410 QPoint pv = viewportToContents(p - viewport()->pos());
411 int row, col = charAt(pv, &row);
412 if (row < 0 || col < 0)
413 return false;
415 // isolate the word at row, col
416 QString line = text(row);
417 if (!isident(line[col]))
418 return false;
420 int begin = col;
421 while (begin > 0 && isident(line[begin-1]))
422 --begin;
424 ++col;
425 while (col < int(line.length()) && isident(line[col]));
427 r = QRect(p, p);
428 r.addCoords(-5,-5,5,5);
429 word = line.mid(begin, col-begin);
430 return true;
433 void SourceWindow::paletteChange(const QPalette& oldPal)
435 setFont(KGlobalSettings::fixedFont());
436 QTextEdit::paletteChange(oldPal);
440 * Two file names (possibly full paths) match if the last parts - the file
441 * names - match.
443 bool SourceWindow::fileNameMatches(const QString& other)
445 const QString& me = fileName();
447 // check for null file names first
448 if (me.isNull() || other.isNull()) {
449 return me.isNull() && other.isNull();
453 * Get file names. Note: Either there is a slash, then skip it, or
454 * there is no slash, then -1 + 1 = 0!
456 int sme = me.findRev('/') + 1;
457 int sother = other.findRev('/') + 1;
458 return strcmp(me.data() + sme, other.data() + sother) == 0;
461 void SourceWindow::disassembled(int lineNo, const QList<DisassembledCode>& disass)
463 TRACE("disassembled line " + QString().setNum(lineNo));
464 if (lineNo < 0 || lineNo >= int(m_sourceCode.size()))
465 return;
467 SourceLine& sl = m_sourceCode[lineNo];
469 // copy disassembled code and its addresses
470 sl.disass.resize(disass.count());
471 sl.disassAddr.resize(disass.count());
472 sl.canDisass = disass.count() > 0;
473 for (uint i = 0; i < disass.count(); i++) {
474 const DisassembledCode* c =
475 const_cast<QList<DisassembledCode>&>(disass).at(i);
476 QString code = c->code;
477 while (code.endsWith("\n"))
478 code.truncate(code.length()-1);
479 sl.disass[i] = c->address.asString() + ' ' + code;
480 sl.disassAddr[i] = c->address;
483 int row = lineToRow(lineNo);
484 if (sl.canDisass) {
485 expandRow(row);
486 } else {
487 // clear expansion marker
488 update();
492 int SourceWindow::rowToLine(int row, int* sourceRow)
494 int line = row >= 0 ? m_rowToLine[row] : -1;
495 if (sourceRow != 0) {
496 // search back until we hit the first entry with the current line number
497 while (row > 0 && m_rowToLine[row-1] == line)
498 row--;
499 *sourceRow = row;
501 return line;
505 * Rows showing diassembled code have the same line number as the
506 * corresponding source code line number. Therefore, the line numbers in
507 * m_rowToLine are monotonically increasing with blocks of equal line
508 * numbers for a source line and its disassembled code that follows it.
510 * Hence, m_rowToLine always obeys the following condition:
512 * m_rowToLine[i] <= i
515 int SourceWindow::lineToRow(int line)
517 // line is zero-based!
519 assert(line < int(m_rowToLine.size()));
521 // quick test for common case
522 if (line < 0 || m_rowToLine[line] == line)
523 return line;
525 assert(m_rowToLine[line] < line);
528 * Binary search between row == line and end of list. In the loop below
529 * we use the fact that the line numbers m_rowToLine do not contain
530 * holes.
532 int l = line;
533 int h = m_rowToLine.size();
534 while (l < h && m_rowToLine[l] != line)
536 assert(h == int(m_rowToLine.size()) || m_rowToLine[l] < m_rowToLine[h]);
539 * We want to round down the midpoint so that we find the
540 * lowest row that belongs to the line we seek.
542 int mid = (l+h)/2;
543 if (m_rowToLine[mid] <= line)
544 l = mid;
545 else
546 h = mid;
548 // Found! Result is in l:
549 assert(m_rowToLine[l] == line);
552 * We might not have hit the lowest index for the line.
554 while (l > 0 && m_rowToLine[l-1] == line)
555 --l;
557 return l;
560 int SourceWindow::lineToRow(int line, const DbgAddr& address)
562 int row = lineToRow(line);
563 if (isRowExpanded(row)) {
564 row += m_sourceCode[line].findAddressRowOffset(address);
566 return row;
569 bool SourceWindow::isRowExpanded(int row)
571 assert(row >= 0);
572 return row < int(m_rowToLine.size())-1 &&
573 m_rowToLine[row] == m_rowToLine[row+1];
576 bool SourceWindow::isRowDisassCode(int row)
578 return row > 0 && row < int(m_rowToLine.size()) &&
579 m_rowToLine[row] == m_rowToLine[row-1];
582 void SourceWindow::expandRow(int row)
584 TRACE("expanding row " + QString().setNum(row));
585 // get disassembled code
586 int line = rowToLine(row);
587 const std::vector<QString>& disass = m_sourceCode[line].disass;
589 // remove PC (must be set again in slot of signal expanded())
590 m_lineItems[row] &= ~(liPC|liPCup);
592 // adjust current row
593 if (m_curRow > row) {
594 m_curRow += disass.size();
595 // highlight is moved automatically
598 // insert new lines
599 setUpdatesEnabled(false);
600 ++row;
601 for (size_t i = 0; i < disass.size(); i++) {
602 m_rowToLine.insert(m_rowToLine.begin()+row, line);
603 m_lineItems.insert(m_lineItems.begin()+row, 0);
604 insertParagraph(disass[i], row++);
606 setUpdatesEnabled(true);
607 viewport()->update();
608 update(); // line items
610 emit expanded(line); /* must set PC */
613 void SourceWindow::collapseRow(int row)
615 TRACE("collapsing row " + QString().setNum(row));
616 int line = rowToLine(row);
618 // find end of this block
619 int end = row+1;
620 while (end < int(m_rowToLine.size()) && m_rowToLine[end] == m_rowToLine[row]) {
621 end++;
623 ++row;
624 // adjust current row
625 if (m_curRow >= row) {
626 m_curRow -= end-row;
627 if (m_curRow < row) // was m_curRow in disassembled code?
628 m_curRow = -1;
630 setUpdatesEnabled(false);
631 while (--end >= row) {
632 m_rowToLine.erase(m_rowToLine.begin()+end);
633 m_lineItems.erase(m_lineItems.begin()+end);
634 removeParagraph(end);
636 setUpdatesEnabled(true);
637 viewport()->update();
638 update(); // line items
640 emit collapsed(line);
643 void SourceWindow::activeLine(int& line, DbgAddr& address)
645 int row = m_curRow;
647 int sourceRow;
648 line = rowToLine(row, &sourceRow);
649 if (row > sourceRow) {
650 int off = row - sourceRow; /* offset from source line */
651 address = m_sourceCode[line].disassAddr[off-1];
656 * Returns the offset from the line displaying the source code to
657 * the line containing the specified address. If the address is not
658 * found, 0 is returned.
660 int SourceWindow::SourceLine::findAddressRowOffset(const DbgAddr& address) const
662 if (address.isEmpty())
663 return 0;
665 for (size_t i = 0; i < disassAddr.size(); i++) {
666 if (disassAddr[i] == address) {
667 // found exact address
668 return i+1;
670 if (disassAddr[i] > address) {
672 * We have already advanced too far; the address is before this
673 * index, but obviously we haven't found an exact match
674 * earlier. address is somewhere between the displayed
675 * addresses. We return the previous line.
677 return i;
680 // not found
681 return 0;
684 void SourceWindow::actionExpandRow(int row)
686 if (row < 0 || isRowExpanded(row) || isRowDisassCode(row))
687 return;
689 // disassemble
690 int line = rowToLine(row);
691 const SourceLine& sl = m_sourceCode[line];
692 if (!sl.canDisass)
693 return;
694 if (sl.disass.size() == 0) {
695 emit disassemble(m_fileName, line);
696 } else {
697 expandRow(row);
701 void SourceWindow::actionCollapseRow(int row)
703 if (row < 0 || !isRowExpanded(row) || isRowDisassCode(row))
704 return;
706 collapseRow(row);
709 void SourceWindow::setTabWidth(int numChars)
711 if (numChars <= 0)
712 numChars = 8;
713 QFontMetrics fm(currentFont());
714 QString s;
715 int w = fm.width(s.fill('x', numChars));
716 setTabStopWidth(w);
719 void SourceWindow::cursorChanged(int row)
721 if (row == m_curRow)
722 return;
724 if (m_curRow >= 0 && m_curRow < paragraphs())
725 clearParagraphBackground(m_curRow);
726 m_curRow = row;
727 setParagraphBackgroundColor(row, colorGroup().background());
729 emit lineChanged();
733 * We must override the context menu handling because QTextEdit's handling
734 * requires that it receives ownership of the popup menu; but the popup menu
735 * returned from the GUI factory is owned by the factory.
738 void SourceWindow::contextMenuEvent(QContextMenuEvent* e)
740 // get the context menu from the GUI factory
741 QWidget* top = this;
743 top = top->parentWidget();
744 while (!top->isTopLevel());
745 KMainWindow* mw = static_cast<KMainWindow*>(top);
746 QPopupMenu* m =
747 static_cast<QPopupMenu*>(mw->factory()->container("popup_files", mw));
748 m->exec(e->globalPos());
751 bool SourceWindow::eventFilter(QObject* watched, QEvent* e)
753 if (e->type() == QEvent::ContextMenu && watched == viewport())
755 contextMenuEvent(static_cast<QContextMenuEvent*>(e));
756 return true;
758 return QTextEdit::eventFilter(watched, e);
761 HighlightCpp::HighlightCpp(SourceWindow* srcWnd) :
762 QSyntaxHighlighter(srcWnd),
763 m_srcWnd(srcWnd)
767 enum HLState {
768 hlCommentLine = 1,
769 hlCommentBlock,
770 hlIdent,
771 hlString
774 static const QString ckw[] =
776 "and",
777 "and_eq",
778 "asm",
779 "auto",
780 "bitand",
781 "bitor",
782 "bool",
783 "break",
784 "case",
785 "catch",
786 "char",
787 "class",
788 "compl",
789 "const",
790 "const_cast",
791 "continue",
792 "default",
793 "delete",
794 "do",
795 "double",
796 "dynamic_cast",
797 "else",
798 "enum",
799 "explicit",
800 "export",
801 "extern",
802 "false",
803 "float",
804 "for",
805 "friend",
806 "goto",
807 "if",
808 "inline",
809 "int",
810 "long",
811 "mutable",
812 "namespace",
813 "new",
814 "not",
815 "not_eq",
816 "operator",
817 "or",
818 "or_eq",
819 "private",
820 "protected",
821 "public",
822 "reinterpret_cast",
823 "register",
824 "return",
825 "short",
826 "signed",
827 "sizeof",
828 "static",
829 "static_cast",
830 "struct",
831 "switch",
832 "template",
833 "this",
834 "throw",
835 "true",
836 "try",
837 "typedef",
838 "typeid",
839 "typename",
840 "using",
841 "union",
842 "unsigned",
843 "virtual",
844 "void",
845 "volatile",
846 "wchar_t",
847 "while",
848 "xor",
849 "xor_eq"
852 int HighlightCpp::highlightParagraph(const QString& text, int state)
854 int row = currentParagraph();
855 // highlight assembly lines
856 if (m_srcWnd->isRowDisassCode(row))
858 setFormat(0, text.length(), blue);
859 return state;
862 if (state == -2) // initial state
863 state = 0;
865 // check for preprocessor line
866 if (state == 0 && text.stripWhiteSpace().startsWith("#"))
868 setFormat(0, text.length(), QColor("dark green"));
869 return 0;
872 // a font for keywords
873 QFont identFont = textEdit()->currentFont();
874 identFont.setBold(!identFont.bold());
876 unsigned start = 0;
877 while (start < text.length())
879 int end;
880 switch (state) {
881 case hlCommentLine:
882 end = text.length();
883 state = 0;
884 setFormat(start, end-start, QColor("gray50"));
885 break;
886 case hlCommentBlock:
887 end = text.find("*/", start);
888 if (end >= 0)
889 end += 2, state = 0;
890 else
891 end = text.length();
892 setFormat(start, end-start, QColor("gray50"));
893 break;
894 case hlString:
895 for (end = start+1; end < int(text.length()); end++) {
896 if (text[end] == '\\') {
897 if (end < int(text.length()))
898 ++end;
899 } else if (text[end] == text[start]) {
900 ++end;
901 break;
904 state = 0;
905 setFormat(start, end-start, QColor("dark red"));
906 break;
907 case hlIdent:
908 for (end = start+1; end < int(text.length()); end++) {
909 if (!text[end].isLetterOrNumber() && text[end] != '_')
910 break;
912 state = 0;
913 if (std::binary_search(ckw, ckw + sizeof(ckw)/sizeof(ckw[0]),
914 text.mid(start, end-start)))
916 setFormat(start, end-start, identFont);
917 } else {
918 setFormat(start, end-start, m_srcWnd->colorGroup().text());
920 break;
921 default:
922 for (end = start; end < int(text.length()); end++)
924 if (text[end] == '/')
926 if (end+1 < int(text.length())) {
927 if (text[end+1] == '/') {
928 state = hlCommentLine;
929 break;
930 } else if (text[end+1] == '*') {
931 state = hlCommentBlock;
932 break;
936 else if (text[end] == '"' || text[end] == '\'')
938 state = hlString;
939 break;
941 else if (text[end] >= 'A' && text[end] <= 'Z' ||
942 text[end] >= 'a' && text[end] <= 'z' ||
943 text[end] == '_')
945 state = hlIdent;
946 break;
949 setFormat(start, end-start, m_srcWnd->colorGroup().text());
951 start = end;
953 return state;
956 #include "sourcewnd.moc"