]> git.lyx.org Git - lyx.git/blob - src/insets/InsetFloat.cpp
Improve handling of top and bottom margin
[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                 InsetFloatParams params;
166                 string2params(to_utf8(cmd.argument()), params);
167                 cur.recordUndoInset(this);
168
169                 // placement, wide and sideways are not used for subfloats
170                 if (!params_.subfloat) {
171                         params_.placement = params.placement;
172                         params_.wide      = params.wide;
173                         params_.sideways  = params.sideways;
174                 }
175                 params_.alignment  = params.alignment;
176                 setNewLabel();
177                 if (params_.type != params.type)
178                         setCaptionType(params.type);
179                 // what we really want here is a TOC update, but that means
180                 // a full buffer update
181                 cur.forceBufferUpdate();
182                 break;
183         }
184
185         case LFUN_INSET_DIALOG_UPDATE: {
186                 cur.bv().updateDialog("float", params2string(params()));
187                 break;
188         }
189
190         default:
191                 InsetCaptionable::doDispatch(cur, cmd);
192                 break;
193         }
194 }
195
196
197 bool InsetFloat::getStatus(Cursor & cur, FuncRequest const & cmd,
198                 FuncStatus & flag) const
199 {
200         switch (cmd.action()) {
201
202         case LFUN_INSET_MODIFY:
203         case LFUN_INSET_DIALOG_UPDATE:
204                 flag.setEnabled(true);
205                 return true;
206
207         case LFUN_INSET_SETTINGS:
208                 if (InsetCaptionable::getStatus(cur, cmd, flag)) {
209                         flag.setEnabled(flag.enabled() && !params_.subfloat);
210                         return true;
211                 } else
212                         return false;
213
214         case LFUN_NEWLINE_INSERT:
215                 if (params_.subfloat) {
216                         flag.setEnabled(false);
217                         return true;
218                 }
219                 // no subfloat:
220                 // fall through
221
222         default:
223                 return InsetCaptionable::getStatus(cur, cmd, flag);
224         }
225 }
226
227
228 bool InsetFloat::hasSubCaptions(ParIterator const & it) const
229 {
230         return (it.innerInsetOfType(FLOAT_CODE) || it.innerInsetOfType(WRAP_CODE));
231 }
232
233
234 string InsetFloat::getAlignment() const
235 {
236         string alignment;
237         string const buf_alignment = buffer().params().float_alignment;
238         if (params_.alignment == "document"
239             && !buf_alignment.empty()) {
240                 alignment = buf_alignment;
241         } else if (!params_.alignment.empty()
242                    && params_.alignment != "class"
243                    && params_.alignment != "document") {
244                 alignment = params_.alignment;
245         }
246         return alignment;
247 }
248
249
250 LyXAlignment InsetFloat::contentAlignment() const
251 {
252         LyXAlignment align = LYX_ALIGN_NONE;
253         string alignment = getAlignment();
254         if (alignment == "left")
255                 align = LYX_ALIGN_LEFT;
256         else if (alignment == "center")
257                 align = LYX_ALIGN_CENTER;
258         else if (alignment == "right")
259                 align = LYX_ALIGN_RIGHT;
260
261         return align;
262 }
263
264
265 void InsetFloatParams::write(ostream & os) const
266 {
267         if (type.empty()) {
268                 // Better this than creating a parse error. This in fact happens in the
269                 // parameters dialog via InsetFloatParams::params2string.
270                 os << "senseless" << '\n';
271         } else
272                 os << type << '\n';
273
274         if (!placement.empty())
275                 os << "placement " << placement << "\n";
276         if (!alignment.empty())
277                 os << "alignment " << alignment << "\n";
278
279         if (wide)
280                 os << "wide true\n";
281         else
282                 os << "wide false\n";
283
284         if (sideways)
285                 os << "sideways true\n";
286         else
287                 os << "sideways false\n";
288 }
289
290
291 void InsetFloatParams::read(Lexer & lex)
292 {
293         lex.setContext("InsetFloatParams::read");
294         lex >> type;
295         if (lex.checkFor("placement"))
296                 lex >> placement;
297         if (lex.checkFor("alignment"))
298                 lex >> alignment;
299         lex >> "wide" >> wide;
300         lex >> "sideways" >> sideways;
301 }
302
303
304 void InsetFloat::write(ostream & os) const
305 {
306         os << "Float ";
307         params_.write(os);
308         InsetCaptionable::write(os);
309 }
310
311
312 void InsetFloat::read(Lexer & lex)
313 {
314         params_.read(lex);
315         InsetCaptionable::read(lex);
316         setCaptionType(params_.type);
317 }
318
319
320 void InsetFloat::validate(LaTeXFeatures & features) const
321 {
322         if (support::contains(params_.placement, 'H'))
323                 features.require("float");
324
325         if (params_.sideways)
326                 features.require("rotfloat");
327
328         if (features.inFloat())
329                 features.require("subfig");
330
331         if (features.inDeletedInset()) {
332                 features.require("tikz");
333                 features.require("ct-tikz-object-sout");
334         }
335
336         features.useFloat(params_.type, features.inFloat());
337         features.inFloat(true);
338         InsetCaptionable::validate(features);
339         features.inFloat(false);
340 }
341
342
343 docstring InsetFloat::xhtml(XMLStream & xs, OutputParams const & rp) const
344 {
345         FloatList const & floats = buffer().params().documentClass().floats();
346         Floating const & ftype = floats.getType(params_.type);
347         string const & htmltype = ftype.htmlTag();
348         string const & attr = ftype.htmlAttrib();
349
350         odocstringstream ods;
351         XMLStream newxs(ods);
352         newxs << xml::StartTag(htmltype, attr);
353         InsetText::XHTMLOptions const opts =
354                 InsetText::WriteLabel | InsetText::WriteInnerTag;
355         docstring deferred = InsetText::insetAsXHTML(newxs, rp, opts);
356         newxs << xml::EndTag(htmltype);
357
358         if (rp.inFloat == OutputParams::NONFLOAT) {
359                 // In this case, this float needs to be deferred, but we'll put it
360                 // before anything the text itself deferred.
361                 deferred = ods.str() + '\n' + deferred;
362         } else {
363                 // In this case, the whole thing is already being deferred, so
364                 // we can write to the stream.
365                 // Note that things will already have been escaped, so we do not
366                 // want to escape them again.
367                 xs << XMLStream::ESCAPE_NONE << ods.str();
368         }
369         return deferred;
370 }
371
372
373 void InsetFloat::latex(otexstream & os, OutputParams const & runparams_in) const
374 {
375         if (runparams_in.inFloat != OutputParams::NONFLOAT) {
376                 if (!paragraphs().empty() && !runparams_in.nice)
377                         // improve TexRow precision in non-nice mode
378                         os << safebreakln;
379
380                 if (runparams_in.moving_arg)
381                         os << "\\protect";
382                 os << "\\subfloat";
383
384                 OutputParams rp = runparams_in;
385                 rp.moving_arg = true;
386                 os << getCaption(rp);
387                 os << '{';
388                 // The main argument is the contents of the float. This is not a moving argument.
389                 rp.moving_arg = false;
390                 rp.inFloat = OutputParams::SUBFLOAT;
391                 InsetText::latex(os, rp);
392                 os << "}";
393
394                 return;
395         }
396         OutputParams runparams(runparams_in);
397         runparams.inFloat = OutputParams::MAINFLOAT;
398
399         FloatList const & floats = buffer().params().documentClass().floats();
400         string tmptype = params_.type;
401         if (params_.sideways && floats.allowsSideways(params_.type))
402                 tmptype = "sideways" + params_.type;
403         if (params_.wide && floats.allowsWide(params_.type)
404                 && (!params_.sideways ||
405                      params_.type == "figure" ||
406                      params_.type == "table"))
407                 tmptype += "*";
408         // Figure out the float placement to use.
409         // From lowest to highest:
410         // - float default placement
411         // - document wide default placement
412         // - specific float placement
413         string tmpplacement;
414         string const buf_placement = buffer().params().float_placement;
415         string const def_placement = floats.defaultPlacement(params_.type);
416         if (params_.placement == "document"
417             && !buf_placement.empty()
418             && buf_placement != def_placement) {
419                 tmpplacement = buf_placement;
420         } else if (!params_.placement.empty()
421                    && params_.placement != "document"
422                    && params_.placement != def_placement) {
423                 tmpplacement = params_.placement;
424         }
425
426         // Check if placement is allowed by this float
427         string const allowed_placement =
428                 floats.allowedPlacement(params_.type);
429         string placement;
430         string::const_iterator lit = tmpplacement.begin();
431         string::const_iterator end = tmpplacement.end();
432         for (; lit != end; ++lit) {
433                 if (contains(allowed_placement, *lit))
434                         placement += *lit;
435         }
436
437         // Force \begin{<floatname>} to appear in a new line.
438         os << breakln << "\\begin{" << from_ascii(tmptype) << '}';
439         if (runparams.lastid != -1)
440                 os.texrow().start(runparams.lastid, runparams.lastpos);
441         // We only output placement if different from the def_placement.
442         // sidewaysfloats always use their own page,
443         // therefore don't output the p option that is always set
444         if (!placement.empty()
445             && (!params_.sideways || from_ascii(placement) != "p"))
446                 os << '[' << from_ascii(placement) << ']';
447         os << '\n';
448
449         if (runparams.inDeletedInset) {
450                 // This has to be done manually since we need it inside the float
451                 OutputParams::CtObject ctobject = runparams.ctObject;
452                 runparams.ctObject = OutputParams::CT_DISPLAYOBJECT;
453                 Changes::latexMarkChange(os, buffer().params(), Change(Change::UNCHANGED),
454                                          Change(Change::DELETED), runparams);
455                 runparams.ctObject = ctobject;
456         }
457
458         string alignment = getAlignment();
459         if (alignment == "left")
460                 os << "\\raggedright" << breakln;
461         else if (alignment == "center")
462                 os << "\\centering" << breakln;
463         else if (alignment == "right")
464                 os << "\\raggedleft" << breakln;
465
466         InsetText::latex(os, runparams);
467
468         if (runparams.inDeletedInset)
469                 os << "}";
470
471         // Force \end{<floatname>} to appear in a new line.
472         os << breakln << "\\end{" << from_ascii(tmptype) << "}\n";
473 }
474
475
476 int InsetFloat::plaintext(odocstringstream & os, OutputParams const & runparams, size_t max_length) const
477 {
478         os << '[' << buffer().B_("float") << ' '
479                 << floatName(params_.type) << ":\n";
480         InsetText::plaintext(os, runparams, max_length);
481         os << "\n]";
482
483         return PLAINTEXT_NEWLINE + 1; // one char on a separate line
484 }
485
486
487 std::vector<const InsetBox *> findSubfiguresInParagraph(const Paragraph &par)
488 {
489
490         // Don't make the hypothesis that all subfigures are in the same paragraph.
491         // Similarly, there may be several subfigures in the same paragraph (most likely case, based on the documentation).
492         // Any box is considered as a subfigure, even though the most likely case is \minipage.
493         std::vector<const InsetBox *> subfigures;
494         for (pos_type pos = 0; pos < par.size(); ++pos) {
495                 const Inset *inset = par.getInset(pos);
496                 if (!inset)
497                         continue;
498                 if (const auto box = dynamic_cast<const InsetBox *>(inset))
499                         subfigures.push_back(box);
500         }
501         return subfigures;
502 }
503
504
505 const InsetLabel* findLabelInParagraph(const Paragraph &par)
506 {
507         for (pos_type pos = 0; pos < par.size(); ++pos) {
508                 // If this inset is a subfigure, skip it.
509                 const Inset *inset = par.getInset(pos);
510                 if (dynamic_cast<const InsetBox *>(inset)) {
511                         continue;
512                 }
513
514                 // Maybe an inset is directly a label, in which case no more work is needed.
515                 if (inset && dynamic_cast<const InsetLabel *>(inset))
516                         return dynamic_cast<const InsetLabel *>(inset);
517
518                 // More likely, the label is hidden in an inset of a paragraph (only if a subtype of InsetText).
519                 if (!dynamic_cast<const InsetText *>(inset))
520                         continue;
521
522                 auto insetAsText = dynamic_cast<const InsetText *>(inset);
523                 auto itIn = insetAsText->paragraphs().begin();
524                 auto endIn = insetAsText->paragraphs().end();
525                 for (; itIn != endIn; ++itIn) {
526                         for (pos_type posIn = 0; posIn < itIn->size(); ++posIn) {
527                                 const Inset *insetIn = itIn->getInset(posIn);
528                                 if (insetIn && dynamic_cast<const InsetLabel *>(insetIn)) {
529                                         return dynamic_cast<const InsetLabel *>(insetIn);
530                                 }
531                         }
532                 }
533
534                 // Obviously, this solution does not scale with more levels of paragraphs-insets, but this should be enough.
535         }
536
537         return nullptr;
538 }
539
540
541 const InsetCaption* findCaptionInParagraph(const Paragraph &par)
542 {
543         // Don't dive too deep, otherwise, this could be a subfigure caption.
544         for (pos_type pos = 0; pos < par.size(); ++pos) {
545                 // If this inset is a subfigure, skip it.
546                 const Inset *inset = par.getInset(pos);
547                 if (dynamic_cast<const InsetBox *>(inset))
548                         continue;
549
550                 if (inset && dynamic_cast<const InsetCaption *>(inset))
551                         return dynamic_cast<const InsetCaption *>(inset);
552         }
553
554         return nullptr;
555 }
556
557
558 void InsetFloat::docbook(XMLStream & xs, OutputParams const & runparams) const
559 {
560         // Determine whether the float has a title or not. For this, iterate through the paragraphs and look
561         // for an InsetCaption. Do the same for labels and subfigures.
562         // The caption and the label for each subfigure is handled by recursive calls.
563         const InsetCaption* caption = nullptr;
564         const InsetLabel* label = nullptr;
565         std::vector<const InsetBox *> subfigures;
566
567         auto end = paragraphs().end();
568         for (auto it = paragraphs().begin(); it != end; ++it) {
569                 std::vector<const InsetBox *> foundSubfigures = findSubfiguresInParagraph(*it);
570                 if (!foundSubfigures.empty()) {
571                         subfigures.reserve(subfigures.size() + foundSubfigures.size());
572                         subfigures.insert(subfigures.end(), foundSubfigures.begin(), foundSubfigures.end());
573                 }
574
575                 if (!caption)
576                         caption = findCaptionInParagraph(*it);
577                 if (!label)
578                         label = findLabelInParagraph(*it);
579         }
580
581         // Gather a few things from global environment that are shared between all following cases.
582         FloatList const &floats = buffer().params().documentClass().floats();
583         Floating const &ftype = floats.getType(params_.type);
584         string const &titleTag = ftype.docbookCaption();
585
586         // Ensure there is no label output, it is supposed to be handled as xml:id.
587         OutputParams rpNoLabel = runparams;
588         if (label)
589                 rpNoLabel.docbook_anchors_to_ignore.emplace(label->screenLabel());
590
591         // Ensure the float does not output its caption, as it is handled here (DocBook mandates a specific place for
592         // captions, they cannot appear at the end of the float, albeit LyX is happy with that).
593         OutputParams rpNoTitle = runparams;
594         rpNoTitle.docbook_in_float = true;
595
596         // Deal with subfigures.
597         if (!subfigures.empty()) {
598                 // First, open the formal group.
599                 docstring attr = docstring();
600                 if (label)
601                         attr += "xml:id=\"" + xml::cleanID(label->screenLabel()) + "\"";
602
603                 xs.startDivision(false);
604                 xs << xml::StartTag("formalgroup", attr);
605                 xs << xml::CR();
606
607                 xs << xml::StartTag("title", attr);
608                 if (caption) {
609                         caption->getCaptionAsDocBook(xs, rpNoLabel);
610                 } else {
611                         xs << "No caption";
612                         // No caption has been detected, but this tag is required for the document to be valid DocBook.
613                 }
614                 xs << xml::EndTag("title");
615                 xs << xml::CR();
616
617                 // Deal with each subfigure individually. This should also deal with their caption and their label.
618                 // This should be a recursive call to InsetFloat.
619                 for (const InsetBox *subfigure: subfigures) {
620                         // If there is no InsetFloat in the paragraphs, output a warning.
621                         bool foundInsetFloat = false;
622                         for (auto it = subfigure->paragraphs().begin(); it != subfigure->paragraphs().end(); ++it) {
623                                 for (pos_type posIn = 0; posIn < it->size(); ++posIn) {
624                                         const Inset *inset = it->getInset(posIn);
625                                         if (inset && dynamic_cast<const InsetFloat*>(inset)) {
626                                                 foundInsetFloat = true;
627                                                 break;
628                                         }
629                                 }
630
631                                 if (foundInsetFloat)
632                                         break;
633                         }
634
635                         if (!foundInsetFloat)
636                                 xs << XMLStream::ESCAPE_NONE << "Error: no float found in the box. "
637                                                                         "To use subfigures in DocBook, elements must be wrapped in a float "
638                                                     "inset and have a title/caption.";
639                         // 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.
640
641                         // Finally, recurse.
642                         subfigure->docbook(xs, runparams);
643                 }
644
645                 // Every subfigure is done: close the formal group.
646                 xs << xml::EndTag("formalgroup");
647                 xs << xml::CR();
648                 xs.endDivision();
649         }
650
651         // Here, ensured not to have subfigures.
652
653         // Organisation: <float> <title if any/> <contents without title/> </float>
654         docstring attr = docstring();
655         if (label)
656                 attr += "xml:id=\"" + xml::cleanID(label->screenLabel()) + "\"";
657         if (!ftype.docbookAttr().empty()) {
658                 if (!attr.empty())
659                         attr += " ";
660                 attr += from_utf8(ftype.docbookAttr());
661         }
662
663         xs << xml::StartTag(ftype.docbookTag(caption != nullptr), attr);
664         xs << xml::CR();
665         if (caption != nullptr) {
666                 xs << xml::StartTag(titleTag);
667                 caption->getCaptionAsDocBook(xs, rpNoLabel);
668                 xs << xml::EndTag(titleTag);
669                 xs << xml::CR();
670         }
671         InsetText::docbook(xs, rpNoTitle);
672         xs << xml::EndTag(ftype.docbookTag(caption != nullptr));
673         xs << xml::CR();
674 }
675
676
677 bool InsetFloat::insetAllowed(InsetCode code) const
678 {
679         // The case that code == FLOAT_CODE is handled in Text3.cpp,
680         // because we need to know what type of float is meant.
681         switch(code) {
682         case WRAP_CODE:
683         case FOOT_CODE:
684         case MARGIN_CODE:
685                 return false;
686         default:
687                 return InsetCaptionable::insetAllowed(code);
688         }
689 }
690
691
692 void InsetFloat::setWide(bool w, bool update_label)
693 {
694         if (!buffer().params().documentClass().floats().allowsWide(params_.type))
695                 params_.wide = false;
696         else
697             params_.wide = w;
698         if (update_label)
699                 setNewLabel();
700 }
701
702
703 void InsetFloat::setSideways(bool s, bool update_label)
704 {
705         if (!buffer().params().documentClass().floats().allowsSideways(params_.type))
706                 params_.sideways = false;
707         else
708                 params_.sideways = s;
709         if (update_label)
710                 setNewLabel();
711 }
712
713
714 void InsetFloat::setSubfloat(bool s, bool update_label)
715 {
716         params_.subfloat = s;
717         if (update_label)
718                 setNewLabel();
719 }
720
721
722 void InsetFloat::setNewLabel()
723 {
724         docstring lab = _("float: ");
725
726         if (params_.subfloat)
727                 lab = _("subfloat: ");
728
729         lab += floatName(params_.type);
730
731         FloatList const & floats = buffer().params().documentClass().floats();
732
733         if (params_.wide && floats.allowsWide(params_.type))
734                 lab += '*';
735
736         if (params_.sideways && floats.allowsSideways(params_.type))
737                 lab += _(" (sideways)");
738
739         setLabel(lab);
740 }
741
742
743 bool InsetFloat::allowsCaptionVariation(std::string const & newtype) const
744 {
745         return !params_.subfloat && newtype != "Unnumbered";
746 }
747
748
749 TexString InsetFloat::getCaption(OutputParams const & runparams) const
750 {
751         InsetCaption const * ins = getCaptionInset();
752         if (ins == 0)
753                 return TexString();
754
755         otexstringstream os;
756         ins->getArgs(os, runparams);
757
758         if (!runparams.nice)
759                 // increase TexRow precision in non-nice mode
760                 os << safebreakln;
761         os << '[';
762         otexstringstream os2;
763         ins->getArgument(os2, runparams);
764         TexString ts = os2.release();
765         docstring & arg = ts.str;
766         // Protect ']'
767         if (arg.find(']') != docstring::npos)
768                 arg = '{' + arg + '}';
769         os << move(ts);
770         os << ']';
771         if (!runparams.nice)
772                 os << safebreakln;
773         return os.release();
774 }
775
776
777 void InsetFloat::string2params(string const & in, InsetFloatParams & params)
778 {
779         params = InsetFloatParams();
780         if (in.empty())
781                 return;
782
783         istringstream data(in);
784         Lexer lex;
785         lex.setStream(data);
786         lex.setContext("InsetFloat::string2params");
787         params.read(lex);
788 }
789
790
791 string InsetFloat::params2string(InsetFloatParams const & params)
792 {
793         ostringstream data;
794         params.write(data);
795         return data.str();
796 }
797
798
799 } // namespace lyx