]> git.lyx.org Git - lyx.git/blob - src/frontends/xforms/FormParagraph.C
Change Assert to BOOST_ASSERT.
[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  * \author Rob Lahaye
8  *
9  * Full author contact details are available in file CREDITS.
10  */
11
12 #include <config.h>
13
14 #include "FormParagraph.h"
15 #include "ControlParagraph.h"
16 #include "forms/form_paragraph.h"
17
18 #include "checkedwidgets.h"
19 #include "input_validators.h"
20 #include "Tooltips.h"
21 #include "xforms_helpers.h"
22 #include "xformsBC.h"
23
24 #include "controllers/helper_funcs.h"
25
26 #include "lyxrc.h" // to set the deafult length values
27 #include "ParagraphParameters.h"
28 #include "Spacing.h"
29 #include "vspace.h"
30
31 #include "support/lstrings.h"
32 #include "support/tostr.h"
33
34 #include "lyx_forms.h"
35
36 using namespace lyx::support;
37
38 using std::bind2nd;
39 using std::remove_if;
40
41 using std::vector;
42
43
44 namespace
45 {
46
47 string defaultUnit("cm");
48
49 void validateVSpaceWidgets(FL_OBJECT * choice_type, FL_OBJECT * input_length);
50
51 VSpace const setVSpaceFromWidgets(FL_OBJECT * choice_type,
52                                   FL_OBJECT * input_length,
53                                   FL_OBJECT * choice_length,
54                                   FL_OBJECT * check_keep);
55
56 void setWidgetsFromVSpace(VSpace const & space,
57                           FL_OBJECT * choice_type,
58                           FL_OBJECT * input_length,
59                           FL_OBJECT * choice_length,
60                           FL_OBJECT * check_keep);
61
62 } // namespace anon
63
64
65 typedef FormController<ControlParagraph, FormView<FD_paragraph> > base_class;
66
67 FormParagraph::FormParagraph(Dialog & parent)
68         : base_class(parent, _("Paragraph Settings"))
69 {}
70
71
72 void FormParagraph::build()
73 {
74         // the tabbed folder
75         dialog_.reset(build_paragraph(this));
76
77         // Manage the ok, apply, restore and cancel/close buttons
78         bcview().setOK(dialog_->button_ok);
79         bcview().setApply(dialog_->button_apply);
80         bcview().setCancel(dialog_->button_close);
81         bcview().setRestore(dialog_->button_restore);
82
83         // disable for read-only documents
84         bcview().addReadOnly(dialog_->check_line_above);
85         bcview().addReadOnly(dialog_->check_pagebreak_above);
86         bcview().addReadOnly(dialog_->choice_space_above);
87         bcview().addReadOnly(dialog_->input_space_above);
88         bcview().addReadOnly(dialog_->check_space_above);
89
90         bcview().addReadOnly(dialog_->check_noindent);
91         bcview().addReadOnly(dialog_->choice_linespacing);
92         bcview().addReadOnly(dialog_->input_linespacing);
93
94         bcview().addReadOnly(dialog_->check_line_below);
95         bcview().addReadOnly(dialog_->check_pagebreak_below);
96         bcview().addReadOnly(dialog_->choice_space_below);
97         bcview().addReadOnly(dialog_->input_space_below);
98         bcview().addReadOnly(dialog_->check_space_below);
99
100         bcview().addReadOnly(dialog_->input_labelwidth);
101
102         // check validity of "length + unit" input
103         addCheckedGlueLength(bcview(),
104                              dialog_->input_space_above,
105                              dialog_->choice_space_above);
106         addCheckedGlueLength(bcview(),
107                              dialog_->input_space_below,
108                              dialog_->choice_space_below);
109
110         // trigger an input event for cut&paste with middle mouse button.
111         setPrehandler(dialog_->input_space_above);
112         setPrehandler(dialog_->input_space_below);
113         setPrehandler(dialog_->input_linespacing);
114         setPrehandler(dialog_->input_labelwidth);
115
116         fl_set_input_return(dialog_->input_space_above, FL_RETURN_CHANGED);
117         fl_set_input_return(dialog_->input_space_below, FL_RETURN_CHANGED);
118         fl_set_input_return(dialog_->input_labelwidth,  FL_RETURN_CHANGED);
119         fl_set_input_return(dialog_->input_linespacing, FL_RETURN_CHANGED);
120
121         // limit these inputs to unsigned floats
122         fl_set_input_filter(dialog_->input_linespacing, fl_unsigned_float_filter);
123
124         // add alignment radio buttons
125         alignment_.init(dialog_->radio_align_left,   LYX_ALIGN_LEFT);
126         alignment_.init(dialog_->radio_align_right,  LYX_ALIGN_RIGHT);
127         alignment_.init(dialog_->radio_align_block,  LYX_ALIGN_BLOCK);
128         alignment_.init(dialog_->radio_align_center, LYX_ALIGN_CENTER);
129
130         string const parspacing = _("None|DefSkip|SmallSkip|MedSkip|BigSkip|VFill|Length");
131         fl_addto_choice(dialog_->choice_space_above, parspacing.c_str());
132         fl_addto_choice(dialog_->choice_space_below, parspacing.c_str());
133
134         string const linespacing = _("Default|Single|OneHalf|Double|Custom");
135         fl_addto_choice(dialog_->choice_linespacing, linespacing.c_str());
136
137         // Create the contents of the unit choices; don't include the "%" terms.
138         vector<string> units_vec = getLatexUnits();
139         vector<string>::iterator del =
140                 remove_if(units_vec.begin(), units_vec.end(),
141                           bind2nd(contains_functor(), "%"));
142         units_vec.erase(del, units_vec.end());
143
144         string const units = getStringFromVector(units_vec, "|");
145         fl_addto_choice(dialog_->choice_unit_space_above, units.c_str());
146         fl_addto_choice(dialog_->choice_unit_space_below, units.c_str());
147
148         // set up the tooltips
149         string str = _("Add a separator line above this paragraph.");
150         tooltips().init(dialog_->check_line_above, str);
151         str = _("Enforce a page break above this paragraph.");
152         tooltips().init(dialog_->check_pagebreak_above, str);
153         str = _("Add additional space above this paragraph.");
154         tooltips().init(dialog_->choice_space_above, str);
155         str = _("Never suppress space (e.g. at top of page or new page).");
156         tooltips().init(dialog_->check_space_above, str);
157
158         str = _("Add a separator line below this paragraph.");
159         tooltips().init(dialog_->check_line_below, str);
160         str = _("Enforce a page break below this paragraph.");
161         tooltips().init(dialog_->check_pagebreak_below, str);
162         str = _("Add additional space below this paragraph.");
163         tooltips().init(dialog_->choice_space_below, str);
164         str = _("Never suppress space (e.g. at bottom of page or new page).");
165         tooltips().init(dialog_->check_space_below, str);
166
167         // set default unit for custom length
168         switch (lyxrc.default_papersize) {
169                 case PAPER_DEFAULT:
170                 case PAPER_USLETTER:
171                 case PAPER_LEGALPAPER:
172                 case PAPER_EXECUTIVEPAPER:
173                         defaultUnit = "in";
174                         break;
175                 case PAPER_A3PAPER:
176                 case PAPER_A4PAPER:
177                 case PAPER_A5PAPER:
178                 case PAPER_B5PAPER:
179                         defaultUnit = "cm";
180                         break;
181         }
182 }
183
184
185 void FormParagraph::apply()
186 {
187         if (!form()) return;
188
189         // spacing
190         // If a vspace choice is "Length" but there's no text in
191         // the input field, reset the choice to "None".
192         validateVSpaceWidgets(dialog_->choice_space_above,
193                               dialog_->input_space_above);
194
195         VSpace const space_above =
196                 setVSpaceFromWidgets(dialog_->choice_space_above,
197                                      dialog_->input_space_above,
198                                      dialog_->choice_unit_space_above,
199                                      dialog_->check_space_above);
200
201         controller().params().spaceTop(space_above);
202
203         validateVSpaceWidgets(dialog_->choice_space_below,
204                               dialog_->input_space_below);
205
206         VSpace const space_below =
207                 setVSpaceFromWidgets(dialog_->choice_space_below,
208                                      dialog_->input_space_below,
209                                      dialog_->choice_unit_space_below,
210                                      dialog_->check_space_below);
211
212         controller().params().spaceBottom(space_below);
213
214         // lines and pagebreaks
215         bool const line_above = fl_get_button(dialog_->check_line_above);
216         controller().params().lineTop(line_above);
217
218         bool const line_below = fl_get_button(dialog_->check_line_below);
219         controller().params().lineBottom(line_below);
220
221         bool const pagebreak_above =
222                 fl_get_button(dialog_->check_pagebreak_above);
223         controller().params().pagebreakTop(pagebreak_above);
224
225         bool const pagebreak_below =
226                 fl_get_button(dialog_->check_pagebreak_below);
227         controller().params().pagebreakBottom(pagebreak_below);
228
229
230         // alignment
231         LyXAlignment const alignment =
232                 static_cast<LyXAlignment>(alignment_.get());
233         controller().params().align(alignment);
234
235         // label width
236         string const labelwidthstring =
237                 getString(dialog_->input_labelwidth);
238         controller().params().labelWidthString(labelwidthstring);
239
240         // indendation
241         bool const noindent = fl_get_button(dialog_->check_noindent);
242         controller().params().noindent(noindent);
243
244         // get spacing
245         Spacing::Space linespacing = Spacing::Default;
246         string other;
247         switch (fl_get_choice(dialog_->choice_linespacing)) {
248         case 1:
249                 linespacing = Spacing::Default;
250                 break;
251         case 2:
252                 linespacing = Spacing::Single;
253                 break;
254         case 3:
255                 linespacing = Spacing::Onehalf;
256                 break;
257         case 4:
258                 linespacing = Spacing::Double;
259                 break;
260         case 5:
261                 // reset to default if input is empty
262                 other = getString(dialog_->input_linespacing);
263                 if (!other.empty()) {
264                         linespacing = Spacing::Other;
265                 } else {
266                         linespacing = Spacing::Default;
267                         fl_set_choice(dialog_->choice_linespacing, 1);
268                 }
269                 break;
270         }
271         Spacing const spacing(linespacing, other);
272         controller().params().spacing(spacing);
273 }
274
275
276 void FormParagraph::update()
277 {
278         if (!dialog_.get())
279                 return;
280
281         // label width
282         string const labelwidth = controller().params().labelWidthString();
283         fl_set_input(dialog_->input_labelwidth, labelwidth.c_str());
284         setEnabled(dialog_->input_labelwidth,
285                    labelwidth != _("Senseless with this layout!"));
286
287         // alignment
288         alignment_.set(controller().params().align());
289
290         // mark default alignment
291         LyXAlignment const default_alignment = controller().alignDefault();
292
293         string label = _("Block");
294         if (default_alignment == LYX_ALIGN_BLOCK) {
295                 label += _(" (default)");
296         }
297         fl_set_object_label(dialog_->radio_align_block, label.c_str());
298         fl_set_button_shortcut(dialog_->radio_align_block, "#B", 1);
299
300         label = _("Center");
301         if (default_alignment == LYX_ALIGN_CENTER) {
302                 label += _(" (default)");
303         }
304         fl_set_object_label(dialog_->radio_align_center, label.c_str());
305         fl_set_button_shortcut(dialog_->radio_align_center, "#C", 1);
306
307         label = _("Left");
308         if (default_alignment == LYX_ALIGN_LEFT) {
309                 label += _(" (default)");
310         }
311         fl_set_object_label(dialog_->radio_align_left, label.c_str());
312         fl_set_button_shortcut(dialog_->radio_align_left, "#L", 1);
313
314         label = _("Right");
315         if (default_alignment == LYX_ALIGN_RIGHT) {
316                 label = _(" (default)");
317         }
318         fl_set_object_label(dialog_->radio_align_right, label.c_str());
319         fl_set_button_shortcut(dialog_->radio_align_right, "#R", 1);
320
321         // Ensure that there's no crud left on the screen from this change
322         // of labels.
323         fl_redraw_form(form());
324
325         LyXAlignment alignpos = controller().alignPossible();
326         setEnabled(dialog_->radio_align_block,
327                    bool(alignpos & LYX_ALIGN_BLOCK));
328         setEnabled(dialog_->radio_align_center,
329                    bool(alignpos & LYX_ALIGN_CENTER));
330         setEnabled(dialog_->radio_align_left,
331                    bool(alignpos & LYX_ALIGN_LEFT));
332         setEnabled(dialog_->radio_align_right,
333                    bool(alignpos & LYX_ALIGN_RIGHT));
334
335         // no inset-text-owned paragraph may have pagebreaks
336         bool ininset = controller().inInset();
337         setEnabled(dialog_->check_pagebreak_above, !ininset);
338         setEnabled(dialog_->check_pagebreak_below, !ininset);
339
340         // lines, pagebreaks and indent
341         fl_set_button(dialog_->check_line_above,
342                       controller().params().lineTop());
343         fl_set_button(dialog_->check_line_below,
344                       controller().params().lineBottom());
345         fl_set_button(dialog_->check_pagebreak_above,
346                       controller().params().pagebreakTop());
347         fl_set_button(dialog_->check_pagebreak_below,
348                       controller().params().pagebreakBottom());
349         fl_set_button(dialog_->check_noindent,
350                       controller().params().noindent());
351
352         // linespacing
353         Spacing const space = controller().params().spacing();
354
355         int pos;
356         switch (space.getSpace()) {
357         case Spacing::Other:
358                 pos = 5;
359                 break;
360         case Spacing::Double:
361                 pos = 4;
362                 break;
363         case Spacing::Onehalf:
364                 pos = 3;
365                 break;
366         case Spacing::Single:
367                 pos = 2;
368                 break;
369         case Spacing::Default:
370         default:
371                 pos = 1;
372                 break;
373         }
374         fl_set_choice(dialog_->choice_linespacing, pos);
375
376         bool const spacing_other = space.getSpace() == Spacing::Other;
377         setEnabled(dialog_->input_linespacing, spacing_other);
378         if (spacing_other) {
379                 string const sp = tostr(space.getValue());
380                 fl_set_input(dialog_->input_linespacing, sp.c_str());
381         } else {
382                 fl_set_input(dialog_->input_linespacing, "");
383         }
384
385         // vspace top
386         setWidgetsFromVSpace(controller().params().spaceTop(),
387                              dialog_->choice_space_above,
388                              dialog_->input_space_above,
389                              dialog_->choice_unit_space_above,
390                              dialog_->check_space_above);
391
392         // vspace bottom
393         setWidgetsFromVSpace(controller().params().spaceBottom(),
394                              dialog_->choice_space_below,
395                              dialog_->input_space_below,
396                              dialog_->choice_unit_space_below,
397                              dialog_->check_space_below);
398
399         // no indent
400         fl_set_button(dialog_->check_noindent,
401                       controller().params().noindent());
402 }
403
404
405 ButtonPolicy::SMInput FormParagraph::input(FL_OBJECT * ob, long)
406 {
407         // Enable input when custum length is choosen,
408         // disable 'keep' when no space is choosen
409         if (ob == dialog_->choice_space_above) {
410                 bool const custom_length =
411                         fl_get_choice(dialog_->choice_space_above) == 7;
412                 setEnabled(dialog_->input_space_above, custom_length);
413                 setEnabled(dialog_->choice_unit_space_above, custom_length);
414
415                 bool const space =
416                         fl_get_choice(dialog_->choice_space_above) != 1;
417                 setEnabled(dialog_->check_space_above, space);
418
419         } else if (ob == dialog_->choice_space_below) {
420                 bool const custom_length =
421                         fl_get_choice(dialog_->choice_space_below) == 7;
422                 setEnabled(dialog_->input_space_below, custom_length);
423                 setEnabled(dialog_->choice_unit_space_below, custom_length);
424
425                 bool const space =
426                         fl_get_choice(dialog_->choice_space_below) != 1;
427                 setEnabled(dialog_->check_space_below, space);
428
429         } else if (ob == dialog_->choice_linespacing) {
430                 bool const custom_spacing =
431                         fl_get_choice(dialog_->choice_linespacing) == 5;
432                 setEnabled(dialog_->input_linespacing, custom_spacing);
433         }
434
435         return ButtonPolicy::SMI_VALID;
436 }
437
438
439 namespace {
440
441 void validateVSpaceWidgets(FL_OBJECT * choice_type, FL_OBJECT * input_length)
442 {
443         // Paranoia check!
444         BOOST_ASSERT(choice_type  && choice_type->objclass  == FL_CHOICE &&
445                     input_length && input_length->objclass == FL_INPUT);
446
447         if (fl_get_choice(choice_type) != 7)
448                 return;
449
450         // If a vspace kind is "Length" but there's no text in
451         // the input field, reset the kind to "None".
452         string const input = rtrim(getString(input_length));
453         if (input.empty())
454                 fl_set_choice(choice_type, 1);
455 }
456
457
458 VSpace const setVSpaceFromWidgets(FL_OBJECT * choice_type,
459                                   FL_OBJECT * input_length,
460                                   FL_OBJECT * choice_length,
461                                   FL_OBJECT * check_keep)
462 {
463         // Paranoia check!
464         BOOST_ASSERT(choice_type   && choice_type->objclass   == FL_CHOICE &&
465                     input_length  && input_length->objclass  == FL_INPUT &&
466                     choice_length && choice_length->objclass == FL_CHOICE &&
467                     check_keep    && check_keep->objclass    == FL_CHECKBUTTON);
468
469         VSpace space;
470         switch (fl_get_choice(choice_type)) {
471         case 1:
472                 space = VSpace(VSpace::NONE);
473                 break;
474         case 2:
475                 space = VSpace(VSpace::DEFSKIP);
476                 break;
477         case 3:
478                 space = VSpace(VSpace::SMALLSKIP);
479                 break;
480         case 4:
481                 space = VSpace(VSpace::MEDSKIP);
482                 break;
483         case 5:
484                 space = VSpace(VSpace::BIGSKIP);
485                 break;
486         case 6:
487                 space = VSpace(VSpace::VFILL);
488                 break;
489         case 7:
490                 {
491                 string const length =
492                         getLengthFromWidgets(input_length, choice_length);
493                 space = VSpace(LyXGlueLength(length));
494                 break;
495                 }
496         }
497
498         if (fl_get_button(check_keep))
499                 space.setKeep(true);
500
501         return space;
502 }
503
504
505 void setWidgetsFromVSpace(VSpace const & space,
506                           FL_OBJECT * choice_type,
507                           FL_OBJECT * input_length,
508                           FL_OBJECT * choice_length,
509                           FL_OBJECT * check_keep)
510 {
511         // Paranoia check!
512         BOOST_ASSERT(choice_type   && choice_type->objclass   == FL_CHOICE &&
513                     input_length  && input_length->objclass  == FL_INPUT &&
514                     choice_length && choice_length->objclass == FL_CHOICE &&
515                     check_keep    && check_keep->objclass    == FL_CHECKBUTTON);
516
517         fl_set_button(check_keep, space.keep());
518
519         int pos = 1;
520         switch (space.kind()) {
521         case VSpace::NONE:
522                 pos = 1;
523                 break;
524         case VSpace::DEFSKIP:
525                 pos = 2;
526                 break;
527         case VSpace::SMALLSKIP:
528                 pos = 3;
529                 break;
530         case VSpace::MEDSKIP:
531                 pos = 4;
532                 break;
533         case VSpace::BIGSKIP:
534                 pos = 5;
535                 break;
536         case VSpace::VFILL:
537                 pos = 6;
538                 break;
539         case VSpace::LENGTH:
540                 pos = 7;
541                 break;
542         }
543         fl_set_choice(choice_type, pos);
544
545         bool const custom_vspace = space.kind() == VSpace::LENGTH;
546         setEnabled(input_length, custom_vspace);
547         setEnabled(choice_length, custom_vspace);
548         if (custom_vspace) {
549                 string const length = space.length().asString();
550                 updateWidgetsFromLengthString(input_length, choice_length,
551                                               length, defaultUnit);
552         } else {
553                 bool const no_vspace = space.kind() == VSpace::NONE;
554                 setEnabled(check_keep, !no_vspace);
555                 fl_set_input(input_length, "");
556                 fl_set_choice_text(choice_length, defaultUnit.c_str());
557         }
558 }
559
560 } // namespace anon