]> git.lyx.org Git - lyx.git/blob - src/frontends/gtk/GGraphics.C
minor fixes to gtk graphics dialog
[lyx.git] / src / frontends / gtk / GGraphics.C
1 /**
2  * \file GGraphics.C
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author John Spray
7  *
8  * Full author contact details are available in file CREDITS.
9  */
10
11 #include <config.h>
12
13 #include "GGraphics.h"
14 #include "ControlGraphics.h"
15
16 #include "ghelpers.h"
17
18 #include "controllers/helper_funcs.h"
19
20 #include "insets/insetgraphicsParams.h"
21 #include "paper.h"
22 #include "lyxrc.h" // for lyxrc.default_papersize
23
24 #include "support/lyxlib.h"  // for float_equal
25 #include "support/lstrings.h"
26
27 #include "debug.h"
28
29 using std::vector;
30 using std::string;
31
32 namespace lyx {
33
34 using support::float_equal;
35 using support::token;
36
37 namespace frontend {
38
39 namespace {
40 string defaultUnit("cm");
41 } // namespace anon
42
43 GGraphics::GGraphics(Dialog & parent)
44         : GViewCB<ControlGraphics, GViewGladeB>(parent, _("Graphics"), false)
45 {}
46
47
48 void GGraphics::doBuild()
49 {
50         string const gladeName = findGladeFile("graphics");
51         xml_ = Gnome::Glade::Xml::create(gladeName);
52
53         Gtk::Button * button;
54         xml_->get_widget("Close", button);
55         setCancel(button);
56         xml_->get_widget("Ok", button);
57         setOK(button);
58         xml_->get_widget("Apply", button);
59         setApply(button);
60         xml_->get_widget("Restore", button);
61         setRestore(button);
62
63         xml_->get_widget("Notebook", notebook_);
64
65         // File Page
66         xml_->get_widget("File", fileentry_);
67         xml_->get_widget("DisplayScale", displayscalespin_);
68         xml_->get_widget("OutputScale", outputscalespin_);
69         xml_->get_widget("Browse", browsebutton_);
70         xml_->get_widget("Edit", editbutton_);
71         xml_->get_widget("Display", displaycombo_);
72         xml_->get_widget("Width", widthspin_);
73         xml_->get_widget("Height", heightspin_);
74         xml_->get_widget("MaintainAspectRatio", aspectcheck_);
75         xml_->get_widget("WidthUnits", widthunitscombo_);
76         xml_->get_widget("HeightUnits", heightunitscombo_);
77         xml_->get_widget("SetScaling", setscalingradio_);
78         xml_->get_widget("SetSize", setsizeradio_);
79
80         // Bounding Box Page
81         xml_->get_widget("ClipToBoundingBox", clipcheck_);
82         xml_->get_widget("RightTopX", righttopxspin_);
83         xml_->get_widget("RightTopY", righttopyspin_);
84         xml_->get_widget("LeftBottomX", leftbottomxspin_);
85         xml_->get_widget("LeftBottomY", leftbottomyspin_);
86         xml_->get_widget("BoundingUnits", bbunitscombo_);
87         xml_->get_widget("GetFromFile", bbfromfilebutton_);
88
89         // Extra Page
90         xml_->get_widget("Angle", anglespin_);
91         xml_->get_widget("Origin", origincombo_);
92         xml_->get_widget("UseSubfigure", subfigcheck_);
93         xml_->get_widget("SubfigureCaption", subfigentry_);
94         xml_->get_widget("LatexOptions", latexoptsentry_);
95         xml_->get_widget("DraftMode", draftcheck_);
96         xml_->get_widget("UnzipOnExport", unzipcheck_);
97
98         // Setup the columnrecord we use for combos
99         cols_.add(stringcol_);
100
101         // The file page
102
103         // Disable for read-only documents.
104         bcview().addReadOnly(browsebutton_);
105         bcview().addReadOnly(aspectcheck_);
106
107         fileentry_->signal_changed().connect(
108                 sigc::mem_fun(*this, &GGraphics::onInput));
109         displayscalespin_->signal_changed().connect(
110                 sigc::mem_fun(*this, &GGraphics::onInput));
111         displaycombo_->signal_changed().connect(
112                 sigc::mem_fun(*this, &GGraphics::onInput));
113         outputscalespin_->signal_changed().connect(
114                 sigc::mem_fun(*this, &GGraphics::onInput));
115         heightspin_->signal_changed().connect(
116                 sigc::mem_fun(*this, &GGraphics::onInput));
117         heightunitscombo_->signal_changed().connect(
118                 sigc::mem_fun(*this, &GGraphics::onInput));
119         widthspin_->signal_changed().connect(
120                 sigc::mem_fun(*this, &GGraphics::onInput));
121         widthunitscombo_->signal_changed().connect(
122                 sigc::mem_fun(*this, &GGraphics::onInput));
123         aspectcheck_->signal_toggled().connect(
124                 sigc::mem_fun(*this, &GGraphics::onInput));
125
126         setscalingradio_->signal_toggled().connect(
127                 sigc::mem_fun(*this, &GGraphics::onSizingModeChange));
128         setsizeradio_->signal_toggled().connect(
129                 sigc::mem_fun(*this, &GGraphics::onSizingModeChange));
130
131         browsebutton_->signal_clicked().connect(
132                 sigc::mem_fun(*this, &GGraphics::onBrowseClicked));
133
134         editbutton_->signal_clicked().connect(
135                 sigc::mem_fun(*this, &GGraphics::onEditClicked));
136
137         vector<string> const unit_list = buildLengthUnitList();
138         PopulateComboBox(heightunitscombo_, unit_list);
139         PopulateComboBox(widthunitscombo_, unit_list);
140
141         // the bounding box page
142         leftbottomxspin_->signal_changed().connect(
143                 sigc::mem_fun(*this, &GGraphics::onBBChange));
144         leftbottomyspin_->signal_changed().connect(
145                 sigc::mem_fun(*this, &GGraphics::onBBChange));
146         righttopxspin_->signal_changed().connect(
147                 sigc::mem_fun(*this, &GGraphics::onBBChange));
148         righttopyspin_->signal_changed().connect(
149                 sigc::mem_fun(*this, &GGraphics::onBBChange));
150         bbunitscombo_->signal_changed().connect(
151                 sigc::mem_fun(*this, &GGraphics::onBBChange));
152         clipcheck_->signal_toggled().connect(
153                 sigc::mem_fun(*this, &GGraphics::onBBChange));
154
155         bbfromfilebutton_->signal_clicked().connect(
156                 sigc::mem_fun(*this, &GGraphics::onBBFromFileClicked));
157
158         // disable for read-only documents
159         bcview().addReadOnly(bbfromfilebutton_);
160         bcview().addReadOnly(clipcheck_);
161         PopulateComboBox(bbunitscombo_, getBBUnits());
162
163
164         // the extra section
165
166         // disable for read-only documents
167         bcview().addReadOnly(anglespin_);
168         bcview().addReadOnly(origincombo_);
169         bcview().addReadOnly(subfigcheck_);
170         bcview().addReadOnly(latexoptsentry_);
171         bcview().addReadOnly(draftcheck_);
172         bcview().addReadOnly(unzipcheck_);
173
174         anglespin_->signal_changed().connect(
175                 sigc::mem_fun(*this, &GGraphics::onInput));
176         origincombo_->signal_changed().connect(
177                 sigc::mem_fun(*this, &GGraphics::onInput));
178         subfigentry_->signal_changed().connect(
179                 sigc::mem_fun(*this, &GGraphics::onInput));
180         latexoptsentry_->signal_changed().connect(
181                 sigc::mem_fun(*this, &GGraphics::onInput));
182         draftcheck_->signal_toggled().connect(
183                 sigc::mem_fun(*this, &GGraphics::onInput));
184         unzipcheck_->signal_toggled().connect(
185                 sigc::mem_fun(*this, &GGraphics::onInput));
186
187         subfigcheck_->signal_toggled().connect(
188                 sigc::mem_fun(*this, &GGraphics::onSubFigCheckToggled));
189
190         vector<RotationOriginPair> origindata = getRotationOriginData();
191
192         // Store the identifiers for later
193         origins_ = getSecond(origindata);
194         PopulateComboBox(origincombo_, getFirst(origindata));
195
196         // set the right default unit
197         switch (lyxrc.default_papersize) {
198                 case PAPER_DEFAULT: break;
199                 case PAPER_USLETTER:
200                 case PAPER_LEGALPAPER:
201                 case PAPER_EXECUTIVEPAPER: defaultUnit = "in"; break;
202                 case PAPER_A3PAPER:
203                 case PAPER_A4PAPER:
204                 case PAPER_A5PAPER:
205                 case PAPER_B5PAPER: defaultUnit = "cm"; break;
206         }
207 }
208
209
210 void GGraphics::onSizingModeChange()
211 {
212         bool const scalingmode = setscalingradio_->get_active();
213
214         outputscalespin_->set_sensitive(scalingmode);
215         widthspin_->set_sensitive(!scalingmode);
216         heightspin_->set_sensitive(!scalingmode);
217         widthunitscombo_->set_sensitive(!scalingmode);
218         heightunitscombo_->set_sensitive(!scalingmode);
219         aspectcheck_->set_sensitive(!scalingmode);
220         bc().input(ButtonPolicy::SMI_VALID);
221 }
222
223
224 void GGraphics::PopulateComboBox(Gtk::ComboBox * combo,
225                                   vector<string> const & strings)
226 {
227         Glib::RefPtr<Gtk::ListStore> model = Gtk::ListStore::create(cols_);
228         vector<string>::const_iterator it = strings.begin();
229         vector<string>::const_iterator end = strings.end();
230         for (int rowindex = 0; it != end; ++it, ++rowindex) {
231                 Gtk::TreeModel::iterator row = model->append();
232                 (*row)[stringcol_] = *it;
233         }
234
235         combo->set_model(model);
236         Gtk::CellRendererText * cell = Gtk::manage(new Gtk::CellRendererText);
237         combo->pack_start(*cell, true);
238         combo->add_attribute(*cell, "text", 0);
239 }
240
241
242 void GGraphics::updateComboFromLength(Gtk::ComboBox * combo,
243                              LyXLength const & len)
244 {
245         string unit = stringFromUnit(len.unit());
246         if (unit.empty())
247                 unit = defaultUnit;
248
249         Gtk::TreeModel::iterator it = combo->get_model()->children().begin();
250         Gtk::TreeModel::iterator end = combo->get_model()->children().end();
251         for (; it != end ; ++it) {
252                 if ((*it)[stringcol_] == unit) {
253                         combo->set_active(it);
254                         return;
255                 }
256         }
257         // Fallen through, we didn't find the target length!
258         combo->set_active(0);
259         lyxerr << "GGraphics::updateComboFromLength: couldn't find "
260                 "target unit '" << unit << "'\n";
261 }
262
263
264 void GGraphics::apply()
265 {
266         // Create the parameters structure and fill the data from the dialog.
267         InsetGraphicsParams & igp = controller().params();
268
269         // the file section
270         igp.filename.set(fileentry_->get_text(),
271                          kernel().bufferFilepath());
272
273         igp.lyxscale =
274                 static_cast<int>(displayscalespin_->get_adjustment()->get_value());
275         if (igp.lyxscale == 0) {
276                 igp.lyxscale = 100;
277         }
278
279         switch (displaycombo_->get_active_row_number()) {
280         case 4:
281                 igp.display = graphics::NoDisplay;
282                 break;
283         case 3:
284                 igp.display = graphics::ColorDisplay;
285                 break;
286         case 2:
287                 igp.display = graphics::GrayscaleDisplay;
288                 break;
289         case 1:
290                 igp.display = graphics::MonochromeDisplay;
291                 break;
292         case 0:
293                 igp.display = graphics::DefaultDisplay;
294         }
295
296         if (setscalingradio_->get_active()) {
297                 igp.scale = outputscalespin_->get_adjustment()->get_value();
298                 if (float_equal(igp.scale, 0.0, 0.05))
299                         igp.scale = 100.0;
300                 igp.width = LyXLength();
301         } else {
302                 igp.scale = 0.0;
303                 Glib::ustring const widthunit =
304                         (*widthunitscombo_->get_active())[stringcol_];
305                 igp.width = LyXLength(widthspin_->get_text() + widthunit);
306         }
307
308
309         Glib::ustring const heightunit =
310                 (*heightunitscombo_->get_active())[stringcol_];
311         igp.height = LyXLength(heightspin_->get_text() + heightunit);
312
313         igp.keepAspectRatio = aspectcheck_->get_active();
314         igp.draft = draftcheck_->get_active();
315         igp.noUnzip = !unzipcheck_->get_active();
316
317         // the bb section
318
319         if (!controller().bbChanged) {
320                 // don't write anything
321                 igp.bb.erase();
322         } else {
323                 Glib::ustring const bbunit = (*bbunitscombo_->get_active())[stringcol_];
324                 string bb;
325
326                 if (leftbottomxspin_->get_text().empty())
327                         bb = "0";
328                 else
329                         bb = leftbottomxspin_->get_text() + bbunit;
330
331                 bb += ' ';
332
333                 if (leftbottomyspin_->get_text().empty())
334                         bb += "0";
335                 else
336                         bb += leftbottomyspin_->get_text() + bbunit;
337
338                 bb += ' ';
339
340                 if (righttopxspin_->get_text().empty())
341                         bb += "0";
342                 else
343                         bb += righttopxspin_->get_text() + bbunit;
344
345                 bb += ' ';
346
347                 if (righttopyspin_->get_text().empty())
348                         bb += "0";
349                 else
350                         bb += righttopyspin_->get_text() + bbunit;
351
352                 /// The bounding box with "xLB yLB yRT yRT ", divided by a space!
353                 igp.bb = bb;
354         }
355         igp.clip = clipcheck_->get_active();
356
357         // the extra section
358         igp.rotateAngle = anglespin_->get_adjustment()->get_value();
359
360         int const origin_pos = origincombo_->get_active_row_number();
361         igp.rotateOrigin = origins_[origin_pos];
362
363         igp.subcaption = subfigcheck_->get_active();
364         igp.subcaptionText = subfigentry_->get_text();
365
366         igp.special = latexoptsentry_->get_text();
367 }
368
369
370 void GGraphics::update() {
371         // Update dialog with details from inset
372         InsetGraphicsParams & igp = controller().params();
373
374         // the file section
375         string const name =
376                 igp.filename.outputFilename(kernel().bufferFilepath());
377         fileentry_->set_text(name);
378         displayscalespin_->get_adjustment()->set_value(igp.lyxscale);
379
380
381         switch (igp.display) {
382         case graphics::NoDisplay:
383                 displaycombo_->set_active(4);
384                 break;
385         case graphics::ColorDisplay:
386                 displaycombo_->set_active(3);
387                 break;
388         case graphics::GrayscaleDisplay:
389                 displaycombo_->set_active(2);
390                 break;
391         case graphics::MonochromeDisplay:
392                 displaycombo_->set_active(1);
393                 break;
394         case graphics::DefaultDisplay:
395                 displaycombo_->set_active(0);
396         }
397
398         outputscalespin_->get_adjustment()->set_value(igp.scale);
399         widthspin_->get_adjustment()->set_value(igp.width.value());
400         updateComboFromLength(widthunitscombo_, igp.width);
401         heightspin_->get_adjustment()->set_value(igp.height.value());
402         updateComboFromLength(heightunitscombo_, igp.height);
403
404         if (!float_equal(igp.scale, 0.0, 0.05)) {
405                 // scaling sizing mode
406                 setscalingradio_->set_active(true);
407         } else {
408                 setsizeradio_->set_active(true);
409         }
410         onSizingModeChange();
411
412         aspectcheck_->set_active(igp.keepAspectRatio);
413         draftcheck_->set_active(igp.draft);
414         unzipcheck_->set_active(!igp.noUnzip);
415
416         // the bb section
417         // set the bounding box values, if exists. First we need the whole
418         // path, because the controller knows nothing about the doc-dir
419         updateBB(igp.filename.absFilename(), igp.bb);
420         clipcheck_->set_active(igp.clip);
421
422         // the extra section
423         anglespin_->get_adjustment()->set_value(igp.rotateAngle);
424
425         int origin_pos;
426         if (igp.rotateOrigin.empty()) {
427                 origin_pos = 0;
428         } else {
429                 origin_pos = findPos(origins_, igp.rotateOrigin);
430         }
431         origincombo_->set_active(origin_pos);
432
433
434         subfigcheck_->set_active(igp.subcaption);
435         subfigentry_->set_text(igp.subcaptionText);
436         subfigentry_->set_sensitive(subfigcheck_->get_active());
437         latexoptsentry_->set_text(igp.special);
438
439         // open dialog in the file-tab, whenever filename is empty
440         if (igp.filename.empty()) {
441                 notebook_->set_current_page(0);
442         }
443
444
445         bc().input(ButtonPolicy::SMI_INVALID);
446 }
447
448
449 void GGraphics::updateBB(string const & filename, string const & bb_inset)
450 {
451         // Update dialog with details from inset
452         // set the bounding box values, if exists. First we need the whole
453         // path, because the controller knows nothing about the doc-dir
454
455         if (bb_inset.empty()) {
456                 lyxerr[Debug::GRAPHICS]
457                         << "GGraphics::updateBB() [no BoundingBox]\n";
458                 string const bb = controller().readBB(filename);
459                 if (!bb.empty()) {
460                         // get the values from the file
461                         // in this case we always have the point-unit
462                         leftbottomxspin_->set_text(token(bb, ' ', 0));
463                         leftbottomyspin_->set_text(token(bb, ' ', 1));
464                         righttopxspin_->set_text(token(bb, ' ', 2));
465                         righttopyspin_->set_text(token(bb, ' ', 3));
466                 } else {
467                         // no bb from file
468                         leftbottomxspin_->set_text("");
469                         leftbottomyspin_->set_text("");
470                         righttopxspin_->set_text("");
471                         righttopyspin_->set_text("");
472                 }
473                 updateComboFromLength(bbunitscombo_,LyXLength("bp"));
474         } else {
475                 // get the values from the inset
476                 lyxerr[Debug::GRAPHICS]
477                         << "FormGraphics::updateBB(): igp has BoundingBox"
478                         << " ["<< bb_inset << "]\n";
479
480                 LyXLength anyLength;
481                 anyLength = LyXLength(token(bb_inset, ' ', 0));
482
483                 updateComboFromLength(bbunitscombo_, anyLength);
484
485                 leftbottomxspin_->get_adjustment()->set_value(anyLength.value());
486
487                 anyLength = LyXLength(token(bb_inset, ' ', 1));
488                 leftbottomyspin_->get_adjustment()->set_value(anyLength.value());
489
490                 anyLength = LyXLength(token(bb_inset, ' ', 2));
491                 righttopxspin_->get_adjustment()->set_value(anyLength.value());
492
493                 anyLength = LyXLength(token(bb_inset, ' ', 3));
494                 righttopyspin_->get_adjustment()->set_value(anyLength.value());
495         }
496         controller().bbChanged = false;
497 }
498
499
500 void GGraphics::onBrowseClicked()
501 {
502         // Get the filename from the dialog
503         string const in_name = fileentry_->get_text();
504         string const out_name = controller().browse(in_name);
505         lyxerr[Debug::GRAPHICS]
506                 << "[FormGraphics]out_name: " << out_name << "\n";
507         if (out_name != in_name && !out_name.empty()) {
508                 fileentry_->set_text(out_name);
509         }
510         if (controller().isFilenameValid(out_name) &&
511             !controller().bbChanged) {
512                 updateBB(out_name, string());
513         }
514
515         bc().input(ButtonPolicy::SMI_VALID);
516 }
517
518
519 void GGraphics::onBBChange()
520 {
521         controller().bbChanged = true;
522         bc().input(ButtonPolicy::SMI_VALID);
523 }
524
525
526 void GGraphics::onBBFromFileClicked()
527 {
528         string const filename = fileentry_->get_text();
529         if (!filename.empty()) {
530                 string bb = controller().readBB(filename);
531                 if (!bb.empty()) {
532                         leftbottomxspin_->set_text(token(bb, ' ', 0));
533                         leftbottomyspin_->set_text(token(bb, ' ', 1));
534                         righttopxspin_->set_text(token(bb, ' ', 2));
535                         righttopyspin_->set_text(token(bb, ' ', 3));
536                         updateComboFromLength(bbunitscombo_,LyXLength("bp"));
537                 }
538                 controller().bbChanged = false;
539         } else {
540                 leftbottomxspin_->set_text("");
541                 leftbottomyspin_->set_text("");
542                 righttopxspin_->set_text("");
543                 righttopyspin_->set_text("");
544                 updateComboFromLength(bbunitscombo_,LyXLength("bp"));
545         }
546         bc().input(ButtonPolicy::SMI_VALID);
547 }
548
549
550 void GGraphics::onSubFigCheckToggled()
551 {
552         subfigentry_->set_sensitive(subfigcheck_->get_active());
553         bc().input(ButtonPolicy::SMI_VALID);
554 }
555
556
557 void GGraphics::onEditClicked()
558 {
559         controller().editGraphics();
560 }
561
562
563 void GGraphics::onInput()
564 {
565         bc().input(ButtonPolicy::SMI_VALID);
566 }
567
568 } // namespace frontend
569 } // namespace lyx