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