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