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