]> git.lyx.org Git - lyx.git/blobdiff - src/Paragraph.cpp
Fix bug #7059: Display the vertical alignment of tables.
[lyx.git] / src / Paragraph.cpp
index 265945ae06ba8af6c95980503a44545e54472f8e..d44d0473e11f7ad8384429d5e82ace4b794040db 100644 (file)
@@ -51,6 +51,7 @@
 
 #include "insets/InsetBibitem.h"
 #include "insets/InsetLabel.h"
+#include "insets/InsetSpecialChar.h"
 
 #include "support/debug.h"
 #include "support/docstring_list.h"
@@ -74,6 +75,53 @@ char_type const META_INSET = 0x200001;
 };
 
 
+/////////////////////////////////////////////////////////////////////
+//
+// SpellResultRange
+//
+/////////////////////////////////////////////////////////////////////
+
+class SpellResultRange {
+public:
+       SpellResultRange(FontSpan range, SpellChecker::Result result)
+       : range_(range), result_(result)
+       {}
+       ///
+       FontSpan const & range() const { return range_; }
+       ///
+       void range(FontSpan const & r) { range_ = r; }
+       ///
+       SpellChecker::Result result() const { return result_; }
+       ///
+       void result(SpellChecker::Result r) { result_ = r; }
+       ///
+       bool inside(pos_type pos) const { return range_.inside(pos); }
+       ///
+       bool covered(FontSpan const & r) const
+       {
+               // 1. first of new range inside current range or
+               // 2. last of new range inside current range or
+               // 3. first of current range inside new range or
+               // 4. last of current range inside new range
+               return range_.inside(r.first) || range_.inside(r.last) ||
+                       r.inside(range_.first) || r.inside(range_.last);
+       }
+       ///
+       void shift(pos_type pos, int offset)
+       {
+               if (range_.first > pos) {
+                       range_.first += offset;
+                       range_.last += offset;
+               } else if (range_.last > pos) {
+                       range_.last += offset;
+               }
+       }
+private:
+       FontSpan range_ ;
+       SpellChecker::Result result_ ;
+};
+
+
 /////////////////////////////////////////////////////////////////////
 //
 // SpellCheckerState
@@ -91,7 +139,7 @@ public:
        {
                eraseCoveredRanges(fp);
                if (state != SpellChecker::WORD_OK)
-                       ranges_[fp] = state;
+                       ranges_.push_back(SpellResultRange(fp, state));
        }
 
        void increasePosAfterPos(pos_type pos)
@@ -106,16 +154,20 @@ public:
                needsRefresh(pos);
        }
 
