]> git.lyx.org Git - lyx.git/blobdiff - src/Paragraph.cpp
Limit the nopassthurchars case in beamer to URL
[lyx.git] / src / Paragraph.cpp
index d69d09a912b4f53668cf191ec2161b822e3146c7..a63017db328bc0c549c6f8a938719ffcb60de211 100644 (file)
@@ -354,6 +354,7 @@ public:
                                   Font const & running_font,
                                   string & alien_script,
                                   Layout const & style,
+                                  InsetLayout const & il,
                                   pos_type & i,
                                   pos_type end_pos,
                                   unsigned int & column) const;
@@ -760,7 +761,6 @@ void Paragraph::acceptChanges(pos_type start, pos_type end)
                                }
                                break;
                }
-
        }
 }
 
@@ -1173,6 +1173,8 @@ void Paragraph::Private::latexInset(BufferParams const & bparams,
                        ? textinset->hasCProtectContent(runparams.moving_arg)
                          && !textinset->text().isMainText()
                          && inset->lyxCode() != BRANCH_CODE
+                         && !runparams.no_cprotect
+                         && !inset->getLayout().noCProtect()
                        : false;
                unsigned int count2 = basefont.latexWriteStartChanges(os, bparams,
                                                      rp, running_font,
@@ -1234,15 +1236,27 @@ void Paragraph::Private::latexSpecialChar(otexstream & os,
                                          Font const & running_font,
                                          string & alien_script,
                                          Layout const & style,
+                                         InsetLayout const & il,
                                          pos_type & i,
                                          pos_type end_pos,
                                          unsigned int & column) const
 {
        char_type const c = owner_->getUChar(bparams, runparams, i);
 
-       if (style.pass_thru || runparams.pass_thru || runparams.find_effective()
+       // Special case: URLs with hyperref need to escape backslash, # and % (#13012).
+       // Both a layout tag and a dedicated inset seem too much effort for this.
+       string const hr_url_escape_chars = "\\#%";
+       if (contains(hr_url_escape_chars, c) && runparams.use_hyperref && il.latexname() == "url"
+           && il.required().find("url") != il.required().end()) {
+               os << "\\";
+               os.put(c);
+               return;
+       }
+
+       if ((style.pass_thru || runparams.pass_thru || runparams.find_effective()
            || contains(style.pass_thru_chars, c)
-           || contains(runparams.pass_thru_chars, c)) {
+           || contains(runparams.pass_thru_chars, c))
+           && !contains(runparams.no_pass_thru_chars, c)) {
                if (runparams.find_effective()) {
                        switch (c) {
                        case '\\':
@@ -2376,8 +2390,8 @@ bool Paragraph::allowedInContext(Cursor const & cur, InsetLayout const & il) con
        if (in_allowed_inset && inInset().asInsetText() && il.allowedOccurrences() != -1) {
                ParagraphList & pars = cur.text()->paragraphs();
                        for (Paragraph const & par : pars) {
-                               for (auto const & table : par.insetList())
-                               if (table.inset->getLayout().name() == il.name())
+                               for (auto const & elem : par.insetList())
+                               if (elem.inset->getLayout().name() == il.name())
                                        ++have_ins;
                        }
                if (have_ins >= il.allowedOccurrences())
@@ -2413,8 +2427,8 @@ bool Paragraph::allowedInContext(Cursor const & cur, InsetLayout const & il) con
                for (; pit <= lastpit; ++pit) {
                        if (&pars[pit].layout() != d->layout_)
                                break;
-                       for (auto const & table : pars[pit].insetList())
-                               if (table.inset->getLayout().name() == il.name())
+                       for (auto const & elem : pars[pit].insetList())
+                               if (elem.inset->getLayout().name() == il.name())
                                        ++have_ins;
                }
                if (have_ins >= il.allowedOccurrences())
@@ -3015,6 +3029,7 @@ void Paragraph::latex(BufferParams const & bparams,
                                        ? textinset->hasCProtectContent(runparams.moving_arg)
                                          && !textinset->text().isMainText()
                                          && inInset().lyxCode() != BRANCH_CODE
+                                         && !runparams.no_cprotect
                                        : false;
                                column += current_font.latexWriteStartChanges(ots, bparams,
                                                                              runparams, basefont, last_font, false,
@@ -3151,8 +3166,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 << "}";
@@ -3167,8 +3183,8 @@ void Paragraph::latex(BufferParams const & bparams,
                                }
                        }
                        try {
-                               d->latexSpecialChar(os, bparams, rp, running_font,
-                                                                       alien_script, style, i, end_pos, column);
+                               d->latexSpecialChar(os, bparams, rp, running_font, alien_script,
+                                                   style, inInset().getLayout(), i, end_pos, column);
                        } catch (EncodingException & e) {
                                if (runparams.dryrun) {
                                        os << "<" << _("LyX Warning: ")
@@ -3636,11 +3652,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.
@@ -3670,10 +3686,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.
 
@@ -3697,8 +3715,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();
@@ -3706,11 +3724,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) {
@@ -3718,10 +3739,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);
@@ -3745,11 +3772,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() &&
@@ -3782,12 +3810,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)
@@ -4263,9 +4300,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) {
@@ -4403,7 +4441,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);
@@ -4533,6 +4571,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
@@ -4540,10 +4599,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));
 }
 
 
@@ -4554,7 +4613,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) {
@@ -4569,7 +4628,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)
@@ -4590,11 +4649,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));