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