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