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