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