]> git.lyx.org Git - lyx.git/blob - src/frontends/xforms/FormParagraph.C
xforms clean-up, described in detail in my mail of 31 May. See
[lyx.git] / src / frontends / xforms / FormParagraph.C
1 /**
2  * \file FormParagraph.C
3  * Copyright 2000-2001 The LyX Team.
4  * See the file COPYING.
5  *
6  * \author Jürgen Vigna, jug@sad.it
7  */
8
9 #include <config.h>
10
11 #ifdef __GNUG__
12 #pragma implementation
13 #endif
14
15 #include FORMS_H_LOCATION
16
17 #include "FormParagraph.h"
18 #include "forms/form_paragraph.h"
19 #include "ControlParagraph.h"
20 #include "ParagraphParameters.h"
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 "gettext.h"
26 #include "xformsBC.h"
27 #include "layout.h" // LyXAlignment
28
29 #include "support/lstrings.h"
30 #include "support/LAssert.h"
31 #include FORMS_H_LOCATION
32
33 #include <functional>
34
35 using std::vector;
36 using std::bind2nd;
37 using std::remove_if;
38
39 typedef FormCB<ControlParagraph, FormDB<FD_paragraph> > base_class;
40   
41 FormParagraph::FormParagraph(ControlParagraph & c)
42         : base_class(c, _("Paragraph Layout"), false)
43 {}
44
45 void FormParagraph::build()
46 {
47         // the tabbed folder
48         dialog_.reset(build_paragraph(this));
49
50         // Allow the base class to control messages
51         setMessageWidget(dialog_->text_warning);
52
53         fl_addto_choice(dialog_->choice_space_above,
54                         _(" None | Defskip | Smallskip "
55                           "| Medskip | Bigskip | VFill | Length "));
56         fl_addto_choice(dialog_->choice_space_below,
57                         _(" None | Defskip | Smallskip "
58                           "| Medskip | Bigskip | VFill | Length "));
59
60         fl_addto_choice(dialog_->choice_linespacing,
61                         _(" Default | Single | OneHalf | Double | Custom "));
62
63         fl_set_input_return(dialog_->input_space_above, FL_RETURN_CHANGED);
64         fl_set_input_return(dialog_->input_space_below, FL_RETURN_CHANGED);
65         fl_set_input_return(dialog_->input_labelwidth,  FL_RETURN_CHANGED);
66         fl_set_input_return(dialog_->input_linespacing, FL_RETURN_CHANGED);
67         fl_set_input_filter(dialog_->input_linespacing, fl_unsigned_float_filter);
68
69         setPrehandler(dialog_->input_space_above);
70         setPrehandler(dialog_->input_space_below);
71         setPrehandler(dialog_->input_labelwidth);
72         setPrehandler(dialog_->input_linespacing);
73
74         // Create the contents of the unit choices
75         // Don't include the "%" terms...
76         vector<string> units_vec = getLatexUnits();
77 #if 0
78         for (vector<string>::iterator it = units_vec.begin();
79              it != units_vec.end(); ++it) {
80                 if (contains(*it, "%"))
81                         it = units_vec.erase(it, it+1) - 1;
82         }
83 #else
84         // Something similar to this is a better way to erase
85         vector<string>::iterator del =
86                 remove_if(units_vec.begin(), units_vec.end(),
87                           bind2nd(contains_functor(), "%"));
88         units_vec.erase(del, units_vec.end());
89 #endif
90
91         string units = getStringFromVector(units_vec, "|");
92
93         fl_addto_choice(dialog_->choice_value_space_above, units.c_str());
94         fl_addto_choice(dialog_->choice_value_space_below, units.c_str());
95
96         // Manage the ok, apply, restore and cancel/close buttons
97         bc().setOK(dialog_->button_ok);
98         bc().setApply(dialog_->button_apply);
99         bc().setCancel(dialog_->button_close);
100         bc().setRestore(dialog_->button_restore);
101
102         bc().addReadOnly(dialog_->radio_align_right);
103         bc().addReadOnly(dialog_->radio_align_left);
104         bc().addReadOnly(dialog_->radio_align_block);
105         bc().addReadOnly(dialog_->radio_align_center);
106         bc().addReadOnly(dialog_->check_lines_top);
107         bc().addReadOnly(dialog_->check_lines_bottom);
108         bc().addReadOnly(dialog_->check_pagebreaks_top);
109         bc().addReadOnly(dialog_->check_pagebreaks_bottom);
110         bc().addReadOnly(dialog_->choice_space_above);
111         bc().addReadOnly(dialog_->input_space_above);
112         bc().addReadOnly(dialog_->check_space_above);
113         bc().addReadOnly(dialog_->choice_space_below);
114         bc().addReadOnly(dialog_->input_space_below);
115         bc().addReadOnly(dialog_->check_space_below);
116         bc().addReadOnly(dialog_->choice_linespacing);
117         bc().addReadOnly(dialog_->input_linespacing);
118         bc().addReadOnly(dialog_->check_noindent);
119         bc().addReadOnly(dialog_->input_labelwidth);
120 }
121
122 namespace {
123
124 VSpace setVSpaceFromWidgets(FL_OBJECT * choice_type,
125                             FL_OBJECT * input_length,
126                             FL_OBJECT * choice_length,
127                             FL_OBJECT * check_keep)
128 {
129         // Paranoia check!
130         lyx::Assert(choice_type   && choice_type->objclass   == FL_CHOICE &&
131                     input_length  && input_length->objclass  == FL_INPUT &&
132                     choice_length && choice_length->objclass == FL_CHOICE &&
133                     check_keep    && check_keep->objclass    == FL_CHECKBUTTON);
134
135         VSpace space;
136
137         switch (fl_get_choice(choice_type)) {
138         case 1:
139                 space = VSpace(VSpace::NONE);
140                 break;
141         case 2:
142                 space = VSpace(VSpace::DEFSKIP);
143                 break;
144         case 3:
145                 space = VSpace(VSpace::SMALLSKIP);
146                 break;
147         case 4:
148                 space = VSpace(VSpace::MEDSKIP);
149                 break;
150         case 5:
151                 space = VSpace(VSpace::BIGSKIP);
152                 break;
153         case 6:
154                 space = VSpace(VSpace::VFILL);
155                 break;
156         case 7:
157         {
158                 string const length =
159                         getLengthFromWidgets(input_length, choice_length);
160                 space = VSpace(LyXGlueLength(length));
161                 break;
162         }
163         }
164
165         if (fl_get_button(check_keep))
166                 space.setKeep(true);
167
168         return space;
169 }
170
171 void validateVSpaceWidgets(FL_OBJECT * choice_type, FL_OBJECT * input_length)
172 {
173         // Paranoia check!
174         lyx::Assert(choice_type  && choice_type->objclass   == FL_CHOICE &&
175                     input_length && input_length->objclass  == FL_INPUT);
176
177         if (fl_get_choice(choice_type) != 7)
178                 return;
179
180         // If a vspace kind is "Length" but there's no text in
181         // the input field, reset the kind to "None".
182         string const input = strip(getStringFromInput(input_length));
183         if (input.empty())
184                 fl_set_choice(choice_type, 1);
185 }
186
187 } // namespace anon
188
189 void FormParagraph::apply()
190 {
191         if (!form()) return;
192
193         /* spacing */
194         // If a vspace kind is "Length" but there's no text in
195         // the input field, reset the kind to "None".
196         validateVSpaceWidgets(dialog_->choice_space_above,
197                               dialog_->input_space_above);
198         
199         VSpace const space_top =
200                 setVSpaceFromWidgets(dialog_->choice_space_above,
201                                      dialog_->input_space_above,
202                                      dialog_->choice_value_space_above,
203                                      dialog_->check_space_above);
204         
205         controller().params().spaceTop(space_top);
206         
207         validateVSpaceWidgets(dialog_->choice_space_below,
208                               dialog_->input_space_below);
209
210         VSpace const space_bottom =
211                 setVSpaceFromWidgets(dialog_->choice_space_below,
212                                      dialog_->input_space_below,
213                                      dialog_->choice_value_space_below,
214                                      dialog_->check_space_below);
215         
216         controller().params().spaceBottom(space_bottom);
217
218         /* lines and pagebreaks */
219         bool const line_top = fl_get_button(dialog_->check_lines_top);
220         controller().params().lineTop(line_top);
221
222         bool const line_bottom = fl_get_button(dialog_->check_lines_bottom);
223         controller().params().lineBottom(line_bottom);
224
225         bool const pagebreak_top = fl_get_button(dialog_->check_pagebreaks_top);
226         controller().params().pagebreakTop(pagebreak_top);
227         
228         bool const pagebreak_bottom = fl_get_button(dialog_->check_pagebreaks_bottom);
229         controller().params().pagebreakBottom(pagebreak_bottom);
230         
231
232         /* alignment */
233         LyXAlignment align;
234         if (fl_get_button(dialog_->radio_align_left))
235                 align = LYX_ALIGN_LEFT;
236         else if (fl_get_button(dialog_->radio_align_right))
237                 align = LYX_ALIGN_RIGHT;
238         else if (fl_get_button(dialog_->radio_align_center))
239                 align = LYX_ALIGN_CENTER;
240         else
241                 align = LYX_ALIGN_BLOCK;
242         controller().params().align(align);
243         
244         /* label width */
245         string const labelwidthstring =
246                 getStringFromInput(dialog_->input_labelwidth);
247         controller().params().labelWidthString(labelwidthstring);
248
249         /* indendation */
250         bool const noindent = fl_get_button(dialog_->check_noindent);
251         controller().params().noindent(noindent);
252
253         /* get spacing */
254         Spacing::Space linespacing = Spacing::Default;
255         string other;
256         switch (fl_get_choice(dialog_->choice_linespacing)) {
257         case 1:
258                 linespacing = Spacing::Default;
259                 break;
260         case 2:
261                 linespacing = Spacing::Single;
262                 break;
263         case 3:
264                 linespacing = Spacing::Onehalf;
265                 break;
266         case 4:
267                 linespacing = Spacing::Double;
268                 break;
269         case 5:
270                 linespacing = Spacing::Other;
271                 other = getStringFromInput(dialog_->input_linespacing);
272                 break;
273         }
274
275         Spacing const spacing(linespacing, other);
276         controller().params().spacing(spacing);
277         
278 }
279
280 namespace {
281
282 void setWidgetsFromVSpace(VSpace const & space,
283                           FL_OBJECT * choice_type,
284                           FL_OBJECT * input_length,
285                           FL_OBJECT * choice_length,
286                           FL_OBJECT * check_keep)
287 {
288         // Paranoia check!
289         lyx::Assert(choice_type   && choice_type->objclass   == FL_CHOICE &&
290                     input_length  && input_length->objclass  == FL_INPUT &&
291                     choice_length && choice_length->objclass == FL_CHOICE &&
292                     check_keep    && check_keep->objclass    == FL_CHECKBUTTON);
293
294         fl_set_input(input_length, "");
295         setEnabled(input_length,  false);
296         setEnabled(choice_length, false);
297
298         switch (space.kind()) {
299         case VSpace::NONE:
300                 fl_set_choice(choice_type, 1);
301                 break;
302         case VSpace::DEFSKIP:
303                 fl_set_choice(choice_type, 2);
304                 break;
305         case VSpace::SMALLSKIP:
306                 fl_set_choice(choice_type, 3);
307                 break;
308         case VSpace::MEDSKIP:
309                 fl_set_choice(choice_type, 4);
310                 break;
311         case VSpace::BIGSKIP:
312                 fl_set_choice(choice_type, 5);
313                 break;
314         case VSpace::VFILL:
315                 fl_set_choice(choice_type, 6);
316                 break;
317         case VSpace::LENGTH:
318         {
319                 fl_set_choice(choice_type, 7);
320
321                 setEnabled(input_length,  true);
322                 setEnabled(choice_length, true);
323
324                 bool const metric = lyxrc.default_papersize > 3;
325                 string const default_unit = metric ? "cm" : "in";
326                 string const length = space.length().asString();
327
328                 updateWidgetsFromLengthString(input_length, choice_length,
329                                               length, default_unit);
330                 break;
331         }
332         }
333
334         fl_set_button(check_keep, space.keep());
335 }
336
337 } // namespace anon
338
339 void FormParagraph::update()
340 {
341         if (!dialog_.get())
342                 return;
343
344         /* label width */
345         string labelwidth = controller().params().labelWidthString();
346         fl_set_input(dialog_->input_labelwidth, labelwidth.c_str());
347         setEnabled(dialog_->input_labelwidth,
348                    labelwidth != _("Senseless with this layout!"));
349
350         /* alignment */
351         fl_set_button(dialog_->radio_align_right, 0);
352         fl_set_button(dialog_->radio_align_left, 0);
353         fl_set_button(dialog_->radio_align_center, 0);
354         fl_set_button(dialog_->radio_align_block, 0);
355         
356         LyXAlignment align = controller().params().align();
357
358         switch (align) {
359         case LYX_ALIGN_RIGHT:
360                 fl_set_button(dialog_->radio_align_right, 1);
361                 break;
362         case LYX_ALIGN_LEFT:
363                 fl_set_button(dialog_->radio_align_left, 1);
364                 break;
365         case LYX_ALIGN_CENTER:
366                 fl_set_button(dialog_->radio_align_center, 1);
367                 break;
368         default:
369                 fl_set_button(dialog_->radio_align_block, 1);
370                 break;
371         }
372
373         LyXAlignment alignpos = controller().alignPossible();
374
375         setEnabled(dialog_->radio_align_block,  bool(alignpos & LYX_ALIGN_BLOCK));
376         setEnabled(dialog_->radio_align_center, bool(alignpos & LYX_ALIGN_CENTER));
377         setEnabled(dialog_->radio_align_left,   bool(alignpos & LYX_ALIGN_LEFT));
378         setEnabled(dialog_->radio_align_right,  bool(alignpos & LYX_ALIGN_RIGHT));
379
380         // no inset-text-owned paragraph may have pagebreaks
381         bool ininset = controller().inInset();
382         setEnabled(dialog_->check_pagebreaks_top, !ininset);
383         setEnabled(dialog_->check_pagebreaks_bottom, !ininset);
384
385         /* lines, pagebreaks and indent */
386         fl_set_button(dialog_->check_lines_top,
387                       controller().params().lineTop());
388         fl_set_button(dialog_->check_lines_bottom,
389                       controller().params().lineBottom());
390         fl_set_button(dialog_->check_pagebreaks_top,
391                       controller().params().pagebreakTop());
392         fl_set_button(dialog_->check_pagebreaks_bottom,
393                       controller().params().pagebreakBottom());
394         fl_set_button(dialog_->check_noindent,
395                       controller().params().noindent());
396
397         /* linespacing */
398         int linespacing;
399         Spacing const space = controller().params().spacing();
400
401         switch (space.getSpace()) {
402         default: linespacing = 1; break;
403         case Spacing::Single: linespacing = 2; break;
404         case Spacing::Onehalf: linespacing = 3; break;
405         case Spacing::Double: linespacing = 4; break;
406         case Spacing::Other: linespacing = 5; break;
407         }
408
409         fl_set_choice(dialog_->choice_linespacing, linespacing);
410         if (space.getSpace() == Spacing::Other) {
411                 string const sp = tostr(space.getValue());
412                 fl_set_input(dialog_->input_linespacing, sp.c_str());
413                 setEnabled(dialog_->input_linespacing, true);
414         } else {
415                 fl_set_input(dialog_->input_linespacing, "");
416                 setEnabled(dialog_->input_linespacing, false);
417         }
418
419         /* vspace top */
420         setWidgetsFromVSpace(controller().params().spaceTop(),
421                              dialog_->choice_space_above,
422                              dialog_->input_space_above,
423                              dialog_->choice_value_space_above,
424                              dialog_->check_space_above);
425
426         /* vspace bottom */
427         setWidgetsFromVSpace(controller().params().spaceBottom(),
428                              dialog_->choice_space_below,
429                              dialog_->input_space_below,
430                              dialog_->choice_value_space_below,
431                              dialog_->check_space_below);
432
433         /* no indent */
434         fl_set_button(dialog_->check_noindent,
435                       controller().params().noindent());
436 }
437
438 namespace {
439
440 void synchronizeSpaceWidgets(FL_OBJECT * choice_type,
441                              FL_OBJECT * input_length,
442                              FL_OBJECT * choice_length)
443 {
444         // Paranoia check!
445         lyx::Assert(choice_type   && choice_type->objclass   == FL_CHOICE &&
446                     input_length  && input_length->objclass  == FL_INPUT &&
447                     choice_length && choice_length->objclass == FL_CHOICE);
448
449         if (fl_get_choice(choice_type) != 7) {
450                 fl_set_input(input_length, "");
451                 setEnabled(input_length, false);
452                 setEnabled(choice_length, false);
453
454         } else {
455                 setEnabled(input_length, true);
456                 setEnabled(choice_length, true);
457
458                 string const length = getStringFromInput(input_length);
459
460                 if (strip(length).empty()) {
461                         bool const metric = lyxrc.default_papersize > 3;
462                         int const default_unit = metric ? 8 : 9;
463
464                         fl_set_choice(choice_length, default_unit);
465                 }
466         }
467 }
468
469 bool validSpaceWidgets(FL_OBJECT * choice_type, FL_OBJECT * input_length)
470 {
471         // Paranoia check!
472         lyx::Assert(choice_type  && choice_type->objclass   == FL_CHOICE &&
473                     input_length && input_length->objclass  == FL_INPUT);
474
475         if (fl_get_choice(choice_type) != 7)
476                 return true;
477
478         string const input = getStringFromInput(input_length);
479         return (input.empty() ||
480                 isValidGlueLength(input) ||
481                 isStrDbl(input));
482 }
483
484 } // namespace anon
485
486 ButtonPolicy::SMInput FormParagraph::input(FL_OBJECT * ob, long)
487 {
488         clearMessage();
489
490         // First check the buttons which are exclusive and you have to
491         // check only the actuall de/activated button.
492         //
493         // "Synchronize" the choices and input fields, making it
494         // impossible to commit senseless data.
495         if (ob == dialog_->choice_space_above) {
496                 synchronizeSpaceWidgets(dialog_->choice_space_above,
497                                         dialog_->input_space_above,
498                                         dialog_->choice_value_space_above);
499         }
500
501         if (ob == dialog_->choice_space_below) {
502                 synchronizeSpaceWidgets(dialog_->choice_space_below,
503                                         dialog_->input_space_below,
504                                         dialog_->choice_value_space_below);
505         }
506
507         // Display a warning if the input is senseless
508         bool valid = (validSpaceWidgets(dialog_->choice_space_above,
509                                         dialog_->input_space_above) &&
510                       validSpaceWidgets(dialog_->choice_space_below,
511                                         dialog_->input_space_below));
512
513         if (!valid) {
514                 postWarning(_("Invalid Length (valid example: 10mm)"));
515         }
516
517         int const choice_spacing = fl_get_choice(dialog_->choice_linespacing);
518
519         if (choice_spacing == 5)
520                 setEnabled(dialog_->input_linespacing, true);
521         else {
522                 fl_set_input(dialog_->input_linespacing, "");
523                 setEnabled(dialog_->input_linespacing, false);
524         }
525
526         double const spacing =
527                 strToDbl(getStringFromInput(dialog_->input_linespacing));
528
529         if (choice_spacing == 5 && int(spacing) == 0)
530                 valid = false;
531
532         return ButtonPolicy::SMI_VALID;
533 }
534
535