]> git.lyx.org Git - lyx.git/blob - src/frontends/xforms/FormParagraph.C
Fix browse
[lyx.git] / src / frontends / xforms / FormParagraph.C
1 /**
2  * \file FormParagraph.C
3  * Copyright 2000-2001 The LyX Team.
4  * See the file COPYING.
5  *
6  * \author Jürgen Vigna, jug@sad.it
7  */
8
9 #include <config.h>
10
11 #ifdef __GNUG_
12 #pragma implementation
13 #endif
14
15 #include FORMS_H_LOCATION
16
17 #include "FormParagraph.h"
18 #include "form_paragraph.h"
19 #include "Dialogs.h"
20 #include "Liason.h"
21 #include "LyXView.h"
22 #include "buffer.h"
23 #include "lyxtext.h"
24 #include "xforms_helpers.h"
25 #include "lyxrc.h" // to set the deafult length values
26 #include "BufferView.h"
27 #include "lyxtextclasslist.h"
28 #include "Spacing.h"
29 #include "ParagraphParameters.h"
30 #include "input_validators.h"
31 #include "helper_funcs.h"
32
33 #include "support/lstrings.h"
34 #include "support/LAssert.h"
35
36 #include <functional>
37
38 using Liason::setMinibuffer;
39 using SigC::slot;
40 using std::vector;
41 using std::bind2nd;
42 using std::remove_if;
43
44
45 FormParagraph::FormParagraph(LyXView * lv, Dialogs * d)
46         : FormBaseBD(lv, d, _("Paragraph Layout")), par_(0)
47 {
48         // let the dialog be shown
49         // This is a permanent connection so we won't bother
50         // storing a copy because we won't be disconnecting.
51         d->showParagraph.connect(slot(this, &FormParagraph::show));
52 }
53
54
55 void FormParagraph::connect()
56 {
57         cp_ = d_->updateParagraph
58                 .connect(slot(this, &FormParagraph::changedParagraph));
59         FormBaseBD::connect();
60 }
61
62
63 void FormParagraph::disconnect()
64 {
65         cp_.disconnect();
66         FormBaseBD::disconnect();
67 }
68
69
70 Paragraph const * FormParagraph::getCurrentParagraph() const
71 {
72         return lv_->view()->getLyXText()->cursor.par();
73 }
74
75
76 void FormParagraph::changedParagraph()
77 {
78         /// Record the paragraph
79         Paragraph const * const p = getCurrentParagraph();
80         if (p == 0 || p == par_)
81                 return;
82
83         // For now, don't bother checking if the params are different.
84
85         // Will the underlying paragraph accept our changes?
86         Inset * const inset = p->inInset();
87         bool const accept = !(inset && inset->forceDefaultParagraphs(inset));
88         bc().valid(accept);
89
90         if (!accept) {
91                 postWarning(_("Cannot apply paragraph settings to this inset!"));
92         }
93 }
94
95
96 void FormParagraph::redraw()
97 {
98         if (form() && form()->visible)
99                 fl_redraw_form(form());
100 }
101
102
103 FL_FORM * FormParagraph::form() const
104 {
105         if (dialog_.get())
106                 return dialog_->form;
107         return 0;
108 }
109
110
111 void FormParagraph::build()
112 {
113         // the tabbed folder
114         dialog_.reset(build_paragraph());
115
116         // Allow the base class to control messages
117         setMessageWidget(dialog_->text_warning);
118
119         fl_addto_choice(dialog_->choice_space_above,
120                         _(" None | Defskip | Smallskip "
121                           "| Medskip | Bigskip | VFill | Length "));
122         fl_addto_choice(dialog_->choice_space_below,
123                         _(" None | Defskip | Smallskip "
124                           "| Medskip | Bigskip | VFill | Length "));
125
126         fl_addto_choice(dialog_->choice_linespacing,
127                         _(" Default | Single | OneHalf | Double | Other "));
128
129         fl_set_input_return(dialog_->input_space_above, FL_RETURN_CHANGED);
130         fl_set_input_return(dialog_->input_space_below, FL_RETURN_CHANGED);
131         fl_set_input_return(dialog_->input_labelwidth,  FL_RETURN_CHANGED);
132         fl_set_input_return(dialog_->input_linespacing, FL_RETURN_CHANGED);
133         fl_set_input_filter(dialog_->input_linespacing, fl_unsigned_float_filter);
134
135         setPrehandler(dialog_->input_space_above);
136         setPrehandler(dialog_->input_space_below);
137         setPrehandler(dialog_->input_labelwidth);
138         setPrehandler(dialog_->input_linespacing);
139
140         // Create the contents of the unit choices
141         // Don't include the "%" terms...
142         vector<string> units_vec = getLatexUnits();
143 #if 0
144         for (vector<string>::iterator it = units_vec.begin();
145              it != units_vec.end(); ++it) {
146                 if (contains(*it, "%"))
147                         it = units_vec.erase(it, it+1) - 1;
148         }
149 #else
150         // Something similar to this is a better way to erase
151         vector<string>::iterator del =
152                 remove_if(units_vec.begin(), units_vec.end(),
153                           bind2nd(contains_functor(), "%"));
154         units_vec.erase(del, units_vec.end());
155 #endif
156
157         string units = getStringFromVector(units_vec, "|");
158
159         fl_addto_choice(dialog_->choice_value_space_above, units.c_str());
160         fl_addto_choice(dialog_->choice_value_space_below, units.c_str());
161
162         // Manage the ok, apply, restore and cancel/close buttons
163         bc_.setOK(dialog_->button_ok);
164         bc_.setApply(dialog_->button_apply);
165         bc_.setCancel(dialog_->button_close);
166         bc_.setRestore(dialog_->button_restore);
167
168         bc_.addReadOnly(dialog_->radio_align_right);
169         bc_.addReadOnly(dialog_->radio_align_left);
170         bc_.addReadOnly(dialog_->radio_align_block);
171         bc_.addReadOnly(dialog_->radio_align_center);
172         bc_.addReadOnly(dialog_->check_lines_top);
173         bc_.addReadOnly(dialog_->check_lines_bottom);
174         bc_.addReadOnly(dialog_->check_pagebreaks_top);
175         bc_.addReadOnly(dialog_->check_pagebreaks_bottom);
176         bc_.addReadOnly(dialog_->choice_space_above);
177         bc_.addReadOnly(dialog_->input_space_above);
178         bc_.addReadOnly(dialog_->check_space_above);
179         bc_.addReadOnly(dialog_->choice_space_below);
180         bc_.addReadOnly(dialog_->input_space_below);
181         bc_.addReadOnly(dialog_->check_space_below);
182         bc_.addReadOnly(dialog_->choice_linespacing);
183         bc_.addReadOnly(dialog_->input_linespacing);
184         bc_.addReadOnly(dialog_->check_noindent);
185         bc_.addReadOnly(dialog_->input_labelwidth);
186 }
187
188
189 namespace {
190
191 VSpace setVSpaceFromWidgets(FL_OBJECT * choice_type,
192                             FL_OBJECT * input_length,
193                             FL_OBJECT * choice_length,
194                             FL_OBJECT * check_keep)
195 {
196         // Paranoia check!
197         lyx::Assert(choice_type   && choice_type->objclass   == FL_CHOICE &&
198                     input_length  && input_length->objclass  == FL_INPUT &&
199                     choice_length && choice_length->objclass == FL_CHOICE &&
200                     check_keep    && check_keep->objclass    == FL_CHECKBUTTON);
201
202         VSpace space;
203
204         switch (fl_get_choice(choice_type)) {
205         case 1:
206                 space = VSpace(VSpace::NONE);
207                 break;
208         case 2:
209                 space = VSpace(VSpace::DEFSKIP);
210                 break;
211         case 3:
212                 space = VSpace(VSpace::SMALLSKIP);
213                 break;
214         case 4:
215                 space = VSpace(VSpace::MEDSKIP);
216                 break;
217         case 5:
218                 space = VSpace(VSpace::BIGSKIP);
219                 break;
220         case 6:
221                 space = VSpace(VSpace::VFILL);
222                 break;
223         case 7:
224         {
225                 string const length =
226                         getLengthFromWidgets(input_length, choice_length);
227                 space = VSpace(LyXGlueLength(length));
228                 break;
229         }
230         }
231
232         if (fl_get_button(check_keep))
233                 space.setKeep(true);
234
235         return space;
236 }
237
238
239 void validateVSpaceWidgets(FL_OBJECT * choice_type, FL_OBJECT * input_length)
240 {
241         // Paranoia check!
242         lyx::Assert(choice_type  && choice_type->objclass   == FL_CHOICE &&
243                     input_length && input_length->objclass  == FL_INPUT);
244
245         if (fl_get_choice(choice_type) != 7)
246                 return;
247
248         // If a vspace kind is "Length" but there's no text in
249         // the input field, reset the kind to "None".
250         string const input = strip(getStringFromInput(input_length));
251         if (input.empty())
252                 fl_set_choice(choice_type, 1);
253 }
254
255 } // namespace anon
256
257
258 void FormParagraph::apply()
259 {
260         if (!lv_->view()->available() || !dialog_.get())
261                 return;
262
263         // If a vspace kind is "Length" but there's no text in
264         // the input field, reset the kind to "None".
265         validateVSpaceWidgets(dialog_->choice_space_above,
266                               dialog_->input_space_above);
267         validateVSpaceWidgets(dialog_->choice_space_below,
268                               dialog_->input_space_below);
269
270         bool const line_top = fl_get_button(dialog_->check_lines_top);
271         bool const line_bottom = fl_get_button(dialog_->check_lines_bottom);
272         bool const pagebreak_top = fl_get_button(dialog_->check_pagebreaks_top);
273         bool const pagebreak_bottom = fl_get_button(dialog_->check_pagebreaks_bottom);
274
275         VSpace const space_top =
276                 setVSpaceFromWidgets(dialog_->choice_space_above,
277                                      dialog_->input_space_above,
278                                      dialog_->choice_value_space_above,
279                                      dialog_->check_space_above);
280
281         VSpace const space_bottom =
282                 setVSpaceFromWidgets(dialog_->choice_space_below,
283                                      dialog_->input_space_below,
284                                      dialog_->choice_value_space_below,
285                                      dialog_->check_space_below);
286
287         LyXAlignment align;
288         if (fl_get_button(dialog_->radio_align_left))
289                 align = LYX_ALIGN_LEFT;
290         else if (fl_get_button(dialog_->radio_align_right))
291                 align = LYX_ALIGN_RIGHT;
292         else if (fl_get_button(dialog_->radio_align_center))
293                 align = LYX_ALIGN_CENTER;
294         else
295                 align = LYX_ALIGN_BLOCK;
296
297         string const labelwidthstring =
298                 getStringFromInput(dialog_->input_labelwidth);
299
300         bool const noindent = fl_get_button(dialog_->check_noindent);
301
302         Spacing::Space linespacing = Spacing::Default;
303         string other;
304         switch (fl_get_choice(dialog_->choice_linespacing)) {
305         case 1:
306                 linespacing = Spacing::Default;
307                 break;
308         case 2:
309                 linespacing = Spacing::Single;
310                 break;
311         case 3:
312                 linespacing = Spacing::Onehalf;
313                 break;
314         case 4:
315                 linespacing = Spacing::Double;
316                 break;
317         case 5:
318                 linespacing = Spacing::Other;
319                 other = getStringFromInput(dialog_->input_linespacing);
320                 break;
321         }
322
323         Spacing const spacing(linespacing, other);
324
325         LyXText * text(lv_->view()->getLyXText());
326         text->setParagraph(lv_->view(), line_top, line_bottom, pagebreak_top,
327                            pagebreak_bottom, space_top, space_bottom, spacing,
328                            align, labelwidthstring, noindent);
329
330         // Actually apply these settings
331         lv_->view()->update(text,
332                             BufferView::SELECT | BufferView::FITCUR | BufferView::CHANGE);
333         lv_->buffer()->markDirty();
334         setMinibuffer(lv_, _("Paragraph layout set"));
335 }
336
337
338 namespace {
339
340 void setWidgetsFromVSpace(VSpace const & space,
341                           FL_OBJECT * choice_type,
342                           FL_OBJECT * input_length,
343                           FL_OBJECT * choice_length,
344                           FL_OBJECT * check_keep)
345 {
346         // Paranoia check!
347         lyx::Assert(choice_type   && choice_type->objclass   == FL_CHOICE &&
348                     input_length  && input_length->objclass  == FL_INPUT &&
349                     choice_length && choice_length->objclass == FL_CHOICE &&
350                     check_keep    && check_keep->objclass    == FL_CHECKBUTTON);
351
352         fl_set_input(input_length, "");
353         setEnabled(input_length,  false);
354         setEnabled(choice_length, false);
355
356         switch (space.kind()) {
357         case VSpace::NONE:
358                 fl_set_choice(choice_type, 1);
359                 break;
360         case VSpace::DEFSKIP:
361                 fl_set_choice(choice_type, 2);
362                 break;
363         case VSpace::SMALLSKIP:
364                 fl_set_choice(choice_type, 3);
365                 break;
366         case VSpace::MEDSKIP:
367                 fl_set_choice(choice_type, 4);
368                 break;
369         case VSpace::BIGSKIP:
370                 fl_set_choice(choice_type, 5);
371                 break;
372         case VSpace::VFILL:
373                 fl_set_choice(choice_type, 6);
374                 break;
375         case VSpace::LENGTH:
376         {
377                 fl_set_choice(choice_type, 7);
378
379                 setEnabled(input_length,  true);
380                 setEnabled(choice_length, true);
381
382                 bool const metric = lyxrc.default_papersize > 3;
383                 string const default_unit = metric ? "cm" : "in";
384                 string const length = space.length().asString();
385
386                 updateWidgetsFromLengthString(input_length, choice_length,
387                                               length, default_unit);
388                 break;
389         }
390         }
391
392         fl_set_button(check_keep, space.keep());
393 }
394
395 } // namespace anon
396
397
398 void FormParagraph::update()
399 {
400         if (!dialog_.get())
401                 return;
402
403         // Do this first; some objects may be de/activated subsequently.
404         bc_.readOnly(lv_->buffer()->isReadonly());
405
406         /// Record the paragraph
407         par_ = getCurrentParagraph();
408
409         fl_set_input(dialog_->input_labelwidth,
410                      par_->getLabelWidthString().c_str());
411         setEnabled(dialog_->input_labelwidth,
412                    (par_->getLabelWidthString() != _("Senseless with this layout!")));
413
414         fl_set_button(dialog_->radio_align_right, 0);
415         fl_set_button(dialog_->radio_align_left, 0);
416         fl_set_button(dialog_->radio_align_center, 0);
417         fl_set_button(dialog_->radio_align_block, 0);
418
419         LyXTextClass const & tclass =
420                 textclasslist[lv_->view()->buffer()->params.textclass];
421
422         int align = par_->getAlign();
423         if (align == LYX_ALIGN_LAYOUT)
424                 align = tclass[par_->layout()].align;
425
426         switch (align) {
427         case LYX_ALIGN_RIGHT:
428                 fl_set_button(dialog_->radio_align_right, 1);
429                 break;
430         case LYX_ALIGN_LEFT:
431                 fl_set_button(dialog_->radio_align_left, 1);
432                 break;
433         case LYX_ALIGN_CENTER:
434                 fl_set_button(dialog_->radio_align_center, 1);
435                 break;
436         default:
437                 fl_set_button(dialog_->radio_align_block, 1);
438                 break;
439         }
440
441         LyXAlignment alignpos = tclass[par_->layout()].alignpossible;
442
443         setEnabled(dialog_->radio_align_block,  bool(alignpos & LYX_ALIGN_BLOCK));
444         setEnabled(dialog_->radio_align_center, bool(alignpos & LYX_ALIGN_CENTER));
445         setEnabled(dialog_->radio_align_left,   bool(alignpos & LYX_ALIGN_LEFT));
446         setEnabled(dialog_->radio_align_right,  bool(alignpos & LYX_ALIGN_RIGHT));
447
448         // no inset-text-owned paragraph may have pagebreaks
449         setEnabled(dialog_->check_pagebreaks_top, !par_->inInset());
450         setEnabled(dialog_->check_pagebreaks_bottom, !par_->inInset());
451
452         fl_set_button(dialog_->check_lines_top,
453                       par_->params().lineTop());
454         fl_set_button(dialog_->check_lines_bottom,
455                       par_->params().lineBottom());
456         fl_set_button(dialog_->check_pagebreaks_top,
457                       par_->params().pagebreakTop());
458         fl_set_button(dialog_->check_pagebreaks_bottom,
459                       par_->params().pagebreakBottom());
460         fl_set_button(dialog_->check_noindent,
461                       par_->params().noindent());
462
463         int linespacing;
464         Spacing const space = par_->params().spacing();
465
466         switch (space.getSpace()) {
467         default: linespacing = 1; break;
468         case Spacing::Single: linespacing = 2; break;
469         case Spacing::Onehalf: linespacing = 3; break;
470         case Spacing::Double: linespacing = 4; break;
471         case Spacing::Other: linespacing = 5; break;
472         }
473
474         fl_set_choice(dialog_->choice_linespacing, linespacing);
475         if (space.getSpace() == Spacing::Other) {
476                 string const sp = tostr(space.getValue());
477                 fl_set_input(dialog_->input_linespacing, sp.c_str());
478                 setEnabled(dialog_->input_linespacing, true);
479         } else {
480                 fl_set_input(dialog_->input_linespacing, "");
481                 setEnabled(dialog_->input_linespacing, false);
482         }
483
484         setWidgetsFromVSpace(par_->params().spaceTop(),
485                              dialog_->choice_space_above,
486                              dialog_->input_space_above,
487                              dialog_->choice_value_space_above,
488                              dialog_->check_space_above);
489
490         setWidgetsFromVSpace(par_->params().spaceBottom(),
491                              dialog_->choice_space_below,
492                              dialog_->input_space_below,
493                              dialog_->choice_value_space_below,
494                              dialog_->check_space_below);
495
496         fl_set_button(dialog_->check_noindent,
497                       par_->params().noindent());
498 }
499
500
501 namespace {
502
503 void synchronizeSpaceWidgets(FL_OBJECT * choice_type,
504                              FL_OBJECT * input_length,
505                              FL_OBJECT * choice_length,
506                              bool readonly)
507 {
508         // Paranoia check!
509         lyx::Assert(choice_type   && choice_type->objclass   == FL_CHOICE &&
510                     input_length  && input_length->objclass  == FL_INPUT &&
511                     choice_length && choice_length->objclass == FL_CHOICE);
512
513         if (fl_get_choice(choice_type) != 7) {
514                 fl_set_input(input_length, "");
515                 setEnabled(input_length, false);
516                 setEnabled(choice_length, false);
517
518         } else {
519                 setEnabled(input_length,  !readonly);
520                 setEnabled(choice_length, !readonly);
521
522                 string const length = getStringFromInput(input_length);
523
524                 if (strip(length).empty()) {
525                         bool const metric = lyxrc.default_papersize > 3;
526                         int const default_unit = metric ? 8 : 9;
527
528                         fl_set_choice(choice_length, default_unit);
529                 }
530         }
531 }
532
533 bool validSpaceWidgets(FL_OBJECT * choice_type, FL_OBJECT * input_length)
534 {
535         // Paranoia check!
536         lyx::Assert(choice_type  && choice_type->objclass   == FL_CHOICE &&
537                     input_length && input_length->objclass  == FL_INPUT);
538
539         if (fl_get_choice(choice_type) != 7)
540                 return true;
541
542         string const input = getStringFromInput(input_length);
543         return (input.empty() ||
544                 isValidGlueLength(input) ||
545                 isStrDbl(input));
546 }
547
548 } // namespace anon
549
550
551 bool FormParagraph::input(FL_OBJECT * ob, long)
552 {
553         clearMessage();
554
555         // First check the buttons which are exclusive and you have to
556         // check only the actuall de/activated button.
557         //
558         // "Synchronize" the choices and input fields, making it
559         // impossible to commit senseless data.
560
561         if (ob == dialog_->choice_space_above) {
562                 synchronizeSpaceWidgets(dialog_->choice_space_above,
563                                         dialog_->input_space_above,
564                                         dialog_->choice_value_space_above,
565                                         lv_->buffer()->isReadonly());
566         }
567
568         if (ob == dialog_->choice_space_below) {
569                 synchronizeSpaceWidgets(dialog_->choice_space_below,
570                                         dialog_->input_space_below,
571                                         dialog_->choice_value_space_below,
572                                         lv_->buffer()->isReadonly());
573         }
574
575         // Display a warning if the input is senseless
576         bool valid = (validSpaceWidgets(dialog_->choice_space_above,
577                                         dialog_->input_space_above) &&
578                       validSpaceWidgets(dialog_->choice_space_below,
579                                         dialog_->input_space_below));
580
581         if (!valid) {
582                 postWarning(_("Invalid Length (valid example: 10mm)"));
583         }
584
585         int const choice_spacing = fl_get_choice(dialog_->choice_linespacing);
586
587         if (choice_spacing == 5)
588                 setEnabled(dialog_->input_linespacing, true);
589         else {
590                 fl_set_input(dialog_->input_linespacing, "");
591                 setEnabled(dialog_->input_linespacing, false);
592         }
593
594         double const spacing =
595                 strToDbl(getStringFromInput(dialog_->input_linespacing));
596
597         if (choice_spacing == 5 && int(spacing) == 0)
598                 valid = false;
599
600         return valid;
601 }