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 "FormParagraph.h"
15 #include "ControlParagraph.h"
16 #include "forms/form_paragraph.h"
18 #include "checkedwidgets.h"
19 #include "input_validators.h"
21 #include "xforms_helpers.h"
24 #include "controllers/helper_funcs.h"
26 #include "lyxrc.h" // to set the deafult length values
27 #include "ParagraphParameters.h"
31 #include "support/lstrings.h"
32 #include "support/tostr.h"
34 #include "lyx_forms.h"
36 using lyx::support::contains_functor;
37 using lyx::support::getStringFromVector;
38 using lyx::support::rtrim;
50 string defaultUnit("cm");
52 void validateVSpaceWidgets(FL_OBJECT * choice_type, FL_OBJECT * input_length);
54 VSpace const setVSpaceFromWidgets(FL_OBJECT * choice_type,
55 FL_OBJECT * input_length,
56 FL_OBJECT * choice_length,
57 FL_OBJECT * check_keep);
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);
68 typedef FormController<ControlParagraph, FormView<FD_paragraph> > base_class;
70 FormParagraph::FormParagraph(Dialog & parent)
71 : base_class(parent, _("Paragraph Settings"))
75 void FormParagraph::build()
78 dialog_.reset(build_paragraph(this));
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);
86 // disable for read-only documents
87 bcview().addReadOnly(dialog_->check_line_above);
88 bcview().addReadOnly(dialog_->check_pagebreak_above);
89 bcview().addReadOnly(dialog_->choice_space_above);
90 bcview().addReadOnly(dialog_->input_space_above);
91 bcview().addReadOnly(dialog_->check_space_above);
93 bcview().addReadOnly(dialog_->check_noindent);
94 bcview().addReadOnly(dialog_->choice_linespacing);
95 bcview().addReadOnly(dialog_->input_linespacing);
97 bcview().addReadOnly(dialog_->check_line_below);
98 bcview().addReadOnly(dialog_->check_pagebreak_below);
99 bcview().addReadOnly(dialog_->choice_space_below);
100 bcview().addReadOnly(dialog_->input_space_below);
101 bcview().addReadOnly(dialog_->check_space_below);
103 bcview().addReadOnly(dialog_->input_labelwidth);
105 // check validity of "length + unit" input
106 addCheckedGlueLength(bcview(),
107 dialog_->input_space_above,
108 dialog_->choice_space_above);
109 addCheckedGlueLength(bcview(),
110 dialog_->input_space_below,
111 dialog_->choice_space_below);
113 // trigger an input event for cut&paste with middle mouse button.
114 setPrehandler(dialog_->input_space_above);
115 setPrehandler(dialog_->input_space_below);
116 setPrehandler(dialog_->input_linespacing);
117 setPrehandler(dialog_->input_labelwidth);
119 fl_set_input_return(dialog_->input_space_above, FL_RETURN_CHANGED);
120 fl_set_input_return(dialog_->input_space_below, FL_RETURN_CHANGED);
121 fl_set_input_return(dialog_->input_labelwidth, FL_RETURN_CHANGED);
122 fl_set_input_return(dialog_->input_linespacing, FL_RETURN_CHANGED);
124 // limit these inputs to unsigned floats
125 fl_set_input_filter(dialog_->input_linespacing, fl_unsigned_float_filter);
127 // add alignment radio buttons
128 alignment_.init(dialog_->radio_align_left, LYX_ALIGN_LEFT);
129 alignment_.init(dialog_->radio_align_right, LYX_ALIGN_RIGHT);
130 alignment_.init(dialog_->radio_align_block, LYX_ALIGN_BLOCK);
131 alignment_.init(dialog_->radio_align_center, LYX_ALIGN_CENTER);
133 string const parspacing = _("None|DefSkip|SmallSkip|MedSkip|BigSkip|VFill|Length");
134 fl_addto_choice(dialog_->choice_space_above, parspacing.c_str());
135 fl_addto_choice(dialog_->choice_space_below, parspacing.c_str());
137 string const linespacing = _("Default|Single|OneHalf|Double|Custom");
138 fl_addto_choice(dialog_->choice_linespacing, linespacing.c_str());
140 // Create the contents of the unit choices; don't include the "%" terms.
141 vector<string> units_vec = getLatexUnits();
142 vector<string>::iterator del =
143 remove_if(units_vec.begin(), units_vec.end(),
144 bind2nd(contains_functor(), "%"));
145 units_vec.erase(del, units_vec.end());
147 string const units = getStringFromVector(units_vec, "|");
148 fl_addto_choice(dialog_->choice_unit_space_above, units.c_str());
149 fl_addto_choice(dialog_->choice_unit_space_below, units.c_str());
151 // set up the tooltips
152 string str = _("Add a separator line above this paragraph.");
153 tooltips().init(dialog_->check_line_above, str);
154 str = _("Enforce a page break above this paragraph.");
155 tooltips().init(dialog_->check_pagebreak_above, str);
156 str = _("Add additional space above this paragraph.");
157 tooltips().init(dialog_->choice_space_above, str);
158 str = _("Never suppress space (e.g. at top of page or new page).");
159 tooltips().init(dialog_->check_space_above, str);
161 str = _("Add a separator line below this paragraph.");
162 tooltips().init(dialog_->check_line_below, str);
163 str = _("Enforce a page break below this paragraph.");
164 tooltips().init(dialog_->check_pagebreak_below, str);
165 str = _("Add additional space below this paragraph.");
166 tooltips().init(dialog_->choice_space_below, str);
167 str = _("Never suppress space (e.g. at bottom of page or new page).");
168 tooltips().init(dialog_->check_space_below, str);
170 // set default unit for custom length
171 switch (lyxrc.default_papersize) {
174 case PAPER_LEGALPAPER:
175 case PAPER_EXECUTIVEPAPER:
188 void FormParagraph::apply()
193 // If a vspace choice is "Length" but there's no text in
194 // the input field, reset the choice to "None".
195 validateVSpaceWidgets(dialog_->choice_space_above,
196 dialog_->input_space_above);
198 VSpace const space_above =
199 setVSpaceFromWidgets(dialog_->choice_space_above,
200 dialog_->input_space_above,
201 dialog_->choice_unit_space_above,
202 dialog_->check_space_above);
204 controller().params().spaceTop(space_above);
206 validateVSpaceWidgets(dialog_->choice_space_below,
207 dialog_->input_space_below);
209 VSpace const space_below =
210 setVSpaceFromWidgets(dialog_->choice_space_below,
211 dialog_->input_space_below,
212 dialog_->choice_unit_space_below,
213 dialog_->check_space_below);
215 controller().params().spaceBottom(space_below);
217 // lines and pagebreaks
218 bool const line_above = fl_get_button(dialog_->check_line_above);
219 controller().params().lineTop(line_above);
221 bool const line_below = fl_get_button(dialog_->check_line_below);
222 controller().params().lineBottom(line_below);
224 bool const pagebreak_above =
225 fl_get_button(dialog_->check_pagebreak_above);
226 controller().params().pagebreakTop(pagebreak_above);
228 bool const pagebreak_below =
229 fl_get_button(dialog_->check_pagebreak_below);
230 controller().params().pagebreakBottom(pagebreak_below);
234 LyXAlignment const alignment =
235 static_cast<LyXAlignment>(alignment_.get());
236 controller().params().align(alignment);
239 string const labelwidthstring =
240 getString(dialog_->input_labelwidth);
241 controller().params().labelWidthString(labelwidthstring);
244 bool const noindent = fl_get_button(dialog_->check_noindent);
245 controller().params().noindent(noindent);
248 Spacing::Space linespacing = Spacing::Default;
250 switch (fl_get_choice(dialog_->choice_linespacing)) {
252 linespacing = Spacing::Default;
255 linespacing = Spacing::Single;
258 linespacing = Spacing::Onehalf;
261 linespacing = Spacing::Double;
264 // reset to default if input is empty
265 other = getString(dialog_->input_linespacing);
266 if (!other.empty()) {
267 linespacing = Spacing::Other;
269 linespacing = Spacing::Default;
270 fl_set_choice(dialog_->choice_linespacing, 1);
274 Spacing const spacing(linespacing, other);
275 controller().params().spacing(spacing);
279 void FormParagraph::update()
285 string const labelwidth = controller().params().labelWidthString();
286 fl_set_input(dialog_->input_labelwidth, labelwidth.c_str());
287 setEnabled(dialog_->input_labelwidth,
288 labelwidth != _("Senseless with this layout!"));
291 alignment_.set(controller().params().align());
293 // mark default alignment
294 LyXAlignment const default_alignment = controller().alignDefault();
296 string label = _("Block");
297 if (default_alignment == LYX_ALIGN_BLOCK) {
298 label += _(" (default)");
300 fl_set_object_label(dialog_->radio_align_block, label.c_str());
301 fl_set_button_shortcut(dialog_->radio_align_block, "#B", 1);
304 if (default_alignment == LYX_ALIGN_CENTER) {
305 label += _(" (default)");
307 fl_set_object_label(dialog_->radio_align_center, label.c_str());
308 fl_set_button_shortcut(dialog_->radio_align_center, "#C", 1);
311 if (default_alignment == LYX_ALIGN_LEFT) {
312 label += _(" (default)");
314 fl_set_object_label(dialog_->radio_align_left, label.c_str());
315 fl_set_button_shortcut(dialog_->radio_align_left, "#L", 1);
318 if (default_alignment == LYX_ALIGN_RIGHT) {
319 label = _(" (default)");
321 fl_set_object_label(dialog_->radio_align_right, label.c_str());
322 fl_set_button_shortcut(dialog_->radio_align_right, "#R", 1);
324 // Ensure that there's no crud left on the screen from this change
326 fl_redraw_form(form());
328 LyXAlignment alignpos = controller().alignPossible();
329 setEnabled(dialog_->radio_align_block,
330 bool(alignpos & LYX_ALIGN_BLOCK));
331 setEnabled(dialog_->radio_align_center,
332 bool(alignpos & LYX_ALIGN_CENTER));
333 setEnabled(dialog_->radio_align_left,
334 bool(alignpos & LYX_ALIGN_LEFT));
335 setEnabled(dialog_->radio_align_right,
336 bool(alignpos & LYX_ALIGN_RIGHT));
338 // no inset-text-owned paragraph may have pagebreaks
339 bool ininset = controller().inInset();
340 setEnabled(dialog_->check_pagebreak_above, !ininset);
341 setEnabled(dialog_->check_pagebreak_below, !ininset);
343 // lines, pagebreaks and indent
344 fl_set_button(dialog_->check_line_above,
345 controller().params().lineTop());
346 fl_set_button(dialog_->check_line_below,
347 controller().params().lineBottom());
348 fl_set_button(dialog_->check_pagebreak_above,
349 controller().params().pagebreakTop());
350 fl_set_button(dialog_->check_pagebreak_below,
351 controller().params().pagebreakBottom());
352 fl_set_button(dialog_->check_noindent,
353 controller().params().noindent());
356 Spacing const space = controller().params().spacing();
359 switch (space.getSpace()) {
363 case Spacing::Double:
366 case Spacing::Onehalf:
369 case Spacing::Single:
372 case Spacing::Default:
377 fl_set_choice(dialog_->choice_linespacing, pos);
379 bool const spacing_other = space.getSpace() == Spacing::Other;
380 setEnabled(dialog_->input_linespacing, spacing_other);
382 string const sp = tostr(space.getValue());
383 fl_set_input(dialog_->input_linespacing, sp.c_str());
385 fl_set_input(dialog_->input_linespacing, "");
389 setWidgetsFromVSpace(controller().params().spaceTop(),
390 dialog_->choice_space_above,
391 dialog_->input_space_above,
392 dialog_->choice_unit_space_above,
393 dialog_->check_space_above);
396 setWidgetsFromVSpace(controller().params().spaceBottom(),
397 dialog_->choice_space_below,
398 dialog_->input_space_below,
399 dialog_->choice_unit_space_below,
400 dialog_->check_space_below);
403 fl_set_button(dialog_->check_noindent,
404 controller().params().noindent());
408 ButtonPolicy::SMInput FormParagraph::input(FL_OBJECT * ob, long)
410 // Enable input when custum length is choosen,
411 // disable 'keep' when no space is choosen
412 if (ob == dialog_->choice_space_above) {
413 bool const custom_length =
414 fl_get_choice(dialog_->choice_space_above) == 7;
415 setEnabled(dialog_->input_space_above, custom_length);
416 setEnabled(dialog_->choice_unit_space_above, custom_length);
419 fl_get_choice(dialog_->choice_space_above) != 1;
420 setEnabled(dialog_->check_space_above, space);
422 } else if (ob == dialog_->choice_space_below) {
423 bool const custom_length =
424 fl_get_choice(dialog_->choice_space_below) == 7;
425 setEnabled(dialog_->input_space_below, custom_length);
426 setEnabled(dialog_->choice_unit_space_below, custom_length);
429 fl_get_choice(dialog_->choice_space_below) != 1;
430 setEnabled(dialog_->check_space_below, space);
432 } else if (ob == dialog_->choice_linespacing) {
433 bool const custom_spacing =
434 fl_get_choice(dialog_->choice_linespacing) == 5;
435 setEnabled(dialog_->input_linespacing, custom_spacing);
438 return ButtonPolicy::SMI_VALID;
444 void validateVSpaceWidgets(FL_OBJECT * choice_type, FL_OBJECT * input_length)
447 BOOST_ASSERT(choice_type && choice_type->objclass == FL_CHOICE &&
448 input_length && input_length->objclass == FL_INPUT);
450 if (fl_get_choice(choice_type) != 7)
453 // If a vspace kind is "Length" but there's no text in
454 // the input field, reset the kind to "None".
455 string const input = rtrim(getString(input_length));
457 fl_set_choice(choice_type, 1);
461 VSpace const setVSpaceFromWidgets(FL_OBJECT * choice_type,
462 FL_OBJECT * input_length,
463 FL_OBJECT * choice_length,
464 FL_OBJECT * check_keep)
467 BOOST_ASSERT(choice_type && choice_type->objclass == FL_CHOICE &&
468 input_length && input_length->objclass == FL_INPUT &&
469 choice_length && choice_length->objclass == FL_CHOICE &&
470 check_keep && check_keep->objclass == FL_CHECKBUTTON);
473 switch (fl_get_choice(choice_type)) {
475 space = VSpace(VSpace::NONE);
478 space = VSpace(VSpace::DEFSKIP);
481 space = VSpace(VSpace::SMALLSKIP);
484 space = VSpace(VSpace::MEDSKIP);
487 space = VSpace(VSpace::BIGSKIP);
490 space = VSpace(VSpace::VFILL);
494 string const length =
495 getLengthFromWidgets(input_length, choice_length);
496 space = VSpace(LyXGlueLength(length));
501 if (fl_get_button(check_keep))
508 void setWidgetsFromVSpace(VSpace const & space,
509 FL_OBJECT * choice_type,
510 FL_OBJECT * input_length,
511 FL_OBJECT * choice_length,
512 FL_OBJECT * check_keep)
515 BOOST_ASSERT(choice_type && choice_type->objclass == FL_CHOICE &&
516 input_length && input_length->objclass == FL_INPUT &&
517 choice_length && choice_length->objclass == FL_CHOICE &&
518 check_keep && check_keep->objclass == FL_CHECKBUTTON);
520 fl_set_button(check_keep, space.keep());
523 switch (space.kind()) {
527 case VSpace::DEFSKIP:
530 case VSpace::SMALLSKIP:
533 case VSpace::MEDSKIP:
536 case VSpace::BIGSKIP:
546 fl_set_choice(choice_type, pos);
548 bool const custom_vspace = space.kind() == VSpace::LENGTH;
549 setEnabled(input_length, custom_vspace);
550 setEnabled(choice_length, custom_vspace);
552 string const length = space.length().asString();
553 updateWidgetsFromLengthString(input_length, choice_length,
554 length, defaultUnit);
556 bool const no_vspace = space.kind() == VSpace::NONE;
557 setEnabled(check_keep, !no_vspace);
558 fl_set_input(input_length, "");
559 fl_set_choice_text(choice_length, defaultUnit.c_str());