]> git.lyx.org Git - lyx.git/blob - src/frontends/xforms/FormGraphics.C
Small fix from Angus
[lyx.git] / src / frontends / xforms / FormGraphics.C
1 /* FormGraphics.C
2  * FormGraphics Interface Class Implementation
3  */
4
5 /* TODO:
6  *      * Handle the case when the buffer is read-only.
7  *          Initial work is done, if we are read-only the ok/cancel are 
8  *          disabled. Probably we need to find a better way to deal with it.
9  *      
10  */
11
12 #include <config.h> 
13
14 #ifdef __GNUG__
15 #pragma implementation
16 #endif 
17
18 #include "lyx_gui_misc.h"
19 #include "gettext.h"
20 #include FORMS_H_LOCATION
21
22 #include "xform_macros.h"
23 #include "input_validators.h"
24 #include "FormGraphics.h"
25 #include "form_graphics.h"
26 #include "Dialogs.h"
27 #include "LyXView.h"
28 #include "BufferView.h"
29
30 #include "debug.h" // for lyxerr
31
32 #include "support/lstrings.h" // for strToDbl & tostr
33 #include "support/FileInfo.h" // for FileInfo
34 #include "filedlg.h" // for LyXFileDlg
35 #include "support/filetools.h" // for AddName
36 #include "insets/insetgraphics.h"
37 #include "insets/insetgraphicsParams.h"
38
39 #include "RadioButtonGroup.h"
40
41 #include "support/LAssert.h"
42
43 using std::endl;
44
45 C_RETURNCB(FormGraphics, WMHideCB)
46 C_GENERICCB(FormGraphics, OKCB)
47 C_GENERICCB(FormGraphics, ApplyCB)
48 C_GENERICCB(FormGraphics, CancelCB)
49 C_GENERICCB(FormGraphics, BrowseCB)
50 C_GENERICCB(FormGraphics, AdvancedOptionsCB)
51 C_GENERICCB(FormGraphics, InputCB)
52
53
54 FormGraphics::FormGraphics(LyXView * lv, Dialogs * d)
55                 : dialog_(0), lv_(lv), d_(d), inset_(0),
56                 // The buttons c-tor values are the number of buttons we use
57                 // This is only to reduce memory waste.
58                 widthButtons(5), heightButtons(4), displayButtons(4),
59                 bc_(new ButtonController
60                         (new NoRepeatedApplyReadOnlyPolicy, _("Cancel"), _("Close") )
61                 ),
62                 ih_(0), h_(0), u_(0),
63                 last_image_path(".")
64 {
65         Assert(lv_ != 0);
66         Assert(d   != 0);
67         Assert(bc_ != 0);
68         
69         // let the dialog be shown
70         // This is a permanent connection so we won't bother
71         // storing a copy because we won't be disconnecting.
72         d->showGraphics.connect(slot(this, &FormGraphics::showDialog));
73 }
74
75
76 FormGraphics::~FormGraphics()
77 {
78         free();
79         
80         // Free the button controller.
81         delete bc_;
82 }
83
84
85 void FormGraphics::build()
86 {
87         dialog_ = build_graphics();
88         Assert(dialog_ != 0);
89         if (!dialog_) {
90                 lyxerr << "ERROR: Failed to create the Graphics Inset dialog." << endl;
91                 return ;
92         }
93
94         // This is the place to add settings of the dialog that did not go
95         // to the .fd file.
96
97         // Set the input widgets to issue a callback to input() whenever
98         // they change, so we can verify their content.
99         fl_set_input_return (dialog_->input_width,
100                               FL_RETURN_CHANGED);
101         fl_set_input_return (dialog_->input_height,
102                               FL_RETURN_CHANGED);
103         fl_set_input_return (dialog_->input_filename,
104                               FL_RETURN_CHANGED);
105         //    fl_set_input_return(dialog_->input_rotate_angle,
106         //            FL_RETURN_CHANGED);
107
108         // Set the maximum characters that can be written in the input texts.
109         fl_set_input_maxchars(dialog_->input_width, WIDTH_MAXDIGITS);
110         fl_set_input_maxchars(dialog_->input_height, HEIGHT_MAXDIGITS);
111         fl_set_input_maxchars(dialog_->input_filename, FILENAME_MAXCHARS);
112         fl_set_input_maxchars(dialog_->input_rotate_angle, ROTATE_MAXCHARS);
113
114         // Set input filter on width and height to make them accept only
115         // unsigned numbers.
116         fl_set_input_filter(dialog_->input_width,
117                             fl_unsigned_int_filter);
118         fl_set_input_filter(dialog_->input_height,
119                             fl_unsigned_int_filter);
120
121
122         // Add the widgets of the width radio buttons to their group
123         widthButtons.reset();
124         widthButtons.registerRadioButton(dialog_->radio_width_default,
125                                          InsetGraphicsParams::DEFAULT_SIZE);
126         widthButtons.registerRadioButton(dialog_->radio_width_cm,
127                                          InsetGraphicsParams::CM);
128         widthButtons.registerRadioButton(dialog_->radio_width_inch,
129                                          InsetGraphicsParams::INCH);
130         widthButtons.registerRadioButton(dialog_->radio_width_percent_page,
131                                          InsetGraphicsParams::PERCENT_PAGE);
132         widthButtons.registerRadioButton(dialog_->radio_width_percent_column,
133                                          InsetGraphicsParams::PERCENT_COLUMN);
134
135         // Add the widgets of the height radio buttons to their group
136         heightButtons.reset();
137         heightButtons.registerRadioButton(dialog_->radio_height_default,
138                                           InsetGraphicsParams::DEFAULT_SIZE);
139         heightButtons.registerRadioButton(dialog_->radio_height_cm,
140                                           InsetGraphicsParams::CM);
141         heightButtons.registerRadioButton(dialog_->radio_height_inch,
142                                           InsetGraphicsParams::INCH);
143         heightButtons.registerRadioButton(dialog_->radio_height_percent_page,
144                                           InsetGraphicsParams::PERCENT_PAGE);
145
146         // Add the widgets of the display radio buttons to their group
147         displayButtons.reset();
148         displayButtons.registerRadioButton(dialog_->radio_display_color,
149                                            InsetGraphicsParams::COLOR);
150         displayButtons.registerRadioButton(dialog_->radio_display_grayscale,
151                                            InsetGraphicsParams::GRAYSCALE);
152         displayButtons.registerRadioButton(dialog_->radio_display_monochrome,
153                                            InsetGraphicsParams::MONOCHROME);
154         displayButtons.registerRadioButton(dialog_->radio_no_display,
155                                            InsetGraphicsParams::NONE);
156
157         // Connect a signal to hide the window when the window manager orders it.
158         fl_set_form_atclose(dialog_->form,
159                             C_FormGraphicsWMHideCB, 0);
160
161         bc_->setOK(dialog_->button_ok);
162         bc_->setApply(dialog_->button_apply);
163         bc_->setCancel(dialog_->button_cancel);
164         bc_->setUndoAll(0);
165         bc_->refresh();
166 }
167
168 void FormGraphics::show()
169 {
170         // If the dialog doesn't exist yet, build it.
171         if (!dialog_) {
172                 build();
173         }
174
175         // Update the form with the data from the inset.
176         update();
177
178         // If the form is visible
179         if (dialog_->form->visible) {
180                 // Raise it.
181                 fl_raise_form(dialog_->form);
182         } else {
183                 // Otherwise (invisible), show it.
184                 fl_show_form(dialog_->form,
185                              FL_PLACE_MOUSE,
186                              FL_TRANSIENT,
187                              _("Graphics"));
188
189                 // And connect the signals 'updateBufferDependent',
190                 // 'hideBufferDependent' and 'hideGraphics'.
191                 u_ = d_->updateBufferDependent.connect(slot(this,
192                                                        &FormGraphics::update));
193                 h_ = d_->hideBufferDependent.connect(slot(this,
194                                                      &FormGraphics::hide));
195         }
196 }
197
198 void FormGraphics::showDialog(InsetGraphics* inset)
199 {
200         Assert(inset != 0);
201
202         // If we are connected to another inset, disconnect.
203         if (inset_)
204                 ih_.disconnect();
205
206         inset_ = inset;
207
208         if (inset_) {
209                 ih_ = inset_->hide.connect(slot(this, &FormGraphics::hide));
210                 show();
211         }
212 }
213
214
215 void FormGraphics::hide()
216 {
217         // If the dialog exists, and the form is allocated and visible.
218         if (dialog_
219                 && dialog_->form
220                 && dialog_->form->visible) {
221                 // Hide the form
222                 fl_hide_form(dialog_->form);
223
224                 // And disconnect the signals.
225                 u_.disconnect();
226                 h_.disconnect();
227                 ih_.disconnect();
228
229                 // Forget the inset.
230                 inset_ = 0;
231         }
232 }
233
234
235 void FormGraphics::free()
236 {
237         // Remove all associations for the radio buttons
238         widthButtons.reset();
239         heightButtons.reset();
240         displayButtons.reset();
241
242         
243         // Free the form.
244         delete dialog_;
245         dialog_ = 0;
246 }
247
248 void FormGraphics::apply()
249 {
250         Assert(inset_ != 0);
251
252         // Take all dialog details and insert them to the inset.
253
254         // Create the parameters structure and fill the data from the dialog.
255         InsetGraphicsParams igp;
256
257         igp.filename = fl_get_input(dialog_->input_filename);
258
259         igp.display = static_cast < InsetGraphicsParams::DisplayType >
260                       (displayButtons.getButton());
261
262         igp.widthResize = static_cast < InsetGraphicsParams::Resize >
263                           (widthButtons.getButton());
264         igp.widthSize = strToDbl(fl_get_input(dialog_->input_width));
265
266         igp.heightResize = static_cast < InsetGraphicsParams::Resize >
267                            (heightButtons.getButton());
268         igp.heightSize = strToDbl(fl_get_input(dialog_->input_height));
269
270         igp.rotateAngle = strToInt(fl_get_input(dialog_->input_rotate_angle));
271         if (igp.rotateAngle >= 360)
272                 igp.rotateAngle = igp.rotateAngle % 360;
273         if (igp.rotateAngle <= -360)
274                 igp.rotateAngle = - (( -igp.rotateAngle) % 360);
275
276         igp.subcaption = fl_get_button(dialog_->check_subcaption);
277         igp.subcaptionText = fl_get_input(dialog_->input_subcaption);
278
279         igp.inlineFigure = fl_get_button(dialog_->check_inline);
280
281         igp.testInvariant();
282
283         // Set the parameters in the inset, it also returns true if the new
284         // parameters are different from what was in the inset already.
285         bool changed = inset_->setParams(igp);
286
287         // Tell LyX we've got a change, and mark the document dirty, if it changed.
288         lv_->view()->updateInset(inset_, changed);
289 }
290
291
292 // it doesn't look this is capable of updating on a buffer switch
293 // inset_ would be accessible but it's not in the new buffer so
294 // ok or apply will call something that won't like it. ARRae.
295 void FormGraphics::update(bool switched)
296 {
297         if (switched) {
298                 hide();
299                 return;
300         }
301
302         Assert(inset_ != 0);
303
304         // Update dialog with details from inset
305         InsetGraphicsParams igp = inset_->getParams();
306
307         // Update the filename input field
308         fl_set_input(dialog_->input_filename,
309                      igp.filename.c_str());
310
311         // Update the display depth radio buttons
312         displayButtons.setButton(igp.display);
313
314         // Update the width radio buttons and input field
315         widthButtons.setButton(igp.widthResize);
316         fl_set_input(dialog_->input_width,
317                      tostr(igp.widthSize).c_str());
318
319         // Update the height radio buttons and input field
320         heightButtons.setButton(igp.heightResize);
321         fl_set_input(dialog_->input_height,
322                      tostr(igp.heightSize).c_str());
323
324         // Update the rotate angle
325         fl_set_input(dialog_->input_rotate_angle,
326                      tostr(igp.rotateAngle).c_str());
327
328         // Update the subcaption check button and input field
329         fl_set_button(dialog_->check_subcaption,
330                       igp.subcaption);
331         fl_set_input(dialog_->input_subcaption,
332                      igp.subcaptionText.c_str());
333
334         // Update the inline figure check button
335         fl_set_button(dialog_->check_inline,
336                       igp.inlineFigure);
337
338         // Now make sure that the buttons are set correctly.
339         checkInput();
340 }
341
342 bool FormGraphics::checkInput()
343 {
344         // Put verifications that the dialog shows some sane values,
345         // if not disallow clicking on ok/apply.
346         // Possibly use a label in the bottom of the dialog to give the reason.
347
348         // Is all input boxes convey a valid meaning?
349         bool inputOK = true;
350
351         // Things that we check (meaning they are incorrect states):
352         // 1. No filename specified.
353         // 2. Width radio button is not Default and width text is not a number.
354         // 3. Height radio button is not Default and height text is a not a number
355
356         // Note: radio button default means that the user asks for the image
357         // to be included as is with no size change, in this case we don't need
358         // any width or height.
359
360         // We verify now that there is a filename, it exists, it's a file
361         // and it's readable.
362         string filename = fl_get_input(dialog_->input_filename);
363         FileInfo file(filename);
364         if (filename.empty()
365                 || !file.isOK()
366                 || !file.exist()
367                 || !file.isRegular()
368                 || !file.readable()
369            )
370                 inputOK = false;
371
372         // Width radio button not default and no number.
373         if (!fl_get_button(dialog_->radio_width_default)
374                 && strToDbl(fl_get_input(dialog_->input_width)) <= 0.0) {
375
376                 inputOK = false;
377         }
378
379         // Height radio button not default and no number.
380         if (!fl_get_button(dialog_->radio_height_default)
381                 && strToDbl(fl_get_input(dialog_->input_height)) <= 0.0) {
382
383                 inputOK = false;
384         }
385
386         return inputOK;
387 }
388
389
390 // We need these in the file browser.
391 extern string system_lyxdir;
392 extern string user_lyxdir;
393 //extern string system_tempdir;
394
395 // Need to move this to the form_graphics
396 string FormGraphics::browseFile(string const & filename)
397 {
398         if (! filename.empty() )
399                 last_image_path = OnlyPath(filename);
400
401         // Does user clipart directory exist?
402         string bufclip = AddName (user_lyxdir, "clipart");
403         FileInfo fileInfo(bufclip);
404         if (!(fileInfo.isOK() && fileInfo.isDir()))
405                 // No - bail out to system clipart directory
406                 bufclip = AddName (system_lyxdir, "clipart");
407
408         LyXFileDlg fileDlg;
409         fileDlg.SetButton(0, _("Clipart"), bufclip);
410
411         bool error = false;
412         string buf;
413         do {
414                 string p = fileDlg.Select(_("Graphics"),
415                                           last_image_path,
416                                           "*(ps|png)", filename);
417
418                 if (p.empty()) return p;
419
420                 last_image_path = OnlyPath(p);
421
422                 if (p.find_first_of("#~$% ") != string::npos) {
423                         WriteAlert(_("Filename can't contain any "
424                                      "of these characters:"),
425                                    // xgettext:no-c-format
426                                    _("space, '#', '~', '$' or '%'."));
427                         error = true;
428                 } else {
429                         error = false;
430                         buf = p;
431                 }
432         } while (error);
433
434         return buf;
435 }
436
437 void FormGraphics::browse()
438 {
439         // Get the filename from the dialog
440         string const filename = fl_get_input(dialog_->input_filename);
441
442         // Show the file browser dialog
443         string const new_filename = browseFile(filename);
444
445         // Save the filename to the dialog
446         if (new_filename != filename && ! new_filename.empty()) {
447                 fl_set_input(dialog_->input_filename,
448                              new_filename.c_str());
449                 // The above set input doesn't cause an input event so we do
450                 // it manually. Otherwise the user needs to cause an input event
451                 // to get the ok/apply buttons to be activated.
452                 checkInput();
453         }
454
455 }
456
457 int FormGraphics::WMHideCB(FL_FORM * form, void *)
458 {
459         // Ensure that the signal h is disconnected even if the
460         // window manager is used to close the dialog.
461         FormGraphics * pre = static_cast < FormGraphics* > (form->u_vdata);
462         pre->hide();
463
464         pre->bc_->hide();
465         
466         return FL_CANCEL;
467 }
468
469
470 void FormGraphics::OKCB(FL_OBJECT * ob, long)
471 {
472         FormGraphics * pre = static_cast < FormGraphics* > (ob->form->u_vdata);
473         pre->apply();
474         pre->hide();
475         
476         pre->bc_->ok();
477 }
478
479 void FormGraphics::ApplyCB(FL_OBJECT * ob, long)
480 {
481         FormGraphics * pre = static_cast < FormGraphics* > (ob->form->u_vdata);
482         pre->apply();
483         
484         pre->bc_->apply();
485 }
486
487 void FormGraphics::CancelCB(FL_OBJECT * ob, long)
488 {
489         FormGraphics * pre = static_cast < FormGraphics* > (ob->form->u_vdata);
490         pre->hide();
491         
492         pre->bc_->cancel();
493 }
494
495 void FormGraphics::BrowseCB(FL_OBJECT * ob, long)
496 {
497         FormGraphics * pre = static_cast < FormGraphics* > (ob->form->u_vdata);
498         pre->browse();
499 }
500
501 void FormGraphics::AdvancedOptionsCB(FL_OBJECT * /* ob */, long)
502 {
503         //  FormGraphics * pre = static_cast<FormGraphics*>(ob->form->u_vdata);
504         //      pre->showAdvancedOptions();
505         lyxerr << "Advanced Options button depressed, "
506         "show advanced options dialog"
507         << endl;
508 }
509
510 void FormGraphics::InputCB(FL_OBJECT * ob, long)
511 {
512         FormGraphics * pre = static_cast < FormGraphics* > (ob->form->u_vdata);
513         pre->bc_->valid(pre->checkInput());
514 }
515