]> git.lyx.org Git - lyx.git/blob - src/frontends/xforms/FormParagraph.C
The std::string mammoth path.
[lyx.git] / src / frontends / xforms / FormParagraph.C
1 /**
2  * \file FormParagraph.C
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Jürgen Vigna
7  * \author Rob Lahaye
8  *
9  * Full author contact details are available in file CREDITS.
10  */
11
12 #include <config.h>
13
14 #include "FormParagraph.h"
15 #include "ControlParagraph.h"
16 #include "forms/form_paragraph.h"
17
18 #include "checkedwidgets.h"
19 #include "input_validators.h"
20 #include "Tooltips.h"
21 #include "xforms_helpers.h"
22 #include "xformsBC.h"
23
24 #include "controllers/helper_funcs.h"
25
26 #include "lyxrc.h" // to set the deafult length values
27 #include "ParagraphParameters.h"
28 #include "Spacing.h"
29 #include "vspace.h"
30
31 #include "support/lstrings.h"
32 #include "support/tostr.h"
33
34 #include "lyx_forms.h"
35
36 using lyx::support::contains_functor;
37 using lyx::support::getStringFromVector;
38 using lyx::support::rtrim;
39
40 using std::bind2nd;
41 using std::remove_if;
42
43 using std::vector;
44 using std::string;
45
46
47 namespace
48 {
49
50 string defaultUnit("cm");
51
52 void validateVSpaceWidgets(FL_OBJECT * choice_type, FL_OBJECT * input_length);
53
54 VSpace const setVSpaceFromWidgets(FL_OBJECT * choice_type,
55                                   FL_OBJECT * input_length,
56                                   FL_OBJECT * choice_length,
57                                   FL_OBJECT * check_keep);
58
59 void setWidgetsFromVSpace(VSpace const & space,
60                           FL_OBJECT * choice_type,
61                           FL_OBJECT * input_length,
62                           FL_OBJECT * choice_length,
63                           FL_OBJECT * check_keep);
64
65 } // namespace anon
66
67
68 typedef FormController<ControlParagraph, FormView<FD_paragraph> > base_class;
69
70 FormParagraph::FormParagraph(Dialog & parent)
71         : base_class(parent, _("Paragraph Settings"))
72 {}
73
74
75 void FormParagraph::build()
76 {
77         // the tabbed folder
78         dialog_.reset(build_paragraph(this));
79
80         // Manage the ok, apply, restore and cancel/close buttons
81         bcview().setOK(dialog_->button_ok);
82         bcview().setApply(dialog_->button_apply);
83         bcview().setCancel(dialog_->button_close);
84         bcview().setRestore(dialog_->button_restore);
85
86         // disable for read-only documents
87         bcview().addReadOnly(dialog_->check_line_above);
88         bcview().addReadOnly(dialog_->check_pagebreak_above);
89         bcview().addReadOnly(dialog_->choice_space_above);
90         bcview().addReadOnly(dialog_->input_space_above);
91         bcview().addReadOnly(dialog_->check_space_above);
92
93         bcview().addReadOnly(dialog_->check_noindent);
94         bcview().addReadOnly(dialog_->choice_linespacing);
95         bcview().addReadOnly(dialog_->input_linespacing);
96
97         bcview().addReadOnly(dialog_->check_line_below);
98         bcview().addReadOnly(dialog_->check_pagebreak_below);
99         bcview().addReadOnly(dialog_->choice_space_below);
100         bcview().addReadOnly(dialog_->input_space_below);
101         bcview().addReadOnly(dialog_->check_space_below);
102
103         bcview().addReadOnly(dialog_->input_labelwidth);
104
105         // check validity of "length + unit" input
106         addCheckedGlueLength(bcview(),
107                              dialog_->input_space_above,
108                              dialog_->choice_space_above);
109         addCheckedGlueLength(bcview(),
110                              dialog_->input_space_below,
111                              dialog_->choice_space_below);
112
113         // trigger an input event for cut&paste with middle mouse button.
114         setPrehandler(dialog_->input_space_above);
115         setPrehandler(dialog_->input_space_below);
116         setPrehandler(dialog_->input_linespacing);
117         setPrehandler(dialog_->input_labelwidth);
118
119         fl_set_input_return(dialog_->input_space_above, FL_RETURN_CHANGED);
120         fl_set_input_return(dialog_->input_space_below, FL_RETURN_CHANGED);
121         fl_set_input_return(dialog_->input_labelwidth,  FL_RETURN_CHANGED);
122         fl_set_input_return(dialog_->input_linespacing, FL_RETURN_CHANGED);
123
124         // limit these inputs to unsigned floats
125         fl_set_input_filter(dialog_->input_linespacing, fl_unsigned_float_filter);
126
127         // add alignment radio buttons
128         alignment_.init(dialog_->radio_align_left,   LYX_ALIGN_LEFT);
129         alignment_.init(dialog_->radio_align_right,  LYX_ALIGN_RIGHT);
130         alignment_.init(dialog_->radio_align_block,  LYX_ALIGN_BLOCK);
131         alignment_.init(dialog_->radio_align_center, LYX_ALIGN_CENTER);
132
133         string const parspacing = _("None|DefSkip|SmallSkip|MedSkip|BigSkip|VFill|Length");
134         fl_addto_choice(dialog_->choice_space_above, parspacing.c_str());
135         fl_addto_choice(dialog_->choice_space_below, parspacing.c_str());
136
137         string const linespacing = _("Default|Single|OneHalf|Double|Custom");
138         fl_addto_choice(dialog_->choice_linespacing, linespacing.c_str());
139
140         // Create the contents of the unit choices; don't include the "%" terms.
141         vector<string> units_vec = getLatexUnits();
142         vector<string>::iterator del =
143                 remove_if(units_vec.begin(), units_vec.end(),
144                           bind2nd(contains_functor(), "%"));
145         units_vec.erase(del, units_vec.end());
146
147         string const units = getStringFromVector(units_vec, "|");
148         fl_addto_choice(dialog_->choice_unit_space_above, units.c_str());
149         fl_addto_choice(dialog_->choice_unit_space_below, units.c_str());
150
151         // set up the tooltips
152         string str = _("Add a separator line above this paragraph.");
153         tooltips().init(dialog_->check_line_above, str);
154         str = _("Enforce a page break above this paragraph.");
155         tooltips().init(dialog_->check_pagebreak_above, str);
156         str = _("Add additional space above this paragraph.");
157         tooltips().init(dialog_->choice_space_above, str);
158         str = _("Never suppress space (e.g. at top of page or new page).");
159         tooltips().init(dialog_->check_space_above, str);
160
161         str = _("Add a separator line below this paragraph.");
162         tooltips().init(dialog_->check_line_below, str);
163         str = _("Enforce a page break below this paragraph.");
164         tooltips().init(dialog_->check_pagebreak_below, str);
165         str = _("Add additional space below this paragraph.");
166         tooltips().init(dialog_->choice_space_below, str);
167         str = _("Never suppress space (e.g. at bottom of page or new page).");
168         tooltips().init(dialog_->check_space_below, str);
169
170         // set default unit for custom length
171         switch (lyxrc.default_papersize) {
172                 case PAPER_DEFAULT:
173                 case PAPER_USLETTER:
174                 case PAPER_LEGALPAPER:
175                 case PAPER_EXECUTIVEPAPER:
176                         defaultUnit = "in";
177                         break;
178                 case PAPER_A3PAPER:
179                 case PAPER_A4PAPER:
180                 case PAPER_A5PAPER:
181                 case PAPER_B5PAPER:
182                         defaultUnit = "cm";
183                         break;
184         }
185 }
186
187
188 void FormParagraph::apply()
189 {
190         if (!form()) return;
191
192         // spacing
193         // If a vspace choice is "Length" but there's no text in
194         // the input field, reset the choice to "None".
195         validateVSpaceWidgets(dialog_->choice_space_above,
196                               dialog_->input_space_above);
197
198         VSpace const space_above =
199                 setVSpaceFromWidgets(dialog_->choice_space_above,
200                                      dialog_->input_space_above,
201                                      dialog_->choice_unit_space_above,
202                                      dialog_->check_space_above);
203
204         controller().params().spaceTop(space_above);
205
206         validateVSpaceWidgets(dialog_->choice_space_below,
207                               dialog_->input_space_below);
208
209         VSpace const space_below =
210                 setVSpaceFromWidgets(dialog_->choice_space_below,
211                                      dialog_->input_space_below,
212                                      dialog_->choice_unit_space_below,
213                                      dialog_->check_space_below);
214
215         controller().params().spaceBottom(space_below);
216
217         // lines and pagebreaks
218         bool const line_above = fl_get_button(dialog_->check_line_above);
219         controller().params().lineTop(line_above);
220
221         bool const line_below = fl_get_button(dialog_->check_line_below);
222         controller().params().lineBottom(line_below);
223
224         bool const pagebreak_above =
225                 fl_get_button(dialog_->check_pagebreak_above);
226         controller().params().pagebreakTop(pagebreak_above);
227
228         bool const pagebreak_below =
229                 fl_get_button(dialog_->check_pagebreak_below);
230         controller().params().pagebreakBottom(pagebreak_below);
231
232
233         // alignment
234         LyXAlignment const alignment =
235                 static_cast<LyXAlignment>(alignment_.get());
236         controller().params().align(alignment);
237
238         // label width
239         string const labelwidthstring =
240                 getString(dialog_->input_labelwidth);
241         controller().params().labelWidthString(labelwidthstring);
242
243         // indendation
244         bool const noindent = fl_get_button(dialog_->check_noindent);
245         controller().params().noindent(noindent);
246
247         // get spacing
248         Spacing::Space linespacing = Spacing::Default;
249         string other;
250         switch (fl_get_choice(dialog_->choice_linespacing)) {
251         case 1:
252                 linespacing = Spacing::Default;
253                 break;
254         case 2:
255                 linespacing = Spacing::Single;
256                 break;
257         case 3:
258                 linespacing = Spacing::Onehalf;
259                 break;
260         case 4:
261                 linespacing = Spacing::Double;
262                 break;
263         case 5:
264                 // reset to default if input is empty
265                 other = getString(dialog_->input_linespacing);
266                 if (!other.empty()) {
267                         linespacing = Spacing::Other;
268                 } else {
269                         linespacing = Spacing::Default;
270                         fl_set_choice(dialog_->choice_linespacing, 1);
271                 }
272                 break;
273         }
274         Spacing const spacing(linespacing, other);
275         controller().params().spacing(spacing);
276 }
277
278
279 void FormParagraph::update()
280 {
281         if (!dialog_.get())
282                 return;
283
284         // label width
285         string const labelwidth = controller().params().labelWidthString();
286         fl_set_input(dialog_->input_labelwidth, labelwidth.c_str());
287         setEnabled(dialog_->input_labelwidth,
288                    labelwidth != _("Senseless with this layout!"));
289
290         // alignment
291         alignment_.set(controller().params().align());
292
293         // mark default alignment
294         LyXAlignment const default_alignment = controller().alignDefault();
295
296         string label = _("Block");
297         if (default_alignment == LYX_ALIGN_BLOCK) {
298                 label += _(" (default)");
299         }
300         fl_set_object_label(dialog_->radio_align_block, label.c_str());
301         fl_set_button_shortcut(dialog_->radio_align_block, "#B", 1);
302
303         label = _("Center");
304         if (default_alignment == LYX_ALIGN_CENTER) {
305                 label += _(" (default)");
306         }
307         fl_set_object_label(dialog_->radio_align_center, label.c_str());
308         fl_set_button_shortcut(dialog_->radio_align_center, "#C", 1);
309
310         label = _("Left");
311         if (default_alignment == LYX_ALIGN_LEFT) {
312                 label += _(" (default)");
313         }
314         fl_set_object_label(dialog_->radio_align_left, label.c_str());
315         fl_set_button_shortcut(dialog_->radio_align_left, "#L", 1);
316
317         label = _("Right");
318         if (default_alignment == LYX_ALIGN_RIGHT) {
319                 label = _(" (default)");
320         }
321         fl_set_object_label(dialog_->radio_align_right, label.c_str());
322         fl_set_button_shortcut(dialog_->radio_align_right, "#R", 1);
323
324         // Ensure that there's no crud left on the screen from this change
325         // of labels.
326         fl_redraw_form(form());
327
328         LyXAlignment alignpos = controller().alignPossible();
329         setEnabled(dialog_->radio_align_block,
330                    bool(alignpos & LYX_ALIGN_BLOCK));
331         setEnabled(dialog_->radio_align_center,
332                    bool(alignpos & LYX_ALIGN_CENTER));
333         setEnabled(dialog_->radio_align_left,
334                    bool(alignpos & LYX_ALIGN_LEFT));
335         setEnabled(dialog_->radio_align_right,
336                    bool(alignpos & LYX_ALIGN_RIGHT));
337
338         // no inset-text-owned paragraph may have pagebreaks
339         bool ininset = controller().inInset();
340         setEnabled(dialog_->check_pagebreak_above, !ininset);
341         setEnabled(dialog_->check_pagebreak_below, !ininset);
342
343         // lines, pagebreaks and indent
344         fl_set_button(dialog_->check_line_above,
345                       controller().params().lineTop());
346         fl_set_button(dialog_->check_line_below,
347                       controller().params().lineBottom());
348         fl_set_button(dialog_->check_pagebreak_above,
349                       controller().params().pagebreakTop());
350         fl_set_button(dialog_->check_pagebreak_below,
351                       controller().params().pagebreakBottom());
352         fl_set_button(dialog_->check_noindent,
353                       controller().params().noindent());
354
355         // linespacing
356         Spacing const space = controller().params().spacing();
357
358         int pos;
359         switch (space.getSpace()) {
360         case Spacing::Other:
361                 pos = 5;
362                 break;
363         case Spacing::Double:
364                 pos = 4;
365                 break;
366         case Spacing::Onehalf:
367                 pos = 3;
368                 break;
369         case Spacing::Single:
370                 pos = 2;
371                 break;
372         case Spacing::Default:
373         default:
374                 pos = 1;
375                 break;
376         }
377         fl_set_choice(dialog_->choice_linespacing, pos);
378
379         bool const spacing_other = space.getSpace() == Spacing::Other;
380         setEnabled(dialog_->input_linespacing, spacing_other);
381         if (spacing_other) {
382                 string const sp = tostr(space.getValue());
383                 fl_set_input(dialog_->input_linespacing, sp.c_str());
384         } else {
385                 fl_set_input(dialog_->input_linespacing, "");
386         }
387
388         // vspace top
389         setWidgetsFromVSpace(controller().params().spaceTop(),
390                              dialog_->choice_space_above,
391                              dialog_->input_space_above,
392                              dialog_->choice_unit_space_above,
393                              dialog_->check_space_above);
394
395         // vspace bottom
396         setWidgetsFromVSpace(controller().params().spaceBottom(),
397                              dialog_->choice_space_below,
398                              dialog_->input_space_below,
399                              dialog_->choice_unit_space_below,
400                              dialog_->check_space_below);
401
402         // no indent
403         fl_set_button(dialog_->check_noindent,
404                       controller().params().noindent());
405 }
406
407
408 ButtonPolicy::SMInput FormParagraph::input(FL_OBJECT * ob, long)
409 {
410         // Enable input when custum length is choosen,
411         // disable 'keep' when no space is choosen
412         if (ob == dialog_->choice_space_above) {
413                 bool const custom_length =
414                         fl_get_choice(dialog_->choice_space_above) == 7;
415                 setEnabled(dialog_->input_space_above, custom_length);
416                 setEnabled(dialog_->choice_unit_space_above, custom_length);
417
418                 bool const space =
419                         fl_get_choice(dialog_->choice_space_above) != 1;
420                 setEnabled(dialog_->check_space_above, space);
421
422         } else if (ob == dialog_->choice_space_below) {
423                 bool const custom_length =
424                         fl_get_choice(dialog_->choice_space_below) == 7;
425                 setEnabled(dialog_->input_space_below, custom_length);
426                 setEnabled(dialog_->choice_unit_space_below, custom_length);
427
428                 bool const space =
429                         fl_get_choice(dialog_->choice_space_below) != 1;
430                 setEnabled(dialog_->check_space_below, space);
431
432         } else if (ob == dialog_->choice_linespacing) {
433                 bool const custom_spacing =
434                         fl_get_choice(dialog_->choice_linespacing) == 5;
435                 setEnabled(dialog_->input_linespacing, custom_spacing);
436         }
437
438         return ButtonPolicy::SMI_VALID;
439 }
440
441
442 namespace {
443
444 void validateVSpaceWidgets(FL_OBJECT * choice_type, FL_OBJECT * input_length)
445 {
446         // Paranoia check!
447         BOOST_ASSERT(choice_type  && choice_type->objclass  == FL_CHOICE &&
448                     input_length && input_length->objclass == FL_INPUT);
449
450         if (fl_get_choice(choice_type) != 7)
451                 return;
452
453         // If a vspace kind is "Length" but there's no text in
454         // the input field, reset the kind to "None".
455         string const input = rtrim(getString(input_length));
456         if (input.empty())
457                 fl_set_choice(choice_type, 1);
458 }
459
460
461 VSpace const setVSpaceFromWidgets(FL_OBJECT * choice_type,
462                                   FL_OBJECT * input_length,
463                                   FL_OBJECT * choice_length,
464                                   FL_OBJECT * check_keep)
465 {
466         // Paranoia check!
467         BOOST_ASSERT(choice_type   && choice_type->objclass   == FL_CHOICE &&
468                     input_length  && input_length->objclass  == FL_INPUT &&
469                     choice_length && choice_length->objclass == FL_CHOICE &&
470                     check_keep    && check_keep->objclass    == FL_CHECKBUTTON);
471
472         VSpace space;
473         switch (fl_get_choice(choice_type)) {
474         case 1:
475                 space = VSpace(VSpace::NONE);
476                 break;
477         case 2:
478                 space = VSpace(VSpace::DEFSKIP);
479                 break;
480         case 3:
481                 space = VSpace(VSpace::SMALLSKIP);
482                 break;
483         case 4:
484                 space = VSpace(VSpace::MEDSKIP);
485                 break;
486         case 5:
487                 space = VSpace(VSpace::BIGSKIP);
488                 break;
489         case 6:
490                 space = VSpace(VSpace::VFILL);
491                 break;
492         case 7:
493                 {
494                 string const length =
495                         getLengthFromWidgets(input_length, choice_length);
496                 space = VSpace(LyXGlueLength(length));
497                 break;
498                 }
499         }
500
501         if (fl_get_button(check_keep))
502                 space.setKeep(true);
503
504         return space;
505 }
506
507
508 void setWidgetsFromVSpace(VSpace const & space,
509                           FL_OBJECT * choice_type,
510                           FL_OBJECT * input_length,
511                           FL_OBJECT * choice_length,
512                           FL_OBJECT * check_keep)
513 {
514         // Paranoia check!
515         BOOST_ASSERT(choice_type   && choice_type->objclass   == FL_CHOICE &&
516                     input_length  && input_length->objclass  == FL_INPUT &&
517                     choice_length && choice_length->objclass == FL_CHOICE &&
518                     check_keep    && check_keep->objclass    == FL_CHECKBUTTON);
519
520         fl_set_button(check_keep, space.keep());
521
522         int pos = 1;
523         switch (space.kind()) {
524         case VSpace::NONE:
525                 pos = 1;
526                 break;
527         case VSpace::DEFSKIP:
528                 pos = 2;
529                 break;
530         case VSpace::SMALLSKIP:
531                 pos = 3;
532                 break;
533         case VSpace::MEDSKIP:
534                 pos = 4;
535                 break;
536         case VSpace::BIGSKIP:
537                 pos = 5;
538                 break;
539         case VSpace::VFILL:
540                 pos = 6;
541                 break;
542         case VSpace::LENGTH:
543                 pos = 7;
544                 break;
545         }
546         fl_set_choice(choice_type, pos);
547
548         bool const custom_vspace = space.kind() == VSpace::LENGTH;
549         setEnabled(input_length, custom_vspace);
550         setEnabled(choice_length, custom_vspace);
551         if (custom_vspace) {
552                 string const length = space.length().asString();
553                 updateWidgetsFromLengthString(input_length, choice_length,
554                                               length, defaultUnit);
555         } else {
556                 bool const no_vspace = space.kind() == VSpace::NONE;
557                 setEnabled(check_keep, !no_vspace);
558                 fl_set_input(input_length, "");
559                 fl_set_choice_text(choice_length, defaultUnit.c_str());
560         }
561 }
562
563 } // namespace anon