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