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