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