]> git.lyx.org Git - features.git/blob - src/insets/InsetBox.cpp
Further cleanup of InsetFlex, InsetCollapsable and InsetLayout:
[features.git] / src / insets / InsetBox.cpp
1 /**
2  * \file InsetBox.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Angus Leeming
7  * \author Martin Vermeer
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
17 #include "Buffer.h"
18 #include "BufferView.h"
19 #include "Cursor.h"
20 #include "DispatchResult.h"
21 #include "debug.h"
22 #include "FuncStatus.h"
23 #include "FuncRequest.h"
24 #include "gettext.h"
25 #include "LaTeXFeatures.h"
26 #include "Lexer.h"
27 #include "MetricsInfo.h"
28
29 #include "support/Translator.h"
30
31 #include <sstream>
32
33
34 namespace lyx {
35
36 using std::string;
37 using std::istringstream;
38 using std::ostream;
39 using std::ostringstream;
40 using std::endl;
41
42
43 namespace {
44
45 typedef Translator<std::string, InsetBox::BoxType> BoxTranslator;
46 typedef Translator<docstring, InsetBox::BoxType> BoxTranslatorLoc;
47
48 BoxTranslator const init_boxtranslator()
49 {
50         BoxTranslator translator("Boxed", InsetBox::Boxed);
51         translator.addPair("Frameless", InsetBox::Frameless);
52         translator.addPair("ovalbox", InsetBox::ovalbox);
53         translator.addPair("Ovalbox", InsetBox::Ovalbox);
54         translator.addPair("Shadowbox", InsetBox::Shadowbox);
55         translator.addPair("Doublebox",InsetBox::Doublebox);
56         return translator;
57 }
58
59
60 BoxTranslatorLoc const init_boxtranslator_loc()
61 {
62         BoxTranslatorLoc translator(_("Boxed"), InsetBox::Boxed);
63         translator.addPair(_("Frameless"), InsetBox::Frameless);
64         translator.addPair(_("ovalbox"), InsetBox::ovalbox);
65         translator.addPair(_("Ovalbox"), InsetBox::Ovalbox);
66         translator.addPair(_("Shadowbox"), InsetBox::Shadowbox);
67         translator.addPair(_("Doublebox"), InsetBox::Doublebox);
68         return translator;
69 }
70
71
72 BoxTranslator const & boxtranslator()
73 {
74         static BoxTranslator translator = init_boxtranslator();
75         return translator;
76 }
77
78
79 BoxTranslatorLoc const & boxtranslator_loc()
80 {
81         static BoxTranslatorLoc translator = init_boxtranslator_loc();
82         return translator;
83 }
84
85 } // anon
86
87
88 InsetBox::InsetBox(BufferParams const & bp, string const & label)
89         : InsetCollapsable(bp), params_(label)
90 {}
91
92
93 InsetBox::InsetBox(InsetBox const & in)
94         : InsetCollapsable(in), params_(in.params_)
95 {}
96
97
98 InsetBox::~InsetBox()
99 {
100         InsetBoxMailer(*this).hideDialog();
101 }
102
103
104 Inset * InsetBox::clone() const
105 {
106         return new InsetBox(*this);
107 }
108
109
110 docstring const InsetBox::editMessage() const
111 {
112         return _("Opened Box Inset");
113 }
114
115
116 void InsetBox::write(Buffer const & buf, ostream & os) const
117 {
118         params_.write(os);
119         InsetCollapsable::write(buf, os);
120 }
121
122
123 void InsetBox::read(Buffer const & buf, Lexer & lex)
124 {
125         params_.read(lex);
126         InsetCollapsable::read(buf, lex);
127 }
128
129
130 void InsetBox::setButtonLabel()
131 {
132         BoxType btype = boxtranslator().find(params_.type);
133
134         docstring label;
135         label += _("Box");
136         label += " (";
137         if (btype == Frameless) {
138                 if (params_.use_parbox)
139                         label += _("Parbox");
140                 else
141                         label += _("Minipage");
142         } else
143                 label += boxtranslator_loc().find(btype);
144         label += ")";
145
146         setLabel(label);
147 }
148
149
150 bool InsetBox::hasFixedWidth() const
151 {
152       return params_.inner_box || params_.special != "width";
153 }
154
155
156 void InsetBox::metrics(MetricsInfo & m, Dimension & dim) const
157 {
158         // back up textwidth.
159         int textwidth_backup = m.base.textwidth;
160         if (hasFixedWidth())
161                 m.base.textwidth = params_.width.inPixels(m.base.textwidth);
162         InsetCollapsable::metrics(m, dim);
163         // retore textwidth.
164         m.base.textwidth = textwidth_backup;
165 }
166
167
168 bool InsetBox::forceDefaultParagraphs(idx_type) const
169 {
170         return !params_.inner_box;
171 }
172
173
174 bool InsetBox::showInsetDialog(BufferView * bv) const
175 {
176         InsetBoxMailer(const_cast<InsetBox &>(*this)).showDialog(bv);
177         return true;
178 }
179
180
181 void InsetBox::doDispatch(Cursor & cur, FuncRequest & cmd)
182 {
183         switch (cmd.action) {
184
185         case LFUN_INSET_MODIFY: {
186                 //lyxerr << "InsetBox::dispatch MODIFY" << endl;
187                 InsetBoxMailer::string2params(to_utf8(cmd.argument()), params_);
188                 setLayout(cur.buffer().params());
189                 break;
190         }
191
192         case LFUN_INSET_DIALOG_UPDATE:
193                 InsetBoxMailer(*this).updateDialog(&cur.bv());
194                 break;
195
196         case LFUN_MOUSE_RELEASE:
197                 if (cmd.button() == mouse_button::button3 && hitButton(cmd)) {
198                         InsetBoxMailer(*this).showDialog(&cur.bv());
199                         break;
200                 }
201                 InsetCollapsable::doDispatch(cur, cmd);
202                 break;
203
204         default:
205                 InsetCollapsable::doDispatch(cur, cmd);
206                 break;
207         }
208 }
209
210
211 bool InsetBox::getStatus(Cursor & cur, FuncRequest const & cmd,
212                 FuncStatus & flag) const
213 {
214         switch (cmd.action) {
215
216         case LFUN_INSET_MODIFY:
217         case LFUN_INSET_DIALOG_UPDATE:
218                 flag.enabled(true);
219                 return true;
220         case LFUN_BREAK_PARAGRAPH:
221                 if (params_.inner_box) {
222                         return InsetCollapsable::getStatus(cur, cmd, flag);
223                 } else {
224                         flag.enabled(false);
225                         return true;
226                 }
227
228         default:
229                 return InsetCollapsable::getStatus(cur, cmd, flag);
230         }
231 }
232
233
234 int InsetBox::latex(Buffer const & buf, odocstream & os,
235                     OutputParams const & runparams) const
236 {
237         BoxType btype = boxtranslator().find(params_.type);
238
239         string width_string = params_.width.asLatexString();
240         bool stdwidth(false);
241         if (params_.inner_box &&
242                         (width_string.find("1.0\\columnwidth") != string::npos
243                         || width_string.find("1.0\\textwidth") != string::npos)) {
244                 stdwidth = true;
245                 switch (btype) {
246                 case Frameless:
247                         break;
248                 case Boxed:
249                         width_string += " - 2\\fboxsep - 2\\fboxrule";
250                         break;
251                 case ovalbox:
252                         width_string += " - 2\\fboxsep - 0.8pt";
253                         break;
254                 case Ovalbox:
255                         width_string += " - 2\\fboxsep - 1.6pt";
256                         break;
257                 case Shadowbox:
258                         // Shadow falls outside right margin... opinions?
259                         width_string += " - 2\\fboxsep - 2\\fboxrule"/* "-\\shadowsize"*/;
260                         break;
261                 case Doublebox:
262                         width_string += " - 2\\fboxsep - 7.5\\fboxrule - 1.0pt";
263                         break;
264                 }
265         }
266
267         int i = 0;
268         os << "%\n";
269         // Adapt to column/text width correctly also if paragraphs indented:
270         if (stdwidth)
271                 os << "\\noindent";
272
273         switch (btype) {
274         case Frameless:
275                 break;
276         case Boxed:
277                 os << "\\framebox";
278                 if (!params_.inner_box) {
279                         os << "{\\makebox";
280                         // Special widths, see usrguide §3.5
281                         // FIXME UNICODE
282                         if (params_.special != "none") {
283                                 os << "[" << params_.width.value()
284                                    << '\\' << from_utf8(params_.special)
285                                    << ']';
286                         } else
287                                 os << '[' << from_ascii(width_string)
288                                    << ']';
289                         if (params_.hor_pos != 'c')
290                                 os << "[" << params_.hor_pos << "]";
291                 }
292
293                 os << "{";
294                 break;
295         case ovalbox:
296                 os << "\\ovalbox{";
297                 break;
298         case Ovalbox:
299                 os << "\\Ovalbox{";
300                 break;
301         case Shadowbox:
302                 os << "\\shadowbox{";
303                 break;
304         case Doublebox:
305                 os << "\\doublebox{";
306                 break;
307         }
308
309         if (params_.inner_box) {
310                 if (params_.use_parbox)
311                         os << "\\parbox";
312                 else
313                         os << "\\begin{minipage}";
314
315                 os << "[" << params_.pos << "]";
316                 if (params_.height_special == "none") {
317                         // FIXME UNICODE
318                         os << "[" << from_ascii(params_.height.asLatexString()) << "]";
319                 } else {
320                         // Special heights
321                         // set no optional argument when the value is the default "1\height"
322                         // (special units like \height are handled as "in")
323                         // but when the user has chosen a non-default inner_pos, the height
324                         // must be given: \minipage[pos][height][inner-pos]{width}
325                         if ((params_.height != Length("1in") ||
326                                  params_.height_special != "totalheight") ||
327                                 params_.inner_pos != params_.pos) {
328                                 // FIXME UNICODE
329                                 os << "[" << params_.height.value()
330                                         << "\\" << from_utf8(params_.height_special) << "]";
331                         }
332                 }
333                 if (params_.inner_pos != params_.pos)
334                         os << "[" << params_.inner_pos << "]";
335
336                 // FIXME UNICODE
337                 os << '{' << from_ascii(width_string) << '}';
338
339                 if (params_.use_parbox)
340                         os << "{";
341                 os << "%\n";
342                 i += 1;
343         }
344
345         i += InsetText::latex(buf, os, runparams);
346
347         if (params_.inner_box) {
348                 if (params_.use_parbox)
349                         os << "%\n}";
350                 else
351                         os << "%\n\\end{minipage}";
352         }
353
354         switch (btype) {
355         case Frameless:
356                 break;
357         case Boxed:
358                 if (!params_.inner_box)
359                         os << "}"; // for makebox
360                 os << "}";
361                 break;
362         case ovalbox:
363         case Ovalbox:
364         case Doublebox:
365         case Shadowbox:
366                 os << "}";
367                 break;
368         }
369         os << "%\n";
370
371         i += 3;
372
373         return i;
374 }
375
376
377 int InsetBox::plaintext(Buffer const & buf, odocstream & os,
378                         OutputParams const & runparams) const
379 {
380         BoxType const btype = boxtranslator().find(params_.type);
381
382         switch (btype) {
383                 case Frameless: break;
384                 case Boxed:     os << "[\n";  break;
385                 case ovalbox:   os << "(\n";  break;
386                 case Ovalbox:   os << "((\n"; break;
387                 case Shadowbox: os << "[/\n"; break;
388                 case Doublebox: os << "[[\n"; break;
389         }
390
391         InsetText::plaintext(buf, os, runparams);
392
393         int len = 0;
394         switch (btype) {
395                 case Frameless: os << "\n";            break;
396                 case Boxed:     os << "\n]";  len = 1; break;
397                 case ovalbox:   os << "\n)";  len = 1; break;
398                 case Ovalbox:   os << "\n))"; len = 2; break;
399                 case Shadowbox: os << "\n/]"; len = 2; break;
400                 case Doublebox: os << "\n]]"; len = 2; break;
401         }
402
403         return PLAINTEXT_NEWLINE + len; // len chars on a separate line
404 }
405
406
407 int InsetBox::docbook(Buffer const & buf, odocstream & os,
408                       OutputParams const & runparams) const
409 {
410         return InsetText::docbook(buf, os, runparams);
411 }
412
413
414 void InsetBox::validate(LaTeXFeatures & features) const
415 {
416         BoxType btype = boxtranslator().find(params_.type);
417         switch (btype) {
418         case Frameless:
419                 break;
420         case Boxed:
421                 features.require("calc");
422                 break;
423         case ovalbox:
424         case Ovalbox:
425         case Shadowbox:
426         case Doublebox:
427                 features.require("calc");
428                 features.require("fancybox");
429                 break;
430         }
431         InsetText::validate(features);
432 }
433
434
435 InsetBoxMailer::InsetBoxMailer(InsetBox & inset)
436         : inset_(inset)
437 {}
438
439
440 string const InsetBoxMailer::name_ = "box";
441
442
443 string const InsetBoxMailer::inset2string(Buffer const &) const
444 {
445         return params2string(inset_.params());
446 }
447
448
449 string const InsetBoxMailer::params2string(InsetBoxParams const & params)
450 {
451         ostringstream data;
452         data << "box" << ' ';
453         params.write(data);
454         return data.str();
455 }
456
457
458 void InsetBoxMailer::string2params(string const & in,
459                                    InsetBoxParams & params)
460 {
461         params = InsetBoxParams(string());
462         if (in.empty())
463                 return;
464
465         istringstream data(in);
466         Lexer lex(0,0);
467         lex.setStream(data);
468
469         string name;
470         lex >> name;
471         if (!lex || name != name_)
472                 return print_mailer_error("InsetBoxMailer", in, 1, name_);
473
474         // This is part of the inset proper that is usually swallowed
475         // by Text::readInset
476         string id;
477         lex >> id;
478         if (!lex || id != "Box")
479                 return print_mailer_error("InsetBoxMailer", in, 2, "Box");
480
481         params.read(lex);
482 }
483
484
485 InsetBoxParams::InsetBoxParams(string const & label)
486         : type(label),
487           use_parbox(false),
488           inner_box(true),
489           width(Length("100col%")),
490           special("none"),
491           pos('t'),
492           hor_pos('c'),
493           inner_pos('t'),
494           height(Length("1in")),
495           height_special("totalheight") // default is 1\\totalheight
496 {}
497
498
499 void InsetBoxParams::write(ostream & os) const
500 {
501         os << "Box " << type << "\n";
502         os << "position \"" << pos << "\"\n";
503         os << "hor_pos \"" << hor_pos << "\"\n";
504         os << "has_inner_box " << inner_box << "\n";
505         os << "inner_pos \"" << inner_pos << "\"\n";
506         os << "use_parbox " << use_parbox << "\n";
507         os << "width \"" << width.asString() << "\"\n";
508         os << "special \"" << special << "\"\n";
509         os << "height \"" << height.asString() << "\"\n";
510         os << "height_special \"" << height_special << "\"\n";
511 }
512
513
514 void InsetBoxParams::read(Lexer & lex)
515 {
516         if (!lex.isOK())
517                 return;
518
519         lex.next();
520         type = lex.getString();
521
522         if (!lex)
523                 return;
524
525         lex.next();
526         string token;
527         token = lex.getString();
528         if (!lex)
529                 return;
530         if (token == "position") {
531                 lex.next();
532                 // The [0] is needed. We need the first and only char in
533                 // this string -- MV
534                 pos = lex.getString()[0];
535         } else {
536                 lyxerr << "InsetBox::Read: Missing 'position'-tag!" << token << endl;
537                 lex.pushToken(token);
538         }
539
540         lex.next();
541         token = lex.getString();
542         if (!lex)
543                 return;
544         if (token == "hor_pos") {
545                 lex.next();
546                 hor_pos = lex.getString()[0];
547         } else {
548                 lyxerr << "InsetBox::Read: Missing 'hor_pos'-tag!" << token << endl;
549                 lex.pushToken(token);
550         }
551
552         lex.next();
553         token = lex.getString();
554         if (!lex)
555                 return;
556         if (token == "has_inner_box") {
557                 lex.next();
558                 inner_box = lex.getInteger();
559         } else {
560                 lyxerr << "InsetBox::Read: Missing 'has_inner_box'-tag!" << endl;
561                 lex.pushToken(token);
562         }
563
564         lex.next();
565         token = lex.getString();
566         if (!lex)
567                 return;
568         if (token == "inner_pos") {
569                 lex.next();
570                 inner_pos = lex.getString()[0];
571         } else {
572                 lyxerr << "InsetBox::Read: Missing 'inner_pos'-tag!"
573                         << token << endl;
574                 lex.pushToken(token);
575         }
576
577         lex.next();
578         token = lex.getString();
579         if (!lex)
580                 return;
581         if (token == "use_parbox") {
582                 lex.next();
583                 use_parbox = lex.getInteger();
584         } else {
585                 lyxerr << "InsetBox::Read: Missing 'use_parbox'-tag!" << endl;
586                 lex.pushToken(token);
587         }
588
589         lex.next();
590         token = lex.getString();
591         if (!lex)
592                 return;
593         if (token == "width") {
594                 lex.next();
595                 width = Length(lex.getString());
596         } else {
597                 lyxerr << "InsetBox::Read: Missing 'width'-tag!" << endl;
598                 lex.pushToken(token);
599         }
600
601         lex.next();
602         token = lex.getString();
603         if (!lex)
604                 return;
605         if (token == "special") {
606                 lex.next();
607                 special = lex.getString();
608         } else {
609                 lyxerr << "InsetBox::Read: Missing 'special'-tag!" << endl;
610                 lex.pushToken(token);
611         }
612
613         lex.next();
614         token = lex.getString();
615         if (!lex)
616                 return;
617         if (token == "height") {
618                 lex.next();
619                 height = Length(lex.getString());
620         } else {
621                 lyxerr << "InsetBox::Read: Missing 'height'-tag!" << endl;
622                 lex.pushToken(token);
623         }
624
625         lex.next();
626         token = lex.getString();
627         if (!lex)
628                 return;
629         if (token == "height_special") {
630                 lex.next();
631                 height_special = lex.getString();
632         } else {
633                 lyxerr << "InsetBox::Read: Missing 'height_special'-tag!" << endl;
634                 lex.pushToken(token);
635         }
636 }
637
638
639 } // namespace lyx