+       void refreshLast(pos_type pos)
+       {
+               if (pos < refresh_.last)
+                       refresh_.last = pos;
+       }
+
        SpellChecker::Result getState(pos_type pos) const
        {
                SpellChecker::Result result = SpellChecker::WORD_OK;
                RangesIterator et = ranges_.end();
                RangesIterator it = ranges_.begin();
                for (; it != et; ++it) {
-                       FontSpan fc = it->first;
-                       if(fc.first <= pos && pos <= fc.last) {
-                               result = it->second;
-                               break;
+                       if(it->inside(pos)) {
+                               return it->result();
                        }
                }
                return result;
@@ -128,12 +180,12 @@ public:
        SpellChecker::ChangeNumber currentChangeNumber() const {
                return current_change_number_;
        }
-       
+
        void refreshRange(pos_type & first, pos_type & last) const {
                first = refresh_.first;
                last = refresh_.last;
        }
-       
+
        void needsRefresh(pos_type pos) {
                if (needs_refresh_ && pos != -1) {
                        if (pos < refresh_.first)
@@ -153,54 +205,37 @@ public:
                refresh_.last = -1;
                current_change_number_ = change_number;
        }
-       
+
 private:
-       /// store the ranges as map of FontSpan and spell result pairs
-       typedef map<FontSpan, SpellChecker::Result> Ranges;
+       typedef vector<SpellResultRange> Ranges;
        typedef Ranges::const_iterator RangesIterator;
        Ranges ranges_;
-       ///
+       /// the area of the paragraph with pending spell check
        FontSpan refresh_;
        bool needs_refresh_;
+       /// spell state cache version number
        SpellChecker::ChangeNumber current_change_number_;
-       
+
+
        void eraseCoveredRanges(FontSpan const fp)
        {
                Ranges result;
                RangesIterator et = ranges_.end();
                RangesIterator it = ranges_.begin();
                for (; it != et; ++it) {
-                       FontSpan fc = it->first;
-                       // 1. first of new range inside current range or
-                       // 2. last of new range inside current range or
-                       // 3. first of current range inside new range or
-                       // 4. last of current range inside new range
-                       if (fc.inside(fp.first) || fc.inside(fp.last) ||
-                               fp.inside(fc.first) || fp.inside(fc.last))
-                       {
-                               continue;
-                       }
-                       result[fc] = it->second;
+                       if (!it->covered(fp))
+                               result.push_back(SpellResultRange(it->range(), it->result()));
                }
                ranges_ = result;
        }
 
        void correctRangesAfterPos(pos_type pos, int offset)
-       {       
-               Ranges result;
+       {
                RangesIterator et = ranges_.end();
-               RangesIterator it = ranges_.begin();
+               Ranges::iterator it = ranges_.begin();
                for (; it != et; ++it) {
-                       FontSpan m = it->first;
-                       if (m.first > pos) {
-                               m.first += offset;
-                               m.last += offset;
-                       } else if (m.last > pos) {
-                               m.last += offset;
-                       }
-                       result[m] = it->second;
+                       it->shift(pos, offset);
                }
-               ranges_ = result;
        }
 
 };
@@ -305,14 +340,16 @@ public:
        /// match a string against a particular point in the paragraph
        bool isTextAt(string const & str, pos_type pos) const;
 
-       /// a vector of inset positions
-       typedef vector<pos_type> Positions;
-       typedef Positions::const_iterator PositionsIterator;
+       /// a vector of speller skip positions
+       typedef vector<FontSpan> SkipPositions;
+       typedef SkipPositions::const_iterator SkipPositionsIterator;
 
+       void appendSkipPosition(SkipPositions & skips, pos_type const pos) const;
+       
        Language * getSpellLanguage(pos_type const from) const;
 
        Language * locateSpellRange(pos_type & from, pos_type & to,
-                                                               Positions & softbreaks) const;
+                                                               SkipPositions & skips) const;
 
        bool hasSpellerChange() const {
                SpellChecker::ChangeNumber speller_change_number = 0;
@@ -337,16 +374,16 @@ public:
        void requestSpellCheck(pos_type pos) {
                speller_state_.needsRefresh(pos);
        }
-       
+
        void readySpellCheck() {
                speller_state_.needsRefresh(-1);
        }
-       
+
        bool needsSpellCheck() const
        {
                return speller_state_.needsRefresh();
        }
-       
+
        void rangeOfSpellCheck(pos_type & first, pos_type & last) const
        {
                speller_state_.refreshRange(first, last);
@@ -363,27 +400,29 @@ public:
                last = endpos;
        }
 
-       int countSoftbreaks(PositionsIterator & it, PositionsIterator const et, pos_type & start) const
+       int countSkips(SkipPositionsIterator & it, SkipPositionsIterator const et,
+                           int & start) const
        {
-               int numbreaks = 0;
-               while (it != et && *it < start) {
-                       ++start;
-                       ++numbreaks;
+               int numskips = 0;
+               while (it != et && it->first < start) {
+                       int skip = it->last - it->first + 1;
+                       start += skip;
+                       numskips += skip;
                        ++it;
                }
-               return numbreaks;
+               return numskips;
        }
 
        void markMisspelledWords(pos_type const & first, pos_type const & last,
                                                         SpellChecker::Result result,
                                                         docstring const & word,
-                                                        Positions const & softbreaks);
-       
+                                                        SkipPositions const & skips);
+
        InsetCode ownerCode() const
        {
                return inset_owner_ ? inset_owner_->lyxCode() : NO_CODE;
        }
-       
+
        /// Which Paragraph owns us?
        Paragraph * owner_;
 
@@ -411,7 +450,7 @@ public:
        typedef docstring TextContainer;
        ///
        TextContainer text_;
-       
+
        typedef set<docstring> Words;
        typedef map<Language, Words> LangWordsMap;
        ///
@@ -458,10 +497,10 @@ Paragraph::Private::Private(Paragraph * owner, Layout const & layout)
 static int paragraph_id = -1;
 
 Paragraph::Private::Private(Private const & p, Paragraph * owner)
-       : owner_(owner), inset_owner_(p.inset_owner_), fontlist_(p.fontlist_), 
+       : owner_(owner), inset_owner_(p.inset_owner_), fontlist_(p.fontlist_),
          params_(p.params_), changes_(p.changes_), insetlist_(p.insetlist_),
          begin_of_body_(p.begin_of_body_), text_(p.text_), words_(p.words_),
-         layout_(p.layout_), speller_state_(p.speller_state_)
+         layout_(p.layout_)
 {
        id_ = ++paragraph_id;
        requestSpellCheck(p.text_.size());
@@ -474,7 +513,7 @@ Paragraph::Private::Private(Private const & p, Paragraph * owner,
          params_(p.params_), changes_(p.changes_),
          insetlist_(p.insetlist_, beg, end),
          begin_of_body_(p.begin_of_body_), words_(p.words_),
-         layout_(p.layout_), speller_state_(p.speller_state_)
+         layout_(p.layout_)
 {
        id_ = ++paragraph_id;
        if (beg >= pos_type(p.text_.size()))
@@ -674,7 +713,7 @@ void Paragraph::Private::insertChar(pos_type pos, char_type c,
 
        // Update the insets
        insetlist_.increasePosAfterPos(pos);
-       
+
        // Update list of misspelled positions
        speller_state_.increasePosAfterPos(pos);
 }
@@ -719,6 +758,8 @@ bool Paragraph::eraseChar(pos_type pos, bool trackChanges)
                if (!change.changed() ||
                      (change.inserted() && !change.currentAuthor())) {
                        setChange(pos, Change(Change::DELETED));
+                       // request run of spell checker
+                       requestSpellCheck(pos);
                        return false;
                }
 
@@ -750,6 +791,7 @@ bool Paragraph::eraseChar(pos_type pos, bool trackChanges)
 
        // Update list of misspelled positions
        d->speller_state_.decreasePosAfterPos(pos);
+       d->speller_state_.refreshLast(size());
 
        return true;
 }
@@ -1005,7 +1047,7 @@ void Paragraph::Private::latexInset(BufferParams const & bparams,
            // ERT is an exception, it should be output with no
            // decorations at all
            && inset->lyxCode() != ERT_CODE) {
-               if (running_font.language()->lang() == "farsi")
+               if (running_font.language()->lang() == "farsi")
                        os << "\\beginL{}";
                else
                        os << "\\L{";
@@ -1030,10 +1072,10 @@ void Paragraph::Private::latexInset(BufferParams const & bparams,
                unsigned int count = running_font.latexWriteEndChanges(os,
                        bparams, runparams, basefont, basefont, closeLanguage);
                column += count;
-               // if any font properties were closed, update the running_font, 
+               // if any font properties were closed, update the running_font,
                // making sure, however, to leave the language as it was
                if (count > 0) {
-                       // FIXME: probably a better way to keep track of the old 
+                       // FIXME: probably a better way to keep track of the old
                        // language, than copying the entire font?
                        Font const copy_font(running_font);
                        basefont = owner_->getLayoutFont(bparams, outerfont);
@@ -1070,7 +1112,7 @@ void Paragraph::Private::latexInset(BufferParams const & bparams,
                texrow.start(owner_->id(), i + 1);
                column = 0;
        } else {
-               column += os.tellp() - len;
+               column += (unsigned int)(os.tellp() - len);
        }
 
        if (owner_->isDeleted(i))
@@ -1302,12 +1344,12 @@ void Paragraph::Private::validate(LaTeXFeatures & features) const
                        features.addPreambleSnippet("\\" + cmd + content + "}");
                }
        }
-       
-       if (features.runparams().flavor == OutputParams::HTML 
+
+       if (features.runparams().flavor == OutputParams::HTML
            && layout_->htmltitle()) {
                features.setHTMLTitle(owner_->asString(AS_STR_INSETS));
        }
-       
+
        // check the params.
        if (!params_.spacing().isDefault())
                features.require("setspace");
@@ -1357,7 +1399,7 @@ namespace {
        Layout const emptyParagraphLayout;
 }
 
-Paragraph::Paragraph() 
+Paragraph::Paragraph()
        : d(new Paragraph::Private(this, emptyParagraphLayout))
 {
        itemdepth = 0;
@@ -1787,7 +1829,7 @@ void Paragraph::setFont(pos_type pos, Font const & font)
        // First, reduce font against layout/label font
        // Update: The setCharFont() routine in text2.cpp already
        // reduces font, so we don't need to do that here. (Asger)
-       
+
        d->fontlist_.set(pos, font);
 }
 
@@ -1872,14 +1914,14 @@ void Paragraph::setLabelWidthString(docstring const & s)
 }
 
 
-docstring Paragraph::expandLabel(Layout const & layout, 
+docstring Paragraph::expandLabel(Layout const & layout,
                BufferParams const & bparams) const
-{ 
-       return expandParagraphLabel(layout, bparams, true); 
+{
+       return expandParagraphLabel(layout, bparams, true);
 }
 
 
-docstring Paragraph::expandDocBookLabel(Layout const & layout, 
+docstring Paragraph::expandDocBookLabel(Layout const & layout,
                BufferParams const & bparams) const
 {
        return expandParagraphLabel(layout, bparams, false);
@@ -1894,7 +1936,7 @@ docstring Paragraph::expandParagraphLabel(Layout const & layout,
        bool const in_appendix = process_appendix && d->params_.appendix();
        docstring fmt = translateIfPossible(layout.labelstring(in_appendix), lang);
 
-       if (fmt.empty() && layout.labeltype == LABEL_COUNTER 
+       if (fmt.empty() && layout.labeltype == LABEL_COUNTER
            && !layout.counter.empty())
                return tclass.counters().theCounter(layout.counter, lang);
 
@@ -1909,7 +1951,7 @@ docstring Paragraph::expandParagraphLabel(Layout const & layout,
                        if (tclass.hasLayout(parent))
                                docstring label = expandParagraphLabel(tclass[parent], bparams,
                                                      process_appendix);
-                       fmt = docstring(fmt, 0, i) + label 
+                       fmt = docstring(fmt, 0, i) + label
                                + docstring(fmt, j + 1, docstring::npos);
                }
        }
@@ -1922,9 +1964,9 @@ void Paragraph::applyLayout(Layout const & new_layout)
 {
        d->layout_ = &new_layout;
        LyXAlignment const oldAlign = d->params_.align();
-       
+
        if (!(oldAlign & d->layout_->alignpossible)) {
-               frontend::Alert::warning(_("Alignment not permitted"), 
+               frontend::Alert::warning(_("Alignment not permitted"),
                        _("The new layout does not permit the alignment previously used.\nSetting to default."));
                d->params_.align(LYX_ALIGN_LAYOUT);
        }
@@ -2055,7 +2097,7 @@ int Paragraph::Private::startTeXParParams(BufferParams const & bparams,
                os << "\\noindent ";
                column += 10;
        }
-       
+
        LyXAlignment const curAlign = params_.align();
 
        if (curAlign == layout_->align)
@@ -2286,7 +2328,7 @@ void Paragraph::latex(BufferParams const & bparams,
                }
 
                Change const & change = runparams.inDeletedInset ? runparams.changeOfDeletedInset
-                                                                : lookupChange(i);
+                                                                : lookupChange(i);
 
                if (bparams.outputChanges && runningChange != change) {
                        if (open_font) {
@@ -2327,12 +2369,16 @@ void Paragraph::latex(BufferParams const & bparams,
                        open_font = false;
                }
 
+               string const running_lang = runparams.use_polyglossia ?
+                       running_font.language()->polyglossia() : running_font.language()->babel();
                // close babel's font environment before opening CJK.
-               if (!running_font.language()->babel().empty() &&
+               string const lang_end_command = runparams.use_polyglossia ?
+                       "\\end{$$lang}" : lyxrc.language_command_end;
+               if (!running_lang.empty() &&
                    font.language()->encoding()->package() == Encoding::CJK) {
-                               string end_tag = subst(lyxrc.language_command_end,
+                               string end_tag = subst(lang_end_command,
                                                        "$$lang",
-                                                       running_font.language()->babel());
+                                                       running_lang);
                                os << from_ascii(end_tag);
                                column += end_tag.length();
                }
@@ -2371,7 +2417,7 @@ void Paragraph::latex(BufferParams const & bparams,
                        // check if the fontchange ends with a trailing blank
                        // (like "\small " (see bug 3382)
                        else if (suffixIs(fontchange, ' ') && c == ' ')
-                               os << fontchange.substr(0, fontchange.size() - 1) 
+                               os << fontchange.substr(0, fontchange.size() - 1)
                                   << from_ascii("{}");
                        else
                                os << fontchange;
@@ -2470,7 +2516,7 @@ void Paragraph::latex(BufferParams const & bparams,
        if (allowcust && d->endTeXParParams(bparams, os, texrow, runparams)
            && runparams.encoding != prev_encoding) {
                runparams.encoding = prev_encoding;
-               if (!bparams.useXetex)
+               if (!runparams.isFullUnicode())
                        os << setEncoding(prev_encoding->iconvName());
        }
 
@@ -2656,7 +2702,7 @@ docstring Paragraph::simpleLyXHTMLOnePar(Buffer const & buf,
                // let's not show deleted material in the output
                if (isDeleted(i))
                        continue;
-       
+
                Font font = getFont(buf.params(), i, outerfont);
 
                // emphasis
@@ -2684,13 +2730,9 @@ docstring Paragraph::simpleLyXHTMLOnePar(Buffer const & buf,
 
                Inset const * inset = getInset(i);
                if (inset) {
-                       InsetCommand const * ic = inset->asInsetCommand();
-                       InsetLayout const & il = inset->getLayout();
-                       InsetMath const * im = inset->asInsetMath();
-                       if (!runparams.for_toc 
-                           || im || il.isInToc() || (ic && ic->isInToc())) {
+                       if (!runparams.for_toc || inset->isInToc()) {
                                OutputParams np = runparams;
-                               if (!il.htmlisblock())
+                               if (!inset->getLayout().htmlisblock())
                                        np.html_in_par = true;
                                retval += inset->xhtml(xs, np);
                        }
@@ -2717,7 +2759,7 @@ docstring Paragraph::simpleLyXHTMLOnePar(Buffer const & buf,
                                // We don't want to escape the entities. Note that
                                // it is safe to do this, since str can otherwise
                                // only be "-". E.g., it can't be "<".
-                               xs << XHTMLStream::NextRaw() << str;
+                               xs << XHTMLStream::ESCAPE_NONE << str;
                        } else
                                xs << c;
                }
@@ -2856,12 +2898,14 @@ docstring Paragraph::asString(pos_type beg, pos_type end, int options) const
 {
        odocstringstream os;
 
-       if (beg == 0 
+       if (beg == 0
            && options & AS_STR_LABEL
            && !d->params_.labelString().empty())
                os << d->params_.labelString() << ' ';
 
        for (pos_type i = beg; i < end; ++i) {
+               if ((options & AS_STR_SKIPDELETE) && isDeleted(i))
+                       continue;
                char_type const c = d->text_[i];
                if (isPrintable(c) || c == '\t'
                    || (c == '\n' && (options & AS_STR_NEWLINES)))
@@ -2881,7 +2925,7 @@ docstring Paragraph::stringify(pos_type beg, pos_type end, int options, OutputPa
 {
        odocstringstream os;
 
-       if (beg == 0 
+       if (beg == 0
                && options & AS_STR_LABEL
                && !d->params_.labelString().empty())
                os << d->params_.labelString() << ' ';
@@ -2931,14 +2975,14 @@ void Paragraph::setLayout(Layout const & layout)
 
 
 void Paragraph::setDefaultLayout(DocumentClass const & tc)
-{ 
-       setLayout(tc.defaultLayout()); 
+{
+       setLayout(tc.defaultLayout());
 }
 
 
 void Paragraph::setPlainLayout(DocumentClass const & tc)
-{ 
-       setLayout(tc.plainLayout()); 
+{
+       setLayout(tc.plainLayout());
 }
 
 
@@ -3057,8 +3101,8 @@ int Paragraph::checkBiblio(Buffer const & buffer)
        InsetList::iterator end = d->insetlist_.end();
        for (; it != end; ++it)
                if (it->inset->lyxCode() == BIBITEM_CODE
-                   && it->pos > 0) {
-                       InsetBibitem * olditem = static_cast<InsetBibitem *>(it->inset);
+                     && it->pos > 0) {
+                       InsetCommand * olditem = it->inset->asInsetCommand();
                        oldkey = olditem->getParam("key");
                        oldlabel = olditem->getParam("label");
                        erasedInsetPosition = it->pos;
@@ -3074,8 +3118,7 @@ int Paragraph::checkBiblio(Buffer const & buffer)
        // There was an InsetBibitem at the beginning and we did have to
        // erase one. So we give its properties to the beginning inset.
        if (hasbibitem) {
-               InsetBibitem * inset =
-                       static_cast<InsetBibitem *>(d->insetlist_.begin()->inset);
+               InsetCommand * inset = d->insetlist_.begin()->inset->asInsetCommand();
                if (!oldkey.empty())
                        inset->setParam("key", oldkey);
                inset->setParam("label", oldlabel);
@@ -3084,13 +3127,13 @@ int Paragraph::checkBiblio(Buffer const & buffer)
 
        // There was no inset at the beginning, so we need to create one with
        // the key and label of the one we erased.
-       InsetBibitem * inset = 
+       InsetBibitem * inset =
                new InsetBibitem(const_cast<Buffer *>(&buffer), InsetCommandParams(BIBITEM_CODE));
        // restore values of previously deleted item in this par.
        if (!oldkey.empty())
                inset->setParam("key", oldkey);
        inset->setParam("label", oldlabel);
-       insertInset(0, static_cast<Inset *>(inset),
+       insertInset(0, inset,
                    Change(track_changes ? Change::INSERTED : Change::UNCHANGED));
 
        return 1;
@@ -3220,36 +3263,44 @@ void Paragraph::changeCase(BufferParams const & bparams, pos_type pos,
 }
 
 
-bool Paragraph::find(docstring const & str, bool cs, bool mw,
-               pos_type pos, bool del) const
+int Paragraph::find(docstring const & str, bool cs, bool mw,
+               pos_type start_pos, bool del) const
 {
+       pos_type pos = start_pos;
        int const strsize = str.length();
        int i = 0;
        pos_type const parsize = d->text_.size();
-       for (i = 0; pos + i < parsize; ++i) {
-               if (i >= strsize)
-                       break;
-               if (cs && str[i] != d->text_[pos + i])
+       for (i = 0; i < strsize && pos < parsize; ++i, ++pos) {
+               // Ignore ligature break and hyphenation chars while searching
+               while (pos < parsize - 1 && isInset(pos)) {
+                       const InsetSpecialChar *isc = dynamic_cast<const InsetSpecialChar*>(getInset(pos));
+                       if (isc == 0
+                           || (isc->kind() != InsetSpecialChar::HYPHENATION
+                               && isc->kind() != InsetSpecialChar::LIGATURE_BREAK))
+                               break;
+                       pos++;
+               }
+               if (cs && str[i] != d->text_[pos])
                        break;
-               if (!cs && uppercase(str[i]) != uppercase(d->text_[pos + i]))
+               if (!cs && uppercase(str[i]) != uppercase(d->text_[pos]))
                        break;
-               if (!del && isDeleted(pos + i))
+               if (!del && isDeleted(pos))
                        break;
        }
 
        if (i != strsize)
-               return false;
+               return 0;
 
        // if necessary, check whether string matches word
        if (mw) {
-               if (pos > 0 && !isWordSeparator(pos - 1))
-                       return false;
-               if (pos + strsize < parsize
-                       && !isWordSeparator(pos + strsize))
-                       return false;
+               if (start_pos > 0 && !isWordSeparator(start_pos - 1))
+                       return 0;
+               if (pos < parsize
+                       && !isWordSeparator(pos))
+                       return 0;
        }
 
-       return true;
+       return pos - start_pos;
 }
 
 
@@ -3381,9 +3432,24 @@ void Paragraph::updateWords()
 }
 
 
+void Paragraph::Private::appendSkipPosition(SkipPositions & skips, pos_type const pos) const
+{
+       SkipPositionsIterator begin = skips.begin();
+       SkipPositions::iterator end = skips.end();
+       if (pos > 0 && begin < end) {
+               --end;
+               if (end->last == pos - 1) {
+                       end->last = pos;
+                       return;
+               }
+       }
+       skips.insert(end, FontSpan(pos, pos));
+}
+
+
 Language * Paragraph::Private::locateSpellRange(
        pos_type & from, pos_type & to,
-       Positions & softbreaks) const
+       SkipPositions & skips) const
 {
        // skip leading white space
        while (from < to && owner_->isWordSeparator(from))
@@ -3400,7 +3466,9 @@ Language * Paragraph::Private::locateSpellRange(
                // hop to end of word
                while (last < to && !owner_->isWordSeparator(last)) {
                        if (owner_->getInset(last)) {
-                               softbreaks.insert(softbreaks.end(), last);
+                               appendSkipPosition(skips, last);
+                       } else if (owner_->isDeleted(last)) {
+                               appendSkipPosition(skips, last);
                        }
                        ++last;
                }
@@ -3408,6 +3476,9 @@ Language * Paragraph::Private::locateSpellRange(
                while (sameinset && last < to && owner_->isWordSeparator(last)) {
                        if (Inset const * inset = owner_->getInset(last))
                                sameinset = inset->isChar() && inset->isLetter();
+                       if (sameinset && owner_->isDeleted(last)) {
+                               appendSkipPosition(skips, last);
+                       }
                        if (sameinset)
                                last++;
                }
@@ -3472,14 +3543,14 @@ SpellChecker::Result Paragraph::spellCheck(pos_type & from, pos_type & to,
                return result;
 
        locateWord(from, to, WHOLE_WORD);
-       if (from == to || from >= pos_type(d->text_.size()))
+       if (from == to || from >= size())
                return result;
 
-       docstring word = asString(from, to, AS_STR_INSETS);
+       docstring word = asString(from, to, AS_STR_INSETS + AS_STR_SKIPDELETE);
        Language * lang = d->getSpellLanguage(from);
 
        wl = WordLangTuple(word, lang);
-       
+
        if (!word.size())
                return result;
 
@@ -3487,13 +3558,24 @@ SpellChecker::Result Paragraph::spellCheck(pos_type & from, pos_type & to,
                // Ignore words with digits
                // FIXME: make this customizable
                // (note that some checkers ignore words with digits by default)
-               if (!hasDigit(word))
+               if (!hasDigit(word)) {
+                       bool const trailing_dot = to < size() && d->text_[to] == '.';
                        result = speller->check(wl);
+                       if (SpellChecker::misspelled(result) && trailing_dot) {
+                               wl = WordLangTuple(word.append(from_ascii(".")), lang);
+                               result = speller->check(wl);
+                               if (!SpellChecker::misspelled(result)) {
+                                       LYXERR(Debug::GUI, "misspelled word is correct with dot: \"" <<
+                                          word << "\" [" <<
+                                          from << ".." << to << "]");
+                               }
+                       }
+               }
                d->setMisspelled(from, to, result);
        } else {
                result = d->speller_state_.getState(from);
        }
-       
+
        bool const misspelled_ = SpellChecker::misspelled(result) ;
        if (misspelled_ && do_suggestion)
                speller->suggest(wl, suggestions);
@@ -3512,19 +3594,19 @@ void Paragraph::Private::markMisspelledWords(
        pos_type const & first, pos_type const & last,
        SpellChecker::Result result,
        docstring const & word,
-       Positions const & softbreaks)
+       SkipPositions const & skips)
 {
        if (!SpellChecker::misspelled(result)) {
                setMisspelled(first, last, SpellChecker::WORD_OK);
                return;
        }
-       pos_type snext = first;
+       int snext = first;
        SpellChecker * speller = theSpellChecker();
        // locate and enumerate the error positions
        int nerrors = speller->numMisspelledWords();
-       int numbreaks = 0;
-       PositionsIterator it = softbreaks.begin();
-       PositionsIterator et = softbreaks.end();
+       int numskipped = 0;
+       SkipPositionsIterator it = skips.begin();
+       SkipPositionsIterator et = skips.end();
        for (int index = 0; index < nerrors; ++index) {
                int wstart;
                int wlen = 0;
@@ -3532,15 +3614,15 @@ void Paragraph::Private::markMisspelledWords(
                /// should not happen if speller supports range checks
                if (!wlen) continue;
                docstring const misspelled = word.substr(wstart, wlen);
-               wstart += first + numbreaks;
+               wstart += first + numskipped;
                if (snext < wstart) {
                        /// mark the range of correct spelling
-                       numbreaks += countSoftbreaks(it, et, wstart);
+                       numskipped += countSkips(it, et, wstart);
                        setMisspelled(snext,
                                wstart - 1, SpellChecker::WORD_OK);
                }
                snext = wstart + wlen;
-               numbreaks += countSoftbreaks(it, et, snext);
+               numskipped += countSkips(it, et, snext);
                /// mark the range of misspelling
                setMisspelled(wstart, snext, result);
                LYXERR(Debug::GUI, "misspelled word: \"" <<
@@ -3567,16 +3649,16 @@ void Paragraph::spellCheck() const
                // loop until we leave the range
                for (pos_type first = start; first < endpos; ) {
                        pos_type last = endpos;
-                       Private::Positions softbreaks;
-                       Language * lang = d->locateSpellRange(first, last, softbreaks);
+                       Private::SkipPositions skips;
+                       Language * lang = d->locateSpellRange(first, last, skips);
                        if (first >= endpos)
                                break;
                        // start the spell checker on the unit of meaning
-                       docstring word = asString(first, last, AS_STR_INSETS);
+                       docstring word = asString(first, last, AS_STR_INSETS + AS_STR_SKIPDELETE);
                        WordLangTuple wl = WordLangTuple(word, lang);
                        SpellChecker::Result result = word.size() ?
                                speller->check(wl) : SpellChecker::WORD_OK;
-                       d->markMisspelledWords(first, last, result, word, softbreaks);
+                       d->markMisspelledWords(first, last, result, word, skips);
                        first = ++last;
                }
        } else {
@@ -3591,7 +3673,7 @@ void Paragraph::spellCheck() const
        d->readySpellCheck();
 }
 
-       
+
 bool Paragraph::isMisspelled(pos_type pos) const
 {
        return SpellChecker::misspelled(d->speller_state_.getState(pos));