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