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