]> git.lyx.org Git - features.git/blob - src/frontends/gtk/GGraphics.C
8722f9d89cc4e343ce7f5c3a92c47be7da1ab9ca
[features.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
197
198 void GGraphics::onSizingModeChange()
199 {
200         bool const scalingmode = setscalingradio_->get_active();
201
202         outputscalespin_->set_sensitive(scalingmode);
203         widthspin_->set_sensitive(!scalingmode);
204         heightspin_->set_sensitive(!scalingmode);
205         widthunitscombo_->set_sensitive(!scalingmode);
206         heightunitscombo_->set_sensitive(!scalingmode);
207         aspectcheck_->set_sensitive(!scalingmode);
208         bc().input(ButtonPolicy::SMI_VALID);
209 }
210
211
212 void GGraphics::PopulateComboBox(Gtk::ComboBox * combo,
213                                   vector<string> const & strings)
214 {
215         Glib::RefPtr<Gtk::ListStore> model = Gtk::ListStore::create(cols_);
216         vector<string>::const_iterator it = strings.begin();
217         vector<string>::const_iterator end = strings.end();
218         for (int rowindex = 0; it != end; ++it, ++rowindex) {
219                 Gtk::TreeModel::iterator row = model->append();
220                 (*row)[stringcol_] = *it;
221         }
222
223         combo->set_model(model);
224         Gtk::CellRendererText * cell = Gtk::manage(new Gtk::CellRendererText);
225         combo->pack_start(*cell, true);
226         combo->add_attribute(*cell, "text", 0);
227 }
228
229
230 void GGraphics::apply()
231 {
232         // Create the parameters structure and fill the data from the dialog.
233         InsetGraphicsParams & igp = controller().params();
234
235         // the file section
236         igp.filename.set(fileentry_->get_text(),
237                          kernel().bufferFilepath());
238
239         igp.lyxscale =
240                 static_cast<int>(displayscalespin_->get_adjustment()->get_value());
241         if (igp.lyxscale == 0) {
242                 igp.lyxscale = 100;
243         }
244
245         switch (displaycombo_->get_active_row_number()) {
246         case 4:
247                 igp.display = graphics::NoDisplay;
248                 break;
249         case 3:
250                 igp.display = graphics::ColorDisplay;
251                 break;
252         case 2:
253                 igp.display = graphics::GrayscaleDisplay;
254                 break;
255         case 1:
256                 igp.display = graphics::MonochromeDisplay;
257                 break;
258         case 0:
259                 igp.display = graphics::DefaultDisplay;
260         }
261
262         if (setscalingradio_->get_active()) {
263                 igp.scale = outputscalespin_->get_adjustment()->get_value();
264                 if (float_equal(igp.scale, 0.0, 0.05))
265                         igp.scale = 100.0;
266                 igp.width = LyXLength();
267         } else {
268                 igp.scale = 0.0;
269                 Glib::ustring const widthunit =
270                         (*widthunitscombo_->get_active())[stringcol_];
271                 igp.width = LyXLength(widthspin_->get_text() + widthunit);
272         }
273
274
275         Glib::ustring const heightunit =
276                 (*heightunitscombo_->get_active())[stringcol_];
277         igp.height = LyXLength(heightspin_->get_text() + heightunit);
278
279         igp.keepAspectRatio = aspectcheck_->get_active();
280         igp.draft = draftcheck_->get_active();
281         igp.noUnzip = !unzipcheck_->get_active();
282
283         // the bb section
284
285         if (!controller().bbChanged) {
286                 // don't write anything
287                 igp.bb.erase();
288         } else {
289                 Glib::ustring const bbunit = (*bbunitscombo_->get_active())[stringcol_];
290                 string bb;
291
292                 if (leftbottomxspin_->get_text().empty())
293                         bb = "0";
294                 else
295                         bb = leftbottomxspin_->get_text() + bbunit;
296
297                 bb += ' ';
298
299                 if (leftbottomyspin_->get_text().empty())
300                         bb += "0";
301                 else
302                         bb += leftbottomyspin_->get_text() + bbunit;
303
304                 bb += ' ';
305
306                 if (righttopxspin_->get_text().empty())
307                         bb += "0";
308                 else
309                         bb += righttopxspin_->get_text() + bbunit;
310
311                 bb += ' ';
312
313                 if (righttopyspin_->get_text().empty())
314                         bb += "0";
315                 else
316                         bb += righttopyspin_->get_text() + bbunit;
317
318                 /// The bounding box with "xLB yLB yRT yRT ", divided by a space!
319                 igp.bb = bb;
320         }
321         igp.clip = clipcheck_->get_active();
322
323         // the extra section
324         igp.rotateAngle = anglespin_->get_adjustment()->get_value();
325
326         int const origin_pos = origincombo_->get_active_row_number();
327         igp.rotateOrigin = origins_[origin_pos];
328
329         igp.subcaption = subfigcheck_->get_active();
330         igp.subcaptionText = subfigentry_->get_text();
331
332         igp.special = latexoptsentry_->get_text();
333 }
334
335
336 void GGraphics::update() {
337         // set the right default unit
338         defaultUnit = getDefaultUnit();
339
340         // Update dialog with details from inset
341         InsetGraphicsParams & igp = controller().params();
342
343         // the file section
344         string const name =
345                 igp.filename.outputFilename(kernel().bufferFilepath());
346         fileentry_->set_text(name);
347         displayscalespin_->get_adjustment()->set_value(igp.lyxscale);
348
349
350         switch (igp.display) {
351         case graphics::NoDisplay:
352                 displaycombo_->set_active(4);
353                 break;
354         case graphics::ColorDisplay:
355                 displaycombo_->set_active(3);
356                 break;
357         case graphics::GrayscaleDisplay:
358                 displaycombo_->set_active(2);
359                 break;
360         case graphics::MonochromeDisplay:
361                 displaycombo_->set_active(1);
362                 break;
363         case graphics::DefaultDisplay:
364                 displaycombo_->set_active(0);
365         }
366
367         outputscalespin_->get_adjustment()->set_value(igp.scale);
368         widthspin_->get_adjustment()->set_value(igp.width.value());
369         unitsComboFromLength(widthunitscombo_, stringcol_,
370                              igp.width, defaultUnit);
371         heightspin_->get_adjustment()->set_value(igp.height.value());
372         unitsComboFromLength(heightunitscombo_, stringcol_,
373                              igp.height, defaultUnit);
374
375         if (!float_equal(igp.scale, 0.0, 0.05)) {
376                 // scaling sizing mode
377                 setscalingradio_->set_active(true);
378         } else {
379                 setsizeradio_->set_active(true);
380         }
381         onSizingModeChange();
382
383         aspectcheck_->set_active(igp.keepAspectRatio);
384         draftcheck_->set_active(igp.draft);
385         unzipcheck_->set_active(!igp.noUnzip);
386
387         // the bb section
388         // set the bounding box values, if exists. First we need the whole
389         // path, because the controller knows nothing about the doc-dir
390         updateBB(igp.filename.absFilename(), igp.bb);
391         clipcheck_->set_active(igp.clip);
392
393         // the extra section
394         anglespin_->get_adjustment()->set_value(igp.rotateAngle);
395
396         int origin_pos;
397         if (igp.rotateOrigin.empty()) {
398                 origin_pos = 0;
399         } else {
400                 origin_pos = findPos(origins_, igp.rotateOrigin);
401         }
402         origincombo_->set_active(origin_pos);
403
404
405         subfigcheck_->set_active(igp.subcaption);
406         subfigentry_->set_text(igp.subcaptionText);
407         subfigentry_->set_sensitive(subfigcheck_->get_active());
408         latexoptsentry_->set_text(igp.special);
409
410         // open dialog in the file-tab, whenever filename is empty
411         if (igp.filename.empty()) {
412                 notebook_->set_current_page(0);
413         }
414
415
416         bc().input(ButtonPolicy::SMI_INVALID);
417 }
418
419
420 void GGraphics::updateBB(string const & filename, string const & bb_inset)
421 {
422         // Update dialog with details from inset
423         // set the bounding box values, if exists. First we need the whole
424         // path, because the controller knows nothing about the doc-dir
425
426         if (bb_inset.empty()) {
427                 lyxerr[Debug::GRAPHICS]
428                         << "GGraphics::updateBB() [no BoundingBox]\n";
429                 string const bb = controller().readBB(filename);
430                 if (!bb.empty()) {
431                         // get the values from the file
432                         // in this case we always have the point-unit
433                         leftbottomxspin_->set_text(token(bb, ' ', 0));
434                         leftbottomyspin_->set_text(token(bb, ' ', 1));
435                         righttopxspin_->set_text(token(bb, ' ', 2));
436                         righttopyspin_->set_text(token(bb, ' ', 3));
437                 } else {
438                         // no bb from file
439                         leftbottomxspin_->set_text("");
440                         leftbottomyspin_->set_text("");
441                         righttopxspin_->set_text("");
442                         righttopyspin_->set_text("");
443                 }
444                 unitsComboFromLength(bbunitscombo_, stringcol_,
445                                      LyXLength("bp"), defaultUnit);
446         } else {
447                 // get the values from the inset
448                 lyxerr[Debug::GRAPHICS]
449                         << "FormGraphics::updateBB(): igp has BoundingBox"
450                         << " ["<< bb_inset << "]\n";
451
452                 LyXLength anyLength;
453                 anyLength = LyXLength(token(bb_inset, ' ', 0));
454
455                 unitsComboFromLength(bbunitscombo_, stringcol_, anyLength, defaultUnit);
456
457                 leftbottomxspin_->get_adjustment()->set_value(anyLength.value());
458
459                 anyLength = LyXLength(token(bb_inset, ' ', 1));
460                 leftbottomyspin_->get_adjustment()->set_value(anyLength.value());
461
462                 anyLength = LyXLength(token(bb_inset, ' ', 2));
463                 righttopxspin_->get_adjustment()->set_value(anyLength.value());
464
465                 anyLength = LyXLength(token(bb_inset, ' ', 3));
466                 righttopyspin_->get_adjustment()->set_value(anyLength.value());
467         }
468         controller().bbChanged = false;
469 }
470
471
472 void GGraphics::onBrowseClicked()
473 {
474         // Get the filename from the dialog
475         string const in_name = fileentry_->get_text();
476         string const out_name = controller().browse(in_name);
477         lyxerr[Debug::GRAPHICS]
478                 << "[FormGraphics]out_name: " << out_name << "\n";
479         if (out_name != in_name && !out_name.empty()) {
480                 fileentry_->set_text(out_name);
481         }
482         if (controller().isFilenameValid(out_name) &&
483             !controller().bbChanged) {
484                 updateBB(out_name, string());
485         }
486
487         bc().input(ButtonPolicy::SMI_VALID);
488 }
489
490
491 void GGraphics::onBBChange()
492 {
493         controller().bbChanged = true;
494         bc().input(ButtonPolicy::SMI_VALID);
495 }
496
497
498 void GGraphics::onBBFromFileClicked()
499 {
500         string const filename = fileentry_->get_text();
501         if (!filename.empty()) {
502                 string bb = controller().readBB(filename);
503                 if (!bb.empty()) {
504                         leftbottomxspin_->set_text(token(bb, ' ', 0));
505                         leftbottomyspin_->set_text(token(bb, ' ', 1));
506                         righttopxspin_->set_text(token(bb, ' ', 2));
507                         righttopyspin_->set_text(token(bb, ' ', 3));
508                         unitsComboFromLength(bbunitscombo_, stringcol_,
509                                              LyXLength("bp"), defaultUnit);
510                 }
511                 controller().bbChanged = false;
512         } else {
513                 leftbottomxspin_->set_text("");
514                 leftbottomyspin_->set_text("");
515                 righttopxspin_->set_text("");
516                 righttopyspin_->set_text("");
517                 unitsComboFromLength(bbunitscombo_, stringcol_,
518                                      LyXLength("bp"), defaultUnit);
519         }
520         bc().input(ButtonPolicy::SMI_VALID);
521 }
522
523
524 void GGraphics::onSubFigCheckToggled()
525 {
526         subfigentry_->set_sensitive(subfigcheck_->get_active());
527         bc().input(ButtonPolicy::SMI_VALID);
528 }
529
530
531 void GGraphics::onEditClicked()
532 {
533         controller().editGraphics();
534 }
535
536
537 void GGraphics::onInput()
538 {
539         bc().input(ButtonPolicy::SMI_VALID);
540 }
541
542 } // namespace frontend
543 } // namespace lyx