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