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