]> git.lyx.org Git - lyx.git/blob - src/insets/InsetBox.cpp
Revert 23154.
[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.getTextClass().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 const 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(Buffer const & buf, ostream & os) const
130 {
131         params_.write(os);
132         InsetCollapsable::write(buf, os);
133 }
134
135
136 void InsetBox::read(Buffer const & buf, Lexer & lex)
137 {
138         params_.read(lex);
139         InsetCollapsable::read(buf, 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(Buffer const &) const
248 {
249         BoxType btype = boxtranslator().find(params_.type);
250         return btype != Frameless || params_.inner_box;
251 }
252
253
254 int InsetBox::latex(Buffer const & buf, odocstream & os,
255                     OutputParams const & runparams) const
256 {
257         BoxType btype = boxtranslator().find(params_.type);
258
259         string width_string = params_.width.asLatexString();
260         bool stdwidth(false);
261         if (params_.inner_box &&
262                         (width_string.find("1.0\\columnwidth") != string::npos
263                         || width_string.find("1.0\\textwidth") != string::npos)) {
264                 stdwidth = true;
265                 switch (btype) {
266                 case Frameless:
267                 case Framed:
268                         break;
269                 case Boxed:
270                 case Shaded:
271                         width_string += " - 2\\fboxsep - 2\\fboxrule";
272                         break;
273                 case ovalbox:
274                         width_string += " - 2\\fboxsep - 0.8pt";
275                         break;
276                 case Ovalbox:
277                         width_string += " - 2\\fboxsep - 1.6pt";
278                         break;
279                 case Shadowbox:
280                         // Shadow falls outside right margin... opinions?
281                         width_string += " - 2\\fboxsep - 2\\fboxrule"/* "-\\shadowsize"*/;
282                         break;
283                 case Doublebox:
284                         width_string += " - 2\\fboxsep - 7.5\\fboxrule - 1pt";
285                         break;
286                 }
287         }
288
289         int i = 0;
290         os << "%\n";
291         // Adapt to column/text width correctly also if paragraphs indented:
292         if (stdwidth)
293                 os << "\\noindent";
294
295         switch (btype) {
296         case Frameless:
297                 break;
298         case Framed:
299                 os << "\\begin{framed}%\n";
300                 i += 1;
301                 break;
302         case Boxed:
303                 os << "\\framebox";
304                 if (!params_.inner_box) {
305                         os << "{\\makebox";
306                         // Special widths, see usrguide §3.5
307                         // FIXME UNICODE
308                         if (params_.special != "none") {
309                                 os << "[" << params_.width.value()
310                                    << '\\' << from_utf8(params_.special)
311                                    << ']';
312                         } else
313                                 os << '[' << from_ascii(width_string)
314                                    << ']';
315                         if (params_.hor_pos != 'c')
316                                 os << "[" << params_.hor_pos << "]";
317                 }
318
319                 os << "{";
320                 break;
321         case ovalbox:
322                 os << "\\ovalbox{";
323                 break;
324         case Ovalbox:
325                 os << "\\Ovalbox{";
326                 break;
327         case Shadowbox:
328                 os << "\\shadowbox{";
329                 break;
330         case Shaded:
331                 // later
332                 break;
333         case Doublebox:
334                 os << "\\doublebox{";
335                 break;
336         }
337
338         if (params_.inner_box) {
339                 if (params_.use_parbox)
340                         os << "\\parbox";
341                 else
342                         os << "\\begin{minipage}";
343
344                 os << "[" << params_.pos << "]";
345                 if (params_.height_special == "none") {
346                         // FIXME UNICODE
347                         os << "[" << from_ascii(params_.height.asLatexString()) << "]";
348                 } else {
349                         // Special heights
350                         // set no optional argument when the value is the default "1\height"
351                         // (special units like \height are handled as "in")
352                         // but when the user has chosen a non-default inner_pos, the height
353                         // must be given: \minipage[pos][height][inner-pos]{width}
354                         if ((params_.height != Length("1in") ||
355                                  params_.height_special != "totalheight") ||
356                                 params_.inner_pos != params_.pos) {
357                                 // FIXME UNICODE
358                                 os << "[" << params_.height.value()
359                                         << "\\" << from_utf8(params_.height_special) << "]";
360                         }
361                 }
362                 if (params_.inner_pos != params_.pos)
363                         os << "[" << params_.inner_pos << "]";
364
365                 // FIXME UNICODE
366                 os << '{' << from_ascii(width_string) << '}';
367
368                 if (params_.use_parbox)
369                         os << "{";
370                 os << "%\n";
371                 i += 1;
372         }
373         if (btype == Shaded)
374                 os << "\\begin{shaded}%\n";
375                 i += 1;
376
377         i += InsetText::latex(buf, os, runparams);
378
379         if (btype == Shaded)
380                 os << "\\end{shaded}";
381
382         if (params_.inner_box) {
383                 if (params_.use_parbox)
384                         os << "%\n}";
385                 else
386                         os << "%\n\\end{minipage}";
387         }
388
389         switch (btype) {
390         case Frameless:
391                 break;
392         case Framed:
393                 os << "\\end{framed}";
394                 break;
395         case Boxed:
396                 if (!params_.inner_box)
397                         os << "}"; // for makebox
398                 os << "}";
399                 break;
400         case ovalbox:
401         case Ovalbox:
402         case Doublebox:
403         case Shadowbox:
404                 os << "}";
405                 break;
406         case Shaded:
407                 // already done
408                 break;
409         }
410
411         i += 2;
412
413         return i;
414 }
415
416
417 int InsetBox::plaintext(Buffer const & buf, odocstream & os,
418                         OutputParams const & runparams) const
419 {
420         BoxType const btype = boxtranslator().find(params_.type);
421
422         switch (btype) {
423                 case Frameless:
424                         break;
425                 case Framed:
426                 case Boxed:
427                         os << "[\n";
428                         break;
429                 case ovalbox:
430                         os << "(\n";
431                         break;
432                 case Ovalbox:
433                         os << "((\n";
434                         break;
435                 case Shadowbox:
436                 case Shaded:
437                         os << "[/\n";
438                         break;
439                 case Doublebox:
440                         os << "[[\n";
441                         break;
442         }
443
444         InsetText::plaintext(buf, os, runparams);
445
446         int len = 0;
447         switch (btype) {
448                 case Frameless:
449                         os << "\n";
450                         break;
451                 case Framed:
452                 case Boxed:
453                         os << "\n]";
454                         len = 1;
455                         break;
456                 case ovalbox:
457                         os << "\n)";
458                         len = 1;
459                         break;
460                 case Ovalbox:
461                         os << "\n))";
462                         len = 2;
463                         break;
464                 case Shadowbox:
465                 case Shaded:
466                         os << "\n/]";
467                         len = 2;
468                         break;
469                 case Doublebox:
470                         os << "\n]]";
471                         len = 2;
472                         break;
473         }
474
475         return PLAINTEXT_NEWLINE + len; // len chars on a separate line
476 }
477
478
479 int InsetBox::docbook(Buffer const & buf, odocstream & os,
480                       OutputParams const & runparams) const
481 {
482         return InsetText::docbook(buf, os, runparams);
483 }
484
485
486 void InsetBox::validate(LaTeXFeatures & features) const
487 {
488         BoxType btype = boxtranslator().find(params_.type);
489         switch (btype) {
490         case Frameless:
491                 break;
492         case Framed:
493                 features.require("framed");
494                 break;
495         case Boxed:
496                 features.require("calc");
497                 break;
498         case ovalbox:
499         case Ovalbox:
500         case Shadowbox:
501         case Doublebox:
502                 features.require("calc");
503                 features.require("fancybox");
504                 break;
505         case Shaded:
506                 features.require("color");
507                 features.require("framed");
508                 break;
509         }
510         InsetText::validate(features);
511 }
512
513
514 InsetBoxMailer::InsetBoxMailer(InsetBox & inset)
515         : inset_(inset)
516 {}
517
518
519 string const InsetBoxMailer::name_ = "box";
520
521
522 string const InsetBoxMailer::inset2string(Buffer const &) const
523 {
524         return params2string(inset_.params());
525 }
526
527
528 string const InsetBoxMailer::params2string(InsetBoxParams const & params)
529 {
530         ostringstream data;
531         data << "box" << ' ';
532         params.write(data);
533         return data.str();
534 }
535
536
537 void InsetBoxMailer::string2params(string const & in,
538                                    InsetBoxParams & params)
539 {
540         params = InsetBoxParams(string());
541         if (in.empty())
542                 return;
543
544         istringstream data(in);
545         Lexer lex(0,0);
546         lex.setStream(data);
547
548         string name;
549         lex >> name;
550         if (!lex || name != name_)
551                 return print_mailer_error("InsetBoxMailer", in, 1, name_);
552
553         // This is part of the inset proper that is usually swallowed
554         // by Text::readInset
555         string id;
556         lex >> id;
557         if (!lex || id != "Box")
558                 return print_mailer_error("InsetBoxMailer", in, 2, "Box");
559
560         params.read(lex);
561 }
562
563
564 InsetBoxParams::InsetBoxParams(string const & label)
565         : type(label),
566           use_parbox(false),
567           inner_box(true),
568           width(Length("100col%")),
569           special("none"),
570           pos('t'),
571           hor_pos('c'),
572           inner_pos('t'),
573           height(Length("1in")),
574           height_special("totalheight") // default is 1\\totalheight
575 {}
576
577
578 void InsetBoxParams::write(ostream & os) const
579 {
580         os << "Box " << type << "\n";
581         os << "position \"" << pos << "\"\n";
582         os << "hor_pos \"" << hor_pos << "\"\n";
583         os << "has_inner_box " << inner_box << "\n";
584         os << "inner_pos \"" << inner_pos << "\"\n";
585         os << "use_parbox " << use_parbox << "\n";
586         os << "width \"" << width.asString() << "\"\n";
587         os << "special \"" << special << "\"\n";
588         os << "height \"" << height.asString() << "\"\n";
589         os << "height_special \"" << height_special << "\"\n";
590 }
591
592
593 void InsetBoxParams::read(Lexer & lex)
594 {
595         if (!lex.isOK())
596                 return;
597
598         lex.next();
599         type = lex.getString();
600
601         if (!lex)
602                 return;
603
604         lex.next();
605         string token;
606         token = lex.getString();
607         if (!lex)
608                 return;
609         if (token == "position") {
610                 lex.next();
611                 // The [0] is needed. We need the first and only char in
612                 // this string -- MV
613                 pos = lex.getString()[0];
614         } else {
615                 lyxerr << "InsetBox::Read: Missing 'position'-tag!" << token << endl;
616                 lex.pushToken(token);
617         }
618
619         lex.next();
620         token = lex.getString();
621         if (!lex)
622                 return;
623         if (token == "hor_pos") {
624                 lex.next();
625                 hor_pos = lex.getString()[0];
626         } else {
627                 lyxerr << "InsetBox::Read: Missing 'hor_pos'-tag!" << token << endl;
628                 lex.pushToken(token);
629         }
630
631         lex.next();
632         token = lex.getString();
633         if (!lex)
634                 return;
635         if (token == "has_inner_box") {
636                 lex.next();
637                 inner_box = lex.getInteger();
638         } else {
639                 lyxerr << "InsetBox::Read: Missing 'has_inner_box'-tag!" << endl;
640                 lex.pushToken(token);
641         }
642
643         lex.next();
644         token = lex.getString();
645         if (!lex)
646                 return;
647         if (token == "inner_pos") {
648                 lex.next();
649                 inner_pos = lex.getString()[0];
650         } else {
651                 lyxerr << "InsetBox::Read: Missing 'inner_pos'-tag!"
652                         << token << endl;
653                 lex.pushToken(token);
654         }
655
656         lex.next();
657         token = lex.getString();
658         if (!lex)
659                 return;
660         if (token == "use_parbox") {
661                 lex.next();
662                 use_parbox = lex.getInteger();
663         } else {
664                 lyxerr << "InsetBox::Read: Missing 'use_parbox'-tag!" << endl;
665                 lex.pushToken(token);
666         }
667
668         lex.next();
669         token = lex.getString();
670         if (!lex)
671                 return;
672         if (token == "width") {
673                 lex.next();
674                 width = Length(lex.getString());
675         } else {
676                 lyxerr << "InsetBox::Read: Missing 'width'-tag!" << endl;
677                 lex.pushToken(token);
678         }
679
680         lex.next();
681         token = lex.getString();
682         if (!lex)
683                 return;
684         if (token == "special") {
685                 lex.next();
686                 special = lex.getString();
687         } else {
688                 lyxerr << "InsetBox::Read: Missing 'special'-tag!" << endl;
689                 lex.pushToken(token);
690         }
691
692         lex.next();
693         token = lex.getString();
694         if (!lex)
695                 return;
696         if (token == "height") {
697                 lex.next();
698                 height = Length(lex.getString());
699         } else {
700                 lyxerr << "InsetBox::Read: Missing 'height'-tag!" << endl;
701                 lex.pushToken(token);
702         }
703
704         lex.next();
705         token = lex.getString();
706         if (!lex)
707                 return;
708         if (token == "height_special") {
709                 lex.next();
710                 height_special = lex.getString();
711         } else {
712                 lyxerr << "InsetBox::Read: Missing 'height_special'-tag!" << endl;
713                 lex.pushToken(token);
714         }
715 }
716
717
718 } // namespace lyx