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