Remove the line number display from the status bar.
[kdbg.git] / kdbg / sourcewnd.cpp
blobc554d6e70ae7540635c64e56048a3c692682bed3
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),
31 m_widthLineNo(30)
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+m_widthLineNo, 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 // set a font for line numbers
87 m_lineNoFont = currentFont();
88 m_lineNoFont.setPixelSize(11);
90 return true;
93 void SourceWindow::reloadFile()
95 QFile f(m_fileName);
96 if (!f.open(IO_ReadOnly)) {
97 // open failed; leave alone
98 return;
101 // read text into m_sourceCode
102 m_sourceCode.clear(); /* clear old text */
104 QTextStream t(&f);
105 setText(t.read());
106 f.close();
108 m_sourceCode.resize(paragraphs());
109 for (size_t i = 0; i < m_sourceCode.size(); i++) {
110 m_sourceCode[i].code = text(i);
112 // allocate line items
113 m_lineItems.resize(m_sourceCode.size(), 0);
115 m_rowToLine.resize(m_sourceCode.size());
116 for (size_t i = 0; i < m_sourceCode.size(); i++)
117 m_rowToLine[i] = i;
120 void SourceWindow::scrollTo(int lineNo, const DbgAddr& address)
122 if (lineNo < 0 || lineNo >= int(m_sourceCode.size()))
123 return;
125 int row = lineToRow(lineNo, address);
126 scrollToRow(row);
129 void SourceWindow::scrollToRow(int row)
131 setCursorPosition(row, 0);
132 ensureCursorVisible();
135 void SourceWindow::drawFrame(QPainter* p)
137 QTextEdit::drawFrame(p);
139 // and paragraph at the top is...
140 int top = paragraphAt(QPoint(0,contentsY()));
141 int bot = paragraphAt(QPoint(0,contentsY()+visibleHeight()-1));
142 if (bot < 0)
143 bot = paragraphs()-1;
145 p->save();
147 // set a clip rectangle
148 int fw = frameWidth();
149 QRect inside = rect();
150 inside.addCoords(fw,fw,-fw,-fw);
151 QRegion clip = p->clipRegion();
152 clip &= QRegion(inside);
153 p->setClipRegion(clip);
155 p->setFont(m_lineNoFont);
156 p->setPen(colorGroup().text());
157 p->eraseRect(inside);
159 for (int row = top; row <= bot; row++)
161 uchar item = m_lineItems[row];
162 p->save();
164 QRect r = paragraphRect(row);
165 QPoint pt = contentsToViewport(r.topLeft());
166 int h = r.height();
167 p->translate(fw, pt.y()+viewport()->y());
169 if (item & liBP) {
170 // enabled breakpoint
171 int y = (h - m_brkena.height())/2;
172 if (y < 0) y = 0;
173 p->drawPixmap(0,y,m_brkena);
175 if (item & liBPdisabled) {
176 // disabled breakpoint
177 int y = (h - m_brkdis.height())/2;
178 if (y < 0) y = 0;
179 p->drawPixmap(0,y,m_brkdis);
181 if (item & liBPtemporary) {
182 // temporary breakpoint marker
183 int y = (h - m_brktmp.height())/2;
184 if (y < 0) y = 0;
185 p->drawPixmap(0,y,m_brktmp);
187 if (item & liBPconditional) {
188 // conditional breakpoint marker
189 int y = (h - m_brkcond.height())/2;
190 if (y < 0) y = 0;
191 p->drawPixmap(0,y,m_brkcond);
193 if (item & liBPorphan) {
194 // orphaned breakpoint marker
195 int y = (h - m_brkcond.height())/2;
196 if (y < 0) y = 0;
197 p->drawPixmap(0,y,m_brkorph);
199 if (item & liPC) {
200 // program counter in innermost frame
201 int y = (h - m_pcinner.height())/2;
202 if (y < 0) y = 0;
203 p->drawPixmap(0,y,m_pcinner);
205 if (item & liPCup) {
206 // program counter somewhere up the stack
207 int y = (h - m_pcup.height())/2;
208 if (y < 0) y = 0;
209 p->drawPixmap(0,y,m_pcup);
211 p->translate(m_widthItems, 0);
212 if (!isRowDisassCode(row) && m_sourceCode[rowToLine(row)].canDisass) {
213 int w = m_widthPlus;
214 int x = w/2;
215 int y = h/2;
216 p->drawLine(x-2, y, x+2, y);
217 if (!isRowExpanded(row)) {
218 p->drawLine(x, y-2, x, y+2);
221 p->translate(m_widthPlus, 0);
222 if (!isRowDisassCode(row)) {
223 p->drawText(0, 0, m_widthLineNo, h, AlignRight|AlignVCenter,
224 QString().setNum(rowToLine(row)+1));
226 p->restore();
228 p->restore();
231 void SourceWindow::updateLineItems(const KDebugger* dbg)
233 // clear outdated breakpoints
234 for (int i = m_lineItems.size()-1; i >= 0; i--) {
235 if (m_lineItems[i] & liBPany) {
236 // check if this breakpoint still exists
237 int line = rowToLine(i);
238 TRACE(QString().sprintf("checking for bp at %d", line));
239 int j;
240 for (j = dbg->numBreakpoints()-1; j >= 0; j--) {
241 const Breakpoint* bp = dbg->breakpoint(j);
242 if (bp->lineNo == line &&
243 fileNameMatches(bp->fileName) &&
244 lineToRow(line, bp->address) == i)
246 // yes it exists; mode is changed below
247 break;
250 if (j < 0) {
251 /* doesn't exist anymore, remove it */
252 m_lineItems[i] &= ~liBPany;
253 update();
258 // add new breakpoints
259 for (int j = dbg->numBreakpoints()-1; j >= 0; j--) {
260 const Breakpoint* bp = dbg->breakpoint(j);
261 if (fileNameMatches(bp->fileName)) {
262 TRACE(QString().sprintf("updating %s:%d", bp->fileName.data(), bp->lineNo));
263 int i = bp->lineNo;
264 if (i < 0 || i >= int(m_sourceCode.size()))
265 continue;
266 // compute new line item flags for breakpoint
267 uchar flags = bp->enabled ? liBP : liBPdisabled;
268 if (bp->temporary)
269 flags |= liBPtemporary;
270 if (!bp->condition.isEmpty() || bp->ignoreCount != 0)
271 flags |= liBPconditional;
272 if (bp->isOrphaned())
273 flags |= liBPorphan;
274 // update if changed
275 int row = lineToRow(i, bp->address);
276 if ((m_lineItems[row] & liBPany) != flags) {
277 m_lineItems[row] &= ~liBPany;
278 m_lineItems[row] |= flags;
279 update();
285 void SourceWindow::setPC(bool set, int lineNo, const DbgAddr& address, int frameNo)
287 if (lineNo < 0 || lineNo >= int(m_sourceCode.size())) {
288 return;
291 int row = lineToRow(lineNo, address);
293 uchar flag = frameNo == 0 ? liPC : liPCup;
294 if (set) {
295 // set only if not already set
296 if ((m_lineItems[row] & flag) == 0) {
297 m_lineItems[row] |= flag;
298 update();
300 } else {
301 // clear only if not set
302 if ((m_lineItems[row] & flag) != 0) {
303 m_lineItems[row] &= ~flag;
304 update();
309 void SourceWindow::find(const QString& text, bool caseSensitive, FindDirection dir)
311 ASSERT(dir == 1 || dir == -1);
312 if (QTextEdit::find(text, caseSensitive, false, dir > 0))
313 return;
314 // not found; wrap around
315 int para = dir > 0 ? 0 : paragraphs(), index = 0;
316 QTextEdit::find(text, caseSensitive, false, dir > 0, &para, &index);
319 void SourceWindow::mousePressEvent(QMouseEvent* ev)
321 // we handle left and middle button
322 if (ev->button() != LeftButton && ev->button() != MidButton)
324 QTextEdit::mousePressEvent(ev);
325 return;
328 // get row
329 QPoint p = viewportToContents(QPoint(0, ev->y() - viewport()->y()));
330 int row = paragraphAt(p);
331 if (row < 0)
332 return;
334 if (ev->x() > m_widthItems+frameWidth())
336 if (isRowExpanded(row)) {
337 actionCollapseRow(row);
338 } else {
339 actionExpandRow(row);
341 return;
344 int sourceRow;
345 int line = rowToLine(row, &sourceRow);
347 // find address if row is disassembled code
348 DbgAddr address;
349 if (row > sourceRow) {
350 // get offset from source code line
351 int off = row - sourceRow;
352 address = m_sourceCode[line].disassAddr[off-1];
355 switch (ev->button()) {
356 case LeftButton:
357 TRACE(QString().sprintf("left-clicked line %d", line));
358 emit clickedLeft(m_fileName, line, address,
359 (ev->state() & ShiftButton) != 0);
360 break;
361 case MidButton:
362 TRACE(QString().sprintf("mid-clicked row %d", line));
363 emit clickedMid(m_fileName, line, address);
364 break;
365 default:;
369 void SourceWindow::keyPressEvent(QKeyEvent* ev)
371 int top1, top2;
372 QPoint top;
373 switch (ev->key()) {
374 case Key_Plus:
375 actionExpandRow(m_curRow);
376 return;
377 case Key_Minus:
378 actionCollapseRow(m_curRow);
379 return;
380 case Key_Up:
381 if (m_curRow > 0) {
382 setCursorPosition(m_curRow-1, 0);
384 return;
385 case Key_Down:
386 if (m_curRow < paragraphs()-1) {
387 setCursorPosition(m_curRow+1, 0);
389 return;
390 case Key_Home:
391 setCursorPosition(0, 0);
392 return;
393 case Key_End:
394 setCursorPosition(paragraphs()-1, 0);
395 return;
396 case Key_Next:
397 case Key_Prior:
398 top = viewportToContents(QPoint(0,0));
399 top1 = paragraphAt(top);
402 QTextEdit::keyPressEvent(ev);
404 switch (ev->key()) {
405 case Key_Next:
406 case Key_Prior:
407 top = viewportToContents(QPoint(0,0));
408 top2 = paragraphAt(top);
409 setCursorPosition(m_curRow+(top2-top1), 0);
413 static inline bool isident(QChar c)
415 return c.isLetterOrNumber() || c.latin1() == '_';
418 bool SourceWindow::wordAtPoint(const QPoint& p, QString& word, QRect& r)
420 QPoint pv = viewportToContents(p - viewport()->pos());
421 int row, col = charAt(pv, &row);
422 if (row < 0 || col < 0)
423 return false;
425 // isolate the word at row, col
426 QString line = text(row);
427 if (!isident(line[col]))
428 return false;
430 int begin = col;
431 while (begin > 0 && isident(line[begin-1]))
432 --begin;
434 ++col;
435 while (col < int(line.length()) && isident(line[col]));
437 r = QRect(p, p);
438 r.addCoords(-5,-5,5,5);
439 word = line.mid(begin, col-begin);
440 return true;
443 void SourceWindow::paletteChange(const QPalette& oldPal)
445 setFont(KGlobalSettings::fixedFont());
446 QTextEdit::paletteChange(oldPal);
450 * Two file names (possibly full paths) match if the last parts - the file
451 * names - match.
453 bool SourceWindow::fileNameMatches(const QString& other)
455 const QString& me = fileName();
457 // check for null file names first
458 if (me.isNull() || other.isNull()) {
459 return me.isNull() && other.isNull();
463 * Get file names. Note: Either there is a slash, then skip it, or
464 * there is no slash, then -1 + 1 = 0!
466 int sme = me.findRev('/') + 1;
467 int sother = other.findRev('/') + 1;
468 return strcmp(me.data() + sme, other.data() + sother) == 0;
471 void SourceWindow::disassembled(int lineNo, const QList<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.count());
481 sl.disassAddr.resize(disass.count());
482 sl.canDisass = disass.count() > 0;
483 for (uint i = 0; i < disass.count(); i++) {
484 const DisassembledCode* c =
485 const_cast<QList<DisassembledCode>&>(disass).at(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 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 // adjust current row
603 if (m_curRow > row) {
604 m_curRow += disass.size();
605 // highlight is moved automatically
608 // insert new lines
609 setUpdatesEnabled(false);
610 ++row;
611 for (size_t i = 0; i < disass.size(); i++) {
612 m_rowToLine.insert(m_rowToLine.begin()+row, line);
613 m_lineItems.insert(m_lineItems.begin()+row, 0);
614 insertParagraph(disass[i], row++);
616 setUpdatesEnabled(true);
617 viewport()->update();
618 update(); // line items
620 emit expanded(line); /* must set PC */
623 void SourceWindow::collapseRow(int row)
625 TRACE("collapsing row " + QString().setNum(row));
626 int line = rowToLine(row);
628 // find end of this block
629 int end = row+1;
630 while (end < int(m_rowToLine.size()) && m_rowToLine[end] == m_rowToLine[row]) {
631 end++;
633 ++row;
634 // adjust current row
635 if (m_curRow >= row) {
636 m_curRow -= end-row;
637 if (m_curRow < row) // was m_curRow in disassembled code?
638 m_curRow = -1;
640 setUpdatesEnabled(false);
641 while (--end >= row) {
642 m_rowToLine.erase(m_rowToLine.begin()+end);
643 m_lineItems.erase(m_lineItems.begin()+end);
644 removeParagraph(end);
646 setUpdatesEnabled(true);
647 viewport()->update();
648 update(); // line items
650 emit collapsed(line);
653 void SourceWindow::activeLine(int& line, DbgAddr& address)
655 int row = m_curRow;
657 int sourceRow;
658 line = rowToLine(row, &sourceRow);
659 if (row > sourceRow) {
660 int off = row - sourceRow; /* offset from source line */
661 address = m_sourceCode[line].disassAddr[off-1];
666 * Returns the offset from the line displaying the source code to
667 * the line containing the specified address. If the address is not
668 * found, 0 is returned.
670 int SourceWindow::SourceLine::findAddressRowOffset(const DbgAddr& address) const
672 if (address.isEmpty())
673 return 0;
675 for (size_t i = 0; i < disassAddr.size(); i++) {
676 if (disassAddr[i] == address) {
677 // found exact address
678 return i+1;
680 if (disassAddr[i] > address) {
682 * We have already advanced too far; the address is before this
683 * index, but obviously we haven't found an exact match
684 * earlier. address is somewhere between the displayed
685 * addresses. We return the previous line.
687 return i;
690 // not found
691 return 0;
694 void SourceWindow::actionExpandRow(int row)
696 if (row < 0 || isRowExpanded(row) || isRowDisassCode(row))
697 return;
699 // disassemble
700 int line = rowToLine(row);
701 const SourceLine& sl = m_sourceCode[line];
702 if (!sl.canDisass)
703 return;
704 if (sl.disass.size() == 0) {
705 emit disassemble(m_fileName, line);
706 } else {
707 expandRow(row);
711 void SourceWindow::actionCollapseRow(int row)
713 if (row < 0 || !isRowExpanded(row) || isRowDisassCode(row))
714 return;
716 collapseRow(row);
719 void SourceWindow::setTabWidth(int numChars)
721 if (numChars <= 0)
722 numChars = 8;
723 QFontMetrics fm(currentFont());
724 QString s;
725 int w = fm.width(s.fill('x', numChars));
726 setTabStopWidth(w);
729 void SourceWindow::cursorChanged(int row)
731 if (row == m_curRow)
732 return;
734 if (m_curRow >= 0 && m_curRow < paragraphs())
735 clearParagraphBackground(m_curRow);
736 m_curRow = row;
737 setParagraphBackgroundColor(row, colorGroup().background());
741 * We must override the context menu handling because QTextEdit's handling
742 * requires that it receives ownership of the popup menu; but the popup menu
743 * returned from the GUI factory is owned by the factory.
746 void SourceWindow::contextMenuEvent(QContextMenuEvent* e)
748 // get the context menu from the GUI factory
749 QWidget* top = this;
751 top = top->parentWidget();
752 while (!top->isTopLevel());
753 KMainWindow* mw = static_cast<KMainWindow*>(top);
754 QPopupMenu* m =
755 static_cast<QPopupMenu*>(mw->factory()->container("popup_files", mw));
756 m->exec(e->globalPos());
759 bool SourceWindow::eventFilter(QObject* watched, QEvent* e)
761 if (e->type() == QEvent::ContextMenu && watched == viewport())
763 contextMenuEvent(static_cast<QContextMenuEvent*>(e));
764 return true;
766 return QTextEdit::eventFilter(watched, e);
769 HighlightCpp::HighlightCpp(SourceWindow* srcWnd) :
770 QSyntaxHighlighter(srcWnd),
771 m_srcWnd(srcWnd)
775 enum HLState {
776 hlCommentLine = 1,
777 hlCommentBlock,
778 hlIdent,
779 hlString
782 static const QString ckw[] =
784 "and",
785 "and_eq",
786 "asm",
787 "auto",
788 "bitand",
789 "bitor",
790 "bool",
791 "break",
792 "case",
793 "catch",
794 "char",
795 "class",
796 "compl",
797 "const",
798 "const_cast",
799 "continue",
800 "default",
801 "delete",
802 "do",
803 "double",
804 "dynamic_cast",
805 "else",
806 "enum",
807 "explicit",
808 "export",
809 "extern",
810 "false",
811 "float",
812 "for",
813 "friend",
814 "goto",
815 "if",
816 "inline",
817 "int",
818 "long",
819 "mutable",
820 "namespace",
821 "new",
822 "not",
823 "not_eq",
824 "operator",
825 "or",
826 "or_eq",
827 "private",
828 "protected",
829 "public",
830 "reinterpret_cast",
831 "register",
832 "return",
833 "short",
834 "signed",
835 "sizeof",
836 "static",
837 "static_cast",
838 "struct",
839 "switch",
840 "template",
841 "this",
842 "throw",
843 "true",
844 "try",
845 "typedef",
846 "typeid",
847 "typename",
848 "using",
849 "union",
850 "unsigned",
851 "virtual",
852 "void",
853 "volatile",
854 "wchar_t",
855 "while",
856 "xor",
857 "xor_eq"
860 int HighlightCpp::highlightParagraph(const QString& text, int state)
862 int row = currentParagraph();
863 // highlight assembly lines
864 if (m_srcWnd->isRowDisassCode(row))
866 setFormat(0, text.length(), blue);
867 return state;
870 if (state == -2) // initial state
871 state = 0;
873 // check for preprocessor line
874 if (state == 0 && text.stripWhiteSpace().startsWith("#"))
876 setFormat(0, text.length(), QColor("dark green"));
877 return 0;
880 // a font for keywords
881 QFont identFont = textEdit()->currentFont();
882 identFont.setBold(!identFont.bold());
884 unsigned 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("gray50"));
893 break;
894 case hlCommentBlock:
895 end = text.find("*/", start);
896 if (end >= 0)
897 end += 2, state = 0;
898 else
899 end = text.length();
900 setFormat(start, end-start, QColor("gray50"));
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("dark red"));
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->colorGroup().text());
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->colorGroup().text());
959 start = end;
961 return state;
964 #include "sourcewnd.moc"