]> git.lyx.org Git - lyx.git/blob - src/insets/InsetFloat.cpp
InsetFloat: pass back inset-modify that is addressed to other inset
[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 InsetFloat::docbook(XMLStream & xs, OutputParams const & runparams) const
567 {
568         // Determine whether the float has a title or not. For this, iterate through the paragraphs and look
569         // for an InsetCaption. Do the same for labels and subfigures.
570         // The caption and the label for each subfigure is handled by recursive calls.
571         const InsetCaption* caption = nullptr;
572         const InsetLabel* label = nullptr;
573         std::vector<const InsetBox *> subfigures;
574
575         auto end = paragraphs().end();
576         for (auto it = paragraphs().begin(); it != end; ++it) {
577                 std::vector<const InsetBox *> foundSubfigures = findSubfiguresInParagraph(*it);
578                 if (!foundSubfigures.empty()) {
579                         subfigures.reserve(subfigures.size() + foundSubfigures.size());
580                         subfigures.insert(subfigures.end(), foundSubfigures.begin(), foundSubfigures.end());
581                 }
582
583                 if (!caption)
584                         caption = findCaptionInParagraph(*it);
585                 if (!label)
586                         label = findLabelInParagraph(*it);
587         }
588
589         // Gather a few things from global environment that are shared between all following cases.
590         FloatList const &floats = buffer().params().documentClass().floats();
591         Floating const &ftype = floats.getType(params_.type);
592         string const &titleTag = ftype.docbookCaption();
593
594         // Ensure there is no label output, it is supposed to be handled as xml:id.
595         OutputParams rpNoLabel = runparams;
596         if (label)
597                 rpNoLabel.docbook_anchors_to_ignore.emplace(label->screenLabel());
598
599         // Ensure the float does not output its caption, as it is handled here (DocBook mandates a specific place for
600         // captions, they cannot appear at the end of the float, albeit LyX is happy with that).
601         OutputParams rpNoTitle = runparams;
602         rpNoTitle.docbook_in_float = true;
603
604         // Deal with subfigures.
605         if (!subfigures.empty()) {
606                 // First, open the formal group.
607                 docstring attr = docstring();
608                 if (label)
609                         attr += "xml:id=\"" + xml::cleanID(label->screenLabel()) + "\"";
610
611                 xs.startDivision(false);
612                 xs << xml::StartTag("formalgroup", attr);
613                 xs << xml::CR();
614
615                 xs << xml::StartTag("title", attr);
616                 if (caption) {
617                         caption->getCaptionAsDocBook(xs, rpNoLabel);
618                 } else {
619                         xs << "No caption";
620                         // No caption has been detected, but this tag is required for the document to be valid DocBook.
621                 }
622                 xs << xml::EndTag("title");
623                 xs << xml::CR();
624
625                 // Deal with each subfigure individually. This should also deal with their caption and their label.
626                 // This should be a recursive call to InsetFloat.
627                 for (const InsetBox *subfigure: subfigures) {
628                         // If there is no InsetFloat in the paragraphs, output a warning.
629                         bool foundInsetFloat = false;
630                         for (auto it = subfigure->paragraphs().begin(); it != subfigure->paragraphs().end(); ++it) {
631                                 for (pos_type posIn = 0; posIn < it->size(); ++posIn) {
632                                         const Inset *inset = it->getInset(posIn);
633                                         if (inset && dynamic_cast<const InsetFloat*>(inset)) {
634                                                 foundInsetFloat = true;
635                                                 break;
636                                         }
637                                 }
638
639                                 if (foundInsetFloat)
640                                         break;
641                         }
642
643                         if (!foundInsetFloat)
644                                 xs << XMLStream::ESCAPE_NONE << "Error: no float found in the box. "
645                                                                         "To use subfigures in DocBook, elements must be wrapped in a float "
646                                                     "inset and have a title/caption.";
647                         // 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.
648
649                         // Finally, recurse.
650                         subfigure->docbook(xs, runparams);
651                 }
652
653                 // Every subfigure is done: close the formal group.
654                 xs << xml::EndTag("formalgroup");
655                 xs << xml::CR();
656                 xs.endDivision();
657         }
658
659         // Here, ensured not to have subfigures.
660
661         // Organisation: <float> <title if any/> <contents without title/> </float>
662         docstring attr = docstring();
663         if (label)
664                 attr += "xml:id=\"" + xml::cleanID(label->screenLabel()) + "\"";
665         if (!ftype.docbookAttr().empty()) {
666                 if (!attr.empty())
667                         attr += " ";
668                 attr += from_utf8(ftype.docbookAttr());
669         }
670
671         xs << xml::StartTag(ftype.docbookTag(caption != nullptr), attr);
672         xs << xml::CR();
673         if (caption != nullptr) {
674                 xs << xml::StartTag(titleTag);
675                 caption->getCaptionAsDocBook(xs, rpNoLabel);
676                 xs << xml::EndTag(titleTag);
677                 xs << xml::CR();
678         }
679         InsetText::docbook(xs, rpNoTitle);
680         xs << xml::EndTag(ftype.docbookTag(caption != nullptr));
681         xs << xml::CR();
682 }
683
684
685 bool InsetFloat::insetAllowed(InsetCode code) const
686 {
687         // The case that code == FLOAT_CODE is handled in Text3.cpp,
688         // because we need to know what type of float is meant.
689         switch(code) {
690         case WRAP_CODE:
691         case FOOT_CODE:
692         case MARGIN_CODE:
693                 return false;
694         default:
695                 return InsetCaptionable::insetAllowed(code);
696         }
697 }
698
699
700 void InsetFloat::setWide(bool w, bool update_label)
701 {
702         if (!buffer().params().documentClass().floats().allowsWide(params_.type))
703                 params_.wide = false;
704         else
705             params_.wide = w;
706         if (update_label)
707                 setNewLabel();
708 }
709
710
711 void InsetFloat::setSideways(bool s, bool update_label)
712 {
713         if (!buffer().params().documentClass().floats().allowsSideways(params_.type))
714                 params_.sideways = false;
715         else
716                 params_.sideways = s;
717         if (update_label)
718                 setNewLabel();
719 }
720
721
722 void InsetFloat::setSubfloat(bool s, bool update_label)
723 {
724         params_.subfloat = s;
725         if (update_label)
726                 setNewLabel();
727 }
728
729
730 void InsetFloat::setNewLabel()
731 {
732         docstring lab = _("float: ");
733
734         if (params_.subfloat)
735                 lab = _("subfloat: ");
736
737         lab += floatName(params_.type);
738
739         FloatList const & floats = buffer().params().documentClass().floats();
740
741         if (params_.wide && floats.allowsWide(params_.type))
742                 lab += '*';
743
744         if (params_.sideways && floats.allowsSideways(params_.type))
745                 lab += _(" (sideways)");
746
747         setLabel(lab);
748 }
749
750
751 bool InsetFloat::allowsCaptionVariation(std::string const & newtype) const
752 {
753         return !params_.subfloat && newtype != "Unnumbered";
754 }
755
756
757 TexString InsetFloat::getCaption(OutputParams const & runparams) const
758 {
759         InsetCaption const * ins = getCaptionInset();
760         if (ins == 0)
761                 return TexString();
762
763         otexstringstream os;
764         ins->getArgs(os, runparams);
765
766         if (!runparams.nice)
767                 // increase TexRow precision in non-nice mode
768                 os << safebreakln;
769         os << '[';
770         otexstringstream os2;
771         ins->getArgument(os2, runparams);
772         TexString ts = os2.release();
773         docstring & arg = ts.str;
774         // Protect ']'
775         if (arg.find(']') != docstring::npos)
776                 arg = '{' + arg + '}';
777         os << move(ts);
778         os << ']';
779         if (!runparams.nice)
780                 os << safebreakln;
781         return os.release();
782 }
783
784
785 void InsetFloat::string2params(string const & in, InsetFloatParams & params)
786 {
787         params = InsetFloatParams();
788         if (in.empty())
789                 return;
790
791         istringstream data(in);
792         Lexer lex;
793         lex.setStream(data);
794         lex.setContext("InsetFloat::string2params");
795         params.read(lex);
796 }
797
798
799 string InsetFloat::params2string(InsetFloatParams const & params)
800 {
801         ostringstream data;
802         params.write(data);
803         return data.str();
804 }
805
806
807 } // namespace lyx