]> git.lyx.org Git - lyx.git/blob - src/insets/InsetFloat.cpp
Does not compile on older gcc.
[lyx.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                 os << getCaption(rp);
395                 os << '{';
396                 // The main argument is the contents of the float. This is not a moving argument.
397                 rp.moving_arg = false;
398                 rp.inFloat = OutputParams::SUBFLOAT;
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 InsetBox *> 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         std::vector<const InsetBox *> subfigures;
502         for (pos_type pos = 0; pos < par.size(); ++pos) {
503                 const Inset *inset = par.getInset(pos);
504                 if (!inset)
505                         continue;
506                 if (const auto box = dynamic_cast<const InsetBox *>(inset))
507                         subfigures.push_back(box);
508         }
509         return subfigures;
510 }
511
512
513 const InsetLabel* findLabelInParagraph(const Paragraph &par)
514 {
515         for (pos_type pos = 0; pos < par.size(); ++pos) {
516                 // If this inset is a subfigure, skip it.
517                 const Inset *inset = par.getInset(pos);
518                 if (dynamic_cast<const InsetBox *>(inset)) {
519                         continue;
520                 }
521
522                 // Maybe an inset is directly a label, in which case no more work is needed.
523                 if (inset && dynamic_cast<const InsetLabel *>(inset))
524                         return dynamic_cast<const InsetLabel *>(inset);
525
526                 // More likely, the label is hidden in an inset of a paragraph (only if a subtype of InsetText).
527                 if (!dynamic_cast<const InsetText *>(inset))
528                         continue;
529
530                 auto insetAsText = dynamic_cast<const InsetText *>(inset);
531                 auto itIn = insetAsText->paragraphs().begin();
532                 auto endIn = insetAsText->paragraphs().end();
533                 for (; itIn != endIn; ++itIn) {
534                         for (pos_type posIn = 0; posIn < itIn->size(); ++posIn) {
535                                 const Inset *insetIn = itIn->getInset(posIn);
536                                 if (insetIn && dynamic_cast<const InsetLabel *>(insetIn)) {
537                                         return dynamic_cast<const InsetLabel *>(insetIn);
538                                 }
539                         }
540                 }
541
542                 // Obviously, this solution does not scale with more levels of paragraphs-insets, but this should be enough.
543         }
544
545         return nullptr;
546 }
547
548
549 const InsetCaption* findCaptionInParagraph(const Paragraph &par)
550 {
551         // Don't dive too deep, otherwise, this could be a subfigure caption.
552         for (pos_type pos = 0; pos < par.size(); ++pos) {
553                 // If this inset is a subfigure, skip it.
554                 const Inset *inset = par.getInset(pos);
555                 if (dynamic_cast<const InsetBox *>(inset))
556                         continue;
557
558                 if (inset && dynamic_cast<const InsetCaption *>(inset))
559                         return dynamic_cast<const InsetCaption *>(inset);
560         }
561
562         return nullptr;
563 }
564
565
566 void docbookSubfigures(XMLStream & xs, OutputParams const & runparams, const InsetCaption * caption,
567                                            const InsetLabel * label, std::vector<const InsetBox *> & subfigures)
568 {
569         // Ensure there is no label output, it is supposed to be handled as xml:id.
570         OutputParams rpNoLabel = runparams;
571         if (label)
572                 rpNoLabel.docbook_anchors_to_ignore.emplace(label->screenLabel());
573
574         // First, open the formal group.
575         docstring attr = docstring();
576         if (label)
577                 attr += "xml:id=\"" + xml::cleanID(label->screenLabel()) + "\"";
578
579         xs.startDivision(false);
580         xs << xml::StartTag("formalgroup", attr);
581         xs << xml::CR();
582
583         xs << xml::StartTag("title", attr);
584         if (caption) {
585                 caption->getCaptionAsDocBook(xs, rpNoLabel);
586         } else {
587                 xs << "No caption";
588                 // No caption has been detected, but this tag is required for the document to be valid DocBook.
589         }
590         xs << xml::EndTag("title");
591         xs << xml::CR();
592
593         // Deal with each subfigure individually. This should also deal with their caption and their label.
594         // This should be a recursive call to InsetFloat.
595         for (const InsetBox *subfigure: subfigures) {
596                 // If there is no InsetFloat in the paragraphs, output a warning.
597                 bool foundInsetFloat = false;
598                 for (const auto & it : subfigure->paragraphs()) {
599                         for (pos_type posIn = 0; posIn < it.size(); ++posIn) {
600                                 const Inset *inset = it.getInset(posIn);
601                                 if (inset && dynamic_cast<const InsetFloat*>(inset)) {
602                                         foundInsetFloat = true;
603                                         break;
604                                 }
605                         }
606
607                         if (foundInsetFloat)
608                                 break;
609                 }
610
611                 if (!foundInsetFloat)
612                         xs << XMLStream::ESCAPE_NONE << "Error: no float found in the box. "
613                                                                 "To use subfigures in DocBook, elements must be wrapped in a float "
614                                             "inset and have a title/caption.";
615                 // 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.
616
617                 // Finally, recurse.
618                 subfigure->docbook(xs, runparams);
619         }
620
621         // Every subfigure is done: close the formal group.
622         xs << xml::EndTag("formalgroup");
623         xs << xml::CR();
624         xs.endDivision();
625 }
626
627
628 void docbookNoSubfigures(XMLStream & xs, OutputParams const & runparams, const InsetCaption * caption,
629                          const InsetLabel * label, Floating const & ftype, const InsetFloat * thisFloat)
630 {
631         string const &titleTag = ftype.docbookCaption();
632
633         // Ensure there is no label output, it is supposed to be handled as xml:id.
634         OutputParams rpNoLabel = runparams;
635         if (label)
636                 rpNoLabel.docbook_anchors_to_ignore.emplace(label->screenLabel());
637
638         // Ensure the float does not output its caption, as it is handled here (DocBook mandates a specific place for
639         // captions, they cannot appear at the end of the float, albeit LyX is happy with that).
640         OutputParams rpNoTitle = runparams;
641         rpNoTitle.docbook_in_float = true;
642         if (ftype.floattype() == "table")
643                 rpNoTitle.docbook_in_table = true;
644
645         // Organisation: <float> <title if any/> <contents without title/> </float>.
646         docstring attr = docstring();
647         if (label)
648                 attr += "xml:id=\"" + xml::cleanID(label->screenLabel()) + "\"";
649         if (!ftype.docbookAttr().empty()) {
650                 if (!attr.empty())
651                         attr += " ";
652                 attr += from_utf8(ftype.docbookAttr());
653         }
654
655         xs << xml::StartTag(ftype.docbookTag(caption != nullptr), attr);
656         xs << xml::CR();
657         if (caption != nullptr) {
658                 xs << xml::StartTag(titleTag);
659                 caption->getCaptionAsDocBook(xs, rpNoLabel);
660                 xs << xml::EndTag(titleTag);
661                 xs << xml::CR();
662         }
663         thisFloat->InsetText::docbook(xs, rpNoTitle);
664         xs << xml::EndTag(ftype.docbookTag(caption != nullptr));
665         xs << xml::CR();
666 }
667
668
669 void InsetFloat::docbook(XMLStream & xs, OutputParams const & runparams) const
670 {
671         // Determine whether the float has a title or not. For this, iterate through the paragraphs and look
672         // for an InsetCaption. Do the same for labels and subfigures.
673         // The caption and the label for each subfigure is handled by recursive calls.
674         const InsetCaption* caption = nullptr;
675         const InsetLabel* label = nullptr;
676         std::vector<const InsetBox *> subfigures;
677
678         auto end = paragraphs().end();
679         for (auto it = paragraphs().begin(); it != end; ++it) {
680                 std::vector<const InsetBox *> foundSubfigures = findSubfiguresInParagraph(*it);
681                 if (!foundSubfigures.empty()) {
682                         subfigures.reserve(subfigures.size() + foundSubfigures.size());
683                         subfigures.insert(subfigures.end(), foundSubfigures.begin(), foundSubfigures.end());
684                 }
685
686                 if (!caption)
687                         caption = findCaptionInParagraph(*it);
688                 if (!label)
689                         label = findLabelInParagraph(*it);
690         }
691
692         // Gather a few things from global environment that are shared between all following cases.
693         FloatList const &floats = buffer().params().documentClass().floats();
694         Floating const &ftype = floats.getType(params_.type);
695
696         // Switch on subfigures.
697         if (!subfigures.empty())
698                 docbookSubfigures(xs, runparams, caption, label, subfigures);
699         else
700                 docbookNoSubfigures(xs, runparams, caption, label, ftype, this);
701 }
702
703
704 bool InsetFloat::insetAllowed(InsetCode code) const
705 {
706         // The case that code == FLOAT_CODE is handled in Text3.cpp,
707         // because we need to know what type of float is meant.
708         switch(code) {
709         case WRAP_CODE:
710         case FOOT_CODE:
711         case MARGIN_CODE:
712                 return false;
713         default:
714                 return InsetCaptionable::insetAllowed(code);
715         }
716 }
717
718
719 void InsetFloat::setWide(bool w, bool update_label)
720 {
721         if (!buffer().params().documentClass().floats().allowsWide(params_.type))
722                 params_.wide = false;
723         else
724             params_.wide = w;
725         if (update_label)
726                 setNewLabel();
727 }
728
729
730 void InsetFloat::setSideways(bool s, bool update_label)
731 {
732         if (!buffer().params().documentClass().floats().allowsSideways(params_.type))
733                 params_.sideways = false;
734         else
735                 params_.sideways = s;
736         if (update_label)
737                 setNewLabel();
738 }
739
740
741 void InsetFloat::setSubfloat(bool s, bool update_label)
742 {
743         params_.subfloat = s;
744         if (update_label)
745                 setNewLabel();
746 }
747
748
749 void InsetFloat::setNewLabel()
750 {
751         docstring lab = _("float: ");
752
753         if (params_.subfloat)
754                 lab = _("subfloat: ");
755
756         lab += floatName(params_.type);
757
758         FloatList const & floats = buffer().params().documentClass().floats();
759
760         if (params_.wide && floats.allowsWide(params_.type))
761                 lab += '*';
762
763         if (params_.sideways && floats.allowsSideways(params_.type))
764                 lab += _(" (sideways)");
765
766         setLabel(lab);
767 }
768
769
770 bool InsetFloat::allowsCaptionVariation(std::string const & newtype) const
771 {
772         return !params_.subfloat && newtype != "Unnumbered";
773 }
774
775
776 TexString InsetFloat::getCaption(OutputParams const & runparams) const
777 {
778         InsetCaption const * ins = getCaptionInset();
779         if (ins == 0)
780                 return TexString();
781
782         otexstringstream os;
783         ins->getArgs(os, runparams);
784
785         if (!runparams.nice)
786                 // increase TexRow precision in non-nice mode
787                 os << safebreakln;
788         os << '[';
789         otexstringstream os2;
790         ins->getArgument(os2, runparams);
791         TexString ts = os2.release();
792         docstring & arg = ts.str;
793         // Protect ']'
794         if (arg.find(']') != docstring::npos)
795                 arg = '{' + arg + '}';
796         os << move(ts);
797         os << ']';
798         if (!runparams.nice)
799                 os << safebreakln;
800         return os.release();
801 }
802
803
804 void InsetFloat::string2params(string const & in, InsetFloatParams & params)
805 {
806         params = InsetFloatParams();
807         if (in.empty())
808                 return;
809
810         istringstream data(in);
811         Lexer lex;
812         lex.setStream(data);
813         lex.setContext("InsetFloat::string2params");
814         params.read(lex);
815 }
816
817
818 string InsetFloat::params2string(InsetFloatParams const & params)
819 {
820         ostringstream data;
821         params.write(data);
822         return data.str();
823 }
824
825
826 } // namespace lyx