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