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