[KLF Backend][KLF Tools][KLF Home]
KLatexFormula Project
klflatexedit.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  * file klflatexedit.cpp
3  * This file is part of the KLatexFormula Project.
4  * Copyright (C) 2011 by Philippe Faist
5  * philippe.faist at bluewin.ch
6  * *
7  * This program is free software; you can redistribute it and/or modify *
8  * it under the terms of the GNU General Public License as published by *
9  * the Free Software Foundation; either version 2 of the License, or *
10  * (at your option) any later version. *
11  * *
12  * This program is distributed in the hope that it will be useful, *
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15  * GNU General Public License for more details. *
16  * *
17  * You should have received a copy of the GNU General Public License *
18  * along with this program; if not, write to the *
19  * Free Software Foundation, Inc., *
20  * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
21  ***************************************************************************/
22 /* $Id$ */
23 
24 #include <QObject>
25 #include <QWidget>
26 #include <QStack>
27 #include <QTextEdit>
28 #include <QTextDocumentFragment>
29 #include <QTextCursor>
30 #include <QAction>
31 #include <QMenu>
32 
33 #include <klfguiutil.h>
34 
35 
36 #include "klflatexedit.h"
37 #include "klflatexedit_p.h"
38 
39 
40 
41 // --------------------------
42 
43 struct KLFLatexParenSpecsPrivate
44 {
45  typedef KLFLatexParenSpecs::ParenSpec ParenSpec;
46  typedef KLFLatexParenSpecs::ParenModifierSpec ParenModifierSpec;
47 
49  {
50  }
51 
52  QList<ParenSpec> parens;
53  QList<ParenModifierSpec> modifiers;
54 
55  QStringList openParenListCache;
56  QStringList closeParenListCache;
57  QStringList openParenModifiersCache;
58  QStringList closeParenModifiersCache;
59 
60  void load(const QList<ParenSpec>& pl, const QList<ParenModifierSpec>& ml)
61  {
62  openParenListCache.clear();
63  closeParenListCache.clear();
64  openParenModifiersCache.clear();
65  closeParenModifiersCache.clear();
66 
67  parens = pl;
68  modifiers = ml;
69  foreach (ParenSpec p, pl) {
70  openParenListCache << p.open;
71  closeParenListCache << p.close;
72  }
73  foreach (ParenModifierSpec m, ml) {
74  openParenModifiersCache << m.openmod;
75  closeParenModifiersCache << m.closemod;
76  }
77  }
78 };
79 
84  << KLFLatexParenSpecs::ParenSpec("\\{", "\\}")
90  ;
91 
93  << KLFLatexParenSpecs::ParenModifierSpec("\\left", "\\right")
94  << KLFLatexParenSpecs::ParenModifierSpec("\\bigl", "\\bigr")
95  << KLFLatexParenSpecs::ParenModifierSpec("\\Bigl", "\\Bigr")
96  ;
97 
98 // loads the default paren & paren modifier specs
100 {
102  d->load(default_parens, default_mods);
103 }
104 // loads the given paren & paren modifier spec list
106 {
108  d->load(parens, modifiers);
109 }
110 
112 {
114  d->load(other.d->parens, other.d->modifiers);
115 }
117 {
119 }
120 
121 
123 {
124  return d->parens;
125 }
127 {
128  return d->modifiers;
129 }
130 
132 {
133  return d->openParenListCache;
134 }
136 {
137  return d->closeParenListCache;
138 }
140 {
141  return d->openParenModifiersCache;
142 }
144 {
145  return d->closeParenModifiersCache;
146 }
147 
148 int KLFLatexParenSpecs::identifyParen(const QString& parenstr, uint identflags)
149 {
151  klfDbg("parenstr="<<parenstr<<", ifl="<<identflags) ;
152  int k;
153  for (k = 0; k < d->parens.size(); ++k) {
154  ParenSpec p = d->parens[k];
155  if ((identflags & IdentifyFlagOpen) && p.open == parenstr)
156  return k;
157  if ((identflags & IdentifyFlagClose) && p.close == parenstr)
158  return k;
159  }
160  klfWarning("Can't find paren "<<parenstr<<" (fl="<<identflags<<") in our specs!") ;
161  return -1;
162 }
163 
164 int KLFLatexParenSpecs::identifyModifier(const QString& modstr, uint identflags)
165 {
167  klfDbg("modstr="<<modstr<<", ifl="<<identflags) ;
168  int k;
169  for (k = 0; k < d->modifiers.size(); ++k) {
170  ParenModifierSpec p = d->modifiers[k];
171  if ((identflags & IdentifyFlagOpen) && p.openmod == modstr)
172  return k;
173  if ((identflags & IdentifyFlagClose) && p.closemod == modstr)
174  return k;
175  }
176  klfWarning("Can't find paren modifier "<<modstr<<" (fl="<<identflags<<") in our specs!") ;
177  return -1;
178 }
179 
180 
181 // --------------------------
182 
183 // static
184 // this will load the default set of paren specs
186 
188 {
190  "parenSpecIndex is not valid! Using heuristic for parenIsLatexBrace().",
191  return parenstr=="{" || parenstr=="}"; ) ;
192 
194 }
195 
196 
197 // --------------------------
198 
200  : QTextEdit(parent)
201 {
203 
204  d->mSyntaxHighlighter = new KLFLatexSyntaxHighlighter(this, this);
205 
206  connect(this, SIGNAL(cursorPositionChanged()),
207  d->mSyntaxHighlighter, SLOT(refreshAll()));
208 
209  setContextMenuPolicy(Qt::DefaultContextMenu);
210 
211  setProperty("klfDontChange_font", QVariant(true));
212 
213  setProperty("paletteDefault", QVariant::fromValue<QPalette>(palette()));
214  QPalette pal = palette();
215  pal.setColor(QPalette::Base, QColor(255, 255, 255, 150)); // quite transparent, but lighter
216  setProperty("paletteMacBrushedMetalLook", QVariant::fromValue<QPalette>(pal));
217 
218  setWordWrapMode(QTextOption::WrapAnywhere);
219 }
220 
222 {
224 }
225 
227 {
228  return toPlainText();
229 }
231 {
232  return d->pHeightHintLines;
233 }
235 {
236  d->mDropHandler = handler;
237 }
239 {
240  return d->mSyntaxHighlighter;
241 }
242 
244 {
245  setLatex("");
246  setFocus();
247  d->mSyntaxHighlighter->resetEditing();
248 }
249 
250 void KLFLatexEdit::setLatex(const QString& latex)
251 {
252  // don't call setPlainText(); we want to preserve undo history
253  QTextCursor cur = textCursor();
254  cur.beginEditBlock();
255  cur.select(QTextCursor::Document);
256  cur.removeSelectedText();
257  cur.insertText(latex);
258  cur.endEditBlock();
259 }
260 
262 {
263  return wordWrapMode() != QTextOption::NoWrap;
264 }
266 {
267  setWordWrapMode(wrap ? QTextOption::WrapAnywhere : QTextOption::NoWrap);
268 }
269 
270 
272 {
273  QSize superSizeHint = QTextEdit::sizeHint();
274  if (d->pHeightHintLines >= 0) {
275  return QSize(superSizeHint.width(), 4 + QFontMetrics(font()).height()*d->pHeightHintLines);
276  }
277  return superSizeHint;
278 }
279 
281 {
282  d->pHeightHintLines = lines;
283  updateGeometry();
284 }
285 
286 
288 {
289  QPoint pos = event->pos();
290  int k;
291 
292  if ( ! textCursor().hasSelection() ) {
293  // move cursor at that point, but not if we have a selection
294  setTextCursor(cursorForPosition(pos));
295  }
296 
297  QMenu * menu = createStandardContextMenu(mapToGlobal(pos));
298 
299  QList<QAction*> actionList;
300  emit insertContextMenuActions(pos, &actionList);
301 
302  if (actionList.size()) {
303  menu->addSeparator();
304  for (k = 0; k < actionList.size(); ++k) {
305  menu->addAction(actionList[k]);
306  }
307  }
308 
309  menu->popup(mapToGlobal(pos));
310  event->accept();
311 }
312 
313 
315 {
316  klfDbg("formats: "<<data->formats());
317  if (d->mDropHandler != NULL)
318  if (d->mDropHandler->canOpenDropData(data))
319  return true; // data can be opened by main window
320 
321  // or check if we can insert the data ourselves
322  return QTextEdit::canInsertFromMimeData(data);
323 }
324 
326 {
327  klfDbg("formats: "<<data->formats());
328  if (d->mDropHandler != NULL) {
329  int res = d->mDropHandler->openDropData(data);
331  return; // data was opened by main window
333  // NO: eg. for plain text, try again with QTextEdit's paste
334  // // failed to open data, don't insist.
335  // return;
336  }
337  }
338 
339  klfDbg("mDropHandler="<<d->mDropHandler<<" did not handle the paste, doing it ourselves.") ;
340 
341  // insert the data ourselves
342  QTextEdit::insertFromMimeData(data);
343 }
344 
345 void KLFLatexEdit::insertDelimiter(const QString& delim, int charsBack)
346 {
347  QTextCursor c1 = textCursor();
348  c1.beginEditBlock();
349  QString selected = c1.selection().toPlainText();
350  QString toinsert = delim;
351  if (selected.length())
352  toinsert.insert(toinsert.length()-charsBack, selected);
353  c1.removeSelectedText();
354  c1.insertText(toinsert);
355  c1.endEditBlock();
356 
357  if (selected.isEmpty())
358  c1.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor, charsBack);
359 
360  setTextCursor(c1);
361 
362  setFocus();
363 }
364 
366 {
367  QTextEdit::setPalette(pal);
368 }
369 
370 void KLFLatexEditPrivate::slotInsertFromActionSender()
371 {
372  QObject *obj = sender();
373  if (obj == NULL || !obj->inherits("QAction")) {
374  qWarning()<<KLF_FUNC_NAME<<": sender object is not a QAction: "<<obj;
375  return;
376  }
377  QVariant v = qobject_cast<QAction*>(obj)->data();
378  QVariantMap vdata = v.toMap();
379  K->insertDelimiter(vdata["delim"].toString(), vdata["charsBack"].toInt());
380 }
381 
382 
383 // ------------------------------------
384 
385 
387  : QSyntaxHighlighter(parent) , _textedit(textedit)
388 {
389  setDocument(textedit->document());
390 
391  // some reasonable defaults for our config...
392  pConf.enabled = true;
393  pConf.highlightParensOnly = false;
394  pConf.highlightLonelyParens = true;
395 
396  pConf.fmtKeyword.setForeground(QColor(0, 0, 128));
397  pConf.fmtComment.setForeground(QColor(180, 0, 0));
398  pConf.fmtComment.setFontItalic(true);
399  pConf.fmtParenMatch.setBackground(QColor(180, 238, 180));
400  pConf.fmtParenMismatch.setBackground(QColor(255, 20, 147));
401  pConf.fmtLonelyParen.setForeground(QColor(255, 0, 255));
402  pConf.fmtLonelyParen.setFontWeight(QFont::Bold);
403 
404  _caretpos = 0;
405 }
406 
408 {
409 }
410 
411 
413 {
414  pConf.enabled = on;
415 }
416 
418 {
419  pConf.highlightParensOnly = on;
420 }
422 {
423  pConf.highlightLonelyParens = on;
424 }
426 {
428  KLF_ASSERT_CONDITION(f.isCharFormat(), "Format "<<f<<" is not a QTextCharFormat.", return ; ) ;
429  pConf.fmtKeyword = f.toCharFormat();
430 }
432 {
433  KLF_ASSERT_CONDITION(f.isCharFormat(), "Format "<<f<<" is not a QTextCharFormat.", return ; ) ;
434  pConf.fmtComment = f.toCharFormat();
435 }
437 {
438  KLF_ASSERT_CONDITION(f.isCharFormat(), "Format "<<f<<" is not a QTextCharFormat.", return ; ) ;
439  pConf.fmtParenMatch = f.toCharFormat();
440 }
442 {
443  KLF_ASSERT_CONDITION(f.isCharFormat(), "Format "<<f<<" is not a QTextCharFormat.", return ; ) ;
444  pConf.fmtParenMismatch = f.toCharFormat();
445 }
447 {
448  KLF_ASSERT_CONDITION(f.isCharFormat(), "Format "<<f<<" is not a QTextCharFormat.", return ; ) ;
449  pConf.fmtLonelyParen = f.toCharFormat();
450 }
451 
452 
454 /* */ KLFLatexSyntaxHighlighter::parsedBlocksForPos(int pos, unsigned int filter_mask) const
455 {
456  klfDbg("pos="<<pos<<", filter_mask="<<klfFmtCC("%06x", filter_mask)<<"; total # of blocks="
457  <<pParsedBlocks.size()) ;
458  int k;
459  QList<ParsedBlock> blocks;
460  for (k = 0; k < pParsedBlocks.size(); ++k) {
461  klfDbg("testing block #"<<k<<": "<<pParsedBlocks[k]<<"; block/pos+block/len="
462  <<pParsedBlocks[k].pos+pParsedBlocks[k].len<<" compared to pos="<<pos) ;
463  if (pParsedBlocks[k].pos <= pos && pos <= pParsedBlocks[k].pos+pParsedBlocks[k].len) {
464  if (filter_mask & (1 << pParsedBlocks[k].type)) {
465  blocks << pParsedBlocks[k];
466  klfDbg("... added #"<<k) ;
467  }
468  }
469  }
470  return blocks; // return only the relevant blocks that intersect with position 'pos'
471 }
472 
473 
474 
476 {
477  _caretpos = position;
478 }
479 
481 {
482  rehighlight();
483 }
484 
485 void KLFLatexSyntaxHighlighter::parseEverything()
486 {
488 
489  QString text;
490  int i = 0;
491  int blockpos;
492  QList<uint> blocklens; // the length of each block
493  QStack<ParenItem> parens; // the parens that we'll meet
494  QList<LonelyParenItem> lonelyparens; // extra lonely parens that we can't close within the text
495 
496  QTextBlock block = document()->firstBlock();
497 
500  QString sopenrx =
501  "^(?:("+QStringList(klfListMap(ParsedBlock::parenSpecs.openParenModifiers(), &QRegExp::escape)).join("|")+")\\s*)?"
502  "(" + QStringList(klfListMap(ParsedBlock::parenSpecs.openParenList(), &QRegExp::escape)).join("|")+")";
503  QString scloserx =
504  "^(?:("+QStringList(klfListMap(ParsedBlock::parenSpecs.closeParenModifiers(), &QRegExp::escape)).join("|")+")\\s*)?"
505  "(" + QStringList(klfListMap(ParsedBlock::parenSpecs.closeParenList(), &QRegExp::escape)).join("|")+")";
506  klfDbg("open-paren-rx string: "<<sopenrx<<"; close-paren-rx string: "<<scloserx);
507  QRegExp rx_open(sopenrx);
508  QRegExp rx_close(scloserx);
509 
510  // needed to avoid double-parsing of eg. "\\left(" when parsing "\\left(" and then "("
511  int lastparenparsingendpos = 0;
512 
513  _rulestoapply.clear();
514  pParsedBlocks.clear();
515  int k;
516  while (block.isValid()) {
517  text = block.text();
518  i = 0;
519  blockpos = block.position();
520  blocklens.append(block.length());
521 
522  while (text.length() < block.length()) {
523  text += "\n";
524  }
525 
526  i = 0;
527  while ( i < text.length() ) {
528  if (text[i] == '%') {
529  k = 0;
530  while (i+k < text.length() && text[i+k] != '\n')
531  ++k;
532  _rulestoapply.append(FormatRule(blockpos+i, k, FComment));
533  pParsedBlocks.append(ParsedBlock(ParsedBlock::Comment, blockpos+i, k));
534  i += k + 1;
535  continue;
536  }
537  if ( blockpos+i >= lastparenparsingendpos && rx_open.indexIn(text.mid(i)) != -1) {
538  ParenItem p;
539  p.isopening = true;
540  p.parenstr = rx_open.cap(2);
541  p.modifier = rx_open.cap(1);
542  p.beginpos = blockpos+i;
543  p.endpos = blockpos+i+rx_open.matchedLength();
544  p.pos = blockpos+i+p.modifier.length();
545  p.highlight = (_caretpos == p.caretHoverPos());
546  parens.push(p);
547  lastparenparsingendpos = p.endpos;
548  }
549  else if ( blockpos+i >= lastparenparsingendpos && rx_close.indexIn(text.mid(i)) != -1) {
550  ParenItem cp;
551  cp.isopening = false;
552  cp.parenstr = rx_close.cap(2);
553  cp.modifier = rx_close.cap(1);
554  cp.beginpos = blockpos+i;
555  cp.pos = blockpos+i+cp.modifier.length();
556  cp.endpos = blockpos+i+rx_close.matchedLength();
557  cp.highlight = (_caretpos == cp.caretHoverPos());
558  lastparenparsingendpos = cp.endpos;
559 
560  ParenItem p;
561  if (!parens.empty()) {
562  // first try to match the same paren type, perhaps leaving a lonely unmatched paren between,
563  // eg. in "sin[\theta(1+t]", match both square brackets leaving the paren lonely.
564  // Do this on a copy of the stack, in case we don't find a matching paren
565  QStack<ParenItem> ptrymatch = parens;
566  QList<LonelyParenItem> extralonelyparens;
567  while (ptrymatch.size() && !cp.matches(ptrymatch.top())) {
568  extralonelyparens << LonelyParenItem(ptrymatch.top(), cp.beginpos);
569  ptrymatch.pop();
570  }
571  if (ptrymatch.size()) { // found match
572  parens = ptrymatch;
573  lonelyparens << extralonelyparens;
574  p = parens.top();
575  parens.pop();
576  } else {
577  // No match found, report a lonely paren.
578  int topparenstackpos = 0;
579  if (parens.size()) {
580  topparenstackpos = parens.top().endpos;
581  }
582  lonelyparens << LonelyParenItem(cp, topparenstackpos);
583  continue; // mismatch will be reported when processing lonely parens
584  }
585  } else {
586  lonelyparens << LonelyParenItem(cp, 0);
587  continue; // mismatch will be reported when processing lonely parens
588  }
589  Format col;
590  if (cp.matches(p))
591  col = FParenMatch;
592  else
593  col = FParenMismatch;
594 
595  // does this rule span multiple paragraphs, and do we need to show it (eg. cursor right after paren)
596  if (p.highlight || cp.highlight) {
597  if (pConf.highlightParensOnly) {
598  _rulestoapply.append(FormatRule(p.pos, p.poslength(), col, true));
599  _rulestoapply.append(FormatRule(cp.pos, cp.poslength(), col, true));
600  } else {
601  _rulestoapply.append(FormatRule(p.pos, cp.endpos - p.pos, col, true));
602  }
603  }
604  ParsedBlock pblk1(ParsedBlock::Paren, p.beginpos, p.beginposlength());
605  ParsedBlock pblk2(ParsedBlock::Paren, cp.beginpos, cp.beginposlength());
606  pblk1.parenmatch = ((col == FParenMatch) ? ParsedBlock::Matched : ParsedBlock::Mismatched);
607  pblk1.parenisopening = true;
608  pblk1.parenSpecIndex =
610  pblk1.parenstr = p.parenstr;
611  pblk1.parenmodifier = p.modifier;
612  pblk1.parenotherpos = cp.beginpos;
613  pblk2.parenmatch = pblk1.parenmatch;
614  pblk2.parenisopening = false;
615  pblk2.parenSpecIndex =
617  pblk2.parenstr = cp.parenstr;
618  pblk2.parenmodifier = cp.modifier;
619  pblk2.parenotherpos = p.beginpos;
620  pParsedBlocks.append(pblk1);
621  pParsedBlocks.append(pblk2);
622  }
623 
624  if (text[i] == '\\') { // a keyword ("\symbol")
625  ++i;
626  k = 0;
627  if (i >= text.length())
628  continue;
629  while (i+k < text.length() && ( (text[i+k] >= 'a' && text[i+k] <= 'z') ||
630  (text[i+k] >= 'A' && text[i+k] <= 'Z') ))
631  ++k;
632  if (k == 0 && i+1 < text.length())
633  k = 1;
634 
635  QString symbol = text.mid(i-1,k+1); // from i-1, length k+1
636 
637  _rulestoapply.append(FormatRule(blockpos+i-1, k+1, FKeyWord));
638  ParsedBlock pblk(ParsedBlock::Keyword, blockpos+i-1, k+1);
639  pblk.keyword = symbol;
640  pParsedBlocks.append(pblk);
641 
642  if (symbol.size() > 1) { // no empty backslash
643  klfDbg("symbol="<<symbol<<" i="<<i<<" k="<<k<<" caretpos="<<_caretpos<<" blockpos="<<blockpos);
644  if ( (_caretpos < blockpos+i ||_caretpos >= blockpos+i+k+1) &&
645  !pTypedSymbols.contains(symbol)) { // not typing symbol
646  klfDbg("newSymbolTyped() about to be emitted for : "<<symbol);
647  emit newSymbolTyped(symbol);
648  pTypedSymbols.append(symbol);
649  }
650  }
651  i += k;
652  continue;
653  }
654 
655  if (!text[i].isPrint() && text[i] != '\n' && text[i] != '\t' && text[i] != '\r') {
657  _rulestoapply.append(FormatRule(blockpos+i-1, blockpos+i+1, FParenMismatch));
658  }
659 
660  ++i;
661  }
662 
663  block = block.next();
664  }
665 
666  QTextBlock lastblock = document()->lastBlock();
667 
668  int globendpos = lastblock.position()+lastblock.length();
669 
670  klfDbg("maybe have some parens left, that are to be shown as lonely? "<<!parens.empty()) ;
671 
672  // collect lonely parens list: all unclosed parens should be added to the list of collected
673  // unclosed parens.
674  while (!parens.empty()) {
675  lonelyparens << LonelyParenItem(parens.top(), globendpos);
676  parens.pop();
677  }
678 
679  klfDbg("about to treat "<<lonelyparens.size()<<" lonely parens...") ;
680 
681  for (k = 0; k < lonelyparens.size(); ++k) {
682  // for each unclosed paren
683  LonelyParenItem p = lonelyparens[k];
684 
685  ParsedBlock pblk(ParsedBlock::Paren, p.beginpos, p.beginposlength());
686  pblk.parenmatch = ParsedBlock::Lonely;
687  pblk.parenisopening = p.isopening;
689  pblk.parenSpecIndex =
690  ParsedBlock::parenSpecs.identifyParen(p.parenstr, iff);
691  pblk.parenstr = p.parenstr;
692  pblk.parenmodifier = p.modifier;
693  pblk.parenotherpos = -1;
694 
695  pParsedBlocks.append(pblk);
696 
697  // if the paren was marked with flag that it can be alone, do not report error
698  if (pblk.parenSpecIndex >= 0 &&
699  (ParsedBlock::parenSpecs.parenSpecList()[pblk.parenSpecIndex].flags &
701  continue;
702  }
703 
704  // otherwise, report the lonely paren
705 
706  int chp = p.caretHoverPos();
707  if (chp == _caretpos) {
708  if (pConf.highlightParensOnly) {
709  _rulestoapply.append(FormatRule(p.pos, p.poslength(), FParenMismatch, true));
710  } else {
711  // FormatRule will accept a negative length
712  _rulestoapply.append(FormatRule(chp, p.unmatchedpos-chp,
713  FParenMismatch, true));
714  }
715  }
716  // highlight the lonely paren
717  if (pConf.highlightLonelyParens)
718  _rulestoapply.append(FormatRule(p.pos, p.poslength(), FLonelyParen));
719  }
720 
721 }
722 
723 QTextCharFormat KLFLatexSyntaxHighlighter::charfmtForFormat(Format f)
724 {
725  QTextCharFormat fmt;
726  switch (f) {
727  case FNormal:
728  fmt = QTextCharFormat();
729  break;
730  case FKeyWord:
731  fmt = pConf.fmtKeyword;
732  break;
733  case FComment:
734  fmt = pConf.fmtComment;
735  break;
736  case FParenMatch:
737  fmt = pConf.fmtParenMatch;
738  break;
739  case FParenMismatch:
740  fmt = pConf.fmtParenMismatch;
741  break;
742  case FLonelyParen:
743  fmt = pConf.fmtLonelyParen;
744  break;
745  default:
746  fmt = QTextCharFormat();
747  break;
748  };
749  return fmt;
750 }
751 
752 
754 {
756 
757  klfDbg("text is "<<text);
758 
759  if ( ! pConf.enabled )
760  return; // forget everything about synt highlight if we don't want it.
761 
762  QTextBlock block = currentBlock();
763 
764  // printf("\t -- block/position=%d\n", block.position());
765 
766  if (block.position() == 0) {
767  setCaretPos(_textedit->textCursor().position());
768  parseEverything();
769  }
770 
771  QList<FormatRule> blockfmtrules;
772 
773  blockfmtrules.append(FormatRule(0, text.length(), FNormal));
774 
775  int k, j;
776  for (k = 0; k < _rulestoapply.size(); ++k) {
777  int start = _rulestoapply[k].pos - block.position();
778  int len = _rulestoapply[k].len;
779 
780  if (start < 0) { // the rule starts before current paragraph
781  len += start; // "+" because start is negative
782  start = 0;
783  }
784  if (start > text.length())
785  continue;
786  if (len > text.length() - start)
787  len = text.length() - start;
788 
789  if (len <= 0)
790  continue; // empty rule...
791 
792  // apply rule
793  klfDbg("Applying rule start="<<start<<", len="<<len<<", ...") ;
794  blockfmtrules.append(FormatRule(start, len, _rulestoapply[k].format, _rulestoapply[k].onlyIfFocus));
795  }
796 
797  bool hasfocus = _textedit->hasFocus();
798 
799  klfDbg("About to merge text formats... text.length()="<<text.length()) ;
800  QVector<QTextCharFormat> charformats;
801  charformats.resize(text.length());
802  for (k = 0; k < blockfmtrules.size(); ++k) {
803  klfDbg("got block-fmt-rule #"<<k<<"; start="<<blockfmtrules[k].pos<<", len="<<blockfmtrules[k].len
804  <<", end="<<blockfmtrules[k].end()) ;
805  for (j = blockfmtrules[k].pos; j < blockfmtrules[k].end(); ++j) {
806  if ( ! blockfmtrules[k].onlyIfFocus || hasfocus )
807  charformats[j].merge(charfmtForFormat(blockfmtrules[k].format));
808  }
809  }
810  klfDbg("About to apply char formats...") ;
811  for (j = 0; j < charformats.size(); ++j) {
812  setFormat(j, 1, charformats[j]);
813  }
814 
815  return;
816 }
817 
818 
820 {
821  pTypedSymbols = QStringList();
822 }
823 
824 
825 
826 
828 {
829  QString stype;
830  switch (p.type) {
831  case KLFLatexSyntaxHighlighter::ParsedBlock::Normal: stype = "-"; break;
832  case KLFLatexSyntaxHighlighter::ParsedBlock::Keyword: stype = "Keyword"; break;
833  case KLFLatexSyntaxHighlighter::ParsedBlock::Comment: stype = "Comment"; break;
834  case KLFLatexSyntaxHighlighter::ParsedBlock::Paren: stype = "Paren"; break;
835  default: stype = "<error>"; break;
836  }
837  QString smatched;
838  switch (p.parenmatch) {
839  case KLFLatexSyntaxHighlighter::ParsedBlock::None: smatched = "-"; break;
840  case KLFLatexSyntaxHighlighter::ParsedBlock::Matched: smatched = "Matched"; break;
841  case KLFLatexSyntaxHighlighter::ParsedBlock::Mismatched: smatched = "Mismatched"; break;
842  case KLFLatexSyntaxHighlighter::ParsedBlock::Lonely: smatched = "Lonely"; break;
843  default: smatched = "<error>"; break;
844  }
845  str << "ParsedBlock["<<stype.toLatin1()<<": "<<p.pos<<"+"<<p.len;
847  str << ", "<<p.keyword;
849  str << ", "<<smatched.toLatin1()<<(p.parenisopening?"(opening)":"(closing)")<<"#"<<p.parenSpecIndex<<" "
850  <<p.parenmodifier<<p.parenstr<<" otherpos="<<p.parenotherpos;
851  }
852  return str << "]";
853 }
An abstract handler for when data is dropped.
Definition: klfguiutil.h:527
@ OpenDataOk
Opened the data Ok.
Definition: klfguiutil.h:530
@ OpenDataFailed
Could handle data format, but failed to open (no further processing)
Definition: klfguiutil.h:531
A text edit field that edits latex code.
Definition: klflatexedit.h:52
void setPalette(const QPalette &palette)
KLFLatexSyntaxHighlighter * syntaxHighlighter()
void setLatex(const QString &latex)
int heightHintLines
Definition: klflatexedit.h:55
virtual ~KLFLatexEdit()
virtual void contextMenuEvent(QContextMenuEvent *event)
KLFLatexEdit(QWidget *parent)
virtual void insertFromMimeData(const QMimeData *source)
virtual QSize sizeHint() const
void setDropDataHandler(KLFDropDataHandler *handler)
void insertDelimiter(const QString &delim, int charsBack=1)
void setWrapLines(bool wrap)
void setHeightHintLines(int lines)
void insertContextMenuActions(const QPoint &pos, QList< QAction * > *actionList)
QString latex() const
virtual bool canInsertFromMimeData(const QMimeData *source) const
QStringList openParenList() const
QList< ParenModifierSpec > parenModifierSpecList() const
virtual ~KLFLatexParenSpecs()
QStringList openParenModifiers() const
QList< ParenSpec > parenSpecList() const
int identifyModifier(const QString &modstr, uint identflags)
@ IdentifyFlagClose
Identify the paren as closing only.
Definition: klflatexedit.h:163
@ IdentifyFlagOpen
Identify the paren as opening only.
Definition: klflatexedit.h:162
int identifyParen(const QString &parenstr, uint identflags)
QStringList closeParenModifiers() const
QStringList closeParenList() const
void setFmtParenMatch(const QTextFormat &f)
KLFLatexSyntaxHighlighter(QTextEdit *textedit, QObject *parent)
virtual void highlightBlock(const QString &text)
void setFmtKeyword(const QTextFormat &f)
void setFmtLonelyParen(const QTextFormat &f)
void setFmtParenMismatch(const QTextFormat &f)
void newSymbolTyped(const QString &symbolName)
void setCaretPos(int position)
QList< ParsedBlock > parsedBlocksForPos(int pos, unsigned int filter_type=0xffffffff) const
void setHighlightEnabled(bool on)
void setHighlightParensOnly(bool on)
void setFmtComment(const QTextFormat &f)
void setHighlightLonelyParens(bool on)
const char * type
Definition: klfdatautil.cpp:96
#define klfWarning(streamableItems)
#define KLF_DEBUG_BLOCK(msg)
Utility to debug the execution of a block.
#define KLF_ASSERT_CONDITION(expr, msg, failaction)
Asserting Conditions (NON-FATAL)
#define KLF_FUNC_NAME
#define klfDbg(streamableItems)
print debug stream items
#define KLF_PRIVATE_HEAD(ClassName)
Definition: klfdefs.h:81
#define klfFmtCC
Definition: klfdefs.h:61
#define KLF_DELETE_PRIVATE
Definition: klfdefs.h:96
#define KLF_EXPORT
Definition: klfdefs.h:41
#define KLF_INIT_PRIVATE(ClassName)
Definition: klfdefs.h:94
QDebug operator<<(QDebug str, const KLFLatexSyntaxHighlighter::ParsedBlock &p)
QList< T > klfListMap(const QList< T > &list, MapOp op)
Definition: klfutil.h:452
void append(const T &value)
void clear()
iterator end()
int size() const
virtual QStringList formats() const
bool inherits(const char *className) const
QObject * parent() const
void setColor(ColorGroup group, ColorRole role, const QColor &color)
QString escape(const QString &str)
int width() const
void push(const T &t)
T & top()
QString & insert(int position, QChar ch)
bool isEmpty() const
int length() const
QString mid(int position, int n) const
int size() const
QByteArray toLatin1() const
bool contains(const QString &str, Qt::CaseSensitivity cs) const
QString join(const QString &separator) const
QTextBlock currentBlock() const
QTextDocument * document() const
QTextCharFormat format(int position) const
void setDocument(QTextDocument *doc)
void setFormat(int start, int count, const QTextCharFormat &format)
bool isValid() const
int length() const
QTextBlock next() const
int position() const
QString text() const
void beginEditBlock()
void endEditBlock()
void insertText(const QString &text)
bool movePosition(MoveOperation operation, MoveMode mode, int n)
void removeSelectedText()
void select(SelectionType selection)
QTextDocumentFragment selection() const
QTextBlock firstBlock() const
QTextBlock lastBlock() const
QString toPlainText() const
bool isCharFormat() const
QTextCharFormat toCharFormat() const
QMap< QString, QVariant > toMap() const
bool empty() const
void resize(int size)
int size() const
static KLFLatexParenSpecs parenSpecs
Definition: klflatexedit.h:233

Generated by doxygen 1.9.1