]> git.lyx.org Git - lyx.git/blobdiff - src/insets/InsetText.cpp
Loop refactoring
[lyx.git] / src / insets / InsetText.cpp
index ce69bef2554ee3fb954da62c1fa6a4feecb6fb07..10b15c88edad34abd39a31fb90b9223ff2700e91 100644 (file)
 #include "MetricsInfo.h"
 #include "output_docbook.h"
 #include "output_latex.h"
+#include "output_plaintext.h"
 #include "output_xhtml.h"
 #include "OutputParams.h"
-#include "output_plaintext.h"
 #include "Paragraph.h"
 #include "ParagraphParameters.h"
 #include "ParIterator.h"
 #include "Row.h"
-#include "sgml.h"
 #include "TexRow.h"
 #include "texstream.h"
 #include "TextClass.h"
@@ -56,7 +55,6 @@
 #include "frontends/alert.h"
 #include "frontends/Painter.h"
 
-#include "support/bind.h"
 #include "support/convert.h"
 #include "support/debug.h"
 #include "support/gettext.h"
@@ -79,17 +77,18 @@ using graphics::PreviewLoader;
 /////////////////////////////////////////////////////////////////////
 
 InsetText::InsetText(Buffer * buf, UsePlain type)
-       : Inset(buf), drawFrame_(false), frame_color_(Color_insetframe),
+       : Inset(buf), drawFrame_(false), is_changed_(false), intitle_context_(false),
+         frame_color_(Color_insetframe),
        text_(this, type == DefaultLayout)
 {
 }
 
 
 InsetText::InsetText(InsetText const & in)
-       : Inset(in), text_(this, in.text_)
+       : Inset(in), drawFrame_(in.drawFrame_), is_changed_(in.is_changed_),
+         intitle_context_(false), frame_color_(in.frame_color_),
+         text_(this, in.text_)
 {
-       drawFrame_ = in.drawFrame_;
-       frame_color_ = in.frame_color_;
 }
 
 
@@ -141,9 +140,9 @@ Dimension const InsetText::dimensionHelper(BufferView const & bv) const
 {
        TextMetrics const & tm = bv.textMetrics(&text_);
        Dimension dim = tm.dim();
-       dim.wid += 2 * TEXT_TO_INSET_OFFSET;
-       dim.des += TEXT_TO_INSET_OFFSET;
-       dim.asc += TEXT_TO_INSET_OFFSET;
+       dim.wid += leftOffset(&bv) + rightOffset(&bv);
+       dim.des += bottomOffset(&bv);
+       dim.asc += topOffset(&bv);
        return dim;
 }
 
@@ -190,9 +189,11 @@ 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 -= 2 * TEXT_TO_INSET_OFFSET;
+       mi.base.textwidth -= horiz_offset;
 
        // 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
@@ -204,10 +205,10 @@ void InsetText::metrics(MetricsInfo & mi, Dimension & dim) const
                tm.metrics(mi, dim, mi.base.textwidth);
        else
                tm.metrics(mi, dim);
-       mi.base.textwidth += 2 * TEXT_TO_INSET_OFFSET;
-       dim.asc += TEXT_TO_INSET_OFFSET;
-       dim.des += TEXT_TO_INSET_OFFSET;
-       dim.wid += 2 * TEXT_TO_INSET_OFFSET;
+       mi.base.textwidth += horiz_offset;
+       dim.asc += topOffset(mi.base.bv);
+       dim.des += bottomOffset(mi.base.bv);
+       dim.wid += horiz_offset;
 }
 
 
