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