#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"
#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>
namespace lyx {
-using graphics::PreviewLoader;
-
-
/////////////////////////////////////////////////////////////////////
InsetText::InsetText(Buffer * buf, UsePlain type)
}
+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_);
//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).
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;
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()
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.
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
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;
}
-
void InsetText::docbook(XMLStream & xs, OutputParams const & rp) const
{
docbook(xs, rp, WriteEverything);
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();
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) {
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;
}
}
+ 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) {
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...
// 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;
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")
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());
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();
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();
}
+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;
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
bool InsetText::showCompletionCursor() const
{
- return lyxrc.completion_cursor_text;
+ return lyxrc.completion_cursor_text &&
+ (lyxrc.completion_inline_text || lyxrc.completion_popup_text);
}
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())
// 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),