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