]> git.lyx.org Git - lyx.git/blob - src/insets/InsetFloat.cpp
* Text3.cpp (doDispatch): fix the behaviour of word-delete-forward,
[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  *
9  * Full author contact details are available in file CREDITS.
10  */
11
12 #include <config.h>
13
14 #include "InsetFloat.h"
15
16 #include "Buffer.h"
17 #include "BufferParams.h"
18 #include "BufferView.h"
19 #include "Cursor.h"
20 #include "debug.h"
21 #include "DispatchResult.h"
22 #include "Floating.h"
23 #include "FloatList.h"
24 #include "FuncRequest.h"
25 #include "FuncStatus.h"
26 #include "gettext.h"
27 #include "LaTeXFeatures.h"
28 #include "Lexer.h"
29 #include "OutputParams.h"
30
31 #include "support/lstrings.h"
32 #include "support/convert.h"
33
34
35 namespace lyx {
36
37 using support::contains;
38
39 using std::endl;
40 using std::string;
41 using std::auto_ptr;
42 using std::istringstream;
43 using std::ostream;
44 using std::ostringstream;
45
46
47 // With this inset it will be possible to support the latex package
48 // float.sty, and I am sure that with this and some additional support
49 // classes we can support similar functionality in other formats
50 // (read DocBook).
51 // By using float.sty we will have the same handling for all floats, both
52 // for those already in existance (table and figure) and all user created
53 // ones¹. So suddenly we give the users the possibility of creating new
54 // kinds of floats on the fly. (and with a uniform look)
55 //
56 // API to float.sty:
57 //   \newfloat{type}{placement}{ext}[within]
58 //     type      - The "type" of the new class of floats, like program or
59 //                 algorithm. After the appropriate \newfloat, commands
60 //                 such as \begin{program} or \end{algorithm*} will be
61 //                 available.
62 //     placement - The default placement for the given class of floats.
63 //                 They are like in standard LaTeX: t, b, p and h for top,
64 //                 bottom, page, and here, respectively. On top of that
65 //                 there is a new type, H, which does not really correspond
66 //                 to a float, since it means: put it "here" and nowhere else.
67 //                 Note, however that the H specifier is special and, because
68 //                 of implementation details cannot be used in the second
69 //                 argument of \newfloat.
70 //     ext       - The file name extension of an auxiliary file for the list
71 //                 of figures (or whatever). LaTeX writes the captions to
72 //                 this file.
73 //     within    - This (optional) argument determines whether floats of this
74 //                 class will be numbered within some sectional unit of the
75 //                 document. For example, if within is equal to chapter, the
76 //                 floats will be numbered within chapters.
77 //   \floatstyle{style}
78 //     style -  plain, boxed, ruled
79 //   \floatname{float}{floatname}
80 //     float     -
81 //     floatname -
82 //   \floatplacement{float}{placement}
83 //     float     -
84 //     placement -
85 //   \restylefloat{float}
86 //     float -
87 //   \listof{type}{title}
88 //     title -
89
90 // ¹ the algorithm float is defined using the float.sty package. Like this
91 //   \floatstyle{ruled}
92 //   \newfloat{algorithm}{htbp}{loa}[<sect>]
93 //   \floatname{algorithm}{Algorithm}
94 //
95 // The intention is that floats should be definable from two places:
96 //          - layout files
97 //          - the "gui" (i.e. by the user)
98 //
99 // From layout files.
100 // This should only be done for floats defined in a documentclass and that
101 // does not need any additional packages. The two most known floats in this
102 // category is "table" and "figure". Floats defined in layout files are only
103 // stored in lyx files if the user modifies them.
104 //
105 // By the user.
106 // There should be a gui dialog (and also a collection of lyxfuncs) where
107 // the user can modify existing floats and/or create new ones.
108 //
109 // The individual floats will also have some settable
110 // variables: wide and placement.
111 //
112 // Lgb
113
114
115 InsetFloat::InsetFloat(BufferParams const & bp, string const & type)
116         : InsetCollapsable(bp), name_(from_utf8(type))
117 {
118         setLabel(_("float: ") + floatName(type, bp));
119         Font font(Font::ALL_SANE);
120         font.decSize();
121         font.decSize();
122         font.setColor(Color::collapsable);
123         setLabelFont(font);
124         params_.type = type;
125 }
126
127
128 InsetFloat::~InsetFloat()
129 {
130         InsetFloatMailer(*this).hideDialog();
131 }
132
133
134 void InsetFloat::doDispatch(Cursor & cur, FuncRequest & cmd)
135 {
136         switch (cmd.action) {
137
138         case LFUN_INSET_MODIFY: {
139                 InsetFloatParams params;
140                 InsetFloatMailer::string2params(to_utf8(cmd.argument()), params);
141                 params_.placement = params.placement;
142                 params_.wide      = params.wide;
143                 params_.sideways  = params.sideways;
144                 wide(params_.wide, cur.buffer().params());
145                 sideways(params_.sideways, cur.buffer().params());
146                 break;
147         }
148
149         case LFUN_INSET_DIALOG_UPDATE: {
150                 InsetFloatMailer(*this).updateDialog(&cur.bv());
151                 break;
152         }
153
154         case LFUN_MOUSE_RELEASE: {
155                 if (cmd.button() == mouse_button::button3 && hitButton(cmd)) {
156                         InsetFloatMailer(*this).showDialog(&cur.bv());
157                         break;
158                 }
159                 InsetCollapsable::doDispatch(cur, cmd);
160                 break;
161         }
162
163         default:
164                 InsetCollapsable::doDispatch(cur, cmd);
165                 break;
166         }
167 }
168
169
170 bool InsetFloat::getStatus(Cursor & cur, FuncRequest const & cmd,
171                 FuncStatus & flag) const
172 {
173         switch (cmd.action) {
174
175         case LFUN_INSET_MODIFY:
176         case LFUN_INSET_DIALOG_UPDATE:
177                 flag.enabled(true);
178                 return true;
179
180         default:
181                 return InsetCollapsable::getStatus(cur, cmd, flag);
182         }
183 }
184
185
186 void InsetFloatParams::write(ostream & os) const
187 {
188         os << "Float " << type << '\n';
189
190         if (!placement.empty())
191                 os << "placement " << placement << "\n";
192
193         if (wide)
194                 os << "wide true\n";
195         else
196                 os << "wide false\n";
197
198         if (sideways)
199                 os << "sideways true\n";
200         else
201                 os << "sideways false\n";
202 }
203
204
205 void InsetFloatParams::read(Lexer & lex)
206 {
207         string token;
208         lex >> token;
209         if (token == "placement") {
210                 lex >> placement;
211         } else {
212                 // take countermeasures
213                 lex.pushToken(token);
214         }
215         lex >> token;
216         if (token == "wide") {
217                 lex >> wide;
218         } else {
219                 lyxerr << "InsetFloat::Read:: Missing wide!"
220                 << endl;
221                 // take countermeasures
222                 lex.pushToken(token);
223         }
224         lex >> token;
225         if (token == "sideways") {
226                 lex >> sideways;
227         } else {
228                 lyxerr << "InsetFloat::Read:: Missing sideways!"
229                 << endl;
230                 // take countermeasures
231                 lex.pushToken(token);
232         }
233 }
234
235
236 void InsetFloat::write(Buffer const & buf, ostream & os) const
237 {
238         params_.write(os);
239         InsetCollapsable::write(buf, os);
240 }
241
242
243 void InsetFloat::read(Buffer const & buf, Lexer & lex)
244 {
245         params_.read(lex);
246         wide(params_.wide, buf.params());
247         sideways(params_.sideways, buf.params());
248         InsetCollapsable::read(buf, lex);
249 }
250
251
252 void InsetFloat::validate(LaTeXFeatures & features) const
253 {
254         if (contains(params_.placement, 'H')) {
255                 features.require("float");
256         }
257
258         if (params_.sideways)
259                 features.require("rotating");
260
261         features.useFloat(params_.type);
262         InsetCollapsable::validate(features);
263 }
264
265
266 auto_ptr<Inset> InsetFloat::doClone() const
267 {
268         return auto_ptr<Inset>(new InsetFloat(*this));
269 }
270
271
272 docstring const InsetFloat::editMessage() const
273 {
274         return _("Opened Float Inset");
275 }
276
277
278 int InsetFloat::latex(Buffer const & buf, odocstream & os,
279                       OutputParams const & runparams) const
280 {
281         FloatList const & floats = buf.params().getTextClass().floats();
282         string tmptype = (params_.wide ? params_.type + "*" : params_.type);
283         if (params_.sideways) {
284                 if (params_.type == "table")
285                         tmptype = "sidewaystable";
286                 else if (params_.type == "figure")
287                         tmptype = "sidewaysfigure";
288         }
289         // Figure out the float placement to use.
290         // From lowest to highest:
291         // - float default placement
292         // - document wide default placement
293         // - specific float placement
294         string placement;
295         string const buf_placement = buf.params().float_placement;
296         string const def_placement = floats.defaultPlacement(params_.type);
297         if (!params_.placement.empty()
298             && params_.placement != def_placement) {
299                 placement = params_.placement;
300         } else if (params_.placement.empty()
301                    && !buf_placement.empty()
302                    && buf_placement != def_placement) {
303                 placement = buf_placement;
304         }
305
306         // The \n is used to force \begin{<floatname>} to appear in a new line.
307         // The % is needed to prevent two consecutive \n chars in the case
308         // when the current output line is empty.
309         os << "%\n\\begin{" << from_ascii(tmptype) << '}';
310         // We only output placement if different from the def_placement.
311         // sidewaysfloats always use their own page
312         if (!placement.empty() && !params_.sideways) {
313                 os << '[' << from_ascii(placement) << ']';
314         }
315         os << '\n';
316
317         int const i = InsetText::latex(buf, os, runparams);
318
319         // The \n is used to force \end{<floatname>} to appear in a new line.
320         // In this case, we do not case if the current output line is empty.
321         os << "\n\\end{" << from_ascii(tmptype) << "}\n";
322
323         return i + 4;
324 }
325
326
327 int InsetFloat::plaintext(Buffer const & buf, odocstream & os,
328                           OutputParams const & runparams) const
329 {
330         os << '[' << buf.B_("float") << ' ' << floatName(params_.type, buf.params()) << ":\n";
331         InsetText::plaintext(buf, os, runparams);
332         os << "\n]";
333
334         return PLAINTEXT_NEWLINE + 1; // one char on a separate line
335 }
336
337
338 int InsetFloat::docbook(Buffer const & buf, odocstream & os,
339                         OutputParams const & runparams) const
340 {
341         // FIXME UNICODE
342         os << '<' << from_ascii(params_.type) << '>';
343         int const i = InsetText::docbook(buf, os, runparams);
344         os << "</" << from_ascii(params_.type) << '>';
345
346         return i;
347 }
348
349
350 bool InsetFloat::insetAllowed(Inset::Code code) const
351 {
352         return code != Inset::FLOAT_CODE
353             && code != Inset::FOOT_CODE
354             && code != Inset::MARGIN_CODE;
355 }
356
357
358 bool InsetFloat::showInsetDialog(BufferView * bv) const
359 {
360         if (!InsetText::showInsetDialog(bv))
361                 InsetFloatMailer(const_cast<InsetFloat &>(*this)).showDialog(bv);
362         return true;
363 }
364
365
366 void InsetFloat::wide(bool w, BufferParams const & bp)
367 {
368         params_.wide = w;
369         docstring lab = _("float: ") + floatName(params_.type, bp);
370         if (params_.wide)
371                 lab += '*';
372         setLabel(lab);
373 }
374
375
376 void InsetFloat::sideways(bool s, BufferParams const & bp)
377 {
378         params_.sideways = s;
379         docstring lab = _("float: ") + floatName(params_.type, bp);
380         if (params_.sideways)
381                 lab += _(" (sideways)");
382         setLabel(lab);
383 }
384
385
386 string const InsetFloatMailer::name_("float");
387
388 InsetFloatMailer::InsetFloatMailer(InsetFloat & inset)
389         : inset_(inset)
390 {}
391
392
393 string const InsetFloatMailer::inset2string(Buffer const &) const
394 {
395         return params2string(inset_.params());
396 }
397
398
399 void InsetFloatMailer::string2params(string const & in,
400                                      InsetFloatParams & params)
401 {
402         params = InsetFloatParams();
403         if (in.empty())
404                 return;
405
406         istringstream data(in);
407         Lexer lex(0,0);
408         lex.setStream(data);
409
410         string name;
411         lex >> name;
412         if (!lex || name != name_)
413                 return print_mailer_error("InsetFloatMailer", in, 1, name_);
414
415         // This is part of the inset proper that is usually swallowed
416         // by Text::readInset
417         string id;
418         lex >> id;
419         if (!lex || id != "Float")
420                 return print_mailer_error("InsetBoxMailer", in, 2, "Float");
421
422         // We have to read the type here!
423         lex >> params.type;
424         params.read(lex);
425 }
426
427
428 string const InsetFloatMailer::params2string(InsetFloatParams const & params)
429 {
430         ostringstream data;
431         data << name_ << ' ';
432         params.write(data);
433         return data.str();
434 }
435
436
437 } // namespace lyx