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