From 57dc817581faf54babaf3db0c602bac205dc6855 Mon Sep 17 00:00:00 2001 From: Juergen Spitzmueller Date: Sun, 6 May 2018 19:48:21 +0200 Subject: [PATCH] Add \nospellcheck font property This revives a ten year old idea (and patch) by Dov. You can now mark in the character dialog text and exclude it from spell checking. Fixes: #1042 File format change Remaining issue: The instant spell checking marks are not immediately removed, but only after some editing. --- development/FORMAT | 4 + lib/lyx2lyx/lyx_2_4.py | 15 +- src/Font.cpp | 11 +- src/FontInfo.cpp | 21 ++- src/FontInfo.h | 12 +- src/Paragraph.cpp | 3 + src/RowPainter.cpp | 34 +++- src/RowPainter.h | 2 + src/Text.cpp | 3 + src/Text2.cpp | 2 + src/frontends/qt4/GuiCharacter.cpp | 18 ++ src/frontends/qt4/GuiCharacter.h | 3 + src/frontends/qt4/ui/CharacterUi.ui | 274 ++++++++++++++-------------- src/version.h | 4 +- 14 files changed, 257 insertions(+), 149 deletions(-) diff --git a/development/FORMAT b/development/FORMAT index ce0bd80043..8338ee1b39 100644 --- a/development/FORMAT +++ b/development/FORMAT @@ -7,6 +7,10 @@ changes happened in particular if possible. A good example would be ----------------------- +2018-05-06 Jürgen Spitzmüller + * format incremented to 551: add font info param \nospellcheck that prevents + text from being spellchecked. + 2018-04-22 Jürgen Spitzmüller * format incremented to 550: rename \fontencoding global to \fontencoding auto. Semantic change: this is now automatically set depending on the document fonts. diff --git a/lib/lyx2lyx/lyx_2_4.py b/lib/lyx2lyx/lyx_2_4.py index ac84d6a2a1..941703a163 100644 --- a/lib/lyx2lyx/lyx_2_4.py +++ b/lib/lyx2lyx/lyx_2_4.py @@ -202,6 +202,17 @@ def revert_fontenc(document): document.header[i] = document.header[i].replace("auto", "global") +def revert_nospellcheck(document): + " Remove nospellcheck font info param " + + i = 0 + while True: + i = find_token(document.body, '\\nospellcheck', i) + if i == -1: + return + del document.body[i] + + ## # Conversion hub # @@ -213,10 +224,12 @@ convert = [ [547, []], [548, []], [549, []], - [550, [convert_fontenc]] + [550, [convert_fontenc]], + [551, []] ] revert = [ + [549, [revert_nospellcheck]], [549, [revert_fontenc]], [548, []],# dummy format change [547, [revert_lscape]], diff --git a/src/Font.cpp b/src/Font.cpp index b810e454d4..b8a6a29531 100644 --- a/src/Font.cpp +++ b/src/Font.cpp @@ -225,6 +225,8 @@ void Font::lyxWriteChanges(Font const & orgfont, os << "\\emph " << LyXMiscNames[bits_.emph()] << "\n"; if (orgfont.fontInfo().number() != bits_.number()) os << "\\numeric " << LyXMiscNames[bits_.number()] << "\n"; + if (orgfont.fontInfo().nospellcheck() != bits_.nospellcheck()) + os << "\\nospellcheck " << LyXMiscNames[bits_.nospellcheck()] << "\n"; if (orgfont.fontInfo().underbar() != bits_.underbar()) { // This is only for backwards compatibility switch (bits_.underbar()) { @@ -619,6 +621,7 @@ string Font::toString(bool const toggle) const << "uwave " << bits_.uwave() << '\n' << "noun " << bits_.noun() << '\n' << "number " << bits_.number() << '\n' + << "nospellcheck " << bits_.nospellcheck() << '\n' << "color " << bits_.color() << '\n' << "language " << lang << '\n' << "toggleall " << convert(toggle); @@ -660,7 +663,8 @@ bool Font::fromString(string const & data, bool & toggle) } else if (token == "emph" || token == "underbar" || token == "noun" || token == "number" || token == "uuline" || token == "uwave" - || token == "strikeout" || token == "xout") { + || token == "strikeout" || token == "xout" + || token == "nospellcheck") { int const next = lex.getInteger(); FontState const misc = FontState(next); @@ -681,6 +685,8 @@ bool Font::fromString(string const & data, bool & toggle) bits_.setNoun(misc); else if (token == "number") bits_.setNumber(misc); + else if (token == "nospellcheck") + bits_.setNoSpellcheck(misc); } else if (token == "color") { int const next = lex.getInteger(); @@ -809,7 +815,8 @@ ostream & operator<<(ostream & os, FontInfo const & f) << " uuline " << f.uuline() << " uwave " << f.uwave() << " noun " << f.noun() - << " number " << f.number(); + << " number " << f.number() + << " nospellcheck " << f.nospellcheck(); } diff --git a/src/FontInfo.cpp b/src/FontInfo.cpp index 2aaab38e5d..1d69d2b21b 100644 --- a/src/FontInfo.cpp +++ b/src/FontInfo.cpp @@ -71,6 +71,7 @@ FontInfo const sane_font( FONT_OFF, FONT_OFF, FONT_OFF, + FONT_OFF, FONT_OFF); FontInfo const inherit_font( @@ -87,7 +88,8 @@ FontInfo const inherit_font( FONT_INHERIT, FONT_INHERIT, FONT_INHERIT, - FONT_OFF); + FONT_OFF, + FONT_INHERIT); FontInfo const ignore_font( IGNORE_FAMILY, @@ -103,6 +105,7 @@ FontInfo const ignore_font( FONT_IGNORE, FONT_IGNORE, FONT_IGNORE, + FONT_IGNORE, FONT_IGNORE); @@ -226,6 +229,8 @@ void FontInfo::reduce(FontInfo const & tmplt) color_ = Color_inherit; if (background_ == tmplt.background_) background_ = Color_inherit; + if (nospellcheck_ == tmplt.nospellcheck_) + noun_ = FONT_INHERIT; } @@ -276,6 +281,9 @@ FontInfo & FontInfo::realize(FontInfo const & tmplt) if (background_ == Color_inherit) background_ = tmplt.background_; + if (nospellcheck_ == FONT_INHERIT) + nospellcheck_ = tmplt.nospellcheck_; + return *this; } @@ -375,6 +383,7 @@ void FontInfo::update(FontInfo const & newfont, bool toggleall) setUwave(setMisc(newfont.uwave_, uwave_)); setNoun(setMisc(newfont.noun_, noun_)); setNumber(setMisc(newfont.number_, number_)); + setNoSpellcheck(setMisc(newfont.nospellcheck_, nospellcheck_)); if (newfont.color_ == color_ && toggleall) setColor(Color_inherit); // toggle 'back' @@ -396,7 +405,7 @@ bool FontInfo::resolved() const && uuline_ != FONT_INHERIT && uwave_ != FONT_INHERIT && strikeout_ != FONT_INHERIT && xout_ != FONT_INHERIT && noun_ != FONT_INHERIT && color_ != Color_inherit - && background_ != Color_inherit); + && background_ != Color_inherit && nospellcheck_ != FONT_INHERIT); } @@ -689,6 +698,10 @@ FontInfo lyxRead(Lexer & lex, FontInfo const & fi) f.setUwave(FONT_ON); } else if (ttok == "noun") { f.setNoun(FONT_ON); + } else if (ttok == "nospellcheck") { + f.setNoSpellcheck(FONT_ON); + } else if (ttok == "no_nospellcheck") { + f.setNoSpellcheck(FONT_OFF); } else { lex.printError("Illegal misc type"); } @@ -751,6 +764,10 @@ void lyxWrite(ostream & os, FontInfo const & f, string const & start, int level) oss << indent << "\tMisc Noun\n"; else if (f.noun() == FONT_OFF) oss << indent << "\tMisc No_Noun\n"; + if (f.nospellcheck() == FONT_ON) + oss << indent << "\tMisc NoSpellcheck\n"; + else if (f.nospellcheck() == FONT_OFF) + oss << indent << "\tMisc No_NoSpellcheck\n"; if (f.color() != Color_inherit && f.color() != Color_none) oss << indent << "\tColor " << lcolor.getLyXName(f.color()) << '\n'; diff --git a/src/FontInfo.h b/src/FontInfo.h index 9f38000e66..696e64db47 100644 --- a/src/FontInfo.h +++ b/src/FontInfo.h @@ -48,11 +48,12 @@ public: FontState uuline, FontState uwave, FontState noun, - FontState number) + FontState number, + FontState nospellcheck) : family_(family), series_(series), shape_(shape), size_(size), style_(LM_ST_TEXT), color_(color), background_(background), emph_(emph), underbar_(underbar), strikeout_(strikeout), xout_(xout), uuline_(uuline), - uwave_(uwave), noun_(noun), number_(number) + uwave_(uwave), noun_(noun), number_(number), nospellcheck_(nospellcheck) {} /// Decreases font size by one @@ -92,6 +93,8 @@ public: void setColor(ColorCode c) { color_ = c; } ColorCode background() const { return background_; } void setBackground(ColorCode b) { background_ = b; } + FontState nospellcheck() const { return nospellcheck_; } + void setNoSpellcheck(FontState n) { nospellcheck_ = n; } //@} /// @@ -192,6 +195,8 @@ private: FontState noun_; /// FontState number_; + /// + FontState nospellcheck_; }; @@ -210,7 +215,8 @@ inline bool operator==(FontInfo const & lhs, FontInfo const & rhs) && lhs.uuline_ == rhs.uuline_ && lhs.uwave_ == rhs.uwave_ && lhs.noun_ == rhs.noun_ - && lhs.number_ == rhs.number_; + && lhs.number_ == rhs.number_ + && lhs.nospellcheck_ == rhs.nospellcheck_; } diff --git a/src/Paragraph.cpp b/src/Paragraph.cpp index 7aa0d1fc56..2b07456b50 100644 --- a/src/Paragraph.cpp +++ b/src/Paragraph.cpp @@ -4231,6 +4231,9 @@ SpellChecker::Result Paragraph::spellCheck(pos_type & from, pos_type & to, docstring word = asString(from, to, AS_STR_INSETS | AS_STR_SKIPDELETE); Language * lang = d->getSpellLanguage(from); + if (getFontSettings(d->inset_owner_->buffer().params(), from).fontInfo().nospellcheck() == FONT_ON) + return result; + wl = WordLangTuple(word, lang); if (word.empty()) diff --git a/src/RowPainter.cpp b/src/RowPainter.cpp index f093805213..32636136b5 100644 --- a/src/RowPainter.cpp +++ b/src/RowPainter.cpp @@ -139,6 +139,13 @@ void RowPainter::paintInset(Row::Element const & e) const } +void RowPainter::paintLanguageMarkings(Row::Element const & e) const +{ + paintForeignMark(e); + paintNoSpellingMark(e); +} + + void RowPainter::paintForeignMark(Row::Element const & e) const { Language const * lang = e.font.language(); @@ -157,6 +164,23 @@ void RowPainter::paintForeignMark(Row::Element const & e) const } +void RowPainter::paintNoSpellingMark(Row::Element const & e) const +{ + //if (!lyxrc.mark_no_spelling) + // return; + if (e.font.language() == latex_language) + return; + if (!e.font.fontInfo().nospellcheck()) + return; + + int const desc = e.inset ? e.dim.descent() : 0; + int const y = yo_ + pi_.base.solidLineOffset() + + desc + pi_.base.solidLineThickness() / 2; + pi_.pain.line(int(x_), y, int(x_ + e.full_width()), y, Color_language, + Painter::line_onoffdash, pi_.base.solidLineThickness()); +} + + void RowPainter::paintMisspelledMark(Row::Element const & e) const { // if changed the misspelled marker gets placed slightly lower than normal @@ -541,8 +565,9 @@ void RowPainter::paintOnlyInsets() Row::Element const & e = *cit; if (e.type == Row::INSET) { paintInset(e); - // The line that indicates word in a different language - paintForeignMark(e); + // The markings of foreign languages + // and of text ignored for spellchecking + paintLanguageMarkings(e); // change tracking (not for insets that handle it themselves) if (!e.inset->canPaintChange(*pi_.base.bv)) paintChange(e); @@ -578,8 +603,9 @@ void RowPainter::paintText() pi_.pain.textDecoration(e.font.fontInfo(), int(x_), yo_, int(e.full_width())); } - // The line that indicates word in a different language - paintForeignMark(e); + // The markings of foreign languages + // and of text ignored for spellchecking + paintLanguageMarkings(e); // change tracking (not for insets that handle it themselves) if (e.type != Row::INSET || ! e.inset->canPaintChange(*pi_.base.bv)) diff --git a/src/RowPainter.h b/src/RowPainter.h index 20febee826..18b4c85226 100644 --- a/src/RowPainter.h +++ b/src/RowPainter.h @@ -57,7 +57,9 @@ public: void paintSelection() const; private: + void paintLanguageMarkings(Row::Element const & e) const; void paintForeignMark(Row::Element const & e) const; + void paintNoSpellingMark(Row::Element const & e) const; void paintStringAndSel(Row::Element const & e) const; void paintMisspelledMark(Row::Element const & e) const; void paintChange(Row::Element const & e) const; diff --git a/src/Text.cpp b/src/Text.cpp index 301cd5f2e3..876c5d0cb1 100644 --- a/src/Text.cpp +++ b/src/Text.cpp @@ -445,6 +445,9 @@ void Text::readParToken(Paragraph & par, Lexer & lex, } else if (token == "\\numeric") { lex.next(); font.fontInfo().setNumber(setLyXMisc(lex.getString())); + } else if (token == "\\nospellcheck") { + lex.next(); + font.fontInfo().setNoSpellcheck(setLyXMisc(lex.getString())); } else if (token == "\\emph") { lex.next(); font.fontInfo().setEmph(setLyXMisc(lex.getString())); diff --git a/src/Text2.cpp b/src/Text2.cpp index bbd30a72d3..869684ed86 100644 --- a/src/Text2.cpp +++ b/src/Text2.cpp @@ -347,6 +347,8 @@ void Text::setFont(Cursor & cur, Font const & font, bool toggleall) newfi.setNoun(oldfi.noun() == FONT_OFF ? FONT_ON : FONT_OFF); if (newfi.number() == FONT_TOGGLE) newfi.setNumber(oldfi.number() == FONT_OFF ? FONT_ON : FONT_OFF); + if (newfi.nospellcheck() == FONT_TOGGLE) + newfi.setNoSpellcheck(oldfi.nospellcheck() == FONT_OFF ? FONT_ON : FONT_OFF); } setFont(cur.bv(), cur.selectionBegin().top(), diff --git a/src/frontends/qt4/GuiCharacter.cpp b/src/frontends/qt4/GuiCharacter.cpp index 777ca1ca92..f82bd8ac8c 100644 --- a/src/frontends/qt4/GuiCharacter.cpp +++ b/src/frontends/qt4/GuiCharacter.cpp @@ -261,6 +261,7 @@ GuiCharacter::GuiCharacter(GuiView & lv) bc().addReadOnly(strikeCO); bc().addReadOnly(nounCB); bc().addReadOnly(emphCB); + bc().addReadOnly(nospellcheckCB); bc().addReadOnly(langCO); bc().addReadOnly(colorCO); bc().addReadOnly(autoapplyCB); @@ -296,6 +297,18 @@ void GuiCharacter::on_nounCB_clicked() } +void GuiCharacter::on_nospellcheckCB_clicked() +{ + // skip intermediate state at user click + if (!nospellcheck_) { + nospellcheckCB->setCheckState(Qt::Checked); + nospellcheck_ = true; + } + change_adaptor(); +} + + + void GuiCharacter::change_adaptor() { changed(); @@ -418,6 +431,8 @@ void GuiCharacter::updateContents() font.fontInfo().setEmph(FONT_IGNORE); if (fi.noun() != tmp.fontInfo().noun()) font.fontInfo().setNoun(FONT_IGNORE); + if (fi.nospellcheck() != tmp.fontInfo().nospellcheck()) + font.fontInfo().setNoSpellcheck(FONT_IGNORE); if (fi.color() != tmp.fontInfo().color()) font.fontInfo().setColor(Color_ignore); if (fi.underbar() != tmp.fontInfo().underbar() @@ -519,8 +534,10 @@ void GuiCharacter::paramsToDialog(Font const & font) colorCO->setCurrentIndex(colorCO->findData(toqstr(lcolor.getLyXName(fi.color())))); emphCB->setCheckState(getMarkupState(fi.emph())); nounCB->setCheckState(getMarkupState(fi.noun())); + nospellcheckCB->setCheckState(getMarkupState(fi.nospellcheck())); emph_ = emphCB->checkState() == Qt::Checked; noun_ = nounCB->checkState() == Qt::Checked; + nospellcheck_ = nospellcheckCB->checkState() == Qt::Checked; // reset_language is a null pointer. QString const lang = (font.language() == reset_language) @@ -538,6 +555,7 @@ void GuiCharacter::applyView() fi.setSize(size[sizeCO->currentIndex()].second); fi.setEmph(setMarkupState(emphCB->checkState())); fi.setNoun(setMarkupState(nounCB->checkState())); + fi.setNoSpellcheck(setMarkupState(nospellcheckCB->checkState())); setBar(fi, bar[ulineCO->currentIndex()].second); setStrike(fi, strike[strikeCO->currentIndex()].second); fi.setColor(lcolor.getFromLyXName(fromqstr(colorCO->itemData(colorCO->currentIndex()).toString()))); diff --git a/src/frontends/qt4/GuiCharacter.h b/src/frontends/qt4/GuiCharacter.h index e9148e7aa0..d26ca470f4 100644 --- a/src/frontends/qt4/GuiCharacter.h +++ b/src/frontends/qt4/GuiCharacter.h @@ -65,6 +65,7 @@ protected Q_SLOTS: void change_adaptor(); void on_emphCB_clicked(); void on_nounCB_clicked(); + void on_nospellcheckCB_clicked(); private: /// \name Dialog inherited methods @@ -102,6 +103,8 @@ private: bool emph_; /// bool noun_; + /// + bool nospellcheck_; }; } // namespace frontend diff --git a/src/frontends/qt4/ui/CharacterUi.ui b/src/frontends/qt4/ui/CharacterUi.ui index 80db75151b..ca07dd0e31 100644 --- a/src/frontends/qt4/ui/CharacterUi.ui +++ b/src/frontends/qt4/ui/CharacterUi.ui @@ -7,7 +7,7 @@ 0 0 523 - 365 + 412 @@ -16,8 +16,86 @@ true - - + + + + + 6 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Apply each change automatically + + + Apply changes &immediately + + + + + + + Qt::Horizontal + + + QSizePolicy::Expanding + + + + 20 + 20 + + + + + + + + &OK + + + true + + + + + + + &Apply + + + false + + + + + + + Close + + + false + + + false + + + + + + @@ -243,14 +321,24 @@ - - - - - &Language - - - + + + Language Settings + + + + + + + + &Language: + + + langCO + + + + @@ -264,132 +352,48 @@ - - - - - - Semantic Markup - - - - - - Semantic emphasizing (italic by default, but can be adapted) - - - &Emphasized - - - - - - - Semantic markup of nouns (small caps by default, but can be adapted) - - - &Noun - - - - - - - - - - - - Qt::Vertical - - - QSizePolicy::Expanding - - - - 20 - 20 - - - + + + + + If this is selected, the marked text will not be spellchecked + + + E&xclude from Spellchecking + + + + + - - - - 6 + + + + Semantic Markup - - 0 - - - 0 - - - 0 - - - 0 - - - - - Apply each change automatically - - - Apply changes &immediately - - - - - - - Qt::Horizontal - - - QSizePolicy::Expanding - - - - 20 - 20 - - - - - - - - &OK - - - true - - - - - - - &Apply - - - false - - - - - - - Close - - - false - - - false - - - - + + + + + Semantic emphasizing (italic by default, but can be adapted) + + + &Emphasized + + + + + + + Semantic markup of nouns (small caps by default, but can be adapted) + + + &Noun + + + + + diff --git a/src/version.h b/src/version.h index e415ec9809..ede6c33411 100644 --- a/src/version.h +++ b/src/version.h @@ -32,8 +32,8 @@ extern char const * const lyx_version_info; // Do not remove the comment below, so we get merge conflict in // independent branches. Instead add your own. -#define LYX_FORMAT_LYX 550 // spitz: \fontenc auto -#define LYX_FORMAT_TEX2LYX 550 +#define LYX_FORMAT_LYX 551 // spitz: \nospellcheck font param +#define LYX_FORMAT_TEX2LYX 551 #if LYX_FORMAT_TEX2LYX != LYX_FORMAT_LYX #ifndef _MSC_VER -- 2.39.2