]> git.lyx.org Git - lyx.git/blobdiff - src/Paragraph.cpp
Update Win installer for new dictionary links. Untested.
[lyx.git] / src / Paragraph.cpp
index d7fa35ef2ef526dff929cd0fe0ea3b779bbe6145..4282defa4a8fc5fed58bd71507138b012a8a6537 100644 (file)
@@ -25,6 +25,7 @@
 #include "BufferEncodings.h"
 #include "Changes.h"
 #include "Counters.h"
+#include "Cursor.h"
 #include "InsetList.h"
 #include "Language.h"
 #include "LaTeXFeatures.h"
@@ -759,7 +760,6 @@ void Paragraph::acceptChanges(pos_type start, pos_type end)
                                }
                                break;
                }
-
        }
 }
 
@@ -1097,11 +1097,12 @@ void Paragraph::Private::latexInset(BufferParams const & bparams,
        odocstream::pos_type const len = os.os().tellp();
 
        if (inset->forceLTR(runparams)
-           // babel with Xe/LuaTeX does not need a switch
+           // babel with LuaTeX does not need a switch
+           // babel with XeTeX needs a switch only if bidi is used
            // and \L is not defined there.
-           && (!runparams.isFullUnicode() || !runparams.use_babel)
+           && (!runparams.isFullUnicode() || bparams.useBidiPackage(runparams) || runparams.use_polyglossia)
            && running_font.isRightToLeft()) {
-               if (runparams.use_polyglossia) {
+               if (bparams.useBidiPackage(runparams) || runparams.use_polyglossia) {
                        // (lua)bidi
                        // Displayed environments go in an LTR environment
                        if (disp_env) {
@@ -2050,15 +2051,16 @@ char_type Paragraph::getUChar(BufferParams const & bparams,
 {
        char_type c = d->text_[pos];
 
-       // Return unchanged character in LTR languages
-       // or if we use poylglossia/bidi (XeTeX)
-       // or with babel and Xe/LuaTeX.
+       // Return unchanged character
+       // 1. in all LTR languages
+       // 2. if we use XeTeX (both with babel and polyglossia)
+       // 3. if we use LuaTeX with babel
        if (!getFontSettings(bparams, pos).isRightToLeft()
-           || rp.useBidiPackage()
-           || (rp.use_babel && rp.isFullUnicode()))
+           || rp.flavor == Flavor::XeTeX
+           || (rp.use_babel && rp.flavor == Flavor::LuaTeX))
                return c;
 
-       // Without polyglossia/bidi, we need to account for some special cases.
+       // For the remaining cases, we need to account for some special cases.
        // FIXME This needs to be audited!
        // Check if:
        // * The input is as expected for all delimiters
@@ -2069,18 +2071,27 @@ char_type Paragraph::getUChar(BufferParams const & bparams,
        //   => checked for Hebrew!
        // * In arabic_arabi, brackets are transformed to Arabic
        //   Ornate Parentheses. Is this is really wanted?
+       //   => Yes, in file ararabeyes.enc from the arabi bundle
+       //      the slot of the left bracket (slot 91) is encoded as
+       //      "ornaterightparenthesis". This is also the reason
+       //      brackets don't need to be mirrored with arabi
 
        string const & lang = getFontSettings(bparams, pos).language()->lang();
        char_type uc = c;
 
-       // 1. In the following languages, parentheses need to be reversed.
-       //    Also with polyglodia/luabidi
-       bool const reverseparens = (lang == "hebrew" || rp.use_polyglossia);
-
-       // 2. In the following languages, brackets don't need to be reversed.
-       bool const reversebrackets = lang != "arabic_arabtex"
-                       && lang != "arabic_arabi"
-                       && lang != "farsi";
+       // These are the cases where we need to mirror delimiters in RTL context
+       // in the remaining cases (polyglossia + LuaTeX or classic [pdf]latex):
+       // 1. With polyglossia and LuaTeX (luabidi) parentheses and brackets
+       //    need to be mirrored in RTL, regardless of the language, or script.
+       // 2. In the languages that follow, parentheses need to be mirrored
+       //    in classic (pdf)latex
+       bool const reverseparens = (rp.use_polyglossia || lang == "hebrew");
+       // 3. In all RTL languages except for those that follow, brackets
+       //    need to be mirrored in classic (pdf)latex
+       bool const reversebrackets = rp.use_polyglossia
+                       || (lang != "arabic_arabtex"
+                           && lang != "arabic_arabi"
+                           && lang != "farsi");
 
        // Now swap delimiters if needed.
        switch (c) {
@@ -2338,6 +2349,84 @@ bool Paragraph::parbreakIsNewline() const
 }
 
 
+bool Paragraph::allowedInContext(Cursor const & cur, InsetLayout const & il) const
+{
+       set<docstring> const & allowed_insets = il.allowedInInsets();
+       set<docstring> const & allowed_layouts = il.allowedInLayouts();
+
+       bool in_allowed_inset =
+               allowed_insets.find(inInset().getLayout().name()) != allowed_insets.end();
+
+       bool in_allowed_layout =
+               allowed_layouts.find(d->layout_->name()) != allowed_layouts.end();
+
+       if (!in_allowed_inset && inInset().asInsetArgument()) {
+               // check if the argument allows the inset in question
+               if (cur.depth() > 1) {
+                       docstring parlayout = cur[cur.depth() - 2].inset().getLayout().name()
+                                       + from_ascii("@") + from_ascii(inInset().asInsetArgument()->name());
+                       if (allowed_insets.find(parlayout) != allowed_insets.end())
+                               in_allowed_inset = true;
+               }
+       }
+       
+       int have_ins = 0;
+       // check if we exceed the number of allowed insets in this inset
+       if (in_allowed_inset && inInset().asInsetText() && il.allowedOccurrences() != -1) {
+               ParagraphList & pars = cur.text()->paragraphs();
+                       for (Paragraph const & par : pars) {
+                               for (auto const & elem : par.insetList())
+                               if (elem.inset->getLayout().name() == il.name())
+                                       ++have_ins;
+                       }
+               if (have_ins >= il.allowedOccurrences())
+                       return false;
+       }
+       
+       have_ins = 0;
+       // check if we exceed the number of allowed insets in the layout group
+       if (in_allowed_layout && il.allowedOccurrences() != -1) {
+               pit_type pit = cur.pit();
+               pit_type lastpit = cur.pit();
+               ParagraphList & pars = cur.text()->paragraphs();
+               // If we are not on a list-type environment or AllowedOccurrencesPerItem
+               // is false, we check the whole paragraph group
+               if (d->layout_->isEnvironment()
+                   && !(il.allowedOccurrencesPerItem()
+                        && (d->layout_->latextype == LATEX_LIST_ENVIRONMENT
+                            || d->layout_->latextype == LATEX_ITEM_ENVIRONMENT))) {
+                       lastpit = cur.lastpit();
+                       // get the first paragraph in sequence with this layout
+                       depth_type const current_depth = params().depth();
+                       while (true) {
+                               if (pit == 0)
+                                       break;
+                               Paragraph cpar = pars[pit - 1];
+                               if (&cpar.layout() == d->layout_
+                                   && cpar.params().depth() == current_depth)
+                                       --pit;
+                               else
+                                       break;
+                       }
+               }
+               for (; pit <= lastpit; ++pit) {
+                       if (&pars[pit].layout() != d->layout_)
+                               break;
+                       for (auto const & elem : pars[pit].insetList())
+                               if (elem.inset->getLayout().name() == il.name())
+                                       ++have_ins;
+               }
+               if (have_ins >= il.allowedOccurrences())
+                       return false;
+       }
+       
+       if (in_allowed_layout || in_allowed_inset)
+               return true;
+
+       return (allowed_insets.empty() && allowed_layouts.empty());
+}
+
+
 bool Paragraph::isPartOfTextSequence() const
 {
        for (pos_type i = 0; i < size(); ++i) {
@@ -2469,7 +2558,7 @@ int Paragraph::Private::startTeXParParams(BufferParams const & bparams,
        // RTL in classic (PDF)LaTeX (without the Bidi package)
        // Luabibdi (used by LuaTeX) behaves like classic
        bool const rtl_classic = owner_->getParLanguage(bparams)->rightToLeft()
-               && !runparams.useBidiPackage();
+               && !bparams.useBidiPackage(runparams);
 
        switch (curAlign) {
        case LYX_ALIGN_NONE:
@@ -2533,7 +2622,7 @@ bool Paragraph::Private::endTeXParParams(BufferParams const & bparams,
        // RTL in classic (PDF)LaTeX (without the Bidi package)
        // Luabibdi (used by LuaTeX) behaves like classic
        bool const rtl_classic = owner_->getParLanguage(bparams)->rightToLeft()
-               && !runparams.useBidiPackage();
+               && !bparams.useBidiPackage(runparams);
 
        switch (curAlign) {
        case LYX_ALIGN_NONE:
@@ -3061,8 +3150,9 @@ void Paragraph::latex(BufferParams const & bparams,
                                }
                        }
                } else if (i >= start_pos && (end_pos == -1 || i < end_pos)) {
-                       if (!bparams.useNonTeXFonts)
-                         script = Encodings::isKnownScriptChar(c);
+                       if (!bparams.useNonTeXFonts && !runparams.pass_thru
+                           && !contains(runparams.pass_thru_chars, c))
+                               script = Encodings::isKnownScriptChar(c);
                        if (script != alien_script) {
                                if (!alien_script.empty()) {
                                        os << "}";
@@ -3546,11 +3636,11 @@ std::tuple<vector<xml::FontTag>, vector<xml::EndFontTag>> computeDocBookFontSwit
 
 std::tuple<std::vector<docstring>, std::vector<docstring>, std::vector<docstring>>
     Paragraph::simpleDocBookOnePar(Buffer const & buf,
-                                                      OutputParams const & runparams,
-                                                      Font const & outerfont,
-                                                      pos_type initial,
-                                                      bool is_last_par,
-                                                      bool ignore_fonts) const
+                                   OutputParams const & runparams,
+                                   Font const & outerfont,
+                                   pos_type initial,
+                                   bool is_last_par,
+                                   bool ignore_fonts) const
 {
        // Return values: segregation of the content of this paragraph.
        std::vector<docstring> prependedParagraphs; // Anything that must be output before the main tag of this paragraph.
@@ -3580,10 +3670,12 @@ std::tuple<std::vector<docstring>, std::vector<docstring>, std::vector<docstring
             }
         }
     }
+       rp.lastid = id();
 
     // State variables for the main loop.
     auto xs = new XMLStream(os); // XMLStream has no copy constructor: to create a new object, the only solution
     // is to hold a pointer to the XMLStream (xs = XMLStream(os) is not allowed once the first object is built).
+       xs->startDivision(false);
     std::vector<docstring> delayedChars; // When a font tag ends with a space, output it after the closing font tag.
     // This requires to store delayed characters at some point.
 
@@ -3607,8 +3699,8 @@ std::tuple<std::vector<docstring>, std::vector<docstring>, std::vector<docstring
                if (isDeleted(i))
                        continue;
 
-               // If this is an InsetNewline, generate a new paragraph. Also reset the fonts, so that tags are closed in
-               // this paragraph.
+               // If this is an InsetNewline, generate a new paragraph (this is the reason why generatedParagraphs is a list
+               // of paragraphs). Also reset the fonts, so that tags are closed in this paragraph.
                if (getInset(i) && getInset(i)->lyxCode() == NEWLINE_CODE) {
                        if (!ignore_fonts_i)
                                xs->closeFontTags();
@@ -3616,11 +3708,14 @@ std::tuple<std::vector<docstring>, std::vector<docstring>, std::vector<docstring
                        // Output one paragraph (i.e. one string entry in generatedParagraphs).
                        generatedParagraphs.push_back(os.str());
 
+                       xs->endDivision();
+
                        // Create a new XMLStream for the new paragraph, completely independent of the previous one. This implies
                        // that the string stream must be reset.
                        os.str(from_ascii(""));
                        delete xs;
                        xs = new XMLStream(os);
+                       xs->startDivision(false);
 
                        // Restore the fonts for the new paragraph, so that the right tags are opened for the new entry.
                        if (!ignore_fonts_i) {
@@ -3628,10 +3723,16 @@ std::tuple<std::vector<docstring>, std::vector<docstring>, std::vector<docstring
                        }
                }
 
-               // Determine which tags should be opened or closed regarding fonts.
+               // Determine which tags should be opened or closed regarding fonts. Consider the last output character (i.e. not
+               // deleted).
+               int last_output_char = (i == 0) ? 0 : i - 1;
+               if (i > 0) {
+                       while (last_output_char > 0 && isDeleted(last_output_char))
+                               --last_output_char;
+               }
                FontInfo const font_old = (i == 0 ?
                                (style.labeltype == LABEL_MANUAL ? style.labelfont : style.font) :
-                               getFont(buf.masterBuffer()->params(), i - 1, outerfont).fontInfo());
+                               getFont(buf.masterBuffer()->params(), last_output_char, outerfont).fontInfo());
                Font const font = getFont(buf.masterBuffer()->params(), i, outerfont);
         tie(tagsToOpen, tagsToClose) = computeDocBookFontSwitch(
                                font_old, font, buf.masterBuffer()->params().fonts_default_family, fs);
@@ -3655,11 +3756,12 @@ std::tuple<std::vector<docstring>, std::vector<docstring>, std::vector<docstring
                        vector<xml::FontTag>::const_iterator sen = tagsToOpen.end();
                        for (; sit != sen; ++sit)
                                *xs << *sit;
-
-                       tagsToClose.clear();
-                       tagsToOpen.clear();
                }
 
+               // The font tags are no longer useful; free their memory right now.
+               tagsToClose.clear();
+               tagsToOpen.clear();
+
         // Finally, write the next character or inset.
                if (Inset const * inset = getInset(i)) {
                    bool inset_is_argument_elsewhere = getInset(i)->asInsetArgument() &&
@@ -3692,12 +3794,21 @@ std::tuple<std::vector<docstring>, std::vector<docstring>, std::vector<docstring
                }
        }
 
+       // Ensure that the tags are closed at the right place. Otherwise, there might be an open font tag with no content
+       // that no other code cares to close.
+       *xs << xml::NullTag();
+
        // FIXME, this code is just imported from XHTML
        // I'm worried about what happens if a branch, say, is itself
        // wrapped in some font stuff. I think that will not work.
        if (!ignore_fonts)
                xs->closeFontTags();
 
+       // Close the potentially remaining tags, like pending font tags.
+       // There is no need to check for ignore_fonts, as these tags won't be
+       // inserted in the stack in the first place if ignore_fonts is false.
+       xs->endDivision();
+
        // Deal with the delayed characters *after* closing font tags.
        if (!delayedChars.empty()) {
                for (const docstring &c: delayedChars)
@@ -4173,9 +4284,10 @@ bool Paragraph::isHardHyphenOrApostrophe(pos_type pos) const
 bool Paragraph::needsCProtection(bool const fragile) const
 {
        // first check the layout of the paragraph, but only in insets
+       // and not in tables
        InsetText const * textinset = inInset().asInsetText();
        bool const maintext = textinset
-               ? textinset->text().isMainText()
+               ? textinset->text().isMainText() || inInset().lyxCode() == CELL_CODE
                : false;
 
        if (!maintext && layout().needcprotect) {
@@ -4313,7 +4425,7 @@ docstring Paragraph::asString(pos_type beg, pos_type end, int options, const Out
                else if (c == META_INSET && (options & AS_STR_INSETS)) {
                        if (c == META_INSET && (options & AS_STR_PLAINTEXT)) {
                                LASSERT(runparams != nullptr, return docstring());
-                               if (runparams->find_effective() && getInset(i)->hasToString())
+                               if (runparams->find_effective() && getInset(i)->findUsesToString())
                                        getInset(i)->toString(os);
                                else
                                        getInset(i)->plaintext(os, *runparams);
@@ -4443,6 +4555,27 @@ bool Paragraph::allowEmpty() const
 }
 
 
+int Paragraph::getInsetPos(InsetCode const code, int startpos,
+                          bool ignore_deleted) const
+{
+       while (startpos != -1) {
+               int found_pos = d->insetlist_.find(code, startpos);
+               if (found_pos == -1)
+                       // nothing found
+                       return -1;
+               if (isDeleted(found_pos) && ignore_deleted) {
+                       // we're not interested in deleted insets
+                       if (found_pos + 1 == size())
+                               return -1;
+                       startpos = found_pos + 1;
+                       continue;
+               } else
+                       return found_pos;
+       }
+       return -1;
+}
+
+
 bool Paragraph::brokenBiblio() const
 {
        // There is a problem if there is no bibitem at position 0 in
@@ -4450,10 +4583,10 @@ bool Paragraph::brokenBiblio() const
        // paragraph or if this paragraph is not supposed to have
        // a bibitem inset at all.
        return ((d->layout_->labeltype == LABEL_BIBLIO
-               && (d->insetlist_.find(BIBITEM_CODE) != 0
-                   || d->insetlist_.find(BIBITEM_CODE, 1) > 0))
+               && (getInsetPos(BIBITEM_CODE, 0, true) != 0
+                   || getInsetPos(BIBITEM_CODE, 1, true) > 0))
                || (d->layout_->labeltype != LABEL_BIBLIO
-                   && d->insetlist_.find(BIBITEM_CODE) != -1));
+                   && getInsetPos(BIBITEM_CODE, 0, true) != -1));
 }
 
 
@@ -4464,7 +4597,7 @@ int Paragraph::fixBiblio(Buffer const & buffer)
        // cursor cannot be correctly updated.
 
        bool const track_changes = buffer.params().track_changes;
-       int bibitem_pos = d->insetlist_.find(BIBITEM_CODE);
+       int bibitem_pos = getInsetPos(BIBITEM_CODE, 0, true);
 
        // The case where paragraph is not BIBLIO
        if (d->layout_->labeltype != LABEL_BIBLIO) {
@@ -4479,7 +4612,7 @@ int Paragraph::fixBiblio(Buffer const & buffer)
 
        bool const hasbibitem0 = bibitem_pos == 0;
        if (hasbibitem0) {
-               bibitem_pos = d->insetlist_.find(BIBITEM_CODE, 1);
+               bibitem_pos = getInsetPos(BIBITEM_CODE, 0, true);
                // There was an InsetBibitem at pos 0,
                // and no other one => OK
                if (bibitem_pos == -1)
@@ -4500,11 +4633,19 @@ int Paragraph::fixBiblio(Buffer const & buffer)
        // We need to create an inset at the beginning
        Inset * inset = nullptr;
        if (bibitem_pos > 0) {
-               // there was one somewhere in the paragraph, let's move it
-               inset = d->insetlist_.release(bibitem_pos);
+               // There was one somewhere in the paragraph, let's move it
+               // * With change tracking, we use a clone
+               //   and leave the old inset at its position
+               //   (marked deleted)
+               // * Without change tracking, we release the inset
+               //   from its previous InsetList position
+               inset = track_changes
+                               ? new InsetBibitem(const_cast<Buffer *>(&buffer),
+                                                  getInset(bibitem_pos)->asInsetCommand()->params())
+                               : d->insetlist_.release(bibitem_pos);
                eraseChar(bibitem_pos, track_changes);
        } else
-               // make a fresh one
+               // No inset found -- make a fresh one
                inset = new InsetBibitem(const_cast<Buffer *>(&buffer),
                                         InsetCommandParams(BIBITEM_CODE));