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