@@ -215,10 +216,11 @@ void InsetText::draw(PainterInfo & pi, int x, int y) const
 {
        TextMetrics & tm = pi.base.bv->textMetrics(&text_);
 
-       int const w = tm.width() + TEXT_TO_INSET_OFFSET;
-       int const yframe = y - TEXT_TO_INSET_OFFSET - tm.ascent();
-       int const h = tm.height() + 2 * TEXT_TO_INSET_OFFSET;
-       int const xframe = x + TEXT_TO_INSET_OFFSET / 2;
+       int const horiz_offset = leftOffset(pi.base.bv) + rightOffset(pi.base.bv);
+       int const w = tm.width() + (horiz_offset - horiz_offset / 2);
+       int const yframe = y - topOffset(pi.base.bv) - tm.ascent();
+       int const h = tm.height() + topOffset(pi.base.bv) + bottomOffset(pi.base.bv);
+       int const xframe = x + leftOffset(pi.base.bv) / 2;
        bool change_drawn = false;
        if (pi.full_repaint)
                        pi.pain.fillRectangle(xframe, yframe, w, h,
@@ -228,8 +230,8 @@ void InsetText::draw(PainterInfo & pi, int x, int y) const
                Changer dummy = make_change(pi.background_color,
                                            pi.backgroundColor(this, false));
                // The change tracking cue must not be inherited
-               Changer dummy2 = make_change(pi.change_, Change());
-               tm.draw(pi, x + TEXT_TO_INSET_OFFSET, y);
+               Changer dummy2 = make_change(pi.change, Change());
+               tm.draw(pi, x + leftOffset(pi.base.bv), y);
        }
 
        if (drawFrame_) {
@@ -237,7 +239,7 @@ void InsetText::draw(PainterInfo & pi, int x, int y) const
                // Only do so if the color is not custom. But do so even if RowPainter
                // handles the strike-through already.
                Color c;
-               if (pi.change_.changed()
+               if (pi.change.changed()
                    // Originally, these are the colors with role Text, from role() in
                    // ColorCache.cpp.  The code is duplicated to avoid depending on Qt
                    // types, and also maybe it need not match in the future.
@@ -246,18 +248,18 @@ void InsetText::draw(PainterInfo & pi, int x, int y) const
                        || frameColor() == Color_preview
                        || frameColor() == Color_tabularline
                        || frameColor() == Color_previewframe)) {
-                       c = pi.change_.color();
+                       c = pi.change.color();
                        change_drawn = true;
                } else
                        c = frameColor();
                pi.pain.rectangle(xframe, yframe, w, h, c);
        }
 
-       if (canPaintChange(*pi.base.bv) && (!change_drawn || pi.change_.deleted()))
+       if (canPaintChange(*pi.base.bv) && (!change_drawn || pi.change.deleted()))
                // Do not draw the change tracking cue if already done by RowPainter and
                // do not draw the cue for INSERTED if the information is already in the
                // color of the frame
-               pi.change_.paintCue(pi, xframe, yframe, xframe + w, yframe + h);
+               pi.change.paintCue(pi, xframe, yframe, xframe + w, yframe + h);
 }
 
 
@@ -318,10 +320,8 @@ void InsetText::doDispatch(Cursor & cur, FuncRequest & cmd)
                bool const main_inset = text_.isMainText();
                bool const target_inset = cmd.argument().empty()
                        || cmd.getArg(0) == insetName(lyxCode());
-               // cur.inset() is the tabular when this is a single cell (bug #9954)
-               bool const one_cell = cur.inset().nargs() == 1;
 
