]> git.lyx.org Git - lyx.git/blob - src/frontends/xforms/FormGraphics.C
major GUII cleanup + Baruchs patch + Angus's patch + removed a couple of generated...
[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 #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,
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->visible) {
166         // Raise it.
167                 fl_raise_form(dialog_->form);
168         } else {
169         // Otherwise (invisible), show it.
170                 fl_show_form(dialog_->form,
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
208                 && dialog_->form->visible) {
209         // Hide the form
210                 fl_hide_form(dialog_->form);
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         // Remove all associations for the radio buttons
229         widthButtons.reset();
230         heightButtons.reset();
231         displayButtons.reset();
232             
233         // Free the form.
234         delete dialog_;
235         dialog_ = 0;
236 }
237
238 void FormGraphics::apply()
239 {
240 #ifdef ENABLE_ASSERTIONS
241     Assert(inset_ != 0);
242 #endif    
243     if (! inset_)
244         return;
245
246     // Take all dialog details and insert them to the inset.
247
248     // Create the parameters structure and fill the data from the dialog.
249     InsetGraphicsParams igp;
250
251     igp.filename = fl_get_input(dialog_->input_filename);
252
253     igp.display = static_cast<InsetGraphicsParams::DisplayType>
254             (displayButtons.getButton());
255     
256     igp.widthResize = static_cast<InsetGraphicsParams::Resize>
257             (widthButtons.getButton());
258     igp.widthSize = strToDbl(fl_get_input(dialog_->input_width));
259
260     igp.heightResize = static_cast<InsetGraphicsParams::Resize>
261             (heightButtons.getButton());
262     igp.heightSize = strToDbl(fl_get_input(dialog_->input_height));
263
264     igp.rotateAngle = strToInt(fl_get_input(dialog_->input_rotate_angle));
265     if (igp.rotateAngle >= 360)
266         igp.rotateAngle = igp.rotateAngle % 360;
267     if (igp.rotateAngle <= -360)
268         igp.rotateAngle = - ((-igp.rotateAngle) % 360);
269    
270     igp.subcaption = fl_get_button(dialog_->check_subcaption);
271     igp.subcaptionText = fl_get_input(dialog_->input_subcaption);
272
273     igp.inlineFigure = fl_get_button(dialog_->check_inline);
274     
275 #ifdef ENABLE_ASSERTIONS    
276     igp.testInvariant();
277 #endif
278
279     // Set the parameters in the inset, it also returns true if the new
280     // parameters are different from what was in the inset already.
281     bool changed = inset_->setParams(igp);
282
283     // Tell LyX we've got a change, and mark the document dirty, if it changed.
284     lv_->view()->updateInset(inset_, changed);
285 }
286
287 void FormGraphics::update()
288 {
289 #ifdef ENABLE_ASSERTIONS
290     Assert(inset_ != 0);
291 #endif    
292     if (! inset_)
293         return;
294
295     // Update dialog with details from inset
296     InsetGraphicsParams igp = inset_->getParams();
297
298     // Update the filename input field
299     fl_set_input(dialog_->input_filename,
300             igp.filename.c_str());
301
302     // Update the display depth radio buttons
303     displayButtons.setButton(igp.display);
304     
305     // Update the width radio buttons and input field
306     widthButtons.setButton(igp.widthResize);
307     fl_set_input(dialog_->input_width,
308             tostr(igp.widthSize).c_str());
309
310     // Update the height radio buttons and input field
311     heightButtons.setButton(igp.heightResize);
312     fl_set_input(dialog_->input_height,
313             tostr(igp.heightSize).c_str());
314
315     // Update the rotate angle
316     fl_set_input(dialog_->input_rotate_angle,
317             tostr(igp.rotateAngle).c_str());
318   
319     // Update the subcaption check button and input field
320     fl_set_button(dialog_->check_subcaption,
321             igp.subcaption);
322     fl_set_input(dialog_->input_subcaption,
323             igp.subcaptionText.c_str());
324    
325     // Update the inline figure check button
326     fl_set_button(dialog_->check_inline,
327             igp.inlineFigure);
328    
329     // Now make sure that the buttons are set correctly. 
330     input();
331 }
332
333 void FormGraphics::input() 
334 {
335     // Put verifications that the dialog shows some sane values,
336     // if not disallow clicking on ok/apply.
337     // Possibly use a label in the bottom of the dialog to give the reason.
338
339     // Is all input boxes convey a valid meaning?
340     bool inputOK = true;
341
342     // Things that we check (meaning they are incorrect states):
343     // 1. No filename specified. 
344     // 2. Width radio button is not Default and width text is not a number.
345     // 3. Height radio button is not Default and height text is a not a number
346   
347     // Note: radio button default means that the user asks for the image
348     // to be included as is with no size change, in this case we don't need
349     // any width or height.
350   
351     // We verify now that there is a filename, it exists, it's a file
352     // and it's readable.
353     string filename = fl_get_input(dialog_->input_filename);
354     FileInfo file(filename);
355     if (filename.empty()       
356             || !file.isOK()       
357             || !file.exist()     
358             || !file.isRegular()     
359             || !file.readable()
360        )
361         inputOK = false;
362
363     // Width radio button not default and no number.
364     if  (!fl_get_button(dialog_->radio_width_default)
365            && strToDbl(fl_get_input(dialog_->input_width)) <= 0.0) {
366             
367         inputOK = false;
368     }
369
370     // Height radio button not default and no number.
371     if (!fl_get_button(dialog_->radio_height_default) 
372             && strToDbl(fl_get_input(dialog_->input_height)) <= 0.0) {
373         
374         inputOK = false;
375     }
376
377
378     // Now set the buttons to the correct state.
379     if (inputOK && ! lv_->buffer()->isReadonly()) {
380         fl_activate_object(dialog_->button_ok);
381         fl_activate_object(dialog_->button_apply);
382         fl_set_object_lcol(dialog_->button_ok, FL_BLACK);
383         fl_set_object_lcol(dialog_->button_apply, FL_BLACK);
384     } else {
385         fl_deactivate_object(dialog_->button_ok);
386         fl_deactivate_object(dialog_->button_apply);
387         fl_set_object_lcol(dialog_->button_ok, FL_INACTIVE);
388         fl_set_object_lcol(dialog_->button_apply, FL_INACTIVE);
389     }
390 }
391
392
393 // We need these in the file browser.
394 extern string system_lyxdir;
395 extern string user_lyxdir;
396 //extern string system_tempdir;
397
398 // Need to move this to the form_graphics
399 string FormGraphics::browseFile(string const & filename)
400 {
401     if (! filename.empty() )
402         last_image_path = OnlyPath(filename);
403
404     // Does user clipart directory exist?
405     string bufclip = AddName (user_lyxdir, "clipart");  
406     FileInfo fileInfo(bufclip);
407     if (!(fileInfo.isOK() && fileInfo.isDir()))
408         // No - bail out to system clipart directory
409         bufclip = AddName (system_lyxdir, "clipart");   
410
411     LyXFileDlg fileDlg;
412     fileDlg.SetButton(0, _("Clipart"), bufclip); 
413
414     bool error = false;
415     string buf;
416     do {
417         string p = fileDlg.Select(_("Graphics"),
418                 last_image_path,
419                 "*(ps|png)", filename);
420
421         if (p.empty()) return p;
422
423         last_image_path = OnlyPath(p);
424
425         if (p.find_first_of("#~$% ") != string::npos) {
426                         WriteAlert(_("Filename can't contain any "
427                                      "of these characters:"),
428                                    // xgettext:no-c-format
429                                    _("space, '#', '~', '$' or '%'.")); 
430                         error = true;
431                 } else {
432             error = false;
433             buf = p;
434         }
435         } while (error);
436
437         return buf;
438 }
439
440 void FormGraphics::browse()
441 {
442     // Get the filename from the dialog
443     string filename = fl_get_input(dialog_->input_filename);
444
445     // Show the file browser dialog
446     string new_filename = browseFile(filename);
447
448     // Save the filename to the dialog
449     if (new_filename != filename && ! new_filename.empty()) {
450         fl_set_input(dialog_->input_filename,
451                      new_filename.c_str());
452         // The above set input doesn't cause an input event so we do
453         // it manually. Otherwise the user needs to cause an input event
454         // to get the ok/apply buttons to be activated.
455         input();
456     }
457     
458 }
459
460 int FormGraphics::WMHideCB(FL_FORM * form, void *)
461 {
462         // Ensure that the signal h is disconnected even if the
463         // window manager is used to close the dialog.
464         FormGraphics * pre = static_cast<FormGraphics*>(form->u_vdata);
465         pre->hide();
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
477 void FormGraphics::ApplyCB(FL_OBJECT * ob, long)
478 {
479         FormGraphics * pre = static_cast<FormGraphics*>(ob->form->u_vdata);
480         pre->apply();
481 }
482
483 void FormGraphics::CancelCB(FL_OBJECT * ob, long)
484 {
485         FormGraphics * pre = static_cast<FormGraphics*>(ob->form->u_vdata);
486         pre->hide();
487 }
488
489 void FormGraphics::BrowseCB(FL_OBJECT * ob, long)
490 {
491         FormGraphics * pre = static_cast<FormGraphics*>(ob->form->u_vdata);
492     pre->browse();
493 }
494
495 void FormGraphics::AdvancedOptionsCB(FL_OBJECT * /* ob */, long)
496 {
497 //  FormGraphics * pre = static_cast<FormGraphics*>(ob->form->u_vdata);
498 //      pre->showAdvancedOptions();
499     lyxerr << "Advanced Options button depressed, "
500               "show advanced options dialog"
501            << endl;
502 }
503
504 void FormGraphics::InputCB(FL_OBJECT * ob, long)
505 {
506     FormGraphics * pre = static_cast<FormGraphics*>(ob->form->u_vdata);
507         pre->input();
508 }
509