]> git.lyx.org Git - lyx.git/blobdiff - src/Text.cpp
GuiAbout: enforce a reasonable width (show all tabs)
[lyx.git] / src / Text.cpp
index 70805dfe8408b4c465bb1ffe054805eac85a022d..eac26ea5d8f5c01c994186d3de6bff2f8a233782 100644 (file)
@@ -30,6 +30,7 @@
 #include "Cursor.h"
 #include "CutAndPaste.h"
 #include "DispatchResult.h"
+#include "Encoding.h"
 #include "ErrorList.h"
 #include "FuncRequest.h"
 #include "factory.h"
 #include "support/gettext.h"
 #include "support/lassert.h"
 #include "support/lstrings.h"
+#include "support/lyxalgo.h"
 #include "support/textutils.h"
 
-#include <boost/next_prior.hpp>
-
 #include <limits>
 #include <sstream>
 
@@ -136,7 +136,7 @@ void breakParagraphConservative(BufferParams const & bparams,
        ParagraphList & pars, pit_type par_offset, pos_type pos)
 {
        // create a new paragraph
-       Paragraph & tmp = *pars.insert(boost::next(pars.begin(), par_offset + 1),
+       Paragraph & tmp = *pars.insert(next(pars.begin(), par_offset + 1),
                                       Paragraph());
        Paragraph & par = pars[par_offset];
 
@@ -156,7 +156,7 @@ void breakParagraphConservative(BufferParams const & bparams,
                }
                // Move over the end-of-par change information
                tmp.setChange(tmp.size(), par.lookupChange(par.size()));
-               par.setChange(par.size(), Change(bparams.trackChanges ?
+               par.setChange(par.size(), Change(bparams.track_changes ?
                                           Change::INSERTED : Change::UNCHANGED));
        }
 }
@@ -192,12 +192,12 @@ void mergeParagraph(BufferParams const & bparams,
        // move the change of the end-of-paragraph character
        par.setChange(par.size(), change);
 
-       pars.erase(boost::next(pars.begin(), par_offset + 1));
+       pars.erase(lyx::next(pars.begin(), par_offset + 1));
 }
 
 
 Text::Text(InsetText * owner, bool use_default_layout)
-       : owner_(owner), autoBreakRows_(false), undo_counter_(0)
+       : owner_(owner)
 {
        pars_.push_back(Paragraph());
        Paragraph & par = pars_.back();
@@ -211,7 +211,7 @@ Text::Text(InsetText * owner, bool use_default_layout)
 
 
 Text::Text(InsetText * owner, Text const & text)
-       : owner_(owner), autoBreakRows_(text.autoBreakRows_), undo_counter_(0)
+       : owner_(owner)
 {
        pars_ = text.pars_;
        ParagraphList::iterator const end = pars_.end();
@@ -485,18 +485,42 @@ void Text::readParToken(Paragraph & par, Lexer & lex,
        } else if (token == "\\color") {
                lex.next();
                setLyXColor(lex.getString(), font.fontInfo());
-       } else if (token == "\\SpecialChar") {
+       } else if (token == "\\SpecialChar" ||
+                  (token == "\\SpecialCharNoPassThru" &&
+                   !par.layout().pass_thru && !inset().isPassThru())) {
                auto_ptr<Inset> inset;
                inset.reset(new InsetSpecialChar);
                inset->read(lex);
                inset->setBuffer(*buf);
                par.insertInset(par.size(), inset.release(), font, change);
+       } else if (token == "\\SpecialCharNoPassThru") {
+               lex.next();
+               docstring const s = ltrim(lex.getDocString(), "\\");
+               par.insert(par.size(), s, font, change);
        } else if (token == "\\IPAChar") {
                auto_ptr<Inset> inset;
                inset.reset(new InsetIPAChar);
                inset->read(lex);
                inset->setBuffer(*buf);
                par.insertInset(par.size(), inset.release(), font, change);
+       } else if (token == "\\twohyphens" || token == "\\threehyphens") {
+               // Ideally, this should be done by lyx2lyx, but lyx2lyx does not know the
+               // running font and does not know anything about layouts (and CopyStyle).
+               Layout const & layout(par.layout());
+               FontInfo info = font.fontInfo();
+               info.realize(layout.resfont);
+               if (layout.pass_thru || inset().isPassThru() ||
+                   info.family() == TYPEWRITER_FAMILY) {
+                       if (token == "\\twohyphens")
+                               par.insert(par.size(), from_ascii("--"), font, change);
+                       else
+                               par.insert(par.size(), from_ascii("---"), font, change);
+               } else {
+                       if (token == "\\twohyphens")
+                               par.insertChar(par.size(), 0x2013, font, change);
+                       else
+                               par.insertChar(par.size(), 0x2014, font, change);
+               }
        } else if (token == "\\backslash") {
                par.appendChar('\\', font, change);
        } else if (token == "\\LyXTable") {
@@ -638,7 +662,7 @@ static void breakParagraph(Text & text, pit_type par_offset, pos_type pos,
        ParagraphList & pars = text.paragraphs();
        // create a new paragraph, and insert into the list
        ParagraphList::iterator tmp =
-               pars.insert(boost::next(pars.begin(), par_offset + 1),
+               pars.insert(next(pars.begin(), par_offset + 1),
                            Paragraph());
 
        Paragraph & par = pars[par_offset];
@@ -690,7 +714,7 @@ static void breakParagraph(Text & text, pit_type par_offset, pos_type pos,
 
        // Move over the end-of-par change information
        tmp->setChange(tmp->size(), par.lookupChange(par.size()));
-       par.setChange(par.size(), Change(bparams.trackChanges ?
+       par.setChange(par.size(), Change(bparams.track_changes ?
                                           Change::INSERTED : Change::UNCHANGED));
 
        if (pos) {
@@ -748,7 +772,7 @@ void Text::breakParagraph(Cursor & cur, bool inverse_logic)
        // Always break behind a space
        // It is better to erase the space (Dekel)
        if (cur.pos() != cur.lastpos() && cpar.isLineSeparator(cur.pos()))
-               cpar.eraseChar(cur.pos(), cur.buffer()->params().trackChanges);
+               cpar.eraseChar(cur.pos(), cur.buffer()->params().track_changes);
 
        // What should the layout for the new paragraph be?
        bool keep_layout = layout.isEnvironment()
@@ -783,7 +807,7 @@ void Text::breakParagraph(Cursor & cur, bool inverse_logic)
        }
 
        while (!pars_[next_par].empty() && pars_[next_par].isNewline(0)) {
-               if (!pars_[next_par].eraseChar(0, cur.buffer()->params().trackChanges))
+               if (!pars_[next_par].eraseChar(0, cur.buffer()->params().track_changes))
                        break; // the character couldn't be deleted physically due to change tracking
        }
 
@@ -814,7 +838,7 @@ void Text::insertStringAsLines(Cursor & cur, docstring const & str,
            cit != str.end(); ++cit) {
                Paragraph & par = pars_[pit];
                if (*cit == '\n') {
-                       if (autoBreakRows_ && (!par.empty() || par.allowEmpty())) {
+                       if (inset().allowMultiPar() && (!par.empty() || par.allowEmpty())) {
                                lyx::breakParagraph(*this, pit, pos,
                                        par.layout().isEnvironment());
                                ++pit;
@@ -823,18 +847,18 @@ void Text::insertStringAsLines(Cursor & cur, docstring const & str,
                        } else {
                                continue;
                        }
-                       // do not insert consecutive spaces if !free_spacing
+               // do not insert consecutive spaces if !free_spacing
                } else if ((*cit == ' ' || *cit == '\t') &&
                           space_inserted && !par.isFreeSpacing()) {
                        continue;
                } else if (*cit == '\t') {
                        if (!par.isFreeSpacing()) {
                                // tabs are like spaces here
-                               par.insertChar(pos, ' ', font, bparams.trackChanges);
+                               par.insertChar(pos, ' ', font, bparams.track_changes);
                                ++pos;
                                space_inserted = true;
                        } else {
-                               par.insertChar(pos, *cit, font, bparams.trackChanges);
+                               par.insertChar(pos, *cit, font, bparams.track_changes);
                                ++pos;
                                space_inserted = true;
                        }
@@ -843,7 +867,7 @@ void Text::insertStringAsLines(Cursor & cur, docstring const & str,
                        continue;
                } else {
                        // just insert the character
-                       par.insertChar(pos, *cit, font, bparams.trackChanges);
+                       par.insertChar(pos, *cit, font, bparams.track_changes);
                        ++pos;
                        space_inserted = (*cit == ' ');
                }
@@ -920,6 +944,7 @@ void Text::insertChar(Cursor & cur, char_type c)
                                if (contains(number_unary_operators, c) &&
                                    (cur.pos() == 1
                                     || par.isSeparator(cur.pos() - 2)
+                                    || par.isEnvSeparator(cur.pos() - 2)
                                     || par.isNewline(cur.pos() - 2))
                                  ) {
                                        setCharFont(pit, cur.pos() - 1, cur.current_font,
@@ -1004,14 +1029,49 @@ void Text::insertChar(Cursor & cur, char_type c)
                }
        }
 
-       par.insertChar(cur.pos(), c, cur.current_font,
-               cur.buffer()->params().trackChanges);
+       // Prevent to insert uncodable characters in verbatim and ERT
+       // (workaround for bug 9012)
+       // Don't do it for listings inset, since InsetListings::latex() tries
+       // to switch to a usable encoding which works in many cases (bug 9102).
+       if (cur.paragraph().isPassThru() && owner_->lyxCode() != LISTINGS_CODE &&
+           cur.current_font.language()) {
+               Encoding const * e = cur.current_font.language()->encoding();
+               if (!e->encodable(c)) {
+                       cur.message(_("Character is uncodable in verbatim paragraphs."));
+                       return;
+               }
+       }
+
+       pos_type pos = cur.pos();
+       if (!cur.paragraph().isPassThru() && owner_->lyxCode() != IPA_CODE &&
+           cur.current_font.fontInfo().family() != TYPEWRITER_FAMILY &&
+           c == '-' && pos > 0) {
+               if (par.getChar(pos - 1) == '-') {
+                       // convert "--" to endash
+                       par.eraseChar(pos - 1, cur.buffer()->params().track_changes);
+                       c = 0x2013;
+                       pos--;
+               } else if (par.getChar(pos - 1) == 0x2013) {
+                       // convert "---" to emdash
+                       par.eraseChar(pos - 1, cur.buffer()->params().track_changes);
+                       c = 0x2014;
+                       pos--;
+               } else if (par.getChar(pos - 1) == 0x2014) {
+                       // convert "----" to "-"
+                       par.eraseChar(pos - 1, cur.buffer()->params().track_changes);
+                       c = '-';
+                       pos--;
+               }
+       }
+
+       par.insertChar(pos, c, cur.current_font,
+               cur.buffer()->params().track_changes);
        cur.checkBufferStructure();
 
 //             cur.screenUpdateFlags(Update::Force);
        bool boundary = cur.boundary()
-               || tm.isRTLBoundary(cur.pit(), cur.pos() + 1);
-       setCursor(cur, cur.pit(), cur.pos() + 1, false, boundary);
+               || tm.isRTLBoundary(cur.pit(), pos + 1);
+       setCursor(cur, cur.pit(), pos + 1, false, boundary);
        charInserted(cur);
 }
 
@@ -1020,15 +1080,6 @@ void Text::charInserted(Cursor & cur)
 {
        Paragraph & par = cur.paragraph();
 
-       // Here we call finishUndo for every 20 characters inserted.
-       // This is from my experience how emacs does it. (Lgb)
-       if (undo_counter_ < 20) {
-               ++undo_counter_;
-       } else {
-               cur.finishUndo();
-               undo_counter_ = 0;
-       }
-
        // register word if a non-letter was entered
        if (cur.pos() > 1
            && !par.isWordSeparator(cur.pos() - 2)
@@ -1063,7 +1114,7 @@ bool Text::cursorForwardOneWord(Cursor & cur)
        if (lyxrc.mac_like_cursor_movement) {
                // Skip through trailing punctuation and spaces.
                while (pos != lastpos && (par.isChar(pos) || par.isSpace(pos)))
-                        ++pos;
+                       ++pos;
 
                // Skip over either a non-char inset or a full word
                if (pos != lastpos && par.isWordSeparator(pos))
@@ -1261,8 +1312,7 @@ void Text::acceptOrRejectChanges(Cursor & cur, ChangeOp op)
        LBUFERR(this == cur.text());
 
        if (!cur.selection()) {
-               bool const changed = cur.paragraph().isChanged(cur.pos());
-               if (!(changed && findNextChange(&cur.bv())))
+               if (!selectChange(cur))
                        return;
        }
 
@@ -1278,7 +1328,6 @@ void Text::acceptOrRejectChanges(Cursor & cur, ChangeOp op)
        bool endsBeforeEndOfPar = (endPos < pars_[endPit].size());
 
        // first, accept/reject changes within each individual paragraph (do not consider end-of-par)
-
        for (pit_type pit = begPit; pit <= endPit; ++pit) {
                pos_type parSize = pars_[pit].size();
 
@@ -1354,10 +1403,7 @@ void Text::acceptOrRejectChanges(Cursor & cur, ChangeOp op)
        }
 
        // finally, invoke the DEPM
-
-       deleteEmptyParagraphMechanism(begPit, endPit, cur.buffer()->params().trackChanges);
-
-       //
+       deleteEmptyParagraphMechanism(begPit, endPit, cur.buffer()->params().track_changes);
 
        cur.finishUndo();
        cur.clearSelection();
@@ -1371,7 +1417,7 @@ void Text::acceptChanges()
 {
        BufferParams const & bparams = owner_->buffer().params();
        lyx::acceptChanges(pars_, bparams);
-       deleteEmptyParagraphMechanism(0, pars_.size() - 1, bparams.trackChanges);
+       deleteEmptyParagraphMechanism(0, pars_.size() - 1, bparams.track_changes);
 }
 
 
@@ -1407,7 +1453,7 @@ void Text::rejectChanges()
        }
 
        // finally, invoke the DEPM
-       deleteEmptyParagraphMechanism(0, pars_size - 1, bparams.trackChanges);
+       deleteEmptyParagraphMechanism(0, pars_size - 1, bparams.track_changes);
 }
 
 
@@ -1511,7 +1557,7 @@ bool Text::handleBibitems(Cursor & cur)
        // if a bibitem is deleted, merge with previous paragraph
        // if this is a bibliography item as well
        if (cur.pit() > 0 && par.layout() == prevpar.layout()) {
-               cur.recordUndo(ATOMIC_UNDO, prevcur.pit());
+               cur.recordUndo(prevcur.pit());
                mergeParagraph(bufparams, cur.text()->paragraphs(),
                                                        prevcur.pit());
                cur.forceBufferUpdate();
@@ -1537,7 +1583,7 @@ bool Text::erase(Cursor & cur)
                // any paragraphs
                cur.recordUndo(DELETE_UNDO);
                bool const was_inset = cur.paragraph().isInset(cur.pos());
-               if(!par.eraseChar(cur.pos(), cur.buffer()->params().trackChanges))
+               if(!par.eraseChar(cur.pos(), cur.buffer()->params().track_changes))
                        // the character has been logically deleted only => skip it
                        cur.top().forwardPos();
 
@@ -1550,7 +1596,7 @@ bool Text::erase(Cursor & cur)
                if (cur.pit() == cur.lastpit())
                        return dissolveInset(cur);
 
-               if (!par.isMergedOnEndOfParDeletion(cur.buffer()->params().trackChanges)) {
+               if (!par.isMergedOnEndOfParDeletion(cur.buffer()->params().track_changes)) {
                        par.setChange(cur.pos(), Change(Change::DELETED));
                        cur.forwardPos();
                        needsUpdate = true;
@@ -1593,15 +1639,15 @@ bool Text::backspacePos0(Cursor & cur)
        // is it an empty paragraph?
        if (cur.lastpos() == 0
            || (cur.lastpos() == 1 && par.isSeparator(0))) {
-               cur.recordUndo(ATOMIC_UNDO, prevcur.pit(), cur.pit());
-               plist.erase(boost::next(plist.begin(), cur.pit()));
+               cur.recordUndo(prevcur.pit());
+               plist.erase(next(plist.begin(), cur.pit()));
                needsUpdate = true;
        }
        // is previous par empty?
        else if (prevcur.lastpos() == 0
                 || (prevcur.lastpos() == 1 && prevpar.isSeparator(0))) {
-               cur.recordUndo(ATOMIC_UNDO, prevcur.pit(), cur.pit());
-               plist.erase(boost::next(plist.begin(), prevcur.pit()));
+               cur.recordUndo(prevcur.pit());
+               plist.erase(next(plist.begin(), prevcur.pit()));
                needsUpdate = true;
        }
        // Pasting is not allowed, if the paragraphs have different
@@ -1612,7 +1658,7 @@ bool Text::backspacePos0(Cursor & cur)
        else if (par.layout() == prevpar.layout()
                 || tclass.isDefaultLayout(par.layout())
                 || tclass.isPlainLayout(par.layout())) {
-               cur.recordUndo(ATOMIC_UNDO, prevcur.pit());
+               cur.recordUndo(prevcur.pit());
                mergeParagraph(bufparams, plist, prevcur.pit());
                needsUpdate = true;
        }
@@ -1637,8 +1683,8 @@ bool Text::backspace(Cursor & cur)
                Cursor prev_cur = cur;
                --prev_cur.pit();
 
-               if (!prev_cur.paragraph().isMergedOnEndOfParDeletion(cur.buffer()->params().trackChanges)) {
-                       cur.recordUndo(ATOMIC_UNDO, prev_cur.pit(), prev_cur.pit());
+               if (!prev_cur.paragraph().isMergedOnEndOfParDeletion(cur.buffer()->params().track_changes)) {
+                       cur.recordUndo(prev_cur.pit(), prev_cur.pit());
                        prev_cur.paragraph().setChange(prev_cur.lastpos(), Change(Change::DELETED));
                        setCursorIntern(cur, prev_cur.pit(), prev_cur.lastpos());
                        return true;
@@ -1658,7 +1704,7 @@ bool Text::backspace(Cursor & cur)
                setCursorIntern(cur, cur.pit(), cur.pos() - 1,
                                false, cur.boundary());
                bool const was_inset = cur.paragraph().isInset(cur.pos());
-               cur.paragraph().eraseChar(cur.pos(), cur.buffer()->params().trackChanges);
+               cur.paragraph().eraseChar(cur.pos(), cur.buffer()->params().track_changes);
                if (was_inset)
                        cur.forceBufferUpdate();
                else
@@ -1700,7 +1746,7 @@ bool Text::dissolveInset(Cursor & cur)
                spos += cur.pos();
        spit += cur.pit();
        Buffer & b = *cur.buffer();
-       cur.paragraph().eraseChar(cur.pos(), b.params().trackChanges);
+       cur.paragraph().eraseChar(cur.pos(), b.params().track_changes);
 
        if (!plist.empty()) {
                // see bug 7319
@@ -1835,7 +1881,7 @@ docstring Text::currentState(Cursor const & cur) const
        Paragraph const & par = cur.paragraph();
        odocstringstream os;
 
-       if (buf.params().trackChanges)
+       if (buf.params().track_changes)
                os << _("[Change Tracking] ");
 
        Change change = par.lookupChange(cur.pos());
@@ -2001,14 +2047,14 @@ docstring Text::asString(pit_type beg, pit_type end, int options) const
 }
 
 
-void Text::forToc(docstring & os, size_t maxlen, bool shorten) const
+void Text::forOutliner(docstring & os, size_t maxlen, bool shorten) const
 {
        if (maxlen == 0)
                maxlen = std::numeric_limits<std::size_t>::max();
        else
                LASSERT(maxlen >= 8, maxlen = TOC_ENTRY_LENGTH);
        for (size_t i = 0; i != pars_.size() && os.length() < maxlen; ++i)
-               pars_[i].forToc(os, maxlen);
+               pars_[i].forOutliner(os, maxlen);
        if (shorten && os.length() >= maxlen)
                os = os.substr(0, maxlen - 3) + from_ascii("...");
 }
@@ -2056,7 +2102,7 @@ void Text::charsTranspose(Cursor & cur)
 
        // And finally, we are ready to perform the transposition.
        // Track the changes if Change Tracking is enabled.
-       bool const trackChanges = cur.buffer()->params().trackChanges;
+       bool const trackChanges = cur.buffer()->params().track_changes;
 
        cur.recordUndo();
 
@@ -2109,7 +2155,7 @@ bool Text::completionSupported(Cursor const & cur) const
 
 CompletionList const * Text::createCompletionList(Cursor const & cur) const
 {
-       WordList const * list = theWordList(*cur.getFont().language());
+       WordList const * list = theWordList(cur.getFont().language()->lang());
        return new TextCompletionList(cur, list);
 }