]> git.lyx.org Git - features.git/blobdiff - src/insets/InsetText.cpp
DocBook: merge code duplicates for HTML and CALS tables.
[features.git] / src / insets / InsetText.cpp
index 8e0500d9b2230d04806ce03cec8a3c82cd19cd30..a4521848c310f280d9a0cbb6c9a42fdc9bf2ba75 100644 (file)
 
 #include <config.h>
 
-#include "InsetLayout.h"
 #include "InsetText.h"
 
+#include "mathed/MacroTable.h"
+
 #include "insets/InsetArgument.h"
 #include "insets/InsetLayout.h"
+#include "insets/InsetPreview.h"
+
+#include "graphics/PreviewImage.h"
+#include "graphics/PreviewLoader.h"
 
 #include "buffer_funcs.h"
 #include "Buffer.h"
@@ -26,6 +31,7 @@
 #include "CutAndPaste.h"
 #include "DispatchResult.h"
 #include "ErrorList.h"
+#include "Exporter.h"
 #include "FuncRequest.h"
 #include "FuncStatus.h"
 #include "InsetList.h"
 
 #include "support/convert.h"
 #include "support/debug.h"
+#include "support/filetools.h"
 #include "support/gettext.h"
 #include "support/lassert.h"
 #include "support/lstrings.h"
 #include "support/Changer.h"
+#include "support/FileName.h"
 
 #include <algorithm>
 #include <stack>
@@ -72,9 +80,6 @@ using namespace lyx::support;
 
 namespace lyx {
 
-using graphics::PreviewLoader;
-
-
 /////////////////////////////////////////////////////////////////////
 
 InsetText::InsetText(Buffer * buf, UsePlain type)
@@ -184,6 +189,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_);
@@ -191,11 +205,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).
@@ -327,13 +343,14 @@ void InsetText::doDispatch(Cursor & cur, FuncRequest & cmd)
                        || 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;
@@ -594,7 +611,6 @@ int InsetText::plaintext(odocstringstream & os,
 }
 
 
