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
15 #pragma implementation
18 #include "ControlParagraph.h"
19 #include "FormParagraph.h"
20 #include "forms/form_paragraph.h"
23 #include "ParagraphParameters.h"
25 #include "xforms_helpers.h"
26 #include "lyxrc.h" // to set the deafult length values
27 #include "input_validators.h"
28 #include "helper_funcs.h"
29 #include "checkedwidgets.h"
32 #include "layout.h" // LyXAlignment
34 #include "support/lstrings.h"
35 #include "support/LAssert.h"
37 #include FORMS_H_LOCATION
49 string defaultUnit("cm");
51 void validateVSpaceWidgets(FL_OBJECT * choice_type, FL_OBJECT * input_length);
53 VSpace const setVSpaceFromWidgets(FL_OBJECT * choice_type,
54 FL_OBJECT * input_length,
55 FL_OBJECT * choice_length,
56 FL_OBJECT * check_keep);
58 void setWidgetsFromVSpace(VSpace const & space,
59 FL_OBJECT * choice_type,
60 FL_OBJECT * input_length,
61 FL_OBJECT * choice_length,
62 FL_OBJECT * check_keep);
67 typedef FormCB<ControlParagraph, FormDB<FD_paragraph> > base_class;
69 FormParagraph::FormParagraph()
70 : base_class(_("Paragraph Layout"))
74 void FormParagraph::build()
77 dialog_.reset(build_paragraph(this));
79 // Manage the ok, apply, restore and cancel/close buttons
80 bc().setOK(dialog_->button_ok);
81 bc().setApply(dialog_->button_apply);
82 bc().setCancel(dialog_->button_close);
83 bc().setRestore(dialog_->button_restore);
85 // disable for read-only documents
86 bc().addReadOnly(dialog_->check_line_above);
87 bc().addReadOnly(dialog_->check_pagebreak_above);
88 bc().addReadOnly(dialog_->choice_space_above);
89 bc().addReadOnly(dialog_->input_space_above);
90 bc().addReadOnly(dialog_->check_space_above);
92 bc().addReadOnly(dialog_->check_noindent);
93 bc().addReadOnly(dialog_->choice_linespacing);
94 bc().addReadOnly(dialog_->input_linespacing);
96 bc().addReadOnly(dialog_->check_line_below);
97 bc().addReadOnly(dialog_->check_pagebreak_below);
98 bc().addReadOnly(dialog_->choice_space_below);
99 bc().addReadOnly(dialog_->input_space_below);
100 bc().addReadOnly(dialog_->check_space_below);
102 bc().addReadOnly(dialog_->input_labelwidth);
104 // check validity of "length + unit" input
105 addCheckedGlueLength(bc(),
106 dialog_->input_space_above,
107 dialog_->choice_space_above);
108 addCheckedGlueLength(bc(),
109 dialog_->input_space_below,
110 dialog_->choice_space_below);
112 // trigger an input event for cut&paste with middle mouse button.
113 setPrehandler(dialog_->input_space_above);
114 setPrehandler(dialog_->input_space_below);
115 setPrehandler(dialog_->input_linespacing);
116 setPrehandler(dialog_->input_labelwidth);
118 fl_set_input_return(dialog_->input_space_above, FL_RETURN_CHANGED);
119 fl_set_input_return(dialog_->input_space_below, FL_RETURN_CHANGED);
120 fl_set_input_return(dialog_->input_labelwidth, FL_RETURN_CHANGED);
121 fl_set_input_return(dialog_->input_linespacing, FL_RETURN_CHANGED);
123 // limit these inputs to unsigned floats
124 fl_set_input_filter(dialog_->input_linespacing, fl_unsigned_float_filter);
126 // add alignment radio buttons
127 alignment_.init(dialog_->radio_align_left, LYX_ALIGN_LEFT);
128 alignment_.init(dialog_->radio_align_right, LYX_ALIGN_RIGHT);
129 alignment_.init(dialog_->radio_align_block, LYX_ALIGN_BLOCK);
130 alignment_.init(dialog_->radio_align_center, LYX_ALIGN_CENTER);
132 string const parspacing = _("None|Defskip|Smallskip|Medskip|Bigskip|VFill|Length");
133 fl_addto_choice(dialog_->choice_space_above, parspacing.c_str());
134 fl_addto_choice(dialog_->choice_space_below, parspacing.c_str());
136 string const linespacing = _("Default|Single|OneHalf|Double|Custom");
137 fl_addto_choice(dialog_->choice_linespacing, linespacing.c_str());
139 // Create the contents of the unit choices; don't include the "%" terms.
140 vector<string> units_vec = getLatexUnits();
141 vector<string>::iterator del =
142 remove_if(units_vec.begin(), units_vec.end(),
143 bind2nd(contains_functor(), "%"));
144 units_vec.erase(del, units_vec.end());
146 string const units = getStringFromVector(units_vec, "|");
147 fl_addto_choice(dialog_->choice_unit_space_above, units.c_str());
148 fl_addto_choice(dialog_->choice_unit_space_below, units.c_str());
150 // set up the tooltips
151 string str = _("Add a separator line above this paragraph.");
152 tooltips().init(dialog_->check_line_above, str);
153 str = _("Enforce a page break above this paragraph.");
154 tooltips().init(dialog_->check_pagebreak_above, str);
155 str = _("Add additional space above this paragraph.");
156 tooltips().init(dialog_->choice_space_above, str);
157 str = _("Never suppress space (e.g. at top of page or new page).");
158 tooltips().init(dialog_->check_space_above, str);
160 str = _("Add a separator line below this paragraph.");
161 tooltips().init(dialog_->check_line_below, str);
162 str = _("Enforce a page break below this paragraph.");
163 tooltips().init(dialog_->check_pagebreak_below, str);
164 str = _("Add additional space below this paragraph.");
165 tooltips().init(dialog_->choice_space_below, str);
166 str = _("Never suppress space (e.g. at bottom of page or new page).");
167 tooltips().init(dialog_->check_space_below, str);
169 // set default unit for custom length
170 switch (lyxrc.default_papersize) {
171 case BufferParams::PAPER_DEFAULT:
172 case BufferParams::PAPER_USLETTER:
173 case BufferParams::PAPER_LEGALPAPER:
174 case BufferParams::PAPER_EXECUTIVEPAPER:
177 case BufferParams::PAPER_A3PAPER:
178 case BufferParams::PAPER_A4PAPER:
179 case BufferParams::PAPER_A5PAPER:
180 case BufferParams::PAPER_B5PAPER:
187 void FormParagraph::apply()
192 // If a vspace choice is "Length" but there's no text in
193 // the input field, reset the choice to "None".
194 validateVSpaceWidgets(dialog_->choice_space_above,
195 dialog_->input_space_above);
197 VSpace const space_above =
198 setVSpaceFromWidgets(dialog_->choice_space_above,
199 dialog_->input_space_above,
200 dialog_->choice_unit_space_above,
201 dialog_->check_space_above);
203 controller().params().spaceTop(space_above);
205 validateVSpaceWidgets(dialog_->choice_space_below,
206 dialog_->input_space_below);
208 VSpace const space_below =
209 setVSpaceFromWidgets(dialog_->choice_space_below,
210 dialog_->input_space_below,
211 dialog_->choice_unit_space_below,
212 dialog_->check_space_below);
214 controller().params().spaceBottom(space_below);
216 // lines and pagebreaks
217 bool const line_above = fl_get_button(dialog_->check_line_above);
218 controller().params().lineTop(line_above);
220 bool const line_below = fl_get_button(dialog_->check_line_below);
221 controller().params().lineBottom(line_below);
223 bool const pagebreak_above =
224 fl_get_button(dialog_->check_pagebreak_above);
225 controller().params().pagebreakTop(pagebreak_above);
227 bool const pagebreak_below =
228 fl_get_button(dialog_->check_pagebreak_below);
229 controller().params().pagebreakBottom(pagebreak_below);
233 LyXAlignment const alignment =
234 static_cast<LyXAlignment>(alignment_.get());
235 controller().params().align(alignment);
238 string const labelwidthstring =
239 getString(dialog_->input_labelwidth);
240 controller().params().labelWidthString(labelwidthstring);
243 bool const noindent = fl_get_button(dialog_->check_noindent);
244 controller().params().noindent(noindent);
247 Spacing::Space linespacing = Spacing::Default;
249 switch (fl_get_choice(dialog_->choice_linespacing)) {
251 linespacing = Spacing::Default;
254 linespacing = Spacing::Single;
257 linespacing = Spacing::Onehalf;
260 linespacing = Spacing::Double;
263 // reset to default if input is empty
264 other = getString(dialog_->input_linespacing);
265 if (!other.empty()) {
266 linespacing = Spacing::Other;
268 linespacing = Spacing::Default;
269 fl_set_choice(dialog_->choice_linespacing, 1);
273 Spacing const spacing(linespacing, other);
274 controller().params().spacing(spacing);
278 void FormParagraph::update()
284 string const labelwidth = controller().params().labelWidthString();
285 fl_set_input(dialog_->input_labelwidth, labelwidth.c_str());
286 setEnabled(dialog_->input_labelwidth,
287 labelwidth != _("Senseless with this layout!"));
290 alignment_.set(controller().params().align());
292 // mark default alignment
293 LyXAlignment const default_alignment = controller().alignDefault();
295 string label = _("Block");
296 if (default_alignment == LYX_ALIGN_BLOCK) {
297 label += _(" (default)");
299 fl_set_object_label(dialog_->radio_align_block, label.c_str());
300 fl_set_button_shortcut(dialog_->radio_align_block, "#B", 1);
303 if (default_alignment == LYX_ALIGN_CENTER) {
304 label += _(" (default)");
306 fl_set_object_label(dialog_->radio_align_center, label.c_str());
307 fl_set_button_shortcut(dialog_->radio_align_center, "#C", 1);
310 if (default_alignment == LYX_ALIGN_LEFT) {
311 label += _(" (default)");
313 fl_set_object_label(dialog_->radio_align_left, label.c_str());
314 fl_set_button_shortcut(dialog_->radio_align_left, "#L", 1);
317 if (default_alignment == LYX_ALIGN_RIGHT) {
318 label = _(" (default)");
320 fl_set_object_label(dialog_->radio_align_right, label.c_str());
321 fl_set_button_shortcut(dialog_->radio_align_right, "#R", 1);
323 // Ensure that there's no crud left on the screen from this change
325 fl_redraw_form(form());
327 LyXAlignment alignpos = controller().alignPossible();
328 setEnabled(dialog_->radio_align_block,
329 bool(alignpos & LYX_ALIGN_BLOCK));
330 setEnabled(dialog_->radio_align_center,
331 bool(alignpos & LYX_ALIGN_CENTER));
332 setEnabled(dialog_->radio_align_left,
333 bool(alignpos & LYX_ALIGN_LEFT));
334 setEnabled(dialog_->radio_align_right,
335 bool(alignpos & LYX_ALIGN_RIGHT));
337 // no inset-text-owned paragraph may have pagebreaks
338 bool ininset = controller().inInset();
339 setEnabled(dialog_->check_pagebreak_above, !ininset);
340 setEnabled(dialog_->check_pagebreak_below, !ininset);
342 // lines, pagebreaks and indent
343 fl_set_button(dialog_->check_line_above,
344 controller().params().lineTop());
345 fl_set_button(dialog_->check_line_below,
346 controller().params().lineBottom());
347 fl_set_button(dialog_->check_pagebreak_above,
348 controller().params().pagebreakTop());
349 fl_set_button(dialog_->check_pagebreak_below,
350 controller().params().pagebreakBottom());
351 fl_set_button(dialog_->check_noindent,
352 controller().params().noindent());
355 Spacing const space = controller().params().spacing();
358 switch (space.getSpace()) {
362 case Spacing::Double:
365 case Spacing::Onehalf:
368 case Spacing::Single:
371 case Spacing::Default:
376 fl_set_choice(dialog_->choice_linespacing, pos);
378 bool const spacing_other = space.getSpace() == Spacing::Other;
379 setEnabled(dialog_->input_linespacing, spacing_other);
381 string const sp = tostr(space.getValue());
382 fl_set_input(dialog_->input_linespacing, sp.c_str());
384 fl_set_input(dialog_->input_linespacing, "");
388 setWidgetsFromVSpace(controller().params().spaceTop(),
389 dialog_->choice_space_above,
390 dialog_->input_space_above,
391 dialog_->choice_unit_space_above,
392 dialog_->check_space_above);
395 setWidgetsFromVSpace(controller().params().spaceBottom(),
396 dialog_->choice_space_below,
397 dialog_->input_space_below,
398 dialog_->choice_unit_space_below,
399 dialog_->check_space_below);
402 fl_set_button(dialog_->check_noindent,
403 controller().params().noindent());
407 ButtonPolicy::SMInput FormParagraph::input(FL_OBJECT * ob, long)
409 // Enable input when custum length is choosen,
410 // disable 'keep' when no space is choosen
411 if (ob == dialog_->choice_space_above) {
412 bool const custom_length =
413 fl_get_choice(dialog_->choice_space_above) == 7;
414 setEnabled(dialog_->input_space_above, custom_length);
415 setEnabled(dialog_->choice_unit_space_above, custom_length);
418 fl_get_choice(dialog_->choice_space_above) != 1;
419 setEnabled(dialog_->check_space_above, space);
421 } else if (ob == dialog_->choice_space_below) {
422 bool const custom_length =
423 fl_get_choice(dialog_->choice_space_below) == 7;
424 setEnabled(dialog_->input_space_below, custom_length);
425 setEnabled(dialog_->choice_unit_space_below, custom_length);
428 fl_get_choice(dialog_->choice_space_below) != 1;
429 setEnabled(dialog_->check_space_below, space);
431 } else if (ob == dialog_->choice_linespacing) {
432 bool const custom_spacing =
433 fl_get_choice(dialog_->choice_linespacing) == 5;
434 setEnabled(dialog_->input_linespacing, custom_spacing);
437 return ButtonPolicy::SMI_VALID;
443 void validateVSpaceWidgets(FL_OBJECT * choice_type, FL_OBJECT * input_length)
446 lyx::Assert(choice_type && choice_type->objclass == FL_CHOICE &&
447 input_length && input_length->objclass == FL_INPUT);
449 if (fl_get_choice(choice_type) != 7)
452 // If a vspace kind is "Length" but there's no text in
453 // the input field, reset the kind to "None".
454 string const input = rtrim(getString(input_length));
456 fl_set_choice(choice_type, 1);
460 VSpace const setVSpaceFromWidgets(FL_OBJECT * choice_type,
461 FL_OBJECT * input_length,
462 FL_OBJECT * choice_length,
463 FL_OBJECT * check_keep)
466 lyx::Assert(choice_type && choice_type->objclass == FL_CHOICE &&
467 input_length && input_length->objclass == FL_INPUT &&
468 choice_length && choice_length->objclass == FL_CHOICE &&
469 check_keep && check_keep->objclass == FL_CHECKBUTTON);
472 switch (fl_get_choice(choice_type)) {
474 space = VSpace(VSpace::NONE);
477 space = VSpace(VSpace::DEFSKIP);
480 space = VSpace(VSpace::SMALLSKIP);
483 space = VSpace(VSpace::MEDSKIP);
486 space = VSpace(VSpace::BIGSKIP);
489 space = VSpace(VSpace::VFILL);
493 string const length =
494 getLengthFromWidgets(input_length, choice_length);
495 space = VSpace(LyXGlueLength(length));
500 if (fl_get_button(check_keep))
507 void setWidgetsFromVSpace(VSpace const & space,
508 FL_OBJECT * choice_type,
509 FL_OBJECT * input_length,
510 FL_OBJECT * choice_length,
511 FL_OBJECT * check_keep)
514 lyx::Assert(choice_type && choice_type->objclass == FL_CHOICE &&
515 input_length && input_length->objclass == FL_INPUT &&
516 choice_length && choice_length->objclass == FL_CHOICE &&
517 check_keep && check_keep->objclass == FL_CHECKBUTTON);
519 fl_set_button(check_keep, space.keep());
522 switch (space.kind()) {
526 case VSpace::DEFSKIP:
529 case VSpace::SMALLSKIP:
532 case VSpace::MEDSKIP:
535 case VSpace::BIGSKIP:
545 fl_set_choice(choice_type, pos);
547 bool const custom_vspace = space.kind() == VSpace::LENGTH;
548 setEnabled(input_length, custom_vspace);
549 setEnabled(choice_length, custom_vspace);
551 string const length = space.length().asString();
552 updateWidgetsFromLengthString(input_length, choice_length,
553 length, defaultUnit);
555 bool const no_vspace = space.kind() == VSpace::NONE;
556 setEnabled(check_keep, !no_vspace);
557 fl_set_input(input_length, "");
558 fl_set_choice_text(choice_length, defaultUnit.c_str());