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