-
 void InsetText::docbook(XMLStream & xs, OutputParams const & rp) const
 {
        docbook(xs, rp, WriteEverything);
@@ -603,8 +619,7 @@ void InsetText::docbook(XMLStream & xs, OutputParams const & rp) const
 
 void InsetText::docbook(XMLStream & xs, OutputParams const & rp, XHTMLOptions opts) const
 {
-       // we will always want to output all our paragraphs when we are
-       // called this way.
+       // Always output all the paragraphs.
        OutputParams runparams = rp;
        runparams.par_begin = 0;
        runparams.par_end = text().paragraphs().size();
@@ -616,13 +631,92 @@ void InsetText::docbook(XMLStream & xs, OutputParams const & rp, XHTMLOptions op
                return;
        }
 
-       InsetLayout const & il = getLayout();
+       InsetLayout const &il = getLayout();
 
-       // 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).
+       // 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;
 
+       // Maybe this inset must be rendered before being output.
+       if (il.docbookrenderasimage()) {
+               docbookRenderAsImage(xs, runparams, opts);
+               return;
+       }
+
+       // If none of the special cases before apply, output the inset.
+       docbookText(xs, runparams, opts);
+}
+
+
+void InsetText::docbookRenderAsImage(XMLStream & xs, OutputParams const & rp, XHTMLOptions opts) const
+{
+       LASSERT(getLayout().docbookrenderasimage(), return);
+
+       // Generate the LaTeX code to compile in order to get the image.
+       // This code actually does the same as an InsetPreview, but without
+       // an InsetPreview.
+       // Also, the image must be generated before the DocBook output is finished,
+       // unlike a preview that is not immediately required for display.
+       docstring const latex_snippet = insetToLaTeXSnippet(&buffer(), this);
+       std::string const snippet = support::trim(to_utf8(latex_snippet));
+       // TODO: no real support for Unicode. This code is very similar to RenderPreview::addPreview, the same gotcha applies.
+
+       graphics::PreviewLoader* loader = buffer().loader();
+       // This should be OK because we are exporting
+       LASSERT(loader != nullptr, return);
+       loader->add(snippet);
+       loader->startLoading(true); // Generate the image and wait until done.
+       graphics::PreviewImage const * img = loader->preview(snippet);
+       LASSERT(img != nullptr, return);
+       support::FileName const & filename = img->filename();
+
+       // Use a file name that is only determined by the LaTeX code: the name of
+       // the snippet is more or less random (i.e., if the user generates the file
+       // several times, they will have a clutter of preview files).
+       // Hence: use a cryptographic hash of the snippet. If the snippet changes,
+       // the file name will change a lot; two snippets are unlikely to have the
+       // same hash (by design of cryptographic hash functions). Computing a hash
+       // is typically slow, but extremely fast compared to compilation of the
+       // preview and image rendering.
+       std::string newFileName = "lyx_" + sanitizeFileName(toHexHash(snippet)) + "." + filename.extension();
+
+       // Copy the image into the right folder.
+       rp.exportdata->addExternalFile("docbook5", filename, newFileName);
+
+       // TODO: deal with opts. What exactly is the WriterOuterTag here, for instance?
+       // Start writing the DocBook code for the image.
+       xs << xml::StartTag("mediaobject")
+          << xml::CR();
+
+       // Output the rendered inset.
+       xs << xml::StartTag("imageobject")
+          << xml::CR()
+          << xml::CompTag("imagedata", std::string("fileref='") + newFileName + "'")
+          << xml::CR()
+          << xml::EndTag("imageobject")
+          << xml::CR();
+
+       // Output the raw content.
+       xs << xml::StartTag("textobject")
+          << xml::CR()
+          << xml::StartTag("programlisting", "language='latex' role='" + getLayout().latexname() + "'");
+       docbookText(xs, rp, opts);
+       xs << xml::EndTag("programlisting")
+          << xml::CR()
+          << xml::EndTag("textobject")
+          << xml::CR();
+
+       xs << xml::EndTag("mediaobject");
+}
+
+
+void InsetText::docbookText(XMLStream & xs, OutputParams const & rp, XHTMLOptions opts) const
+{
+       InsetLayout const &il = getLayout();
+       OutputParams runparams = rp;
+
        // In some cases, the input parameters must be overridden for outer tags.
        bool writeOuterTag = opts & WriteOuterTag;
        if (writeOuterTag) {
@@ -635,7 +729,7 @@ void InsetText::docbook(XMLStream & xs, OutputParams const & rp, XHTMLOptions op
                writeOuterTag = !allBibitems;
        }
 
-       // Detect arguments that should be output before the paragraph.
+       // 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;
@@ -649,6 +743,17 @@ void InsetText::docbook(XMLStream & xs, OutputParams const & rp, XHTMLOptions op
                }
        }
 
+       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) {
@@ -663,16 +768,20 @@ void InsetText::docbook(XMLStream & xs, OutputParams const & rp, XHTMLOptions op
                                attrs += from_ascii(" xlink:href=\"") + text_.asString() + from_ascii("\"");
                        xml::openTag(xs, il.docbooktag(), attrs, il.docbooktagtype());
                }
+
+               if (!il.docbookinnertag().empty() && il.docbookinnertag() != "NONE" && il.docbookinnertag() != "IGNORE")
+                       xml::openTag(xs, il.docbookinnertag(), il.docbookinnerattr(), il.docbookinnertagtype());
        }
 
-       // - Think about the arguments.
+       // - 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.
+       // - 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...
@@ -687,7 +796,7 @@ void InsetText::docbook(XMLStream & xs, OutputParams const & rp, XHTMLOptions op
        // 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;
@@ -697,6 +806,10 @@ void InsetText::docbook(XMLStream & xs, OutputParams const & rp, XHTMLOptions op
        docbookParagraphs(text_, buffer(), xs, runparams);
        xs.endDivision();
 
+       // - 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")
@@ -705,6 +818,9 @@ void InsetText::docbook(XMLStream & xs, OutputParams const & rp, XHTMLOptions op
                if (!il.docbookitemwrappertag().empty() && il.docbookitemwrappertag() != "NONE" && il.docbookitemwrappertag() != "IGNORE")
                        xml::closeTag(xs, il.docbookitemwrappertag(), il.docbookitemwrappertagtype());
 
+               if (!il.docbookinnertag().empty() && il.docbookinnertag() != "NONE" && il.docbookinnertag() != "IGNORE")
+                       xml::closeTag(xs, il.docbookinnertag(), il.docbookinnertagtype());
+
                if (!il.docbooktag().empty() && il.docbooktag() != "NONE" && il.docbooktag() != "IGNORE")
                        xml::closeTag(xs, il.docbooktag(), il.docbooktagtype());
 
@@ -867,7 +983,7 @@ void InsetText::appendParagraphs(ParagraphList & plist)
 
 
 void InsetText::addPreview(DocIterator const & text_inset_pos,
-       PreviewLoader & loader) const
+       graphics::PreviewLoader & loader) const
 {
        ParagraphList::const_iterator pit = paragraphs().begin();
        ParagraphList::const_iterator pend = paragraphs().end();
@@ -917,9 +1033,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();
@@ -977,7 +1095,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
@@ -1184,7 +1302,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);
 }