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