-               if (!main_inset && target_inset && one_cell) {
+               if (!main_inset && target_inset) {
                        // Text::dissolveInset assumes that the cursor
                        // is inside the Inset.
                        if (&cur.inset() != this)
@@ -351,11 +351,9 @@ bool InsetText::getStatus(Cursor & cur, FuncRequest const & cmd,
                bool const main_inset = text_.isMainText();
                bool const target_inset = cmd.argument().empty()
                        || cmd.getArg(0) == insetName(lyxCode());
-               // cur.inset() is the tabular when this is a single cell (bug #9954)
-               bool const one_cell = cur.inset().nargs() == 1;
 
                if (target_inset)
-                       status.setEnabled(!main_inset && one_cell);
+                       status.setEnabled(!main_inset);
                return target_inset;
        }
 
@@ -415,6 +413,18 @@ void InsetText::fixParagraphsFont()
 }
 
 
+// bool InsetText::isChanged() const
+// {
+//     ParagraphList::const_iterator pit = paragraphs().begin();
+//     ParagraphList::const_iterator end = paragraphs().end();
+//     for (; pit != end; ++pit) {
+//             if (pit->isChanged())
+//                     return true;
+//     }
+//     return false;
+// }
+
+
 void InsetText::setChange(Change const & change)
 {
        ParagraphList::iterator pit = paragraphs().begin();
@@ -454,14 +464,20 @@ void InsetText::latex(otexstream & os, OutputParams const & runparams) const
        InsetLayout const & il = getLayout();
        if (il.forceOwnlines())
                os << breakln;
+       bool needendgroup = false;
        if (!il.latexname().empty()) {
                if (il.latextype() == InsetLayout::COMMAND) {
                        // FIXME UNICODE
                        // FIXME \protect should only be used for fragile
                        //    commands, but we do not provide this information yet.
-                       if (hasCProtectContent(runparams.moving_arg))
+                       if (hasCProtectContent(runparams.moving_arg)) {
+                               if (contains(runparams.active_chars, '^')) {
+                                       // cprotect relies on ^ being on catcode 7
+                                       os << "\\begingroup\\catcode`\\^=7";
+                                       needendgroup = true;
+                               }
                                os << "\\cprotect";
-                       else if (runparams.moving_arg)
+                       else if (runparams.moving_arg)
                                os << "\\protect";
                        os << '\\' << from_utf8(il.latexname());
                        if (!il.latexargs().empty())
@@ -513,6 +529,9 @@ void InsetText::latex(otexstream & os, OutputParams const & runparams) const
        runparams.encoding = rp.encoding;
        // Pass the post_macros upstream
        runparams.post_macro = rp.post_macro;
+       // These need to be passed upstream as well
+       runparams.need_maketitle = rp.need_maketitle;
+       runparams.have_maketitle = rp.have_maketitle;
 
        if (!il.rightdelim().empty())
                os << il.rightdelim();
@@ -522,6 +541,8 @@ void InsetText::latex(otexstream & os, OutputParams const & runparams) const
                        os << "}";
                        if (!il.postcommandargs().empty())
                                getArgs(os, runparams, true);
+                       if (needendgroup)
+                               os << "\\endgroup";
                } else if (il.latextype() == InsetLayout::ENVIRONMENT) {
                        // A comment environment doesn't need a % before \n\end
                        if (il.isDisplay() || runparams.inComment)
@@ -549,7 +570,7 @@ int InsetText::plaintext(odocstringstream & os,
        for (; it != end; ++it) {
                if (it != beg) {
                        os << '\n';
-                       if (runparams.linelen > 0)
+                       if (runparams.linelen > 0 && !getLayout().parbreakIsNewline())
                                os << '\n';
                }
                odocstringstream oss;
@@ -567,24 +588,56 @@ int InsetText::plaintext(odocstringstream & os,
 }
 
 
-int InsetText::docbook(odocstream & os, OutputParams const & runparams) const
+
+void InsetText::docbook(XMLStream & xs, OutputParams const & rp) const
+{
+       docbook(xs, rp, WriteEverything);
+}
+
+
+void InsetText::docbook(XMLStream & xs, OutputParams const & rp, XHTMLOptions opts) const
 {
-       ParagraphList::const_iterator const beg = paragraphs().begin();
+       // we will always want to output all our paragraphs when we are
+       // called this way.
+       OutputParams runparams = rp;
+       runparams.par_begin = 0;
+       runparams.par_end = text().paragraphs().size();
+
+       if (undefined()) {
+               xs.startDivision(false);
+               docbookParagraphs(text_, buffer(), xs, runparams);
+               xs.endDivision();
+               return;
+       }
 
-       if (!undefined())
-               sgml::openTag(os, getLayout().latexname(),
-                             beg->getID(buffer(), runparams) + getLayout().latexparam());
+       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);
+       }
 
-       docbookParagraphs(text_, buffer(), os, runparams);
+       // No need for labels that are generated from counters.
 
-       if (!undefined())
-               sgml::closeTag(os, getLayout().latexname());
+       // With respect to XHTML, paragraphs are still allowed here.
+       if (!allowMultiPar())
+               runparams.docbook_make_pars = false;
+       if (il.isPassThru())
+               runparams.pass_thru = true;
+
+       xs.startDivision(false);
+       docbookParagraphs(text_, buffer(), xs, runparams);
+       xs.endDivision();
 
-       return 0;
+       if (opts & WriteOuterTag)
+               xs << xml::EndTag(il.docbooktag());
 }
 
 
-docstring InsetText::xhtml(XHTMLStream & xs, OutputParams const & runparams) const
+docstring InsetText::xhtml(XMLStream & xs, OutputParams const & runparams) const
 {
        return insetAsXHTML(xs, runparams, WriteEverything);
 }
@@ -603,7 +656,7 @@ docstring InsetText::xhtml(XHTMLStream & xs, OutputParams const & runparams) con
 // if so, try to close fonts, etc.
 // There are probably limits to how well we can do here, though, and we will
 // have to rely upon users not putting footnotes inside noun-type insets.
-docstring InsetText::insetAsXHTML(XHTMLStream & xs, OutputParams const & rp,
+docstring InsetText::insetAsXHTML(XMLStream & xs, OutputParams const & rp,
                                   XHTMLOptions opts) const
 {
        // we will always want to output all our paragraphs when we are
@@ -621,7 +674,7 @@ docstring InsetText::insetAsXHTML(XHTMLStream & xs, OutputParams const & rp,
 
        InsetLayout const & il = getLayout();
        if (opts & WriteOuterTag)
-               xs << html::StartTag(il.htmltag(), il.htmlattr());
+               xs << xml::StartTag(il.htmltag(), il.htmlattr());
 
        if ((opts & WriteLabel) && !il.counter().empty()) {
                BufferParams const & bp = buffer().masterBuffer()->params();
@@ -633,15 +686,15 @@ docstring InsetText::insetAsXHTML(XHTMLStream & xs, OutputParams const & rp,
                                cntrs.counterLabel(from_utf8(il.htmllabel()), bp.language->code());
                        // FIXME is this check necessary?
                        if (!lbl.empty()) {
-                               xs << html::StartTag(il.htmllabeltag(), il.htmllabelattr());
+                               xs << xml::StartTag(il.htmllabeltag(), il.htmllabelattr());
                                xs << lbl;
-                               xs << html::EndTag(il.htmllabeltag());
+                               xs << xml::EndTag(il.htmllabeltag());
                        }
                }
        }
 
        if (opts & WriteInnerTag)
-               xs << html::StartTag(il.htmlinnertag(), il.htmlinnerattr());
+               xs << xml::StartTag(il.htmlinnertag(), il.htmlinnerattr());
 
        // we will eventually lose information about the containing inset
        if (!allowMultiPar() || opts == JustText)
@@ -654,10 +707,10 @@ docstring InsetText::insetAsXHTML(XHTMLStream & xs, OutputParams const & rp,
        xs.endDivision();
 
        if (opts & WriteInnerTag)
-               xs << html::EndTag(il.htmlinnertag());
+               xs << xml::EndTag(il.htmlinnertag());
 
        if (opts & WriteOuterTag)
-               xs << html::EndTag(il.htmltag());
+               xs << xml::EndTag(il.htmltag());
 
        return docstring();
 }
@@ -683,7 +736,7 @@ void InsetText::getArgs(otexstream & os, OutputParams const & runparams_in,
 void InsetText::cursorPos(BufferView const & bv,
                CursorSlice const & sl, bool boundary, int & x, int & y) const
 {
-       x = bv.textMetrics(&text_).cursorX(sl, boundary) + TEXT_TO_INSET_OFFSET;
+       x = bv.textMetrics(&text_).cursorX(sl, boundary) + leftOffset(&bv);
        y = bv.textMetrics(&text_).cursorY(sl, boundary);
 }
 
@@ -730,8 +783,9 @@ void InsetText::appendParagraphs(ParagraphList & plist)
        mergeParagraph(buffer().params(), pl,
                       distance(pl.begin(), ins) - 1);
 
-       for_each(pit, plist.end(),
-                bind(&ParagraphList::push_back, ref(pl), _1));
+       ParagraphList::iterator const pend = plist.end();
+       for (; pit != pend; ++pit)
+               pl.push_back(*pit);
 }
 
 
@@ -769,12 +823,13 @@ ParagraphList & InsetText::paragraphs()
 }
 
 
-bool InsetText::hasCProtectContent(bool const fragile) const
+bool InsetText::hasCProtectContent(bool fragile) const
 {
+       fragile |= getLayout().isNeedProtect();
        ParagraphList const & pars = paragraphs();
-       pit_type pend = paragraphs().size();
+       pit_type pend = pit_type(paragraphs().size());
        for (pit_type pit = 0; pit != pend; ++pit) {
-               Paragraph const & par = pars[pit];
+               Paragraph const & par = pars[size_type(pit)];
                if (par.needsCProtection(fragile))
                        return true;
        }
@@ -795,7 +850,7 @@ bool InsetText::insetAllowed(InsetCode code) const
 }
 
 
-void InsetText::updateBuffer(ParIterator const & it, UpdateType utype)
+void InsetText::updateBuffer(ParIterator const & it, UpdateType utype, bool const deleted)
 {
        ParIterator it2 = it;
        it2.forwardPos();
@@ -809,12 +864,23 @@ void InsetText::updateBuffer(ParIterator const & it, UpdateType utype)
                        cnt.clearLastLayout();
                        // FIXME cnt.saveLastCounter()?
                }
-               buffer().updateBuffer(it2, utype);
+               buffer().updateBuffer(it2, utype, deleted);
                if (save_layouts) {
                        // LYXERR0("Exiting " << name());
                        cnt.restoreLastLayout();
                        // FIXME cnt.restoreLastCounter()?
                }
+               // Record in 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
+               size_t const n = it.depth();
+               for (size_t i = 0; i < n; ++i) {
+                       if (it[i].paragraph().layout().intitle) {
+                               intitle_context_ = true;
+                               break;
+                       }
+               }
        } else {
                DocumentClass const & tclass = buffer().masterBuffer()->params().documentClass();
                // Note that we do not need to call:
@@ -825,7 +891,7 @@ void InsetText::updateBuffer(ParIterator const & it, UpdateType utype)
                // we need float information even in note insets (#9760)
                tclass.counters().current_float(savecnt.current_float());
                tclass.counters().isSubfloat(savecnt.isSubfloat());
-               buffer().updateBuffer(it2, utype);
+               buffer().updateBuffer(it2, utype, deleted);
                tclass.counters() = move(savecnt);
        }
 }
@@ -1080,12 +1146,12 @@ docstring InsetText::toolTipText(docstring prefix, size_t const len) const
                if ((*it).isRTL(buffer().params()))
                        oss << "<div dir=\"rtl\">";
                writePlaintextParagraph(buffer(), *it, oss, rp, ref_printed, len);
-               if ((*it).isRTL(buffer().params()))
-                       oss << "</div>";
                if (oss.tellp() >= 0 && size_t(oss.tellp()) > len)
                        break;
        }
        docstring str = oss.str();
+       if (isChanged())
+               str += from_ascii("\n\n") + _("[contains tracked changes]");
        support::truncateWithEllipsis(str, len);
        return str;
 }
@@ -1127,15 +1193,15 @@ bool InsetText::needsCProtection(bool const maintext, bool const fragile) const
                '&', '_', '$', '%', '#', '^', '{', '}', '\\'};
 
        ParagraphList const & pars = paragraphs();
-       pit_type pend = paragraphs().size();
+       pit_type pend = pit_type(paragraphs().size());
 
        for (pit_type pit = 0; pit != pend; ++pit) {
-               Paragraph const & par = pars[pit];
+               Paragraph const & par = pars[size_type(pit)];
                if (par.needsCProtection(fragile))
                        return true;
-               docstring const pars = par.asString();
+               docstring const par_str = par.asString();
                for (int k = 0; k < nchars_escape; k++) {
-                       if (contains(pars, chars_escape[k]))
+                       if (contains(par_str, chars_escape[k]))
                                return true;
                }
        }