28 #include <QTextDocumentFragment>
29 #include <QTextCursor>
37 #include "klflatexedit_p.h"
43 struct KLFLatexParenSpecsPrivate
62 openParenListCache.
clear();
63 closeParenListCache.
clear();
64 openParenModifiersCache.
clear();
65 closeParenModifiersCache.
clear();
69 foreach (ParenSpec p, pl) {
70 openParenListCache << p.open;
71 closeParenListCache << p.close;
73 foreach (ParenModifierSpec m, ml) {
74 openParenModifiersCache << m.openmod;
75 closeParenModifiersCache << m.closemod;
102 d->load(default_parens, default_mods);
108 d->load(parens, modifiers);
114 d->load(other.d->parens, other.d->modifiers);
133 return d->openParenListCache;
137 return d->closeParenListCache;
141 return d->openParenModifiersCache;
145 return d->closeParenModifiersCache;
151 klfDbg(
"parenstr="<<parenstr<<
", ifl="<<identflags) ;
153 for (k = 0; k < d->parens.size(); ++k) {
160 klfWarning(
"Can't find paren "<<parenstr<<
" (fl="<<identflags<<
") in our specs!") ;
167 klfDbg(
"modstr="<<modstr<<
", ifl="<<identflags) ;
169 for (k = 0; k < d->modifiers.size(); ++k) {
176 klfWarning(
"Can't find paren modifier "<<modstr<<
" (fl="<<identflags<<
") in our specs!") ;
190 "parenSpecIndex is not valid! Using heuristic for parenIsLatexBrace().",
206 connect(
this, SIGNAL(cursorPositionChanged()),
207 d->mSyntaxHighlighter, SLOT(refreshAll()));
209 setContextMenuPolicy(Qt::DefaultContextMenu);
211 setProperty(
"klfDontChange_font",
QVariant(
true));
213 setProperty(
"paletteDefault", QVariant::fromValue<QPalette>(palette()));
216 setProperty(
"paletteMacBrushedMetalLook", QVariant::fromValue<QPalette>(pal));
218 setWordWrapMode(QTextOption::WrapAnywhere);
228 return toPlainText();
232 return d->pHeightHintLines;
236 d->mDropHandler = handler;
240 return d->mSyntaxHighlighter;
247 d->mSyntaxHighlighter->resetEditing();
255 cur.
select(QTextCursor::Document);
263 return wordWrapMode() != QTextOption::NoWrap;
267 setWordWrapMode(wrap ? QTextOption::WrapAnywhere : QTextOption::NoWrap);
273 QSize superSizeHint = QTextEdit::sizeHint();
274 if (d->pHeightHintLines >= 0) {
277 return superSizeHint;
282 d->pHeightHintLines = lines;
289 QPoint pos =
event->pos();
292 if ( ! textCursor().hasSelection() ) {
294 setTextCursor(cursorForPosition(pos));
297 QMenu * menu = createStandardContextMenu(mapToGlobal(pos));
302 if (actionList.
size()) {
303 menu->addSeparator();
304 for (k = 0; k < actionList.
size(); ++k) {
305 menu->addAction(actionList[k]);
309 menu->popup(mapToGlobal(pos));
317 if (d->mDropHandler != NULL)
318 if (d->mDropHandler->canOpenDropData(data))
322 return QTextEdit::canInsertFromMimeData(data);
328 if (d->mDropHandler != NULL) {
329 int res = d->mDropHandler->openDropData(data);
339 klfDbg(
"mDropHandler="<<d->mDropHandler<<
" did not handle the paste, doing it ourselves.") ;
342 QTextEdit::insertFromMimeData(data);
358 c1.
movePosition(QTextCursor::Left, QTextCursor::MoveAnchor, charsBack);
367 QTextEdit::setPalette(pal);
370 void KLFLatexEditPrivate::slotInsertFromActionSender()
373 if (obj == NULL || !obj->
inherits(
"QAction")) {
374 qWarning()<<
KLF_FUNC_NAME<<
": sender object is not a QAction: "<<obj;
377 QVariant v = qobject_cast<QAction*>(obj)->data();
378 QVariantMap vdata = v.
toMap();
379 K->insertDelimiter(vdata[
"delim"].toString(), vdata[
"charsBack"].toInt());
392 pConf.enabled =
true;
393 pConf.highlightParensOnly =
false;
394 pConf.highlightLonelyParens =
true;
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);
419 pConf.highlightParensOnly = on;
423 pConf.highlightLonelyParens = on;
456 klfDbg(
"pos="<<pos<<
", filter_mask="<<
klfFmtCC(
"%06x", filter_mask)<<
"; total # of blocks="
457 <<pParsedBlocks.size()) ;
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) ;
477 _caretpos = position;
485 void KLFLatexSyntaxHighlighter::parseEverything()
506 klfDbg(
"open-paren-rx string: "<<sopenrx<<
"; close-paren-rx string: "<<scloserx);
511 int lastparenparsingendpos = 0;
513 _rulestoapply.
clear();
514 pParsedBlocks.clear();
527 while ( i < text.
length() ) {
528 if (text[i] ==
'%') {
530 while (i+k < text.
length() && text[i+k] !=
'\n')
532 _rulestoapply.
append(FormatRule(blockpos+i, k, FComment));
537 if ( blockpos+i >= lastparenparsingendpos && rx_open.indexIn(text.
mid(i)) != -1) {
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());
547 lastparenparsingendpos = p.endpos;
549 else if ( blockpos+i >= lastparenparsingendpos && rx_close.indexIn(text.
mid(i)) != -1) {
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;
561 if (!parens.
empty()) {
567 while (ptrymatch.
size() && !cp.matches(ptrymatch.
top())) {
568 extralonelyparens << LonelyParenItem(ptrymatch.
top(), cp.beginpos);
571 if (ptrymatch.
size()) {
573 lonelyparens << extralonelyparens;
578 int topparenstackpos = 0;
580 topparenstackpos = parens.
top().endpos;
582 lonelyparens << LonelyParenItem(cp, topparenstackpos);
586 lonelyparens << LonelyParenItem(cp, 0);
593 col = FParenMismatch;
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));
601 _rulestoapply.
append(FormatRule(p.pos, cp.endpos - p.pos, col,
true));
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);
624 if (text[i] ==
'\\') {
629 while (i+k < text.
length() && ( (text[i+k] >=
'a' && text[i+k] <=
'z') ||
630 (text[i+k] >=
'A' && text[i+k] <=
'Z') ))
632 if (k == 0 && i+1 < text.
length())
637 _rulestoapply.
append(FormatRule(blockpos+i-1, k+1, FKeyWord));
639 pblk.keyword = symbol;
640 pParsedBlocks.append(pblk);
642 if (symbol.
size() > 1) {
643 klfDbg(
"symbol="<<symbol<<
" i="<<i<<
" k="<<k<<
" caretpos="<<_caretpos<<
" blockpos="<<blockpos);
644 if ( (_caretpos < blockpos+i ||_caretpos >= blockpos+i+k+1) &&
646 klfDbg(
"newSymbolTyped() about to be emitted for : "<<symbol);
648 pTypedSymbols.
append(symbol);
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));
663 block = block.
next();
670 klfDbg(
"maybe have some parens left, that are to be shown as lonely? "<<!parens.
empty()) ;
674 while (!parens.
empty()) {
675 lonelyparens << LonelyParenItem(parens.
top(), globendpos);
679 klfDbg(
"about to treat "<<lonelyparens.
size()<<
" lonely parens...") ;
681 for (k = 0; k < lonelyparens.
size(); ++k) {
683 LonelyParenItem p = lonelyparens[k];
687 pblk.parenisopening = p.isopening;
689 pblk.parenSpecIndex =
691 pblk.parenstr = p.parenstr;
692 pblk.parenmodifier = p.modifier;
693 pblk.parenotherpos = -1;
695 pParsedBlocks.append(pblk);
698 if (pblk.parenSpecIndex >= 0 &&
706 int chp = p.caretHoverPos();
707 if (chp == _caretpos) {
708 if (pConf.highlightParensOnly) {
709 _rulestoapply.
append(FormatRule(p.pos, p.poslength(), FParenMismatch,
true));
712 _rulestoapply.
append(FormatRule(chp, p.unmatchedpos-chp,
713 FParenMismatch,
true));
717 if (pConf.highlightLonelyParens)
718 _rulestoapply.
append(FormatRule(p.pos, p.poslength(), FLonelyParen));
731 fmt = pConf.fmtKeyword;
734 fmt = pConf.fmtComment;
737 fmt = pConf.fmtParenMatch;
740 fmt = pConf.fmtParenMismatch;
743 fmt = pConf.fmtLonelyParen;
759 if ( ! pConf.enabled )
773 blockfmtrules.
append(FormatRule(0, text.
length(), FNormal));
776 for (k = 0; k < _rulestoapply.
size(); ++k) {
777 int start = _rulestoapply[k].pos - block.
position();
778 int len = _rulestoapply[k].len;
784 if (start > text.
length())
786 if (len > text.
length() - start)
787 len = text.
length() - start;
793 klfDbg(
"Applying rule start="<<start<<
", len="<<len<<
", ...") ;
794 blockfmtrules.
append(FormatRule(start, len, _rulestoapply[k].
format, _rulestoapply[k].onlyIfFocus));
797 bool hasfocus = _textedit->hasFocus();
799 klfDbg(
"About to merge text formats... text.length()="<<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));
810 klfDbg(
"About to apply char formats...") ;
811 for (j = 0; j < charformats.
size(); ++j) {
835 default: stype =
"<error>";
break;
843 default: smatched =
"<error>";
break;