00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #include <QObject>
00025 #include <QWidget>
00026 #include <QStack>
00027 #include <QTextEdit>
00028 #include <QTextDocumentFragment>
00029 #include <QTextCursor>
00030 #include <QAction>
00031 #include <QMenu>
00032
00033 #include <klfguiutil.h>
00034
00035
00036 #include "klflatexedit.h"
00037 #include "klflatexedit_p.h"
00038
00039
00040
00041
00042
00043 struct KLFLatexParenSpecsPrivate
00044 {
00045 typedef KLFLatexParenSpecs::ParenSpec ParenSpec;
00046 typedef KLFLatexParenSpecs::ParenModifierSpec ParenModifierSpec;
00047
00048 KLF_PRIVATE_HEAD(KLFLatexParenSpecs)
00049 {
00050 }
00051
00052 QList<ParenSpec> parens;
00053 QList<ParenModifierSpec> modifiers;
00054
00055 QStringList openParenListCache;
00056 QStringList closeParenListCache;
00057 QStringList openParenModifiersCache;
00058 QStringList closeParenModifiersCache;
00059
00060 void load(const QList<ParenSpec>& pl, const QList<ParenModifierSpec>& ml)
00061 {
00062 openParenListCache.clear();
00063 closeParenListCache.clear();
00064 openParenModifiersCache.clear();
00065 closeParenModifiersCache.clear();
00066
00067 parens = pl;
00068 modifiers = ml;
00069 foreach (ParenSpec p, pl) {
00070 openParenListCache << p.open;
00071 closeParenListCache << p.close;
00072 }
00073 foreach (ParenModifierSpec m, ml) {
00074 openParenModifiersCache << m.openmod;
00075 closeParenModifiersCache << m.closemod;
00076 }
00077 }
00078 };
00079
00080 static QList<KLFLatexParenSpecs::ParenSpec> default_parens = QList<KLFLatexParenSpecs::ParenSpec>()
00081 << KLFLatexParenSpecs::ParenSpec("(", ")")
00082 << KLFLatexParenSpecs::ParenSpec("[", "]")
00083 << KLFLatexParenSpecs::ParenSpec("{", "}", KLFLatexParenSpecs::ParenSpec::IsLaTeXBrace)
00084 << KLFLatexParenSpecs::ParenSpec("\\{", "\\}")
00085 << KLFLatexParenSpecs::ParenSpec("\\lfloor", "\\rfloor", KLFLatexParenSpecs::ParenSpec::AllowAlone)
00086 << KLFLatexParenSpecs::ParenSpec("\\lceil", "\\rceil", KLFLatexParenSpecs::ParenSpec::AllowAlone)
00087 << KLFLatexParenSpecs::ParenSpec("\\langle", "\\rangle", KLFLatexParenSpecs::ParenSpec::AllowAlone)
00088 << KLFLatexParenSpecs::ParenSpec("\\lvert", "\\rvert", KLFLatexParenSpecs::ParenSpec::AllowAlone)
00089 << KLFLatexParenSpecs::ParenSpec("\\lVert", "\\rVert", KLFLatexParenSpecs::ParenSpec::AllowAlone)
00090 ;
00091
00092 static QList<KLFLatexParenSpecs::ParenModifierSpec> default_mods = QList<KLFLatexParenSpecs::ParenModifierSpec>()
00093 << KLFLatexParenSpecs::ParenModifierSpec("\\left", "\\right")
00094 << KLFLatexParenSpecs::ParenModifierSpec("\\bigl", "\\bigr")
00095 << KLFLatexParenSpecs::ParenModifierSpec("\\Bigl", "\\Bigr")
00096 ;
00097
00098
00099 KLFLatexParenSpecs::KLFLatexParenSpecs()
00100 {
00101 KLF_INIT_PRIVATE(KLFLatexParenSpecs) ;
00102 d->load(default_parens, default_mods);
00103 }
00104
00105 KLFLatexParenSpecs::KLFLatexParenSpecs(const QList<ParenSpec>& parens, const QList<ParenModifierSpec>& modifiers)
00106 {
00107 KLF_INIT_PRIVATE(KLFLatexParenSpecs) ;
00108 d->load(parens, modifiers);
00109 }
00110
00111 KLFLatexParenSpecs::KLFLatexParenSpecs(const KLFLatexParenSpecs& other)
00112 {
00113 KLF_INIT_PRIVATE(KLFLatexParenSpecs) ;
00114 d->load(other.d->parens, other.d->modifiers);
00115 }
00116 KLFLatexParenSpecs::~KLFLatexParenSpecs()
00117 {
00118 KLF_DELETE_PRIVATE ;
00119 }
00120
00121
00122 QList<KLFLatexParenSpecs::ParenSpec> KLFLatexParenSpecs::parenSpecList() const
00123 {
00124 return d->parens;
00125 }
00126 QList<KLFLatexParenSpecs::ParenModifierSpec> KLFLatexParenSpecs::parenModifierSpecList() const
00127 {
00128 return d->modifiers;
00129 }
00130
00131 QStringList KLFLatexParenSpecs::openParenList() const
00132 {
00133 return d->openParenListCache;
00134 }
00135 QStringList KLFLatexParenSpecs::closeParenList() const
00136 {
00137 return d->closeParenListCache;
00138 }
00139 QStringList KLFLatexParenSpecs::openParenModifiers() const
00140 {
00141 return d->openParenModifiersCache;
00142 }
00143 QStringList KLFLatexParenSpecs::closeParenModifiers() const
00144 {
00145 return d->closeParenModifiersCache;
00146 }
00147
00148 int KLFLatexParenSpecs::identifyParen(const QString& parenstr, uint identflags)
00149 {
00150 KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
00151 klfDbg("parenstr="<<parenstr<<", ifl="<<identflags) ;
00152 int k;
00153 for (k = 0; k < d->parens.size(); ++k) {
00154 ParenSpec p = d->parens[k];
00155 if ((identflags & IdentifyFlagOpen) && p.open == parenstr)
00156 return k;
00157 if ((identflags & IdentifyFlagClose) && p.close == parenstr)
00158 return k;
00159 }
00160 klfWarning("Can't find paren "<<parenstr<<" (fl="<<identflags<<") in our specs!") ;
00161 return -1;
00162 }
00163
00164 int KLFLatexParenSpecs::identifyModifier(const QString& modstr, uint identflags)
00165 {
00166 KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
00167 klfDbg("modstr="<<modstr<<", ifl="<<identflags) ;
00168 int k;
00169 for (k = 0; k < d->modifiers.size(); ++k) {
00170 ParenModifierSpec p = d->modifiers[k];
00171 if ((identflags & IdentifyFlagOpen) && p.openmod == modstr)
00172 return k;
00173 if ((identflags & IdentifyFlagClose) && p.closemod == modstr)
00174 return k;
00175 }
00176 klfWarning("Can't find paren modifier "<<modstr<<" (fl="<<identflags<<") in our specs!") ;
00177 return -1;
00178 }
00179
00180
00181
00182
00183
00184
00185 KLF_EXPORT KLFLatexParenSpecs KLFLatexSyntaxHighlighter::ParsedBlock::parenSpecs;
00186
00187 bool KLFLatexSyntaxHighlighter::ParsedBlock::parenIsLatexBrace() const
00188 {
00189 KLF_ASSERT_CONDITION(parenSpecIndex >= 0 && parenSpecIndex < parenSpecs.parenSpecList().size(),
00190 "parenSpecIndex is not valid! Using heuristic for parenIsLatexBrace().",
00191 return parenstr=="{" || parenstr=="}"; ) ;
00192
00193 return parenSpecs.parenSpecList()[parenSpecIndex].flags & KLFLatexParenSpecs::ParenSpec::IsLaTeXBrace;
00194 }
00195
00196
00197
00198
00199 KLFLatexEdit::KLFLatexEdit(QWidget *parent)
00200 : QTextEdit(parent)
00201 {
00202 KLF_INIT_PRIVATE(KLFLatexEdit) ;
00203
00204 d->mSyntaxHighlighter = new KLFLatexSyntaxHighlighter(this, this);
00205
00206 connect(this, SIGNAL(cursorPositionChanged()),
00207 d->mSyntaxHighlighter, SLOT(refreshAll()));
00208
00209 setContextMenuPolicy(Qt::DefaultContextMenu);
00210
00211 setProperty("klfDontChange_font", QVariant(true));
00212
00213 setProperty("paletteDefault", QVariant::fromValue<QPalette>(palette()));
00214 QPalette pal = palette();
00215 pal.setColor(QPalette::Base, QColor(255, 255, 255, 150));
00216 setProperty("paletteMacBrushedMetalLook", QVariant::fromValue<QPalette>(pal));
00217
00218 setWordWrapMode(QTextOption::WrapAnywhere);
00219 }
00220
00221 KLFLatexEdit::~KLFLatexEdit()
00222 {
00223 KLF_DELETE_PRIVATE ;
00224 }
00225
00226 QString KLFLatexEdit::latex() const
00227 {
00228 return toPlainText();
00229 }
00230 int KLFLatexEdit::heightHintLines() const
00231 {
00232 return d->pHeightHintLines;
00233 }
00234 void KLFLatexEdit::setDropDataHandler(KLFDropDataHandler *handler)
00235 {
00236 d->mDropHandler = handler;
00237 }
00238 KLFLatexSyntaxHighlighter *KLFLatexEdit::syntaxHighlighter()
00239 {
00240 return d->mSyntaxHighlighter;
00241 }
00242
00243 void KLFLatexEdit::clearLatex()
00244 {
00245 setLatex("");
00246 setFocus();
00247 d->mSyntaxHighlighter->resetEditing();
00248 }
00249
00250 void KLFLatexEdit::setLatex(const QString& latex)
00251 {
00252
00253 QTextCursor cur = textCursor();
00254 cur.beginEditBlock();
00255 cur.select(QTextCursor::Document);
00256 cur.removeSelectedText();
00257 cur.insertText(latex);
00258 cur.endEditBlock();
00259 }
00260
00261 bool KLFLatexEdit::wrapLines() const
00262 {
00263 return wordWrapMode() != QTextOption::NoWrap;
00264 }
00265 void KLFLatexEdit::setWrapLines(bool wrap)
00266 {
00267 setWordWrapMode(wrap ? QTextOption::WrapAnywhere : QTextOption::NoWrap);
00268 }
00269
00270
00271 QSize KLFLatexEdit::sizeHint() const
00272 {
00273 QSize superSizeHint = QTextEdit::sizeHint();
00274 if (d->pHeightHintLines >= 0) {
00275 return QSize(superSizeHint.width(), 4 + QFontMetrics(font()).height()*d->pHeightHintLines);
00276 }
00277 return superSizeHint;
00278 }
00279
00280 void KLFLatexEdit::setHeightHintLines(int lines)
00281 {
00282 d->pHeightHintLines = lines;
00283 updateGeometry();
00284 }
00285
00286
00287 void KLFLatexEdit::contextMenuEvent(QContextMenuEvent *event)
00288 {
00289 QPoint pos = event->pos();
00290 int k;
00291
00292 if ( ! textCursor().hasSelection() ) {
00293
00294 setTextCursor(cursorForPosition(pos));
00295 }
00296
00297 QMenu * menu = createStandardContextMenu(mapToGlobal(pos));
00298
00299 QList<QAction*> actionList;
00300 emit insertContextMenuActions(pos, &actionList);
00301
00302 if (actionList.size()) {
00303 menu->addSeparator();
00304 for (k = 0; k < actionList.size(); ++k) {
00305 menu->addAction(actionList[k]);
00306 }
00307 }
00308
00309 menu->popup(mapToGlobal(pos));
00310 event->accept();
00311 }
00312
00313
00314 bool KLFLatexEdit::canInsertFromMimeData(const QMimeData *data) const
00315 {
00316 klfDbg("formats: "<<data->formats());
00317 if (d->mDropHandler != NULL)
00318 if (d->mDropHandler->canOpenDropData(data))
00319 return true;
00320
00321
00322 return QTextEdit::canInsertFromMimeData(data);
00323 }
00324
00325 void KLFLatexEdit::insertFromMimeData(const QMimeData *data)
00326 {
00327 klfDbg("formats: "<<data->formats());
00328 if (d->mDropHandler != NULL) {
00329 int res = d->mDropHandler->openDropData(data);
00330 if (res == KLFDropDataHandler::OpenDataOk)
00331 return;
00332 if (res == KLFDropDataHandler::OpenDataFailed) {
00333
00334
00335
00336 }
00337 }
00338
00339 klfDbg("mDropHandler="<<d->mDropHandler<<" did not handle the paste, doing it ourselves.") ;
00340
00341
00342 QTextEdit::insertFromMimeData(data);
00343 }
00344
00345 void KLFLatexEdit::insertDelimiter(const QString& delim, int charsBack)
00346 {
00347 QTextCursor c1 = textCursor();
00348 c1.beginEditBlock();
00349 QString selected = c1.selection().toPlainText();
00350 QString toinsert = delim;
00351 if (selected.length())
00352 toinsert.insert(toinsert.length()-charsBack, selected);
00353 c1.removeSelectedText();
00354 c1.insertText(toinsert);
00355 c1.endEditBlock();
00356
00357 if (selected.isEmpty())
00358 c1.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor, charsBack);
00359
00360 setTextCursor(c1);
00361
00362 setFocus();
00363 }
00364
00365 void KLFLatexEdit::setPalette(const QPalette& pal)
00366 {
00367 QTextEdit::setPalette(pal);
00368 }
00369
00370 void KLFLatexEditPrivate::slotInsertFromActionSender()
00371 {
00372 QObject *obj = sender();
00373 if (obj == NULL || !obj->inherits("QAction")) {
00374 qWarning()<<KLF_FUNC_NAME<<": sender object is not a QAction: "<<obj;
00375 return;
00376 }
00377 QVariant v = qobject_cast<QAction*>(obj)->data();
00378 QVariantMap vdata = v.toMap();
00379 K->insertDelimiter(vdata["delim"].toString(), vdata["charsBack"].toInt());
00380 }
00381
00382
00383
00384
00385
00386 KLFLatexSyntaxHighlighter::KLFLatexSyntaxHighlighter(QTextEdit *textedit, QObject *parent)
00387 : QSyntaxHighlighter(parent) , _textedit(textedit)
00388 {
00389 setDocument(textedit->document());
00390
00391
00392 pConf.enabled = true;
00393 pConf.highlightParensOnly = false;
00394 pConf.highlightLonelyParens = true;
00395
00396 pConf.fmtKeyword.setForeground(QColor(0, 0, 128));
00397 pConf.fmtComment.setForeground(QColor(180, 0, 0));
00398 pConf.fmtComment.setFontItalic(true);
00399 pConf.fmtParenMatch.setBackground(QColor(180, 238, 180));
00400 pConf.fmtParenMismatch.setBackground(QColor(255, 20, 147));
00401 pConf.fmtLonelyParen.setForeground(QColor(255, 0, 255));
00402 pConf.fmtLonelyParen.setFontWeight(QFont::Bold);
00403
00404 _caretpos = 0;
00405 }
00406
00407 KLFLatexSyntaxHighlighter::~KLFLatexSyntaxHighlighter()
00408 {
00409 }
00410
00411
00412 void KLFLatexSyntaxHighlighter::setHighlightEnabled(bool on)
00413 {
00414 pConf.enabled = on;
00415 }
00416
00417 void KLFLatexSyntaxHighlighter::setHighlightParensOnly(bool on)
00418 {
00419 pConf.highlightParensOnly = on;
00420 }
00421 void KLFLatexSyntaxHighlighter::setHighlightLonelyParens(bool on)
00422 {
00423 pConf.highlightLonelyParens = on;
00424 }
00425 void KLFLatexSyntaxHighlighter::setFmtKeyword(const QTextFormat& f)
00426 {
00427 KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
00428 KLF_ASSERT_CONDITION(f.isCharFormat(), "Format "<<f<<" is not a QTextCharFormat.", return ; ) ;
00429 pConf.fmtKeyword = f.toCharFormat();
00430 }
00431 void KLFLatexSyntaxHighlighter::setFmtComment(const QTextFormat& f)
00432 {
00433 KLF_ASSERT_CONDITION(f.isCharFormat(), "Format "<<f<<" is not a QTextCharFormat.", return ; ) ;
00434 pConf.fmtComment = f.toCharFormat();
00435 }
00436 void KLFLatexSyntaxHighlighter::setFmtParenMatch(const QTextFormat& f)
00437 {
00438 KLF_ASSERT_CONDITION(f.isCharFormat(), "Format "<<f<<" is not a QTextCharFormat.", return ; ) ;
00439 pConf.fmtParenMatch = f.toCharFormat();
00440 }
00441 void KLFLatexSyntaxHighlighter::setFmtParenMismatch(const QTextFormat& f)
00442 {
00443 KLF_ASSERT_CONDITION(f.isCharFormat(), "Format "<<f<<" is not a QTextCharFormat.", return ; ) ;
00444 pConf.fmtParenMismatch = f.toCharFormat();
00445 }
00446 void KLFLatexSyntaxHighlighter::setFmtLonelyParen(const QTextFormat& f)
00447 {
00448 KLF_ASSERT_CONDITION(f.isCharFormat(), "Format "<<f<<" is not a QTextCharFormat.", return ; ) ;
00449 pConf.fmtLonelyParen = f.toCharFormat();
00450 }
00451
00452
00453 QList<KLFLatexSyntaxHighlighter::ParsedBlock>
00454 KLFLatexSyntaxHighlighter::parsedBlocksForPos(int pos, unsigned int filter_mask) const
00455 {
00456 klfDbg("pos="<<pos<<", filter_mask="<<klfFmtCC("%06x", filter_mask)<<"; total # of blocks="
00457 <<pParsedBlocks.size()) ;
00458 int k;
00459 QList<ParsedBlock> blocks;
00460 for (k = 0; k < pParsedBlocks.size(); ++k) {
00461 klfDbg("testing block #"<<k<<": "<<pParsedBlocks[k]<<"; block/pos+block/len="
00462 <<pParsedBlocks[k].pos+pParsedBlocks[k].len<<" compared to pos="<<pos) ;
00463 if (pParsedBlocks[k].pos <= pos && pos <= pParsedBlocks[k].pos+pParsedBlocks[k].len) {
00464 if (filter_mask & (1 << pParsedBlocks[k].type)) {
00465 blocks << pParsedBlocks[k];
00466 klfDbg("... added #"<<k) ;
00467 }
00468 }
00469 }
00470 return blocks;
00471 }
00472
00473
00474
00475 void KLFLatexSyntaxHighlighter::setCaretPos(int position)
00476 {
00477 _caretpos = position;
00478 }
00479
00480 void KLFLatexSyntaxHighlighter::refreshAll()
00481 {
00482 rehighlight();
00483 }
00484
00485 void KLFLatexSyntaxHighlighter::parseEverything()
00486 {
00487 KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
00488
00489 QString text;
00490 int i = 0;
00491 int blockpos;
00492 QList<uint> blocklens;
00493 QStack<ParenItem> parens;
00494 QList<LonelyParenItem> lonelyparens;
00495
00496 QTextBlock block = document()->firstBlock();
00497
00500 QString sopenrx =
00501 "^(?:("+QStringList(klfListMap(ParsedBlock::parenSpecs.openParenModifiers(), &QRegExp::escape)).join("|")+")\\s*)?"
00502 "(" + QStringList(klfListMap(ParsedBlock::parenSpecs.openParenList(), &QRegExp::escape)).join("|")+")";
00503 QString scloserx =
00504 "^(?:("+QStringList(klfListMap(ParsedBlock::parenSpecs.closeParenModifiers(), &QRegExp::escape)).join("|")+")\\s*)?"
00505 "(" + QStringList(klfListMap(ParsedBlock::parenSpecs.closeParenList(), &QRegExp::escape)).join("|")+")";
00506 klfDbg("open-paren-rx string: "<<sopenrx<<"; close-paren-rx string: "<<scloserx);
00507 QRegExp rx_open(sopenrx);
00508 QRegExp rx_close(scloserx);
00509
00510
00511 int lastparenparsingendpos = 0;
00512
00513 _rulestoapply.clear();
00514 pParsedBlocks.clear();
00515 int k;
00516 while (block.isValid()) {
00517 text = block.text();
00518 i = 0;
00519 blockpos = block.position();
00520 blocklens.append(block.length());
00521
00522 while (text.length() < block.length()) {
00523 text += "\n";
00524 }
00525
00526 i = 0;
00527 while ( i < text.length() ) {
00528 if (text[i] == '%') {
00529 k = 0;
00530 while (i+k < text.length() && text[i+k] != '\n')
00531 ++k;
00532 _rulestoapply.append(FormatRule(blockpos+i, k, FComment));
00533 pParsedBlocks.append(ParsedBlock(ParsedBlock::Comment, blockpos+i, k));
00534 i += k + 1;
00535 continue;
00536 }
00537 if ( blockpos+i >= lastparenparsingendpos && rx_open.indexIn(text.mid(i)) != -1) {
00538 ParenItem p;
00539 p.isopening = true;
00540 p.parenstr = rx_open.cap(2);
00541 p.modifier = rx_open.cap(1);
00542 p.beginpos = blockpos+i;
00543 p.endpos = blockpos+i+rx_open.matchedLength();
00544 p.pos = blockpos+i+p.modifier.length();
00545 p.highlight = (_caretpos == p.caretHoverPos());
00546 parens.push(p);
00547 lastparenparsingendpos = p.endpos;
00548 }
00549 else if ( blockpos+i >= lastparenparsingendpos && rx_close.indexIn(text.mid(i)) != -1) {
00550 ParenItem cp;
00551 cp.isopening = false;
00552 cp.parenstr = rx_close.cap(2);
00553 cp.modifier = rx_close.cap(1);
00554 cp.beginpos = blockpos+i;
00555 cp.pos = blockpos+i+cp.modifier.length();
00556 cp.endpos = blockpos+i+rx_close.matchedLength();
00557 cp.highlight = (_caretpos == cp.caretHoverPos());
00558 lastparenparsingendpos = cp.endpos;
00559
00560 ParenItem p;
00561 if (!parens.empty()) {
00562
00563
00564
00565 QStack<ParenItem> ptrymatch = parens;
00566 QList<LonelyParenItem> extralonelyparens;
00567 while (ptrymatch.size() && !cp.matches(ptrymatch.top())) {
00568 extralonelyparens << LonelyParenItem(ptrymatch.top(), cp.beginpos);
00569 ptrymatch.pop();
00570 }
00571 if (ptrymatch.size()) {
00572 parens = ptrymatch;
00573 lonelyparens << extralonelyparens;
00574 p = parens.top();
00575 parens.pop();
00576 } else {
00577
00578 int topparenstackpos = 0;
00579 if (parens.size()) {
00580 topparenstackpos = parens.top().endpos;
00581 }
00582 lonelyparens << LonelyParenItem(cp, topparenstackpos);
00583 continue;
00584 }
00585 } else {
00586 lonelyparens << LonelyParenItem(cp, 0);
00587 continue;
00588 }
00589 Format col;
00590 if (cp.matches(p))
00591 col = FParenMatch;
00592 else
00593 col = FParenMismatch;
00594
00595
00596 if (p.highlight || cp.highlight) {
00597 if (pConf.highlightParensOnly) {
00598 _rulestoapply.append(FormatRule(p.pos, p.poslength(), col, true));
00599 _rulestoapply.append(FormatRule(cp.pos, cp.poslength(), col, true));
00600 } else {
00601 _rulestoapply.append(FormatRule(p.pos, cp.endpos - p.pos, col, true));
00602 }
00603 }
00604 ParsedBlock pblk1(ParsedBlock::Paren, p.beginpos, p.beginposlength());
00605 ParsedBlock pblk2(ParsedBlock::Paren, cp.beginpos, cp.beginposlength());
00606 pblk1.parenmatch = ((col == FParenMatch) ? ParsedBlock::Matched : ParsedBlock::Mismatched);
00607 pblk1.parenisopening = true;
00608 pblk1.parenSpecIndex =
00609 ParsedBlock::parenSpecs.identifyParen(p.parenstr, KLFLatexParenSpecs::IdentifyFlagOpen);
00610 pblk1.parenstr = p.parenstr;
00611 pblk1.parenmodifier = p.modifier;
00612 pblk1.parenotherpos = cp.beginpos;
00613 pblk2.parenmatch = pblk1.parenmatch;
00614 pblk2.parenisopening = false;
00615 pblk2.parenSpecIndex =
00616 ParsedBlock::parenSpecs.identifyParen(cp.parenstr, KLFLatexParenSpecs::IdentifyFlagClose);
00617 pblk2.parenstr = cp.parenstr;
00618 pblk2.parenmodifier = cp.modifier;
00619 pblk2.parenotherpos = p.beginpos;
00620 pParsedBlocks.append(pblk1);
00621 pParsedBlocks.append(pblk2);
00622 }
00623
00624 if (text[i] == '\\') {
00625 ++i;
00626 k = 0;
00627 if (i >= text.length())
00628 continue;
00629 while (i+k < text.length() && ( (text[i+k] >= 'a' && text[i+k] <= 'z') ||
00630 (text[i+k] >= 'A' && text[i+k] <= 'Z') ))
00631 ++k;
00632 if (k == 0 && i+1 < text.length())
00633 k = 1;
00634
00635 QString symbol = text.mid(i-1,k+1);
00636
00637 _rulestoapply.append(FormatRule(blockpos+i-1, k+1, FKeyWord));
00638 ParsedBlock pblk(ParsedBlock::Keyword, blockpos+i-1, k+1);
00639 pblk.keyword = symbol;
00640 pParsedBlocks.append(pblk);
00641
00642 if (symbol.size() > 1) {
00643 klfDbg("symbol="<<symbol<<" i="<<i<<" k="<<k<<" caretpos="<<_caretpos<<" blockpos="<<blockpos);
00644 if ( (_caretpos < blockpos+i ||_caretpos >= blockpos+i+k+1) &&
00645 !pTypedSymbols.contains(symbol)) {
00646 klfDbg("newSymbolTyped() about to be emitted for : "<<symbol);
00647 emit newSymbolTyped(symbol);
00648 pTypedSymbols.append(symbol);
00649 }
00650 }
00651 i += k;
00652 continue;
00653 }
00654
00655 if (!text[i].isPrint() && text[i] != '\n' && text[i] != '\t' && text[i] != '\r') {
00657 _rulestoapply.append(FormatRule(blockpos+i-1, blockpos+i+1, FParenMismatch));
00658 }
00659
00660 ++i;
00661 }
00662
00663 block = block.next();
00664 }
00665
00666 QTextBlock lastblock = document()->lastBlock();
00667
00668 int globendpos = lastblock.position()+lastblock.length();
00669
00670 klfDbg("maybe have some parens left, that are to be shown as lonely? "<<!parens.empty()) ;
00671
00672
00673
00674 while (!parens.empty()) {
00675 lonelyparens << LonelyParenItem(parens.top(), globendpos);
00676 parens.pop();
00677 }
00678
00679 klfDbg("about to treat "<<lonelyparens.size()<<" lonely parens...") ;
00680
00681 for (k = 0; k < lonelyparens.size(); ++k) {
00682
00683 LonelyParenItem p = lonelyparens[k];
00684
00685 ParsedBlock pblk(ParsedBlock::Paren, p.beginpos, p.beginposlength());
00686 pblk.parenmatch = ParsedBlock::Lonely;
00687 pblk.parenisopening = p.isopening;
00688 uint iff = p.isopening ? KLFLatexParenSpecs::IdentifyFlagOpen : KLFLatexParenSpecs::IdentifyFlagClose;
00689 pblk.parenSpecIndex =
00690 ParsedBlock::parenSpecs.identifyParen(p.parenstr, iff);
00691 pblk.parenstr = p.parenstr;
00692 pblk.parenmodifier = p.modifier;
00693 pblk.parenotherpos = -1;
00694
00695 pParsedBlocks.append(pblk);
00696
00697
00698 if (pblk.parenSpecIndex >= 0 &&
00699 (ParsedBlock::parenSpecs.parenSpecList()[pblk.parenSpecIndex].flags &
00700 KLFLatexParenSpecs::ParenSpec::AllowAlone)) {
00701 continue;
00702 }
00703
00704
00705
00706 int chp = p.caretHoverPos();
00707 if (chp == _caretpos) {
00708 if (pConf.highlightParensOnly) {
00709 _rulestoapply.append(FormatRule(p.pos, p.poslength(), FParenMismatch, true));
00710 } else {
00711
00712 _rulestoapply.append(FormatRule(chp, p.unmatchedpos-chp,
00713 FParenMismatch, true));
00714 }
00715 }
00716
00717 if (pConf.highlightLonelyParens)
00718 _rulestoapply.append(FormatRule(p.pos, p.poslength(), FLonelyParen));
00719 }
00720
00721 }
00722
00723 QTextCharFormat KLFLatexSyntaxHighlighter::charfmtForFormat(Format f)
00724 {
00725 QTextCharFormat fmt;
00726 switch (f) {
00727 case FNormal:
00728 fmt = QTextCharFormat();
00729 break;
00730 case FKeyWord:
00731 fmt = pConf.fmtKeyword;
00732 break;
00733 case FComment:
00734 fmt = pConf.fmtComment;
00735 break;
00736 case FParenMatch:
00737 fmt = pConf.fmtParenMatch;
00738 break;
00739 case FParenMismatch:
00740 fmt = pConf.fmtParenMismatch;
00741 break;
00742 case FLonelyParen:
00743 fmt = pConf.fmtLonelyParen;
00744 break;
00745 default:
00746 fmt = QTextCharFormat();
00747 break;
00748 };
00749 return fmt;
00750 }
00751
00752
00753 void KLFLatexSyntaxHighlighter::highlightBlock(const QString& text)
00754 {
00755 KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
00756
00757 klfDbg("text is "<<text);
00758
00759 if ( ! pConf.enabled )
00760 return;
00761
00762 QTextBlock block = currentBlock();
00763
00764
00765
00766 if (block.position() == 0) {
00767 setCaretPos(_textedit->textCursor().position());
00768 parseEverything();
00769 }
00770
00771 QList<FormatRule> blockfmtrules;
00772
00773 blockfmtrules.append(FormatRule(0, text.length(), FNormal));
00774
00775 int k, j;
00776 for (k = 0; k < _rulestoapply.size(); ++k) {
00777 int start = _rulestoapply[k].pos - block.position();
00778 int len = _rulestoapply[k].len;
00779
00780 if (start < 0) {
00781 len += start;
00782 start = 0;
00783 }
00784 if (start > text.length())
00785 continue;
00786 if (len > text.length() - start)
00787 len = text.length() - start;
00788
00789 if (len <= 0)
00790 continue;
00791
00792
00793 klfDbg("Applying rule start="<<start<<", len="<<len<<", ...") ;
00794 blockfmtrules.append(FormatRule(start, len, _rulestoapply[k].format, _rulestoapply[k].onlyIfFocus));
00795 }
00796
00797 bool hasfocus = _textedit->hasFocus();
00798
00799 klfDbg("About to merge text formats... text.length()="<<text.length()) ;
00800 QVector<QTextCharFormat> charformats;
00801 charformats.resize(text.length());
00802 for (k = 0; k < blockfmtrules.size(); ++k) {
00803 klfDbg("got block-fmt-rule #"<<k<<"; start="<<blockfmtrules[k].pos<<", len="<<blockfmtrules[k].len
00804 <<", end="<<blockfmtrules[k].end()) ;
00805 for (j = blockfmtrules[k].pos; j < blockfmtrules[k].end(); ++j) {
00806 if ( ! blockfmtrules[k].onlyIfFocus || hasfocus )
00807 charformats[j].merge(charfmtForFormat(blockfmtrules[k].format));
00808 }
00809 }
00810 klfDbg("About to apply char formats...") ;
00811 for (j = 0; j < charformats.size(); ++j) {
00812 setFormat(j, 1, charformats[j]);
00813 }
00814
00815 return;
00816 }
00817
00818
00819 void KLFLatexSyntaxHighlighter::resetEditing()
00820 {
00821 pTypedSymbols = QStringList();
00822 }
00823
00824
00825
00826
00827 QDebug operator<<(QDebug str, const KLFLatexSyntaxHighlighter::ParsedBlock& p)
00828 {
00829 QString stype;
00830 switch (p.type) {
00831 case KLFLatexSyntaxHighlighter::ParsedBlock::Normal: stype = "-"; break;
00832 case KLFLatexSyntaxHighlighter::ParsedBlock::Keyword: stype = "Keyword"; break;
00833 case KLFLatexSyntaxHighlighter::ParsedBlock::Comment: stype = "Comment"; break;
00834 case KLFLatexSyntaxHighlighter::ParsedBlock::Paren: stype = "Paren"; break;
00835 default: stype = "<error>"; break;
00836 }
00837 QString smatched;
00838 switch (p.parenmatch) {
00839 case KLFLatexSyntaxHighlighter::ParsedBlock::None: smatched = "-"; break;
00840 case KLFLatexSyntaxHighlighter::ParsedBlock::Matched: smatched = "Matched"; break;
00841 case KLFLatexSyntaxHighlighter::ParsedBlock::Mismatched: smatched = "Mismatched"; break;
00842 case KLFLatexSyntaxHighlighter::ParsedBlock::Lonely: smatched = "Lonely"; break;
00843 default: smatched = "<error>"; break;
00844 }
00845 str << "ParsedBlock["<<stype.toLatin1()<<": "<<p.pos<<"+"<<p.len;
00846 if (p.type == KLFLatexSyntaxHighlighter::ParsedBlock::Keyword) {
00847 str << ", "<<p.keyword;
00848 } else if (KLFLatexSyntaxHighlighter::ParsedBlock::Paren) {
00849 str << ", "<<smatched.toLatin1()<<(p.parenisopening?"(opening)":"(closing)")<<"#"<<p.parenSpecIndex<<" "
00850 <<p.parenmodifier<<p.parenstr<<" otherpos="<<p.parenotherpos;
00851 }
00852 return str << "]";
00853 }