]> git.lyx.org Git - lyx.git/blobdiff - src/insets/InsetCaption.cpp
Refactor OutputParams
[lyx.git] / src / insets / InsetCaption.cpp
index acee0126bcd79ece105c809b6ba0fce6b19d3640..58242a100a0cb00d01d3c5937e7f3113051a2bf0 100644 (file)
@@ -17,7 +17,6 @@
 #include "Buffer.h"
 #include "BufferParams.h"
 #include "BufferView.h"
-#include "Counters.h"
 #include "Cursor.h"
 #include "Dimension.h"
 #include "Floating.h"
 #include "FuncStatus.h"
 #include "InsetList.h"
 #include "Language.h"
+#include "LyXRC.h"
 #include "MetricsInfo.h"
+#include "xml.h"
 #include "output_latex.h"
 #include "output_xhtml.h"
-#include "OutputParams.h"
 #include "Paragraph.h"
+#include "ParIterator.h"
+#include "TexRow.h"
+#include "texstream.h"
 #include "TextClass.h"
+#include "TextMetrics.h"
 #include "TocBackend.h"
 
 #include "frontends/FontMetrics.h"
@@ -48,23 +52,26 @@ using namespace lyx::support;
 namespace lyx {
 
 
-InsetCaption::InsetCaption(Buffer * buf)
-       : InsetText(buf, InsetText::PlainLayout)
+InsetCaption::InsetCaption(Buffer * buf, string const & type)
+    : InsetText(buf, InsetText::PlainLayout),
+      labelwidth_(0), is_subfloat_(false), is_deleted_(false), type_(type)
 {
-       setAutoBreakRows(true);
        setDrawFrame(true);
-       setFrameColor(Color_captionframe);
+       setFrameColor(Color_collapsibleframe);
 }
 
 
 void InsetCaption::write(ostream & os) const
 {
-       os << "Caption\n";
+       os << "Caption";
+       if (!type_.empty())
+               os << ' ' << type_;
+       os << '\n';
        text().write(os);
 }
 
 
-docstring InsetCaption::name() const
+docstring InsetCaption::layoutName() const
 {
        if (type_.empty())
                return from_ascii("Caption");
@@ -72,18 +79,6 @@ docstring InsetCaption::name() const
 }
 
 
-void InsetCaption::read(Lexer & lex)
-{
-#if 0
-       // We will enably this check again when the compability
-       // code is removed from Buffer::Read (Lgb)
-       lex.setContext("InsetCaption::Read: consistency check");
-       lex >> "Caption";
-#endif
-       InsetText::read(lex);
-}
-
-
 void InsetCaption::cursorPos(BufferView const & bv,
                CursorSlice const & sl, bool boundary, int & x, int & y) const
 {
@@ -92,31 +87,25 @@ void InsetCaption::cursorPos(BufferView const & bv,
 }
 
 
-void InsetCaption::setCustomLabel(docstring const & label)
+void InsetCaption::addToToc(DocIterator const & cpit, bool output_active,
+                                                       UpdateType utype, TocBackend & backend) const
 {
-       if (!isAscii(label) || label.empty())
-               // This must be a user defined layout. We cannot translate
-               // this, since gettext accepts only ascii keys.
-               custom_label_ = label;
-       else
-               custom_label_ = _(to_ascii(label));
-}
-
-
-void InsetCaption::addToToc(DocIterator const & cpit)
-{
-       if (type_.empty())
-               return;
-
+       string const & type = floattype_.empty() ? "senseless" : floattype_;
        DocIterator pit = cpit;
-       pit.push_back(CursorSlice(*this));
-
-       Toc & toc = buffer().tocBackend().toc(type_);
-       docstring const str = full_label_ + ". " + text().getPar(0).asString();
-       toc.push_back(TocItem(pit, 0, str));
-
+       pit.push_back(CursorSlice(const_cast<InsetCaption &>(*this)));
+       int length = (utype == OutputUpdate) ?
+               // For output (e.g. xhtml) all (bug #8603) or nothing
+               (output_active ? INT_MAX : 0) :
+               // TOC for LyX interface
+               TOC_ENTRY_LENGTH;
+       docstring str;
+       if (length > 0) {
+               str = full_label_;
+               text().forOutliner(str, length);
+       }
+       backend.builder(type).captionItem(pit, str, output_active);
        // Proceed with the rest of the inset.
-       InsetText::addToToc(cpit);
+       InsetText::addToToc(cpit, output_active, utype, backend);
 }
 
 
@@ -126,7 +115,7 @@ void InsetCaption::metrics(MetricsInfo & mi, Dimension & dim) const
        mi.base.font = mi.base.bv->buffer().params().getFont().fontInfo();
        labelwidth_ = theFontMetrics(mi.base.font).width(full_label_);
        // add some space to separate the label from the inset text
-       labelwidth_ += 2 * TEXT_TO_INSET_OFFSET;
+       labelwidth_ += leftOffset(mi.base.bv) + rightOffset(mi.base.bv);
        dim.wid = labelwidth_;
        Dimension textdim;
        // Correct for button and label width
@@ -140,6 +129,15 @@ void InsetCaption::metrics(MetricsInfo & mi, Dimension & dim) const
 }
 
 
+void InsetCaption::drawBackground(PainterInfo & pi, int x, int y) const
+{
+       TextMetrics & tm = pi.base.bv->textMetrics(&text());
+       int const h = tm.height() + topOffset(pi.base.bv) + bottomOffset(pi.base.bv);
+       int const yy = y - topOffset(pi.base.bv) - tm.ascent();
+       pi.pain.fillRectangle(x, yy, labelwidth_, h, pi.backgroundColor(this));
+}
+
+
 void InsetCaption::draw(PainterInfo & pi, int x, int y) const
 {
        // We must draw the label, we should get the label string
@@ -152,7 +150,13 @@ void InsetCaption::draw(PainterInfo & pi, int x, int y) const
 
        FontInfo tmpfont = pi.base.font;
        pi.base.font = pi.base.bv->buffer().params().getFont().fontInfo();
-       pi.pain.text(x, y, full_label_, pi.base.font);
+       pi.base.font.setColor(pi.textColor(pi.base.font.color()).baseColor);
+       if (is_deleted_)
+               pi.base.font.setStrikeout(FONT_ON);
+       else if (isChanged() && lyxrc.ct_additions_underlined)
+               pi.base.font.setUnderbar(FONT_ON);
+       int const xx = x + leftOffset(pi.base.bv);
+       pi.pain.text(xx, y, full_label_, pi.base.font);
        InsetText::draw(pi, x + labelwidth_, y);
        pi.base.font = tmpfont;
 }
@@ -176,12 +180,14 @@ bool InsetCaption::insetAllowed(InsetCode code) const
 {
        switch (code) {
        // code that is not allowed in a caption
-       case FLOAT_CODE:
-       case TABULAR_CODE:
-       case WRAP_CODE:
        case CAPTION_CODE:
+       case FLOAT_CODE:
+       case FOOT_CODE:
        case NEWPAGE_CODE:
+       case MARGIN_CODE:
        case MATHMACRO_CODE:
+       case TABULAR_CODE:
+       case WRAP_CODE:
                return false;
        default:
                return InsetText::insetAllowed(code);
@@ -189,18 +195,50 @@ bool InsetCaption::insetAllowed(InsetCode code) const
 }
 
 
-bool InsetCaption::getStatus(Cursor & cur, FuncRequest const & cmd,
-       FuncStatus & status) const
+void InsetCaption::doDispatch(Cursor & cur, FuncRequest & cmd)
 {
-       switch (cmd.action) {
+       switch (cmd.action()) {
+
+       case LFUN_INSET_MODIFY: {
+               if (cmd.getArg(0) == "changetype") {
+                       cur.recordUndoInset(this);
+                       type_ = cmd.getArg(1);
+                       cur.forceBufferUpdate();
+                       break;
+               }
+       }
+       // no "changetype":
+       // fall through
 
-       case LFUN_BREAK_PARAGRAPH:
-               status.setEnabled(false);
-               return true;
+       default:
+               InsetText::doDispatch(cur, cmd);
+               break;
+       }
+}
 
-       case LFUN_OPTIONAL_INSERT:
-               status.setEnabled(cur.paragraph().insetList().find(OPTARG_CODE) == -1);
-               return true;
+
+bool InsetCaption::getStatus(Cursor & cur, FuncRequest const & cmd,
+       FuncStatus & status) const
+{
+       switch (cmd.action()) {
+
+       case LFUN_INSET_MODIFY: {
+               string const first_arg = cmd.getArg(0);
+               if (first_arg == "changetype") {
+                       string const type = cmd.getArg(1);
+                       status.setOnOff(type == type_);
+                       bool varia = type != "Unnumbered";
+                       // check if the immediate parent inset allows caption variation
+                       if (cur.depth() > 1) {
+                               varia = cur[cur.depth() - 2].inset().allowsCaptionVariation(type);
+                       }
+                       status.setEnabled(varia
+                                         && buffer().params().documentClass().hasInsetLayout(
+                                               from_ascii("Caption:" + type)));
+                       return true;
+               }
+               return InsetText::getStatus(cur, cmd, status);
+       }
 
        case LFUN_INSET_TOGGLE:
                // pass back to owner
@@ -213,110 +251,151 @@ bool InsetCaption::getStatus(Cursor & cur, FuncRequest const & cmd,
 }
 
 
-int InsetCaption::latex(odocstream & os,
-                       OutputParams const & runparams_in) const
+void InsetCaption::latex(otexstream & os,
+                        OutputParams const & runparams_in) const
 {
        if (runparams_in.inFloat == OutputParams::SUBFLOAT)
                // caption is output as an optional argument
-               return 0;
+               return;
        // This is a bit too simplistic to take advantage of
        // caption options we must add more later. (Lgb)
        // This code is currently only able to handle the simple
        // \caption{...}, later we will make it take advantage
        // of the one of the caption packages. (Lgb)
        OutputParams runparams = runparams_in;
-       // FIXME: actually, it is moving only when there is no
-       // optional argument.
-       runparams.moving_arg = true;
-       os << "\\caption";
-       int l = latexOptArgInsets(paragraphs()[0], os, runparams, 1);
-       os << '{';
-       l += InsetText::latex(os, runparams);
-       os << "}\n";
+       // Some fragile commands (labels, index entries)
+       // are output after the caption (#2154)
+       runparams.postpone_fragile_stuff = buffer().masterParams().postpone_fragile_content;
+       InsetText::latex(os, runparams);
+       if (!runparams.post_macro.empty()) {
+               // Output the stored fragile commands (labels, indices etc.)
+               // that need to be output after the caption.
+               os << runparams.post_macro;
+               runparams.post_macro.clear();
+       }
+       // Backwards compatibility: We always had a linebreak after
+       // the caption (see #8514)
+       os << breakln;
        runparams_in.encoding = runparams.encoding;
-       return l + 1;
 }
 
 
-int InsetCaption::plaintext(odocstream & os,
-                           OutputParams const & runparams) const
+int InsetCaption::plaintext(odocstringstream & os,
+                           OutputParams const & runparams, size_t max_length) const
 {
        os << '[' << full_label_ << "\n";
-       InsetText::plaintext(os, runparams);
+       InsetText::plaintext(os, runparams, max_length);
        os << "\n]";
 
        return PLAINTEXT_NEWLINE + 1; // one char on a separate line
 }
 
 
-int InsetCaption::docbook(odocstream & os,
-                         OutputParams const & runparams) const
+void InsetCaption::docbook(XMLStream &, OutputParams const &) const
 {
-       int ret;
-       os << "<title>";
-       ret = InsetText::docbook(os, runparams);
-       os << "</title>\n";
-       return ret;
+       // This function should never be called (rather InsetFloat::docbook, the titles should be skipped in floats).
 }
 
 
-docstring InsetCaption::xhtml(XHTMLStream & xs, OutputParams const & rp) const
+docstring InsetCaption::xhtml(XMLStream & xs, OutputParams const & rp) const
 {
        if (rp.html_disable_captions)
                return docstring();
-       string attr = "class='float-caption";
-       if (!type_.empty())
-               attr += " float-caption-" + type_;
-       attr += "'";
-       xs << html::StartTag("div", attr);
+       InsetLayout const & il = getLayout();
+       string const & tag = il.htmltag();
+       string attr = il.htmlattr();
+       if (!type_.empty()) {
+               string const our_class = "float-caption-" + type_;
+               size_t const loc = attr.find("class='");
+               if (loc != string::npos)
+                       attr.insert(loc + 7, our_class + " ");
+               else
+                       attr = attr + " class='" + our_class + "'";
+       }
+       xs << xml::StartTag(tag, attr);
        docstring def = getCaptionAsHTML(xs, rp);
-       xs << html::EndTag("div");
+       xs << xml::EndTag(tag);
        return def;
 }
 
 
-int InsetCaption::getArgument(odocstream & os,
+void InsetCaption::getArgument(otexstream & os,
                        OutputParams const & runparams) const
 {
-       return InsetText::latex(os, runparams);
+       InsetLayout const & il = getLayout();
+
+       if (!il.leftdelim().empty())
+               os << il.leftdelim();
+
+       OutputParams rp = runparams;
+       if (isPassThru())
+               rp.pass_thru = true;
+       if (il.isNeedProtect())
+               rp.moving_arg = true;
+       if (il.isNeedMBoxProtect())
+               ++rp.inulemcmd;
+       rp.par_begin = 0;
+       rp.par_end = paragraphs().size();
+
+       // Output the contents of the inset
+       if (!paragraphs().empty())
+               os.texrow().forceStart(paragraphs()[0].id(), 0);
+       latexParagraphs(buffer(), text(), os, rp);
+       runparams.encoding = rp.encoding;
+
+       if (!il.rightdelim().empty())
+               os << il.rightdelim();
 }
 
 
-int InsetCaption::getOptArg(odocstream & os,
+int InsetCaption::getCaptionAsPlaintext(odocstream & os,
                        OutputParams const & runparams) const
 {
-       return latexOptArgInsets(paragraphs()[0], os, runparams, 1);
+       os << full_label_ << ' ';
+       odocstringstream ods;
+       int const retval = InsetText::plaintext(ods, runparams);
+       os << ods.str();
+       return retval;
 }
 
 
-int InsetCaption::getCaptionAsPlaintext(odocstream & os,
-                       OutputParams const & runparams) const
+void InsetCaption::getCaptionAsDocBook(XMLStream & xs,
+                                                                                OutputParams const & runparams) const
 {
-       os << full_label_ << ' ';
-       return InsetText::plaintext(os, runparams);
+       if (runparams.docbook_in_float)
+               return;
+
+       // Ignore full_label_, as the DocBook processor will deal with the numbering.
+       InsetText::XHTMLOptions opts = InsetText::WriteInnerTag;
+       InsetText::docbook(xs, runparams, opts);
 }
 
 
-docstring InsetCaption::getCaptionAsHTML(XHTMLStream & xs,
+docstring InsetCaption::getCaptionAsHTML(XMLStream & xs,
                        OutputParams const & runparams) const
 {
        xs << full_label_ << ' ';
-       InsetText::XHTMLOptions const opts = 
+       InsetText::XHTMLOptions const opts =
                InsetText::WriteLabel | InsetText::WriteInnerTag;
        return InsetText::insetAsXHTML(xs, runparams, opts);
 }
 
 
-void InsetCaption::updateLabels(ParIterator const & it, bool out)
+void InsetCaption::updateBuffer(ParIterator const & it, UpdateType utype, bool const deleted)
 {
        Buffer const & master = *buffer().masterBuffer();
        DocumentClass const & tclass = master.params().documentClass();
        string const & lang = it.paragraph().getParLanguage(master.params())->code();
        Counters & cnts = tclass.counters();
        string const & type = cnts.current_float();
+       if (utype == OutputUpdate) {
+               // counters are local to the caption
+               cnts.saveLastCounter();
+       }
+       is_deleted_ = deleted;
        // Memorize type for addToToc().
-       type_ = type;
-       if (type.empty())
+       floattype_ = type;
+       if (type.empty() || type == "senseless")
                full_label_ = master.B_("Senseless!!! ");
        else {
                // FIXME: life would be _much_ simpler if listings was
@@ -327,22 +406,49 @@ void InsetCaption::updateLabels(ParIterator const & it, bool out)
                else
                        name = master.B_(tclass.floats().getType(type).name());
                docstring counter = from_utf8(type);
-               if (cnts.isSubfloat()) {
+               is_subfloat_ = cnts.isSubfloat();
+               if (is_subfloat_) {
+                       // only standard captions allowed in subfloats
+                       type_ = "Standard";
                        counter = "sub-" + from_utf8(type);
                        name = bformat(_("Sub-%1$s"),
                                       master.B_(tclass.floats().getType(type).name()));
                }
+               docstring sec;
+               docstring const lstring = getLayout().labelstring();
+               docstring const labelstring = isAscii(lstring) ?
+                               master.B_(to_ascii(lstring)) : lstring;
                if (cnts.hasCounter(counter)) {
-                       cnts.step(counter);
-                       full_label_ = bformat(from_ascii("%1$s %2$s:"), 
-                                             name,
-                                             cnts.theCounter(counter, lang));
-               } else
-                       full_label_ = bformat(from_ascii("%1$s #:"), name);     
+                       int val = cnts.value(counter);
+                       // for longtables, we step the counter upstream
+                       if (!cnts.isLongtable())
+                               cnts.step(counter, utype);
+                       sec = cnts.theCounter(counter, lang);
+                       if (deleted && !cnts.isLongtable())
+                               // un-step after deleted counter
+                               cnts.set(counter, val);
+               }
+               if (labelstring != master.B_("standard")) {
+                       if (!sec.empty())
+                               sec += from_ascii(" ");
+                       sec += bformat(from_ascii("(%1$s)"), labelstring);
+               }
+               if (!sec.empty())
+                       full_label_ = bformat(from_ascii("%1$s %2$s: "), name, sec);
+               else
+                       full_label_ = bformat(from_ascii("%1$s #: "), name);
        }
 
        // Do the real work now.
-       InsetText::updateLabels(it, out);
+       InsetText::updateBuffer(it, utype, deleted);
+       if (utype == OutputUpdate)
+               cnts.restoreLastCounter();
+}
+
+
+string InsetCaption::contextMenuName() const
+{
+       return "context-caption";
 }