2 * \file FormParagraph.C
3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
9 * Full author contact details are available in file CREDITS
14 #include "ControlParagraph.h"
15 #include "FormParagraph.h"
16 #include "forms/form_paragraph.h"
19 #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 "checkedwidgets.h"
28 #include "layout.h" // LyXAlignment
30 #include "support/lstrings.h"
31 #include "support/tostr.h"
32 #include "support/LAssert.h"
34 #include "lyx_forms.h"
38 using namespace lyx::support;
48 string defaultUnit("cm");
50 void validateVSpaceWidgets(FL_OBJECT * choice_type, FL_OBJECT * input_length);
52 VSpace const setVSpaceFromWidgets(FL_OBJECT * choice_type,
53 FL_OBJECT * input_length,
54 FL_OBJECT * choice_length,
55 FL_OBJECT * check_keep);
57 void setWidgetsFromVSpace(VSpace const & space,
58 FL_OBJECT * choice_type,
59 FL_OBJECT * input_length,
60 FL_OBJECT * choice_length,
61 FL_OBJECT * check_keep);
66 typedef FormController<ControlParagraph, FormView<FD_paragraph> > base_class;
68 FormParagraph::FormParagraph(Dialog & parent)
69 : base_class(parent, _("Paragraph Settings"))
73 void FormParagraph::build()
76 dialog_.reset(build_paragraph(this));
78 // Manage the ok, apply, restore and cancel/close buttons
79 bcview().setOK(dialog_->button_ok);
80 bcview().setApply(dialog_->button_apply);
81 bcview().setCancel(dialog_->button_close);
82 bcview().setRestore(dialog_->button_restore);
84 // disable for read-only documents
85 bcview().addReadOnly(dialog_->check_line_above);
86 bcview().addReadOnly(dialog_->check_pagebreak_above);
87 bcview().addReadOnly(dialog_->choice_space_above);
88 bcview().addReadOnly(dialog_->input_space_above);
89 bcview().addReadOnly(dialog_->check_space_above);
91 bcview().addReadOnly(dialog_->check_noindent);
92 bcview().addReadOnly(dialog_->choice_linespacing);
93 bcview().addReadOnly(dialog_->input_linespacing);
95 bcview().addReadOnly(dialog_->check_line_below);
96 bcview().addReadOnly(dialog_->check_pagebreak_below);
97 bcview().addReadOnly(dialog_->choice_space_below);
98 bcview().addReadOnly(dialog_->input_space_below);
99 bcview().addReadOnly(dialog_->check_space_below);
101 bcview().addReadOnly(dialog_->input_labelwidth);
103 // check validity of "length + unit" input
104 addCheckedGlueLength(bcview(),
105 dialog_->input_space_above,
106 dialog_->choice_space_above);
107 addCheckedGlueLength(bcview(),
108 dialog_->input_space_below,
109 dialog_->choice_space_below);
111 // trigger an input event for cut&paste with middle mouse button.
112 setPrehandler(dialog_->input_space_above);
113 setPrehandler(dialog_->input_space_below);
114 setPrehandler(dialog_->input_linespacing);
115 setPrehandler(dialog_->input_labelwidth);
117 fl_set_input_return(dialog_->input_space_above, FL_RETURN_CHANGED);
118 fl_set_input_return(dialog_->input_space_below, FL_RETURN_CHANGED);
119 fl_set_input_return(dialog_->input_labelwidth, FL_RETURN_CHANGED);
120 fl_set_input_return(dialog_->input_linespacing, FL_RETURN_CHANGED);
122 // limit these inputs to unsigned floats
123 fl_set_input_filter(dialog_->input_linespacing, fl_unsigned_float_filter);
125 // add alignment radio buttons
126 alignment_.init(dialog_->radio_align_left, LYX_ALIGN_LEFT);
127 alignment_.init(dialog_->radio_align_right, LYX_ALIGN_RIGHT);
128 alignment_.init(dialog_->radio_align_block, LYX_ALIGN_BLOCK);
129 alignment_.init(dialog_->radio_align_center, LYX_ALIGN_CENTER);
131 string const parspacing = _("None|DefSkip|SmallSkip|MedSkip|BigSkip|VFill|Length");
132 fl_addto_choice(dialog_->choice_space_above, parspacing.c_str());
133 fl_addto_choice(dialog_->choice_space_below, parspacing.c_str());
135 string const linespacing = _("Default|Single|OneHalf|Double|Custom");
136 fl_addto_choice(dialog_->choice_linespacing, linespacing.c_str());
138 // Create the contents of the unit choices; don't include the "%" terms.
139 vector<string> units_vec = getLatexUnits();
140 vector<string>::iterator del =
141 remove_if(units_vec.begin(), units_vec.end(),
142 bind2nd(contains_functor(), "%"));
143 units_vec.erase(del, units_vec.end());
145 string const units = getStringFromVector(units_vec, "|");
146 fl_addto_choice(dialog_->choice_unit_space_above, units.c_str());
147 fl_addto_choice(dialog_->choice_unit_space_below, units.c_str());
149 // set up the tooltips
150 string str = _("Add a separator line above this paragraph.");
151 tooltips().init(dialog_->check_line_above, str);
152 str = _("Enforce a page break above this paragraph.");
153 tooltips().init(dialog_->check_pagebreak_above, str);
154 str = _("Add additional space above this paragraph.");
155 tooltips().init(dialog_->choice_space_above, str);
156 str = _("Never suppress space (e.g. at top of page or new page).");
157 tooltips().init(dialog_->check_space_above, str);
159 str = _("Add a separator line below this paragraph.");
160 tooltips().init(dialog_->check_line_below, str);
161 str = _("Enforce a page break below this paragraph.");
162 tooltips().init(dialog_->check_pagebreak_below, str);
163 str = _("Add additional space below this paragraph.");
164 tooltips().init(dialog_->choice_space_below, str);
165 str = _("Never suppress space (e.g. at bottom of page or new page).");
166 tooltips().init(dialog_->check_space_below, str);
168 // set default unit for custom length
169 switch (lyxrc.default_papersize) {
172 case PAPER_LEGALPAPER:
173 case PAPER_EXECUTIVEPAPER:
186 void FormParagraph::apply()
191 // If a vspace choice is "Length" but there's no text in
192 // the input field, reset the choice to "None".
193 validateVSpaceWidgets(dialog_->choice_space_above,
194 dialog_->input_space_above);
196 VSpace const space_above =
197 setVSpaceFromWidgets(dialog_->choice_space_above,
198 dialog_->input_space_above,
199 dialog_->choice_unit_space_above,
200 dialog_->check_space_above);
202 controller().params().spaceTop(space_above);
204 validateVSpaceWidgets(dialog_->choice_space_below,
205 dialog_->input_space_below);
207 VSpace const space_below =
208 setVSpaceFromWidgets(dialog_->choice_space_below,
209 dialog_->input_space_below,
210 dialog_->choice_unit_space_below,
211 dialog_->check_space_below);
213 controller().params().spaceBottom(space_below);
215 // lines and pagebreaks
216 bool const line_above = fl_get_button(dialog_->check_line_above);
217 controller().params().lineTop(line_above);
219 bool const line_below = fl_get_button(dialog_->check_line_below);
220 controller().params().lineBottom(line_below);
222 bool const pagebreak_above =
223 fl_get_button(dialog_->check_pagebreak_above);
224 controller().params().pagebreakTop(pagebreak_above);
226 bool const pagebreak_below =
227 fl_get_button(dialog_->check_pagebreak_below);
228 controller().params().pagebreakBottom(pagebreak_below);
232 LyXAlignment const alignment =
233 static_cast<LyXAlignment>(alignment_.get());
234 controller().params().align(alignment);
237 string const labelwidthstring =
238 getString(dialog_->input_labelwidth);
239 controller().params().labelWidthString(labelwidthstring);
242 bool const noindent = fl_get_button(dialog_->check_noindent);
243 controller().params().noindent(noindent);
246 Spacing::Space linespacing = Spacing::Default;
248 switch (fl_get_choice(dialog_->choice_linespacing)) {
250 linespacing = Spacing::Default;
253 linespacing = Spacing::Single;
256 linespacing = Spacing::Onehalf;
259 linespacing = Spacing::Double;
262 // reset to default if input is empty
263 other = getString(dialog_->input_linespacing);
264 if (!other.empty()) {
265 linespacing = Spacing::Other;
267 linespacing = Spacing::Default;
268 fl_set_choice(dialog_->choice_linespacing, 1);
272 Spacing const spacing(linespacing, other);
273 controller().params().spacing(spacing);
277 void FormParagraph::update()
283 string const labelwidth = controller().params().labelWidthString();
284 fl_set_input(dialog_->input_labelwidth, labelwidth.c_str());
285 setEnabled(dialog_->input_labelwidth,
286 labelwidth != _("Senseless with this layout!"));
289 alignment_.set(controller().params().align());
291 // mark default alignment
292 LyXAlignment const default_alignment = controller().alignDefault();
294 string label = _("Block");
295 if (default_alignment == LYX_ALIGN_BLOCK) {
296 label += _(" (default)");
298 fl_set_object_label(dialog_->radio_align_block, label.c_str());
299 fl_set_button_shortcut(dialog_->radio_align_block, "#B", 1);
302 if (default_alignment == LYX_ALIGN_CENTER) {
303 label += _(" (default)");
305 fl_set_object_label(dialog_->radio_align_center, label.c_str());
306 fl_set_button_shortcut(dialog_->radio_align_center, "#C", 1);
309 if (default_alignment == LYX_ALIGN_LEFT) {
310 label += _(" (default)");
312 fl_set_object_label(dialog_->radio_align_left, label.c_str());
313 fl_set_button_shortcut(dialog_->radio_align_left, "#L", 1);
316 if (default_alignment == LYX_ALIGN_RIGHT) {
317 label = _(" (default)");
319 fl_set_object_label(dialog_->radio_align_right, label.c_str());
320 fl_set_button_shortcut(dialog_->radio_align_right, "#R", 1);
322 // Ensure that there's no crud left on the screen from this change
324 fl_redraw_form(form());
326 LyXAlignment alignpos = controller().alignPossible();
327 setEnabled(dialog_->radio_align_block,
328 bool(alignpos & LYX_ALIGN_BLOCK));
329 setEnabled(dialog_->radio_align_center,
330 bool(alignpos & LYX_ALIGN_CENTER));
331 setEnabled(dialog_->radio_align_left,
332 bool(alignpos & LYX_ALIGN_LEFT));
333 setEnabled(dialog_->radio_align_right,
334 bool(alignpos & LYX_ALIGN_RIGHT));
336 // no inset-text-owned paragraph may have pagebreaks
337 bool ininset = controller().inInset();
338 setEnabled(dialog_->check_pagebreak_above, !ininset);
339 setEnabled(dialog_->check_pagebreak_below, !ininset);
341 // lines, pagebreaks and indent
342 fl_set_button(dialog_->check_line_above,
343 controller().params().lineTop());
344 fl_set_button(dialog_->check_line_below,
345 controller().params().lineBottom());
346 fl_set_button(dialog_->check_pagebreak_above,
347 controller().params().pagebreakTop());
348 fl_set_button(dialog_->check_pagebreak_below,
349 controller().params().pagebreakBottom());
350 fl_set_button(dialog_->check_noindent,
351 controller().params().noindent());
354 Spacing const space = controller().params().spacing();
357 switch (space.getSpace()) {
361 case Spacing::Double:
364 case Spacing::Onehalf:
367 case Spacing::Single:
370 case Spacing::Default:
375 fl_set_choice(dialog_->choice_linespacing, pos);
377 bool const spacing_other = space.getSpace() == Spacing::Other;
378 setEnabled(dialog_->input_linespacing, spacing_other);
380 string const sp = tostr(space.getValue());
381 fl_set_input(dialog_->input_linespacing, sp.c_str());
383 fl_set_input(dialog_->input_linespacing, "");
387 setWidgetsFromVSpace(controller().params().spaceTop(),
388 dialog_->choice_space_above,
389 dialog_->input_space_above,
390 dialog_->choice_unit_space_above,
391 dialog_->check_space_above);
394 setWidgetsFromVSpace(controller().params().spaceBottom(),
395 dialog_->choice_space_below,
396 dialog_->input_space_below,
397 dialog_->choice_unit_space_below,
398 dialog_->check_space_below);
401 fl_set_button(dialog_->check_noindent,
402 controller().params().noindent());
406 ButtonPolicy::SMInput FormParagraph::input(FL_OBJECT * ob, long)
408 // Enable input when custum length is choosen,
409 // disable 'keep' when no space is choosen
410 if (ob == dialog_->choice_space_above) {
411 bool const custom_length =
412 fl_get_choice(dialog_->choice_space_above) == 7;
413 setEnabled(dialog_->input_space_above, custom_length);
414 setEnabled(dialog_->choice_unit_space_above, custom_length);
417 fl_get_choice(dialog_->choice_space_above) != 1;
418 setEnabled(dialog_->check_space_above, space);
420 } else if (ob == dialog_->choice_space_below) {
421 bool const custom_length =
422 fl_get_choice(dialog_->choice_space_below) == 7;
423 setEnabled(dialog_->input_space_below, custom_length);
424 setEnabled(dialog_->choice_unit_space_below, custom_length);
427 fl_get_choice(dialog_->choice_space_below) != 1;
428 setEnabled(dialog_->check_space_below, space);
430 } else if (ob == dialog_->choice_linespacing) {
431 bool const custom_spacing =
432 fl_get_choice(dialog_->choice_linespacing) == 5;
433 setEnabled(dialog_->input_linespacing, custom_spacing);
436 return ButtonPolicy::SMI_VALID;
442 void validateVSpaceWidgets(FL_OBJECT * choice_type, FL_OBJECT * input_length)
445 Assert(choice_type && choice_type->objclass == FL_CHOICE &&
446 input_length && input_length->objclass == FL_INPUT);
448 if (fl_get_choice(choice_type) != 7)
451 // If a vspace kind is "Length" but there's no text in
452 // the input field, reset the kind to "None".
453 string const input = rtrim(getString(input_length));
455 fl_set_choice(choice_type, 1);
459 VSpace const setVSpaceFromWidgets(FL_OBJECT * choice_type,
460 FL_OBJECT * input_length,
461 FL_OBJECT * choice_length,
462 FL_OBJECT * check_keep)
465 Assert(choice_type && choice_type->objclass == FL_CHOICE &&
466 input_length && input_length->objclass == FL_INPUT &&
467 choice_length && choice_length->objclass == FL_CHOICE &&
468 check_keep && check_keep->objclass == FL_CHECKBUTTON);
471 switch (fl_get_choice(choice_type)) {
473 space = VSpace(VSpace::NONE);
476 space = VSpace(VSpace::DEFSKIP);
479 space = VSpace(VSpace::SMALLSKIP);
482 space = VSpace(VSpace::MEDSKIP);
485 space = VSpace(VSpace::BIGSKIP);
488 space = VSpace(VSpace::VFILL);
492 string const length =
493 getLengthFromWidgets(input_length, choice_length);
494 space = VSpace(LyXGlueLength(length));
499 if (fl_get_button(check_keep))
506 void setWidgetsFromVSpace(VSpace const & space,
507 FL_OBJECT * choice_type,
508 FL_OBJECT * input_length,
509 FL_OBJECT * choice_length,
510 FL_OBJECT * check_keep)
513 Assert(choice_type && choice_type->objclass == FL_CHOICE &&
514 input_length && input_length->objclass == FL_INPUT &&
515 choice_length && choice_length->objclass == FL_CHOICE &&
516 check_keep && check_keep->objclass == FL_CHECKBUTTON);
518 fl_set_button(check_keep, space.keep());
521 switch (space.kind()) {
525 case VSpace::DEFSKIP:
528 case VSpace::SMALLSKIP:
531 case VSpace::MEDSKIP:
534 case VSpace::BIGSKIP:
544 fl_set_choice(choice_type, pos);
546 bool const custom_vspace = space.kind() == VSpace::LENGTH;
547 setEnabled(input_length, custom_vspace);
548 setEnabled(choice_length, custom_vspace);
550 string const length = space.length().asString();
551 updateWidgetsFromLengthString(input_length, choice_length,
552 length, defaultUnit);
554 bool const no_vspace = space.kind() == VSpace::NONE;
555 setEnabled(check_keep, !no_vspace);
556 fl_set_input(input_length, "");
557 fl_set_choice_text(choice_length, defaultUnit.c_str());