]> git.lyx.org Git - lyx.git/blobdiff - src/insets/InsetText.cpp
DocBook: fix handling of footnotes.
[lyx.git] / src / insets / InsetText.cpp
index ec223fa83a13c5350979be155ef4d1dc28ec5d29..95d55b765b1c187d52bc0127d228c852e785019e 100644 (file)
@@ -10,6 +10,7 @@
 
 #include <config.h>
 
+#include "InsetLayout.h"
 #include "InsetText.h"
 
 #include "insets/InsetArgument.h"
 #include "output_latex.h"
 #include "output_plaintext.h"
 #include "output_xhtml.h"
-#include "OutputParams.h"
 #include "Paragraph.h"
 #include "ParagraphParameters.h"
 #include "ParIterator.h"
-#include "Row.h"
 #include "TexRow.h"
 #include "texstream.h"
 #include "TextClass.h"
@@ -61,7 +60,7 @@
 #include "support/gettext.h"
 #include "support/lassert.h"
 #include "support/lstrings.h"
-#include "support/RefChanger.h"
+#include "support/Changer.h"
 
 #include <algorithm>
 #include <stack>
@@ -185,6 +184,15 @@ void InsetText::read(Lexer & lex)
 }
 
 
+void InsetText::setOuterFont(BufferView & bv, FontInfo const & outer) const
+{
+       TextMetrics & tm = bv.textMetrics(&text_);
+       FontInfo tmpfont = getFont();
+       tmpfont.realize(outer);
+       tm.font_.fontInfo() = tmpfont;
+}
+
+
 void InsetText::metrics(MetricsInfo & mi, Dimension & dim) const
 {
        TextMetrics & tm = mi.base.bv->textMetrics(&text_);
@@ -192,11 +200,13 @@ void InsetText::metrics(MetricsInfo & mi, Dimension & dim) const
        //lyxerr << "InsetText::metrics: width: " << mi.base.textwidth << endl;
 
        int const horiz_offset = leftOffset(mi.base.bv) + rightOffset(mi.base.bv);
-
-       // Hand font through to contained lyxtext:
-       tm.font_.fontInfo() = mi.base.font;
        mi.base.textwidth -= horiz_offset;
 
+       // Remember the full outer font
+       setOuterFont(*mi.base.bv, mi.base.font);
+       // and use it in these metrics computation.
+       mi.base.font = tm.font_.fontInfo();
+
        // This can happen when a layout has a left and right margin,
        // and the view is made very narrow. We can't do better than
        // to draw it partly out of view (bug 5890).
@@ -229,10 +239,10 @@ void InsetText::draw(PainterInfo & pi, int x, int y) const
                                pi.backgroundColor(this));
 
        {
-               Changer dummy = make_change(pi.background_color,
+               Changer dummy = changeVar(pi.background_color,
                                            pi.backgroundColor(this, false));
                // The change tracking cue must not be inherited
-               Changer dummy2 = make_change(pi.change, Change());
+               Changer dummy2 = changeVar(pi.change, Change());
                tm.draw(pi, x + leftOffset(pi.base.bv), y);
        }
 
@@ -321,19 +331,21 @@ void InsetText::doDispatch(Cursor & cur, FuncRequest & cmd)
                fixParagraphsFont();
                break;
 
+       case LFUN_INSET_SPLIT:
        case LFUN_INSET_DISSOLVE: {
                bool const main_inset = text_.isMainText();
                bool const target_inset = cmd.argument().empty()
                        || cmd.getArg(0) == insetName(lyxCode());
 
                if (!main_inset && target_inset) {
+                       UndoGroupHelper ugh(&buffer());
                        // Text::dissolveInset assumes that the cursor
                        // is inside the Inset.
-                       if (&cur.inset() != this)
+                       if (&cur.inset() != this) {
+                               cur.recordUndo();
                                cur.pushBackward(*this);
-                       cur.beginUndoGroup();
+                       }
                        text_.dispatch(cur, cmd);
-                       cur.endUndoGroup();
                } else
                        cur.undispatched();
                break;
@@ -352,6 +364,7 @@ bool InsetText::getStatus(Cursor & cur, FuncRequest const & cmd,
        FuncStatus & status) const
 {
        switch (cmd.action()) {
+       case LFUN_INSET_SPLIT:
        case LFUN_INSET_DISSOLVE: {
                bool const main_inset = text_.isMainText();
                bool const target_inset = cmd.argument().empty()
@@ -471,7 +484,7 @@ void InsetText::latex(otexstream & os, OutputParams const & runparams) const
                os << breakln;
        bool needendgroup = false;
        if (!il.latexname().empty()) {
-               if (il.latextype() == InsetLayout::COMMAND) {
+               if (il.latextype() == InsetLaTeXType::COMMAND) {
                        // FIXME UNICODE
                        // FIXME \protect should only be used for fragile
                        //    commands, but we do not provide this information yet.
@@ -490,7 +503,7 @@ void InsetText::latex(otexstream & os, OutputParams const & runparams) const
                        if (!il.latexparam().empty())
                                os << from_utf8(il.latexparam());
                        os << '{';
-               } else if (il.latextype() == InsetLayout::ENVIRONMENT) {
+               } else if (il.latextype() == InsetLaTeXType::ENVIRONMENT) {
                        if (il.isDisplay())
                                os << breakln;
                        else
@@ -542,13 +555,13 @@ void InsetText::latex(otexstream & os, OutputParams const & runparams) const
                os << il.rightdelim();
 
        if (!il.latexname().empty()) {
-               if (il.latextype() == InsetLayout::COMMAND) {
+               if (il.latextype() == InsetLaTeXType::COMMAND) {
                        os << "}";
                        if (!il.postcommandargs().empty())
                                getArgs(os, runparams, true);
                        if (needendgroup)
                                os << "\\endgroup";
-               } else if (il.latextype() == InsetLayout::ENVIRONMENT) {
+               } else if (il.latextype() == InsetLaTeXType::ENVIRONMENT) {
                        // A comment environment doesn't need a % before \n\end
                        if (il.isDisplay() || runparams.inComment)
                                os << breakln;
@@ -616,29 +629,116 @@ void InsetText::docbook(XMLStream & xs, OutputParams const & rp, XHTMLOptions op
        }
 
        InsetLayout const & il = getLayout();
-       if (opts & WriteOuterTag && !il.docbooktag().empty() && il.docbooktag() != "NONE") {
-               docstring attrs = docstring();
-               if (!il.docbookattr().empty())
-                       attrs += from_ascii(il.docbookattr());
-               if (il.docbooktag() == "link")
-                       attrs += from_ascii(" xlink:href=\"") + text_.asString() + from_ascii("\"");
-               xs << xml::StartTag(il.docbooktag(), attrs);
+
+       // Maybe this is an <info> paragraph that should not be generated at all (i.e. right now, its place is somewhere
+       // else, typically outside the current paragraph).
+       if (!rp.docbook_generate_info && il.docbookininfo() != "never")
+               return;
+
+       // In some cases, the input parameters must be overridden for outer tags.
+       bool writeOuterTag = opts & WriteOuterTag;
+       if (writeOuterTag) {
+               // For each paragraph, if there are only Bibitems and the corresponding text, don't write the outer tags.
+               bool allBibitems = std::all_of(text().paragraphs().begin(), text().paragraphs().end(), [](Paragraph const & par) {
+                       auto nInsets = std::distance(par.insetList().begin(), par.insetList().end());
+                       auto parSize = (size_t) par.size();
+                       return nInsets == 1 && parSize > 1 && par.insetList().begin()->inset->lyxCode() == BIBITEM_CODE;
+               });
+               writeOuterTag = !allBibitems;
+       }
+
+       // Detect arguments that should be output before/after the paragraph.
+       // Don't reuse runparams.docbook_prepended_arguments, as the same object is used in InsetArgument to determine
+       // whether the inset should be output or not, whatever the context (i.e. position with respect to the wrapper).
+       std::set<InsetArgument const *> prependedArguments;
+       for (auto const & par : paragraphs()) {
+               for (pos_type i = 0; i < par.size(); ++i) {
+                       if (par.getInset(i) && par.getInset(i)->lyxCode() == ARG_CODE) {
+                               InsetArgument const *arg = par.getInset(i)->asInsetArgument();
+                               if (arg->docbookargumentbeforemaintag())
+                                       prependedArguments.insert(par.getInset(i)->asInsetArgument());
+                       }
+               }
+       }
+
+       std::set<InsetArgument const *> appendedArguments;
+       for (auto const & par : paragraphs()) {
+               for (pos_type i = 0; i < par.size(); ++i) {
+                       if (par.getInset(i) && par.getInset(i)->lyxCode() == ARG_CODE) {
+                               InsetArgument const *arg = par.getInset(i)->asInsetArgument();
+                               if (arg->docbookargumentaftermaintag())
+                    appendedArguments.insert(par.getInset(i)->asInsetArgument());
+                       }
+               }
+       }
+
+       // Start outputting this inset.
+       // - First, wrapper around the inset and its main tag.
+       if (writeOuterTag) {
+               if (!il.docbookwrappertag().empty() && il.docbookwrappertag() != "NONE" && il.docbookwrappertag() != "IGNORE")
+                       xml::openTag(xs, il.docbookwrappertag(), il.docbookwrapperattr(), il.docbookwrappertagtype());
+
+               if (!il.docbooktag().empty() && il.docbooktag() != "NONE" && il.docbooktag() != "IGNORE") {
+                       docstring attrs = docstring();
+                       if (!il.docbookattr().empty())
+                               attrs += from_ascii(il.docbookattr());
+                       if (il.docbooktag() == "link")
+                               attrs += from_ascii(" xlink:href=\"") + text_.asString() + from_ascii("\"");
+                       xml::openTag(xs, il.docbooktag(), attrs, il.docbooktagtype());
+               }
+       }
+
+       // - Think about the arguments before the paragraph.
+       OutputParams np = runparams;
+       np.docbook_in_par = true;
+       for (auto const & arg : prependedArguments)
+               arg->docbook(xs, np);
+
+       // - Mark the newly generated arguments are not-to-be-generated-again. Do the same for arguments that will follow.
+       runparams.docbook_prepended_arguments = std::move(prependedArguments);
+       runparams.docbook_appended_arguments = appendedArguments;
+
+       // - Deal with the first item.
+       // TODO: in things like SciPoster, this should also check if the item tag is allowed. Hard to formalise for now...
+       if (writeOuterTag) {
+               if (!il.docbookitemwrappertag().empty() && il.docbookitemwrappertag() != "NONE" && il.docbookitemwrappertag() != "IGNORE")
+                       xml::openTag(xs, il.docbookitemwrappertag(), il.docbookitemwrapperattr(), il.docbookitemwrappertagtype());
+
+               if (!il.docbookitemtag().empty() && il.docbookitemtag() != "NONE" && il.docbookitemtag() != "IGNORE")
+                       xml::openTag(xs, il.docbookitemtag(), il.docbookitemattr(), il.docbookitemtagtype());
        }
 
-       // No need for labels that are generated from counters.
+       // No need for labels that are generated from counters. They should be handled by the external DocBook processor.
 
        // With respect to XHTML, paragraphs are still allowed here.
-       if (!allowMultiPar())
+       if (runparams.docbook_consider_allow_multi_par && !allowMultiPar())
                runparams.docbook_make_pars = false;
        if (il.isPassThru())
                runparams.pass_thru = true;
 
+       // - Write the main content of the inset.
        xs.startDivision(false);
        docbookParagraphs(text_, buffer(), xs, runparams);
        xs.endDivision();
 
-       if (opts & WriteOuterTag)
-               xs << xml::EndTag(il.docbooktag());
+       // - Think about the arguments after the paragraph.
+       for (auto const & arg : appendedArguments)
+               arg->docbook(xs, np);
+
+       // - Close the required tags.
+       if (writeOuterTag) {
+               if (!il.docbookitemtag().empty() && il.docbookitemtag() != "NONE" && il.docbookitemtag() != "IGNORE")
+                       xml::closeTag(xs, il.docbookitemtag(), il.docbookitemtagtype());
+
+               if (!il.docbookitemwrappertag().empty() && il.docbookitemwrappertag() != "NONE" && il.docbookitemwrappertag() != "IGNORE")
+                       xml::closeTag(xs, il.docbookitemwrappertag(), il.docbookitemwrappertagtype());
+
+               if (!il.docbooktag().empty() && il.docbooktag() != "NONE" && il.docbooktag() != "IGNORE")
+                       xml::closeTag(xs, il.docbooktag(), il.docbooktagtype());
+
+               if (!il.docbookwrappertag().empty() && il.docbookwrappertag() != "NONE" && il.docbookwrappertag() != "IGNORE")
+                       xml::closeTag(xs, il.docbookwrappertag(), il.docbookwrappertagtype());
+       }
 }
 
 
@@ -845,9 +945,11 @@ bool InsetText::hasCProtectContent(bool fragile) const
 bool InsetText::insetAllowed(InsetCode code) const
 {
        switch (code) {
-       // Arguments and (plain) quotes are also allowed in PassThru insets
+       // Arguments, (plain) quotes and counter insets 
+       // are also allowed in PassThru insets
        case ARG_CODE:
        case QUOTE_CODE:
+       case COUNTER_CODE:
                return true;
        default:
                return !isPassThru();
@@ -855,6 +957,36 @@ bool InsetText::insetAllowed(InsetCode code) const
 }
 
 
+bool InsetText::allowSpellCheck() const
+{
+       return getLayout().spellcheck() && !getLayout().isPassThru();
+}
+
+
+bool InsetText::allowMultiPar() const
+{
+       return getLayout().isMultiPar();
+}
+
+
+bool InsetText::forcePlainLayout(idx_type) const
+{
+       return getLayout().forcePlainLayout();
+}
+
+
+bool InsetText::allowParagraphCustomization(idx_type) const
+{
+       return getLayout().allowParagraphCustomization();
+}
+
+
+bool InsetText::forceLocalFontSwitch() const
+{
+       return getLayout().forceLocalFontSwitch();
+}
+
+
 void InsetText::updateBuffer(ParIterator const & it, UpdateType utype, bool const deleted)
 {
        ParIterator it2 = it;
@@ -875,7 +1007,7 @@ void InsetText::updateBuffer(ParIterator const & it, UpdateType utype, bool cons
                        cnt.restoreLastLayout();
                        // FIXME cnt.restoreLastCounter()?
                }
-               // Record in this inset is embedded in a title layout
+               // Record if this inset is embedded in a title layout
                // This is needed to decide when \maketitle is output.
                intitle_context_ = it.paragraph().layout().intitle;
                // Also check embedding layouts
@@ -1082,7 +1214,8 @@ bool InsetText::automaticPopupCompletion() const
 
 bool InsetText::showCompletionCursor() const
 {
-       return lyxrc.completion_cursor_text;
+       return lyxrc.completion_cursor_text &&
+               (lyxrc.completion_inline_text || lyxrc.completion_popup_text);
 }
 
 
@@ -1176,7 +1309,7 @@ bool InsetText::needsCProtection(bool const maintext, bool const fragile) const
                return true;
 
        // Environments generally need cprotection in fragile context
-       if (fragile && getLayout().latextype() == InsetLayout::ENVIRONMENT)
+       if (fragile && getLayout().latextype() == InsetLaTeXType::ENVIRONMENT)
                return true;
 
        if (!getLayout().needsCProtect())
@@ -1184,7 +1317,7 @@ bool InsetText::needsCProtection(bool const maintext, bool const fragile) const
 
        // Environments and "no latex" types (e.g., knitr chunks)
        // need cprotection regardless the content
-       if (!maintext && getLayout().latextype() != InsetLayout::COMMAND)
+       if (!maintext && getLayout().latextype() != InsetLaTeXType::COMMAND)
                return true;
 
        // If the inset does not produce output (e.g. Note or Branch),