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