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) {
154 ParenSpec p = d->parens[k];
155 if ((identflags & IdentifyFlagOpen) && p.
open == parenstr)
157 if ((identflags & IdentifyFlagClose) && p.
close == parenstr)
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) {
170 ParenModifierSpec p = d->modifiers[k];
171 if ((identflags & IdentifyFlagOpen) && p.
openmod == modstr)
173 if ((identflags & IdentifyFlagClose) && p.
closemod == modstr)
176 klfWarning(
"Can't find paren modifier "<<modstr<<
" (fl="<<identflags<<
") in our specs!") ;
190 "parenSpecIndex is not valid! Using heuristic for parenIsLatexBrace().",
191 return parenstr==
"{" || parenstr==
"}"; ) ;
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;
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();
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));
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;
void setFmtLonelyParen(const QTextFormat &f)
QTextBlock currentBlock() const
void setHighlightLonelyParens(bool on)
QString cap(int nth) const
void removeSelectedText()
Identify the paren as opening only.
#define KLF_PRIVATE_HEAD(ClassName)
void setColor(ColorGroup group, ColorRole role, const QColor &color)
static KLFLatexParenSpecs parenSpecs
virtual ~KLFLatexSyntaxHighlighter()
QString escape(const QString &str)
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)
QString join(const QString &separator) const
void setWrapLines(bool wrap)
KLFLatexSyntaxHighlighter(QTextEdit *textedit, QObject *parent)
bool movePosition(MoveOperation operation, MoveMode mode, int n)
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
QTextCharFormat toCharFormat() const
void setFmtParenMatch(const QTextFormat &f)
QStringList openParenModifiers() const
void append(const T &value)
Identify the paren as closing only.
virtual void contextMenuEvent(QContextMenuEvent *event)
QString & insert(int position, QChar ch)
int identifyModifier(const QString &modstr, uint identflags)
QTextBlock firstBlock() const
QTextDocumentFragment selection() const
bool inherits(const char *className) const
#define klfWarning(streamableItems)
QStringList closeParenList() const
void setDropDataHandler(KLFDropDataHandler *handler)
void insertText(const QString &text)
void setHighlightEnabled(bool on)
void setFmtKeyword(const QTextFormat &f)
KLFLatexSyntaxHighlighter * syntaxHighlighter()
KLFLatexEdit(QWidget *parent)
An abstract handler for when data is dropped.
QDebug operator<<(QDebug str, const KLFLatexSyntaxHighlighter::ParsedBlock &p)
QList< T > klfListMap(const QList< T > &list, MapOp op)
virtual void insertFromMimeData(const QMimeData *source)
QTextDocument * document() const
void newSymbolTyped(const QString &symbolName)
A text edit field that edits latex code.
QStringList openParenList() const
QList< ParenModifierSpec > parenModifierSpecList() const
QByteArray toLatin1() const
QMap< QString, QVariant > toMap() const
QString mid(int position, int n) const
bool parenIsLatexBrace() const
void setFmtComment(const QTextFormat &f)
QString toPlainText() const
void setFmtParenMismatch(const QTextFormat &f)
QList< ParsedBlock > parsedBlocksForPos(int pos, unsigned int filter_type=0xffffffff) const
bool isCharFormat() 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)
void setHighlightParensOnly(bool on)
QTextBlock lastBlock() const
#define KLF_INIT_PRIVATE(ClassName)
void setHeightHintLines(int lines)
#define KLF_ASSERT_CONDITION(expr, msg, failaction)
Asserting Conditions (NON-FATAL)
virtual QSize sizeHint() const
virtual bool canInsertFromMimeData(const QMimeData *source) const
QList< ParenSpec > parenSpecList() const
int identifyParen(const QString &parenstr, uint identflags)
void setLatex(const QString &latex)