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