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;
An abstract handler for when data is dropped.
@ OpenDataOk
Opened the data Ok.
@ OpenDataFailed
Could handle data format, but failed to open (no further processing)
A text edit field that edits latex code.
void setPalette(const QPalette &palette)
KLFLatexSyntaxHighlighter * syntaxHighlighter()
void setLatex(const QString &latex)
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)
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.
@ IdentifyFlagOpen
Identify the paren as opening only.
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)
virtual ~KLFLatexSyntaxHighlighter()
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)
#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 klfDbg(streamableItems)
print debug stream items
#define KLF_PRIVATE_HEAD(ClassName)
#define KLF_DELETE_PRIVATE
#define KLF_INIT_PRIVATE(ClassName)
QDebug operator<<(QDebug str, const KLFLatexSyntaxHighlighter::ParsedBlock &p)
QList< T > klfListMap(const QList< T > &list, MapOp op)
void append(const T &value)
bool inherits(const char *className) const
void setColor(ColorGroup group, ColorRole role, const QColor &color)
QString escape(const QString &str)
QString & insert(int position, QChar ch)
QString mid(int position, int n) 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
void setDocument(QTextDocument *doc)
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
static KLFLatexParenSpecs parenSpecs
bool parenIsLatexBrace() const