Cleanup config.h usage.
[kdbg.git] / kdbg / sourcewnd.cpp
blob508885f7f6696b4e97d8b44ff15d6079d53eb458
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 <qfileinfo.h>
14 #include <qkeycode.h>
15 #include <qpopupmenu.h>
16 #include <kapplication.h>
17 #include <kiconloader.h>
18 #include <kglobalsettings.h>
19 #include <kmainwindow.h>
20 #include <algorithm>
21 #include "mydebug.h"
24 SourceWindow::SourceWindow(const QString& fileName, QWidget* parent, const char* name) :
25 QTextEdit(parent, name),
26 m_fileName(fileName),
27 m_curRow(-1),
28 m_widthItems(16),
29 m_widthPlus(12),
30 m_widthLineNo(30)
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+m_widthLineNo, 0, 0 ,0);
43 setAutoFormatting(AutoNone);
44 setTextFormat(Qt::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) >= 0)
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 // set a font for line numbers
86 m_lineNoFont = currentFont();
87 m_lineNoFont.setPixelSize(11);
89 return true;
92 void SourceWindow::reloadFile()
94 QFile f(m_fileName);
95 if (!f.open(IO_ReadOnly)) {
96 // open failed; leave alone
97 return;
100 // read text into m_sourceCode
101 m_sourceCode.clear(); /* clear old text */
103 QTextStream t(&f);
104 setText(t.read());
105 f.close();
107 m_sourceCode.resize(paragraphs());
108 for (size_t i = 0; i < m_sourceCode.size(); i++) {
109 m_sourceCode[i].code = text(i);
111 // expanded lines are collapsed: move existing line items up
112 for (size_t i = 0; i < m_lineItems.size(); i++) {
113 if (m_rowToLine[i] != i) {
114 m_lineItems[m_rowToLine[i]] |= m_lineItems[i];
115 m_lineItems[i] = 0;
118 // allocate line items
119 m_lineItems.resize(m_sourceCode.size(), 0);
121 m_rowToLine.resize(m_sourceCode.size());
122 for (size_t i = 0; i < m_sourceCode.size(); i++)
123 m_rowToLine[i] = i;
125 // Highlighting was applied above when the text was inserted into widget,
126 // but at that time m_rowToLine was not corrected, yet, so that lines
127 // that previously were assembly were painted incorrectly.
128 if (syntaxHighlighter())
129 syntaxHighlighter()->rehighlight();
130 update(); // line numbers
133 void SourceWindow::scrollTo(int lineNo, const DbgAddr& address)
135 if (lineNo < 0 || lineNo >= int(m_sourceCode.size()))
136 return;
138 int row = lineToRow(lineNo, address);
139 scrollToRow(row);
142 void SourceWindow::scrollToRow(int row)
144 setCursorPosition(row, 0);
145 ensureCursorVisible();
148 void SourceWindow::drawFrame(QPainter* p)
150 QTextEdit::drawFrame(p);
152 // and paragraph at the top is...
153 int top = paragraphAt(QPoint(0,contentsY()));
154 int bot = paragraphAt(QPoint(0,contentsY()+visibleHeight()-1));
155 if (bot < 0)
156 bot = paragraphs()-1;
158 p->save();
160 // set a clip rectangle
161 int fw = frameWidth();
162 QRect inside = rect();
163 inside.addCoords(fw,fw,-fw,-fw);
164 QRegion clip = p->clipRegion();
165 clip &= QRegion(inside);
166 p->setClipRegion(clip);
168 p->setFont(m_lineNoFont);
169 p->setPen(colorGroup().text());
170 p->eraseRect(inside);
172 for (int row = top; row <= bot; row++)
174 uchar item = m_lineItems[row];
175 p->save();
177 QRect r = paragraphRect(row);
178 QPoint pt = contentsToViewport(r.topLeft());
179 int h = r.height();
180 p->translate(fw, pt.y()+viewport()->y());
182 if (item & liBP) {
183 // enabled breakpoint
184 int y = (h - m_brkena.height())/2;
185 if (y < 0) y = 0;
186 p->drawPixmap(0,y,m_brkena);
188 if (item & liBPdisabled) {
189 // disabled breakpoint
190 int y = (h - m_brkdis.height())/2;
191 if (y < 0) y = 0;
192 p->drawPixmap(0,y,m_brkdis);
194 if (item & liBPtemporary) {
195 // temporary breakpoint marker
196 int y = (h - m_brktmp.height())/2;
197 if (y < 0) y = 0;
198 p->drawPixmap(0,y,m_brktmp);
200 if (item & liBPconditional) {
201 // conditional breakpoint marker
202 int y = (h - m_brkcond.height())/2;
203 if (y < 0) y = 0;
204 p->drawPixmap(0,y,m_brkcond);
206 if (item & liBPorphan) {
207 // orphaned breakpoint marker
208 int y = (h - m_brkcond.height())/2;
209 if (y < 0) y = 0;
210 p->drawPixmap(0,y,m_brkorph);
212 if (item & liPC) {
213 // program counter in innermost frame
214 int y = (h - m_pcinner.height())/2;
215 if (y < 0) y = 0;
216 p->drawPixmap(0,y,m_pcinner);
218 if (item & liPCup) {
219 // program counter somewhere up the stack
220 int y = (h - m_pcup.height())/2;
221 if (y < 0) y = 0;
222 p->drawPixmap(0,y,m_pcup);
224 p->translate(m_widthItems, 0);
225 if (!isRowDisassCode(row) && m_sourceCode[rowToLine(row)].canDisass) {
226 int w = m_widthPlus;
227 int x = w/2;
228 int y = h/2;
229 p->drawLine(x-2, y, x+2, y);
230 if (!isRowExpanded(row)) {
231 p->drawLine(x, y-2, x, y+2);
234 p->translate(m_widthPlus, 0);
235 if (!isRowDisassCode(row)) {
236 p->drawText(0, 0, m_widthLineNo, h, Qt::AlignRight|Qt::AlignVCenter,
237 QString().setNum(rowToLine(row)+1));
239 p->restore();
241 p->restore();
244 void SourceWindow::updateLineItems(const KDebugger* dbg)
246 // clear outdated breakpoints
247 for (int i = m_lineItems.size()-1; i >= 0; i--) {
248 if (m_lineItems[i] & liBPany) {
249 // check if this breakpoint still exists
250 int line = rowToLine(i);
251 TRACE(QString().sprintf("checking for bp at %d", line));
252 KDebugger::BrkptROIterator bp = dbg->breakpointsBegin();
253 for (; bp != dbg->breakpointsEnd(); ++bp)
255 if (bp->lineNo == line &&
256 fileNameMatches(bp->fileName) &&
257 lineToRow(line, bp->address) == i)
259 // yes it exists; mode is changed below
260 break;
263 if (bp == dbg->breakpointsEnd()) {
264 /* doesn't exist anymore, remove it */
265 m_lineItems[i] &= ~liBPany;
266 update();
271 // add new breakpoints
272 for (KDebugger::BrkptROIterator bp = dbg->breakpointsBegin(); bp != dbg->breakpointsEnd(); ++bp)
274 if (fileNameMatches(bp->fileName)) {
275 TRACE(QString().sprintf("updating %s:%d", bp->fileName.data(), bp->lineNo));
276 int i = bp->lineNo;
277 if (i < 0 || i >= int(m_sourceCode.size()))
278 continue;
279 // compute new line item flags for breakpoint
280 uchar flags = bp->enabled ? liBP : liBPdisabled;
281 if (bp->temporary)
282 flags |= liBPtemporary;
283 if (!bp->condition.isEmpty() || bp->ignoreCount != 0)
284 flags |= liBPconditional;
285 if (bp->isOrphaned())
286 flags |= liBPorphan;
287 // update if changed
288 int row = lineToRow(i, bp->address);
289 if ((m_lineItems[row] & liBPany) != flags) {
290 m_lineItems[row] &= ~liBPany;
291 m_lineItems[row] |= flags;
292 update();
298 void SourceWindow::setPC(bool set, int lineNo, const DbgAddr& address, int frameNo)
300 if (lineNo < 0 || lineNo >= int(m_sourceCode.size())) {
301 return;
304 int row = lineToRow(lineNo, address);
306 uchar flag = frameNo == 0 ? liPC : liPCup;
307 if (set) {
308 // set only if not already set
309 if ((m_lineItems[row] & flag) == 0) {
310 m_lineItems[row] |= flag;
311 update();
313 } else {
314 // clear only if not set
315 if ((m_lineItems[row] & flag) != 0) {
316 m_lineItems[row] &= ~flag;
317 update();
322 void SourceWindow::find(const QString& text, bool caseSensitive, FindDirection dir)
324 ASSERT(dir == 1 || dir == -1);
325 if (QTextEdit::find(text, caseSensitive, false, dir > 0))
326 return;
327 // not found; wrap around
328 int para = dir > 0 ? 0 : paragraphs(), index = 0;
329 QTextEdit::find(text, caseSensitive, false, dir > 0, &para, &index);
332 void SourceWindow::mousePressEvent(QMouseEvent* ev)
334 // we handle left and middle button
335 if (ev->button() != Qt::LeftButton && ev->button() != Qt::MidButton)
337 QTextEdit::mousePressEvent(ev);
338 return;
341 // get row
342 QPoint p = viewportToContents(QPoint(0, ev->y() - viewport()->y()));
343 int row = paragraphAt(p);
344 if (row < 0)
345 return;
347 if (ev->x() > m_widthItems+frameWidth())
349 if (isRowExpanded(row)) {
350 actionCollapseRow(row);
351 } else {
352 actionExpandRow(row);
354 return;
357 int sourceRow;
358 int line = rowToLine(row, &sourceRow);
360 // find address if row is disassembled code
361 DbgAddr address;
362 if (row > sourceRow) {
363 // get offset from source code line
364 int off = row - sourceRow;
365 address = m_sourceCode[line].disassAddr[off-1];
368 switch (ev->button()) {
369 case Qt::LeftButton:
370 TRACE(QString().sprintf("left-clicked line %d", line));
371 emit clickedLeft(m_fileName, line, address,
372 (ev->state() & Qt::ShiftButton) != 0);
373 break;
374 case Qt::MidButton:
375 TRACE(QString().sprintf("mid-clicked row %d", line));
376 emit clickedMid(m_fileName, line, address);
377 break;
378 default:;
382 void SourceWindow::keyPressEvent(QKeyEvent* ev)
384 int top1, top2;
385 QPoint top;
386 switch (ev->key()) {
387 case Qt::Key_Plus:
388 actionExpandRow(m_curRow);
389 return;
390 case Qt::Key_Minus:
391 actionCollapseRow(m_curRow);
392 return;
393 case Qt::Key_Up:
394 if (m_curRow > 0) {
395 setCursorPosition(m_curRow-1, 0);
397 return;
398 case Qt::Key_Down:
399 if (m_curRow < paragraphs()-1) {
400 setCursorPosition(m_curRow+1, 0);
402 return;
403 case Qt::Key_Home:
404 setCursorPosition(0, 0);
405 return;
406 case Qt::Key_End:
407 setCursorPosition(paragraphs()-1, 0);
408 return;
409 case Qt::Key_Next:
410 case Qt::Key_Prior:
411 top = viewportToContents(QPoint(0,0));
412 top1 = paragraphAt(top);
415 QTextEdit::keyPressEvent(ev);
417 switch (ev->key()) {
418 case Qt::Key_Next:
419 case Qt::Key_Prior:
420 top = viewportToContents(QPoint(0,0));
421 top2 = paragraphAt(top);
422 setCursorPosition(m_curRow+(top2-top1), 0);
426 static inline bool isident(QChar c)
428 return c.isLetterOrNumber() || c.latin1() == '_';
431 bool SourceWindow::wordAtPoint(const QPoint& p, QString& word, QRect& r)
433 QPoint pv = viewportToContents(p - viewport()->pos());
434 int row, col = charAt(pv, &row);
435 if (row < 0 || col < 0)
436 return false;
438 // isolate the word at row, col
439 QString line = text(row);
440 if (!isident(line[col]))
441 return false;
443 int begin = col;
444 while (begin > 0 && isident(line[begin-1]))
445 --begin;
447 ++col;
448 while (col < int(line.length()) && isident(line[col]));
450 r = QRect(p, p);
451 r.addCoords(-5,-5,5,5);
452 word = line.mid(begin, col-begin);
453 return true;
456 void SourceWindow::paletteChange(const QPalette& oldPal)
458 setFont(KGlobalSettings::fixedFont());
459 QTextEdit::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 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(), Qt::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"