]> git.lyx.org Git - features.git/blob - src/insets/InsetFloat.cpp
DocBook: handle other cases of subfigures.
[features.git] / src / insets / InsetFloat.cpp
1 /**
2  * \file InsetFloat.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Jürgen Vigna
7  * \author Lars Gullik Bjønnes
8  * \author Jürgen Spitzmüller
9  *
10  * Full author contact details are available in file CREDITS.
11  */
12
13 #include <config.h>
14
15 #include "InsetBox.h"
16 #include "InsetCaption.h"
17 #include "InsetFloat.h"
18 #include "InsetGraphics.h"
19 #include "InsetLabel.h"
20
21 #include "Buffer.h"
22 #include "BufferParams.h"
23 #include "BufferView.h"
24 #include "Counters.h"
25 #include "Cursor.h"
26 #include "DispatchResult.h"
27 #include "Floating.h"
28 #include "FloatList.h"
29 #include "FuncRequest.h"
30 #include "FuncStatus.h"
31 #include "LaTeXFeatures.h"
32 #include "Lexer.h"
33 #include "xml.h"
34 #include "output_docbook.h"
35 #include "output_xhtml.h"
36 #include "ParIterator.h"
37 #include "TexRow.h"
38 #include "texstream.h"
39 #include "TextClass.h"
40 #include "InsetList.h"
41
42 #include "support/debug.h"
43 #include "support/docstream.h"
44 #include "support/gettext.h"
45 #include "support/lstrings.h"
46
47 #include "frontends/Application.h"
48
49 using namespace std;
50 using namespace lyx::support;
51
52
53 namespace lyx {
54
55 // With this inset it will be possible to support the latex package
56 // float.sty, and I am sure that with this and some additional support
57 // classes we can support similar functionality in other formats
58 // (read DocBook).
59 // By using float.sty we will have the same handling for all floats, both
60 // for those already in existence (table and figure) and all user created
61 // ones¹. So suddenly we give the users the possibility of creating new
62 // kinds of floats on the fly. (and with a uniform look)
63 //
64 // API to float.sty:
65 //   \newfloat{type}{placement}{ext}[within]
66 //     type      - The "type" of the new class of floats, like program or
67 //                 algorithm. After the appropriate \newfloat, commands
68 //                 such as \begin{program} or \end{algorithm*} will be
69 //                 available.
70 //     placement - The default placement for the given class of floats.
71 //                 They are like in standard LaTeX: t, b, p and h for top,
72 //                 bottom, page, and here, respectively. On top of that
73 //                 there is a new type, H, which does not really correspond
74 //                 to a float, since it means: put it "here" and nowhere else.
75 //                 Note, however that the H specifier is special and, because
76 //                 of implementation details cannot be used in the second
77 //                 argument of \newfloat.
78 //     ext       - The file name extension of an auxiliary file for the list
79 //                 of figures (or whatever). LaTeX writes the captions to
80 //                 this file.
81 //     within    - This (optional) argument determines whether floats of this
82 //                 class will be numbered within some sectional unit of the
83 //                 document. For example, if within is equal to chapter, the
84 //                 floats will be numbered within chapters.
85 //   \floatstyle{style}
86 //     style -  plain, boxed, ruled
87 //   \floatname{float}{floatname}
88 //     float     -
89 //     floatname -
90 //   \floatplacement{float}{placement}
91 //     float     -
92 //     placement -
93 //   \restylefloat{float}
94 //     float -
95 //   \listof{type}{title}
96 //     title -
97
98 // ¹ the algorithm float is defined using the float.sty package. Like this
99 //   \floatstyle{ruled}
100 //   \newfloat{algorithm}{htbp}{loa}[<sect>]
101 //   \floatname{algorithm}{Algorithm}
102 //
103 // The intention is that floats should be definable from two places:
104 //          - layout files
105 //          - the "gui" (i.e. by the user)
106 //
107 // From layout files.
108 // This should only be done for floats defined in a documentclass and that
109 // does not need any additional packages. The two most known floats in this
110 // category is "table" and "figure". Floats defined in layout files are only
111 // stored in lyx files if the user modifies them.
112 //
113 // By the user.
114 // There should be a gui dialog (and also a collection of lyxfuncs) where
115 // the user can modify existing floats and/or create new ones.
116 //
117 // The individual floats will also have some settable
118 // variables: wide and placement.
119 //
120 // Lgb
121
122 //FIXME: why do we set in stone the type here?
123 InsetFloat::InsetFloat(Buffer * buf, string const & params_str)
124         : InsetCaptionable(buf)
125 {
126         string2params(params_str, params_);
127         setCaptionType(params_.type);
128 }
129
130
131 // Enforce equality of float type and caption type.
132 void InsetFloat::setCaptionType(std::string const & type)
133 {
134         InsetCaptionable::setCaptionType(type);
135         params_.type = captionType();
136         // check if the float type exists
137         if (buffer().params().documentClass().floats().typeExist(params_.type))
138                 setNewLabel();
139         else
140                 setLabel(bformat(_("ERROR: Unknown float type: %1$s"), from_utf8(params_.type)));
141 }
142
143
144 docstring InsetFloat::layoutName() const
145 {
146         return "Float:" + from_utf8(params_.type);
147 }
148
149
150 docstring InsetFloat::toolTip(BufferView const & bv, int x, int y) const
151 {
152         if (isOpen(bv))
153                 return InsetCaptionable::toolTip(bv, x, y);
154
155         OutputParams rp(&buffer().params().encoding());
156         return getCaptionText(rp);
157 }
158
159
160 void InsetFloat::doDispatch(Cursor & cur, FuncRequest & cmd)
161 {
162         switch (cmd.action()) {
163
164         case LFUN_INSET_MODIFY: {
165                 if (!buffer().params().documentClass().floats().typeExist(cmd.getArg(0))) {
166                         // not for us: pass further.
167                         cur.undispatched();
168                         break;
169                 }
170                 InsetFloatParams params;
171                 string2params(to_utf8(cmd.argument()), params);
172                 cur.recordUndoInset(this);
173
174                 // placement, wide and sideways are not used for subfloats
175                 if (!params_.subfloat) {
176                         params_.placement = params.placement;
177                         params_.wide      = params.wide;
178                         params_.sideways  = params.sideways;
179                 }
180                 params_.alignment  = params.alignment;
181                 setNewLabel();
182                 if (params_.type != params.type)
183                         setCaptionType(params.type);
184                 // what we really want here is a TOC update, but that means
185                 // a full buffer update
186                 cur.forceBufferUpdate();
187                 break;
188         }
189
190         case LFUN_INSET_DIALOG_UPDATE: {
191                 cur.bv().updateDialog("float", params2string(params()));
192                 break;
193         }
194
195         default:
196                 InsetCaptionable::doDispatch(cur, cmd);
197                 break;
198         }
199 }
200
201
202 bool InsetFloat::getStatus(Cursor & cur, FuncRequest const & cmd,
203                 FuncStatus & flag) const
204 {
205         switch (cmd.action()) {
206
207         case LFUN_INSET_MODIFY:
208                 if (!buffer().params().documentClass().floats().typeExist(cmd.getArg(0)))
209                         return Inset::getStatus(cur, cmd, flag);
210         // fall through
211         case LFUN_INSET_DIALOG_UPDATE:
212                 flag.setEnabled(true);
213                 return true;
214
215         case LFUN_INSET_SETTINGS:
216                 if (InsetCaptionable::getStatus(cur, cmd, flag)) {
217                         flag.setEnabled(flag.enabled() && !params_.subfloat);
218                         return true;
219                 } else
220                         return false;
221
222         case LFUN_NEWLINE_INSERT:
223                 if (params_.subfloat) {
224                         flag.setEnabled(false);
225                         return true;
226                 }
227                 // no subfloat:
228                 // fall through
229
230         default:
231                 return InsetCaptionable::getStatus(cur, cmd, flag);
232         }
233 }
234
235
236 bool InsetFloat::hasSubCaptions(ParIterator const & it) const
237 {
238         return (it.innerInsetOfType(FLOAT_CODE) || it.innerInsetOfType(WRAP_CODE));
239 }
240
241
242 string InsetFloat::getAlignment() const
243 {
244         string alignment;
245         string const buf_alignment = buffer().params().float_alignment;
246         if (params_.alignment == "document"
247             && !buf_alignment.empty()) {
248                 alignment = buf_alignment;
249         } else if (!params_.alignment.empty()
250                    && params_.alignment != "class"
251                    && params_.alignment != "document") {
252                 alignment = params_.alignment;
253         }
254         return alignment;
255 }
256
257
258 LyXAlignment InsetFloat::contentAlignment() const
259 {
260         LyXAlignment align = LYX_ALIGN_NONE;
261         string alignment = getAlignment();
262         if (alignment == "left")
263                 align = LYX_ALIGN_LEFT;
264         else if (alignment == "center")
265                 align = LYX_ALIGN_CENTER;
266         else if (alignment == "right")
267                 align = LYX_ALIGN_RIGHT;
268
269         return align;
270 }
271
272
273 void InsetFloatParams::write(ostream & os) const
274 {
275         if (type.empty()) {
276                 // Better this than creating a parse error. This in fact happens in the
277                 // parameters dialog via InsetFloatParams::params2string.
278                 os << "senseless" << '\n';
279         } else
280                 os << type << '\n';
281
282         if (!placement.empty())
283                 os << "placement " << placement << "\n";
284         if (!alignment.empty())
285                 os << "alignment " << alignment << "\n";
286
287         if (wide)
288                 os << "wide true\n";
289         else
290                 os << "wide false\n";
291
292         if (sideways)
293                 os << "sideways true\n";
294         else
295                 os << "sideways false\n";
296 }
297
298
299 void InsetFloatParams::read(Lexer & lex)
300 {
301         lex.setContext("InsetFloatParams::read");
302         lex >> type;
303         if (lex.checkFor("placement"))
304                 lex >> placement;
305         if (lex.checkFor("alignment"))
306                 lex >> alignment;
307         lex >> "wide" >> wide;
308         lex >> "sideways" >> sideways;
309 }
310
311
312 void InsetFloat::write(ostream & os) const
313 {
314         os << "Float ";
315         params_.write(os);
316         InsetCaptionable::write(os);
317 }
318
319
320 void InsetFloat::read(Lexer & lex)
321 {
322         params_.read(lex);
323         InsetCaptionable::read(lex);
324         setCaptionType(params_.type);
325 }
326
327
328 void InsetFloat::validate(LaTeXFeatures & features) const
329 {
330         if (support::contains(params_.placement, 'H'))
331                 features.require("float");
332
333         if (params_.sideways)
334                 features.require("rotfloat");
335
336         if (features.inFloat())
337                 features.require("subfig");
338
339         if (features.inDeletedInset()) {
340                 features.require("tikz");
341                 features.require("ct-tikz-object-sout");
342         }
343
344         features.useFloat(params_.type, features.inFloat());
345         features.inFloat(true);
346         InsetCaptionable::validate(features);
347         features.inFloat(false);
348 }
349
350
351 docstring InsetFloat::xhtml(XMLStream & xs, OutputParams const & rp) const
352 {
353         FloatList const & floats = buffer().params().documentClass().floats();
354         Floating const & ftype = floats.getType(params_.type);
355         string const & htmltype = ftype.htmlTag();
356         string const & attr = ftype.htmlAttrib();
357
358         odocstringstream ods;
359         XMLStream newxs(ods);
360         newxs << xml::StartTag(htmltype, attr);
361         InsetText::XHTMLOptions const opts =
362                 InsetText::WriteLabel | InsetText::WriteInnerTag;
363         docstring deferred = InsetText::insetAsXHTML(newxs, rp, opts);
364         newxs << xml::EndTag(htmltype);
365
366         if (rp.inFloat == OutputParams::NONFLOAT) {
367                 // In this case, this float needs to be deferred, but we'll put it
368                 // before anything the text itself deferred.
369                 deferred = ods.str() + '\n' + deferred;
370         } else {
371                 // In this case, the whole thing is already being deferred, so
372                 // we can write to the stream.
373                 // Note that things will already have been escaped, so we do not
374                 // want to escape them again.
375                 xs << XMLStream::ESCAPE_NONE << ods.str();
376         }
377         return deferred;
378 }
379
380
381 void InsetFloat::latex(otexstream & os, OutputParams const & runparams_in) const
382 {
383         if (runparams_in.inFloat != OutputParams::NONFLOAT) {
384                 if (!paragraphs().empty() && !runparams_in.nice)
385                         // improve TexRow precision in non-nice mode
386                         os << safebreakln;
387
388                 if (runparams_in.moving_arg)
389                         os << "\\protect";
390                 os << "\\subfloat";
391
392                 OutputParams rp = runparams_in;
393                 rp.moving_arg = true;
394                 rp.inFloat = OutputParams::SUBFLOAT;
395                 os << getCaption(rp);
396                 os << '{';
397                 // The main argument is the contents of the float. This is not a moving argument.
398                 rp.moving_arg = false;
399                 InsetText::latex(os, rp);
400                 os << "}";
401
402                 return;
403         }
404         OutputParams runparams(runparams_in);
405         runparams.inFloat = OutputParams::MAINFLOAT;
406
407         FloatList const & floats = buffer().params().documentClass().floats();
408         string tmptype = params_.type;
409         if (params_.sideways && floats.allowsSideways(params_.type))
410                 tmptype = "sideways" + params_.type;
411         if (params_.wide && floats.allowsWide(params_.type)
412                 && (!params_.sideways ||
413                      params_.type == "figure" ||
414                      params_.type == "table"))
415                 tmptype += "*";
416         // Figure out the float placement to use.
417         // From lowest to highest:
418         // - float default placement
419         // - document wide default placement
420         // - specific float placement
421         string tmpplacement;
422         string const buf_placement = buffer().params().float_placement;
423         string const def_placement = floats.defaultPlacement(params_.type);
424         if (params_.placement == "document"
425             && !buf_placement.empty()
426             && buf_placement != def_placement) {
427                 tmpplacement = buf_placement;
428         } else if (!params_.placement.empty()
429                    && params_.placement != "document"
430                    && params_.placement != def_placement) {
431                 tmpplacement = params_.placement;
432         }
433
434         // Check if placement is allowed by this float
435         string const allowed_placement =
436                 floats.allowedPlacement(params_.type);
437         string placement;
438         string::const_iterator lit = tmpplacement.begin();
439         string::const_iterator end = tmpplacement.end();
440         for (; lit != end; ++lit) {
441                 if (contains(allowed_placement, *lit))
442                         placement += *lit;
443         }
444
445         // Force \begin{<floatname>} to appear in a new line.
446         os << breakln << "\\begin{" << from_ascii(tmptype) << '}';
447         if (runparams.lastid != -1)
448                 os.texrow().start(runparams.lastid, runparams.lastpos);
449         // We only output placement if different from the def_placement.
450         // sidewaysfloats always use their own page,
451         // therefore don't output the p option that is always set
452         if (!placement.empty()
453             && (!params_.sideways || from_ascii(placement) != "p"))
454                 os << '[' << from_ascii(placement) << ']';
455         os << '\n';
456
457         if (runparams.inDeletedInset) {
458                 // This has to be done manually since we need it inside the float
459                 OutputParams::CtObject ctobject = runparams.ctObject;
460                 runparams.ctObject = OutputParams::CT_DISPLAYOBJECT;
461                 Changes::latexMarkChange(os, buffer().params(), Change(Change::UNCHANGED),
462                                          Change(Change::DELETED), runparams);
463                 runparams.ctObject = ctobject;
464         }
465
466         string alignment = getAlignment();
467         if (alignment == "left")
468                 os << "\\raggedright" << breakln;
469         else if (alignment == "center")
470                 os << "\\centering" << breakln;
471         else if (alignment == "right")
472                 os << "\\raggedleft" << breakln;
473
474         InsetText::latex(os, runparams);
475
476         if (runparams.inDeletedInset)
477                 os << "}";
478
479         // Force \end{<floatname>} to appear in a new line.
480         os << breakln << "\\end{" << from_ascii(tmptype) << "}\n";
481 }
482
483
484 int InsetFloat::plaintext(odocstringstream & os, OutputParams const & runparams, size_t max_length) const
485 {
486         os << '[' << buffer().B_("float") << ' '
487                 << floatName(params_.type) << ":\n";
488         InsetText::plaintext(os, runparams, max_length);
489         os << "\n]";
490
491         return PLAINTEXT_NEWLINE + 1; // one char on a separate line
492 }
493
494
495 std::vector<const InsetCollapsible *> findSubfiguresInParagraph(const Paragraph &par)
496 {
497
498         // Don't make the hypothesis that all subfigures are in the same paragraph.
499         // Similarly, there may be several subfigures in the same paragraph (most likely case, based on the documentation).
500         // Any box is considered as a subfigure, even though the most likely case is \minipage.
501         // Boxes are not required to make subfigures. The common root between InsetBox and InsetFLoat is InsetCollapsible.
502         std::vector<const InsetCollapsible *> subfigures;
503         for (pos_type pos = 0; pos < par.size(); ++pos) {
504                 const Inset *inset = par.getInset(pos);
505                 if (!inset)
506                         continue;
507                 if (const auto box = dynamic_cast<const InsetBox *>(inset))
508                         subfigures.push_back(box);
509                 else if (const auto fl = dynamic_cast<const InsetFloat *>(inset))
510                         subfigures.push_back(fl);
511         }
512         return subfigures;
513 }
514
515
516 const InsetLabel* findLabelInParagraph(const Paragraph &par)
517 {
518         for (pos_type pos = 0; pos < par.size(); ++pos) {
519                 // If this inset is a subfigure, skip it.
520                 const Inset *inset = par.getInset(pos);
521                 if (dynamic_cast<const InsetBox *>(inset)) {
522                         continue;
523                 }
524
525                 // Maybe an inset is directly a label, in which case no more work is needed.
526                 if (inset && dynamic_cast<const InsetLabel *>(inset))
527                         return dynamic_cast<const InsetLabel *>(inset);
528
529                 // More likely, the label is hidden in an inset of a paragraph (only if a subtype of InsetText).
530                 if (!dynamic_cast<const InsetText *>(inset))
531                         continue;
532
533                 auto insetAsText = dynamic_cast<const InsetText *>(inset);
534                 auto itIn = insetAsText->paragraphs().begin();
535                 auto endIn = insetAsText->paragraphs().end();
536                 for (; itIn != endIn; ++itIn) {
537                         for (pos_type posIn = 0; posIn < itIn->size(); ++posIn) {
538                                 const Inset *insetIn = itIn->getInset(posIn);
539                                 if (insetIn && dynamic_cast<const InsetLabel *>(insetIn)) {
540                                         return dynamic_cast<const InsetLabel *>(insetIn);
541                                 }
542                         }
543                 }
544
545                 // Obviously, this solution does not scale with more levels of paragraphs-insets, but this should be enough.
546         }
547
548         return nullptr;
549 }
550
551
552 const InsetCaption* findCaptionInParagraph(const Paragraph &par)
553 {
554         // Don't dive too deep, otherwise, this could be a subfigure caption.
555         for (pos_type pos = 0; pos < par.size(); ++pos) {
556                 // If this inset is a subfigure, skip it.
557                 const Inset *inset = par.getInset(pos);
558                 if (dynamic_cast<const InsetBox *>(inset))
559                         continue;
560
561                 if (inset && dynamic_cast<const InsetCaption *>(inset))
562                         return dynamic_cast<const InsetCaption *>(inset);
563         }
564
565         return nullptr;
566 }
567
568
569 void docbookSubfigures(XMLStream & xs, OutputParams const & runparams, const InsetCaption * caption,
570                                            const InsetLabel * label, std::vector<const InsetCollapsible *> & subfigures)
571 {
572         // Ensure there is no label output, it is supposed to be handled as xml:id.
573         OutputParams rpNoLabel = runparams;
574         if (label)
575                 rpNoLabel.docbook_anchors_to_ignore.emplace(label->screenLabel());
576
577         // First, open the formal group.
578         docstring attr = docstring();
579         if (label)
580                 attr += "xml:id=\"" + xml::cleanID(label->screenLabel()) + "\"";
581
582         xs.startDivision(false);
583         xs << xml::StartTag("formalgroup", attr);
584         xs << xml::CR();
585
586         xs << xml::StartTag("title", attr);
587         if (caption) {
588                 caption->getCaptionAsDocBook(xs, rpNoLabel);
589         } else {
590                 xs << "No caption";
591                 // No caption has been detected, but this tag is required for the document to be valid DocBook.
592         }
593         xs << xml::EndTag("title");
594         xs << xml::CR();
595
596         // Deal with each subfigure individually. This should also deal with their caption and their label.
597         // This should be a recursive call to InsetFloat.
598         // An item in subfigure should either be an InsetBox containing an InsetFloat or directly an InsetFloat.
599         for (const InsetCollapsible *subfigure: subfigures) {
600                 // If there is no InsetFloat in the paragraphs, output a warning.
601                 bool foundInsetFloat = false;
602
603                 // The collapsible may already be a float (InsetFloat).
604                 if (subfigure && dynamic_cast<const InsetFloat *>(subfigure))
605                         foundInsetFloat = true;
606
607                 // Subfigures are in boxes.
608                 if (!foundInsetFloat) {
609                         for (const auto &it : subfigure->paragraphs()) {
610                                 for (pos_type posIn = 0; posIn < it.size(); ++posIn) {
611                                         const Inset *inset = it.getInset(posIn);
612                                         if (inset && dynamic_cast<const InsetFloat *>(inset)) {
613                                                 foundInsetFloat = true;
614                                                 break;
615                                         }
616                                 }
617
618                                 if (foundInsetFloat)
619                                         break;
620                         }
621                 }
622
623                 if (!foundInsetFloat)
624                         xs << XMLStream::ESCAPE_NONE << "Error: no float found in the box. "
625                                                                 "To use subfigures in DocBook, elements must be wrapped in a float "
626                                             "inset and have a title/caption.";
627                 // TODO: could also output a table, that would ensure that the document is correct and *displays* correctly (but without the right semantics), instead of just an error.
628
629                 // Finally, recurse.
630                 subfigure->docbook(xs, runparams);
631         }
632
633         // Every subfigure is done: close the formal group.
634         xs << xml::EndTag("formalgroup");
635         xs << xml::CR();
636         xs.endDivision();
637 }
638
639
640 void docbookNoSubfigures(XMLStream & xs, OutputParams const & runparams, const InsetCaption * caption,
641                          const InsetLabel * label, Floating const & ftype, const InsetFloat * thisFloat)
642 {
643         string const &titleTag = ftype.docbookCaption();
644
645         // Ensure there is no label output, it is supposed to be handled as xml:id.
646         OutputParams rpNoLabel = runparams;
647         if (label)
648                 rpNoLabel.docbook_anchors_to_ignore.emplace(label->screenLabel());
649
650         // Ensure the float does not output its caption, as it is handled here (DocBook mandates a specific place for
651         // captions, they cannot appear at the end of the float, albeit LyX is happy with that).
652         OutputParams rpNoTitle = runparams;
653         rpNoTitle.docbook_in_float = true;
654         if (ftype.docbookFloatType() == "table")
655                 rpNoTitle.docbook_in_table = true;
656
657         // Organisation: <float> <title if any/> <contents without title/> </float>.
658         docstring attr = docstring();
659         if (label)
660                 attr += "xml:id=\"" + xml::cleanID(label->screenLabel()) + "\"";
661         if (!ftype.docbookAttr().empty()) {
662                 if (!attr.empty())
663                         attr += " ";
664                 attr += from_utf8(ftype.docbookAttr());
665         }
666
667         xs << xml::StartTag(ftype.docbookTag(caption != nullptr), attr);
668         xs << xml::CR();
669         if (caption) {
670                 xs << xml::StartTag(titleTag);
671                 caption->getCaptionAsDocBook(xs, rpNoLabel);
672                 xs << xml::EndTag(titleTag);
673                 xs << xml::CR();
674         }
675         thisFloat->InsetText::docbook(xs, rpNoTitle);
676         xs << xml::EndTag(ftype.docbookTag(caption != nullptr));
677         xs << xml::CR();
678 }
679
680
681 void InsetFloat::docbook(XMLStream & xs, OutputParams const & runparams) const
682 {
683         // Determine whether the float has a title or not. For this, iterate through the paragraphs and look
684         // for an InsetCaption. Do the same for labels and subfigures.
685         // The caption and the label for each subfigure is handled by recursive calls.
686         const InsetCaption* caption = nullptr;
687         const InsetLabel* label = nullptr;
688         std::vector<const InsetCollapsible *> subfigures;
689
690         auto end = paragraphs().end();
691         for (auto it = paragraphs().begin(); it != end; ++it) {
692                 std::vector<const InsetCollapsible *> foundSubfigures = findSubfiguresInParagraph(*it);
693                 if (!foundSubfigures.empty()) {
694                         subfigures.reserve(subfigures.size() + foundSubfigures.size());
695                         subfigures.insert(subfigures.end(), foundSubfigures.begin(), foundSubfigures.end());
696                 }
697
698                 if (!caption)
699                         caption = findCaptionInParagraph(*it);
700                 if (!label)
701                         label = findLabelInParagraph(*it);
702         }
703
704         // Gather a few things from global environment that are shared between all following cases.
705         FloatList const &floats = buffer().params().documentClass().floats();
706         Floating const &ftype = floats.getType(params_.type);
707
708         // Switch on subfigures.
709         if (!subfigures.empty())
710                 docbookSubfigures(xs, runparams, caption, label, subfigures);
711         else
712                 docbookNoSubfigures(xs, runparams, caption, label, ftype, this);
713 }
714
715
716 bool InsetFloat::insetAllowed(InsetCode code) const
717 {
718         // The case that code == FLOAT_CODE is handled in Text3.cpp,
719         // because we need to know what type of float is meant.
720         switch(code) {
721         case WRAP_CODE:
722         case FOOT_CODE:
723         case MARGIN_CODE:
724                 return false;
725         default:
726                 return InsetCaptionable::insetAllowed(code);
727         }
728 }
729
730
731 void InsetFloat::setWide(bool w, bool update_label)
732 {
733         if (!buffer().params().documentClass().floats().allowsWide(params_.type))
734                 params_.wide = false;
735         else
736             params_.wide = w;
737         if (update_label)
738                 setNewLabel();
739 }
740
741
742 void InsetFloat::setSideways(bool s, bool update_label)
743 {
744         if (!buffer().params().documentClass().floats().allowsSideways(params_.type))
745                 params_.sideways = false;
746         else
747                 params_.sideways = s;
748         if (update_label)
749                 setNewLabel();
750 }
751
752
753 void InsetFloat::setSubfloat(bool s, bool update_label)
754 {
755         params_.subfloat = s;
756         if (update_label)
757                 setNewLabel();
758 }
759
760
761 void InsetFloat::setNewLabel()
762 {
763         docstring lab = _("float: ");
764
765         if (params_.subfloat)
766                 lab = _("subfloat: ");
767
768         lab += floatName(params_.type);
769
770         FloatList const & floats = buffer().params().documentClass().floats();
771
772         if (params_.wide && floats.allowsWide(params_.type))
773                 lab += '*';
774
775         if (params_.sideways && floats.allowsSideways(params_.type))
776                 lab += _(" (sideways)");
777
778         setLabel(lab);
779 }
780
781
782 bool InsetFloat::allowsCaptionVariation(std::string const & newtype) const
783 {
784         return !params_.subfloat && newtype != "Unnumbered";
785 }
786
787
788 TexString InsetFloat::getCaption(OutputParams const & runparams) const
789 {
790         InsetCaption const * ins = getCaptionInset();
791         if (ins == 0)
792                 return TexString();
793
794         otexstringstream os;
795         ins->getArgs(os, runparams);
796
797         if (!runparams.nice)
798                 // increase TexRow precision in non-nice mode
799                 os << safebreakln;
800         os << '[';
801         otexstringstream os2;
802         ins->getArgument(os2, runparams);
803         TexString ts = os2.release();
804         docstring & arg = ts.str;
805         // Protect ']'
806         if (arg.find(']') != docstring::npos)
807                 arg = '{' + arg + '}';
808         os << move(ts);
809         os << ']';
810         if (!runparams.nice)
811                 os << safebreakln;
812         return os.release();
813 }
814
815
816 void InsetFloat::string2params(string const & in, InsetFloatParams & params)
817 {
818         params = InsetFloatParams();
819         if (in.empty())
820                 return;
821
822         istringstream data(in);
823         Lexer lex;
824         lex.setStream(data);
825         lex.setContext("InsetFloat::string2params");
826         params.read(lex);
827 }
828
829
830 string InsetFloat::params2string(InsetFloatParams const & params)
831 {
832         ostringstream data;
833         params.write(data);
834         return data.str();
835 }
836
837
838 } // namespace lyx