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