[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: klflatexedit.cpp 973 2016-12-30 07:38:23Z phfaist $ */
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 {
189  KLF_ASSERT_CONDITION(parenSpecIndex >= 0 && parenSpecIndex < parenSpecs.parenSpecList().size(),
190  "parenSpecIndex is not valid! Using heuristic for parenIsLatexBrace().",
191  return parenstr=="{" || parenstr=="}"; ) ;
192 
193  return parenSpecs.parenSpecList()[parenSpecIndex].flags & KLFLatexParenSpecs::ParenSpec::IsLaTeXBrace;
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 
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 
261 bool KLFLatexEdit::wrapLines() const
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());
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 }
void setFmtLonelyParen(const QTextFormat &f)
QTextBlock currentBlock() const
void clear()
void setHighlightLonelyParens(bool on)
QString cap(int nth) const
void removeSelectedText()
int width() const
Identify the paren as opening only.
Definition: klflatexedit.h:162
int position() const
#define KLF_PRIVATE_HEAD(ClassName)
Definition: klfdefs.h:81
QTextBlock next() const
void setColor(ColorGroup group, ColorRole role, const QColor &color)
static KLFLatexParenSpecs parenSpecs
Definition: klflatexedit.h:233
Opened the data Ok.
Definition: klfguiutil.h:530
QString escape(const QString &str)
void push(const T &t)
int size() const
const char * type
Definition: klfdatautil.cpp:96
void insertDelimiter(const QString &delim, int charsBack=1)
bool contains(const QString &str, Qt::CaseSensitivity cs) const
#define klfDbg(streamableItems)
print debug stream items
#define KLF_DEBUG_BLOCK(msg)
Utility to debug the execution of a block.
void setPalette(const QPalette &palette)
void setFormat(int start, int count, const QTextCharFormat &format)
QString join(const QString &separator) const
void setWrapLines(bool wrap)
KLFLatexSyntaxHighlighter(QTextEdit *textedit, QObject *parent)
bool movePosition(MoveOperation operation, MoveMode mode, int n)
int size() const
virtual ~KLFLatexParenSpecs()
void select(SelectionType selection)
virtual void highlightBlock(const QString &text)
int matchedLength() const
int indexIn(const QString &str, int offset, CaretMode caretMode) const
void setCaretPos(int position)
#define KLF_DELETE_PRIVATE
Definition: klfdefs.h:96
QTextCharFormat toCharFormat() const
void setFmtParenMatch(const QTextFormat &f)
QStringList openParenModifiers() const
void append(const T &value)
Identify the paren as closing only.
Definition: klflatexedit.h:163
virtual void contextMenuEvent(QContextMenuEvent *event)
QString & insert(int position, QChar ch)
void resize(int size)
#define KLF_EXPORT
Definition: klfdefs.h:41
int identifyModifier(const QString &modstr, uint identflags)
QTextBlock firstBlock() const
virtual ~KLFLatexEdit()
QTextDocumentFragment selection() const
bool inherits(const char *className) const
#define klfWarning(streamableItems)
QStringList closeParenList() const
void setDropDataHandler(KLFDropDataHandler *handler)
void endEditBlock()
void insertText(const QString &text)
void setHighlightEnabled(bool on)
void setFmtKeyword(const QTextFormat &f)
KLFLatexSyntaxHighlighter * syntaxHighlighter()
bool isEmpty() const
bool isValid() const
KLFLatexEdit(QWidget *parent)
An abstract handler for when data is dropped.
Definition: klfguiutil.h:526
QDebug operator<<(QDebug str, const KLFLatexSyntaxHighlighter::ParsedBlock &p)
QList< T > klfListMap(const QList< T > &list, MapOp op)
Definition: klfutil.h:452
virtual void insertFromMimeData(const QMimeData *source)
#define klfFmtCC
Definition: klfdefs.h:61
iterator end()
#define KLF_FUNC_NAME
QString latex() const
QTextDocument * document() const
void newSymbolTyped(const QString &symbolName)
A text edit field that edits latex code.
Definition: klflatexedit.h:51
void beginEditBlock()
QString text() const
QTextCharFormat format(int position) const
QStringList openParenList() const
QList< ParenModifierSpec > parenModifierSpecList() const
QByteArray toLatin1() const
QMap< QString, QVariant > toMap() const
QString mid(int position, int n) const
bool wrapLines() const
void setFmtComment(const QTextFormat &f)
QString toPlainText() const
int height() const
void setFmtParenMismatch(const QTextFormat &f)
QList< ParsedBlock > parsedBlocksForPos(int pos, unsigned int filter_type=0xffffffff) const
int length() const
bool isCharFormat() const
virtual QStringList formats() const
void setDocument(QTextDocument *doc)
QStringList closeParenModifiers() const
void insertContextMenuActions(const QPoint &pos, QList< QAction *> *actionList)
int heightHintLines() const
Could handle data format, but failed to open (no further processing)
Definition: klfguiutil.h:531
void setHighlightParensOnly(bool on)
QTextBlock lastBlock() const
#define KLF_INIT_PRIVATE(ClassName)
Definition: klfdefs.h:94
void setHeightHintLines(int lines)
#define KLF_ASSERT_CONDITION(expr, msg, failaction)
Asserting Conditions (NON-FATAL)
virtual QSize sizeHint() const
int size() const
virtual bool canInsertFromMimeData(const QMimeData *source) const
bool empty() const
QList< ParenSpec > parenSpecList() const
int identifyParen(const QString &parenstr, uint identflags)
void setLatex(const QString &latex)
T & top()
int length() const

Generated by doxygen 1.8.13