]> git.lyx.org Git - lyx.git/blob - src/frontends/gtk/GGraphics.C
some tabular fixes for the problems reported by Helge
[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 _GLIBCXX_CONCEPT_CHECKS
15 #undef _GLIBCXX_CONCEPT_CHECKS
16 #endif
17 #ifdef _GLIBCPP_CONCEPT_CHECKS
18 #undef _GLIBCPP_CONCEPT_CHECKS
19 #endif
20
21 #include "GGraphics.h"
22 #include "ControlGraphics.h"
23
24 #include "ghelpers.h"
25
26 #include "controllers/helper_funcs.h"
27
28 #include "insets/insetgraphicsParams.h"
29 #include "paper.h"
30 #include "lyxrc.h" // for lyxrc.default_papersize
31
32 #include "support/convert.h"
33 #include "support/lyxlib.h"  // for float_equal
34 #include "support/lstrings.h"
35
36 #include "debug.h"
37
38 using std::vector;
39 using std::string;
40
41 namespace lyx {
42
43 using support::float_equal;
44 using support::token;
45
46 namespace frontend {
47
48 namespace {
49 string defaultUnit("cm");
50 } // namespace anon
51
52 GGraphics::GGraphics(Dialog & parent)
53         : GViewCB<ControlGraphics, GViewGladeB>(parent, _("Graphics"), false)
54 {}
55
56
57 void GGraphics::doBuild()
58 {
59         string const gladeName = findGladeFile("graphics");
60         xml_ = Gnome::Glade::Xml::create(gladeName);
61
62         Gtk::Button * button;
63         xml_->get_widget("Close", button);
64         setCancel(button);
65         xml_->get_widget("Ok", button);
66         setOK(button);
67         xml_->get_widget("Apply", button);
68         setApply(button);
69         xml_->get_widget("Restore", button);
70         setRestore(button);
71
72         xml_->get_widget("Notebook", notebook_);
73
74         // File Page
75         xml_->get_widget("File", fileentry_);
76         xml_->get_widget("DisplayScale", displayscalespin_);
77         xml_->get_widget("OutputScale", outputscalespin_);
78         xml_->get_widget("Browse", browsebutton_);
79         xml_->get_widget("Edit", editbutton_);
80         xml_->get_widget("Display", displaycombo_);
81         xml_->get_widget("Width", widthspin_);
82         xml_->get_widget("Height", heightspin_);
83         xml_->get_widget("MaintainAspectRatio", aspectcheck_);
84         
85         Gtk::VBox * box;
86         xml_->get_widget("WidthUnits", box);
87         box->pack_start(widthunitscombo_, true, true, 0);
88         box->show_all();
89         xml_->get_widget("HeightUnits", box);
90         box->pack_start(heightunitscombo_, true, true, 0);
91         box->show_all();
92
93         xml_->get_widget("SetScaling", setscalingradio_);
94         xml_->get_widget("SetSize", setsizeradio_);
95
96         // Bounding Box Page
97         xml_->get_widget("ClipToBoundingBox", clipcheck_);
98         xml_->get_widget("RightTopX", righttopxspin_);
99         xml_->get_widget("RightTopY", righttopyspin_);
100         xml_->get_widget("LeftBottomX", leftbottomxspin_);
101         xml_->get_widget("LeftBottomY", leftbottomyspin_);
102         xml_->get_widget("BoundingUnits", bbunitscombo_);
103         xml_->get_widget("GetFromFile", bbfromfilebutton_);
104
105         // Extra Page
106         xml_->get_widget("Angle", anglespin_);
107         xml_->get_widget("Origin", origincombo_);
108         xml_->get_widget("UseSubfigure", subfigcheck_);
109         xml_->get_widget("SubfigureCaption", subfigentry_);
110         xml_->get_widget("LatexOptions", latexoptsentry_);
111         xml_->get_widget("DraftMode", draftcheck_);
112         xml_->get_widget("UnzipOnExport", unzipcheck_);
113
114         // Setup the columnrecord we use for combos
115         cols_.add(stringcol_);
116
117         // The file page
118
119         // Disable for read-only documents.
120         bcview().addReadOnly(browsebutton_);
121         bcview().addReadOnly(aspectcheck_);
122
123         fileentry_->signal_changed().connect(
124                 sigc::mem_fun(*this, &GGraphics::onInput));
125         displayscalespin_->signal_changed().connect(
126                 sigc::mem_fun(*this, &GGraphics::onInput));
127         displaycombo_->signal_changed().connect(
128                 sigc::mem_fun(*this, &GGraphics::onInput));
129         outputscalespin_->signal_changed().connect(
130                 sigc::mem_fun(*this, &GGraphics::onInput));
131         heightspin_->signal_changed().connect(
132                 sigc::mem_fun(*this, &GGraphics::onInput));
133         heightunitscombo_.signal_changed().connect(
134                 sigc::mem_fun(*this, &GGraphics::onInput));
135         widthspin_->signal_changed().connect(
136                 sigc::mem_fun(*this, &GGraphics::onInput));
137         widthunitscombo_.signal_changed().connect(
138                 sigc::mem_fun(*this, &GGraphics::onInput));
139         aspectcheck_->signal_toggled().connect(
140                 sigc::mem_fun(*this, &GGraphics::onInput));
141
142         setscalingradio_->signal_toggled().connect(
143                 sigc::mem_fun(*this, &GGraphics::onSizingModeChange));
144         setsizeradio_->signal_toggled().connect(
145                 sigc::mem_fun(*this, &GGraphics::onSizingModeChange));
146
147         browsebutton_->signal_clicked().connect(
148                 sigc::mem_fun(*this, &GGraphics::onBrowseClicked));
149
150         editbutton_->signal_clicked().connect(
151                 sigc::mem_fun(*this, &GGraphics::onEditClicked));
152
153         populateUnitCombo(widthunitscombo_, true);
154         populateUnitCombo(heightunitscombo_, true);
155
156         // the bounding box page
157         leftbottomxspin_->signal_changed().connect(
158                 sigc::mem_fun(*this, &GGraphics::onBBChange));
159         leftbottomyspin_->signal_changed().connect(
160                 sigc::mem_fun(*this, &GGraphics::onBBChange));
161         righttopxspin_->signal_changed().connect(
162                 sigc::mem_fun(*this, &GGraphics::onBBChange));
163         righttopyspin_->signal_changed().connect(
164                 sigc::mem_fun(*this, &GGraphics::onBBChange));
165         bbunitscombo_->signal_changed().connect(
166                 sigc::mem_fun(*this, &GGraphics::onBBChange));
167         clipcheck_->signal_toggled().connect(
168                 sigc::mem_fun(*this, &GGraphics::onBBChange));
169
170         bbfromfilebutton_->signal_clicked().connect(
171                 sigc::mem_fun(*this, &GGraphics::onBBFromFileClicked));
172
173         // disable for read-only documents
174         bcview().addReadOnly(bbfromfilebutton_);
175         bcview().addReadOnly(clipcheck_);
176         PopulateComboBox(bbunitscombo_, getBBUnits());
177
178
179         // the extra section
180
181         // disable for read-only documents
182         bcview().addReadOnly(anglespin_);
183         bcview().addReadOnly(origincombo_);
184         bcview().addReadOnly(subfigcheck_);
185         bcview().addReadOnly(latexoptsentry_);
186         bcview().addReadOnly(draftcheck_);
187         bcview().addReadOnly(unzipcheck_);
188
189         anglespin_->signal_changed().connect(
190                 sigc::mem_fun(*this, &GGraphics::onInput));
191         origincombo_->signal_changed().connect(
192                 sigc::mem_fun(*this, &GGraphics::onInput));
193         subfigentry_->signal_changed().connect(
194                 sigc::mem_fun(*this, &GGraphics::onInput));
195         latexoptsentry_->signal_changed().connect(
196                 sigc::mem_fun(*this, &GGraphics::onInput));
197         draftcheck_->signal_toggled().connect(
198                 sigc::mem_fun(*this, &GGraphics::onInput));
199         unzipcheck_->signal_toggled().connect(
200                 sigc::mem_fun(*this, &GGraphics::onInput));
201
202         subfigcheck_->signal_toggled().connect(
203                 sigc::mem_fun(*this, &GGraphics::onSubFigCheckToggled));
204
205         vector<RotationOriginPair> origindata = getRotationOriginData();
206
207         // Store the identifiers for later
208         origins_ = getSecond(origindata);
209         PopulateComboBox(origincombo_, getFirst(origindata));
210 }
211
212
213 void GGraphics::onSizingModeChange()
214 {
215         bool const scalingmode = setscalingradio_->get_active();
216
217         outputscalespin_->set_sensitive(scalingmode);
218         widthspin_->set_sensitive(!scalingmode);
219         heightspin_->set_sensitive(!scalingmode);
220         widthunitscombo_.set_sensitive(!scalingmode);
221         heightunitscombo_.set_sensitive(!scalingmode);
222         aspectcheck_->set_sensitive(!scalingmode);
223         bc().input(ButtonPolicy::SMI_VALID);
224 }
225
226
227 void GGraphics::PopulateComboBox(Gtk::ComboBox * combo,
228                                   vector<string> const & strings)
229 {
230         Glib::RefPtr<Gtk::ListStore> model = Gtk::ListStore::create(cols_);
231         vector<string>::const_iterator it = strings.begin();
232         vector<string>::const_iterator end = strings.end();
233         for (int rowindex = 0; it != end; ++it, ++rowindex) {
234                 Gtk::TreeModel::iterator row = model->append();
235                 (*row)[stringcol_] = *it;
236         }
237
238         combo->set_model(model);
239         Gtk::CellRendererText * cell = Gtk::manage(new Gtk::CellRendererText);
240         combo->pack_start(*cell, true);
241         combo->add_attribute(*cell, "text", 0);
242 }
243
244
245 void GGraphics::apply()
246 {
247         // Create the parameters structure and fill the data from the dialog.
248         InsetGraphicsParams & igp = controller().params();
249
250         // the file section
251         igp.filename.set(fileentry_->get_text(),
252                          kernel().bufferFilepath());
253
254         igp.lyxscale =
255                 static_cast<int>(displayscalespin_->get_adjustment()->get_value());
256         if (igp.lyxscale == 0) {
257                 igp.lyxscale = 100;
258         }
259
260         switch (displaycombo_->get_active_row_number()) {
261         case 4:
262                 igp.display = graphics::NoDisplay;
263                 break;
264         case 3:
265                 igp.display = graphics::ColorDisplay;
266                 break;
267         case 2:
268                 igp.display = graphics::GrayscaleDisplay;
269                 break;
270         case 1:
271                 igp.display = graphics::MonochromeDisplay;
272                 break;
273         case 0:
274                 igp.display = graphics::DefaultDisplay;
275         }
276
277         if (setscalingradio_->get_active()) {
278                 float scaleValue = outputscalespin_->get_adjustment()->get_value();
279                 igp.scale = convert<string>(scaleValue);
280                 if (float_equal(scaleValue, 0.0, 0.05))
281                         igp.scale = string();
282                 igp.width = LyXLength();
283         } else {
284                 igp.scale = string();
285                 igp.width = LyXLength(getLengthFromWidgets(*widthspin_->get_adjustment(), widthunitscombo_));
286         }
287
288         igp.height = LyXLength(getLengthFromWidgets(*heightspin_->get_adjustment(), heightunitscombo_));
289
290         igp.keepAspectRatio = aspectcheck_->get_active();
291         igp.draft = draftcheck_->get_active();
292         igp.noUnzip = !unzipcheck_->get_active();
293
294         // the bb section
295
296         if (!controller().bbChanged) {
297                 // don't write anything
298                 igp.bb.erase();
299         } else {
300                 Glib::ustring const bbunit = (*bbunitscombo_->get_active())[stringcol_];
301                 string bb;
302
303                 if (leftbottomxspin_->get_text().empty())
304                         bb = "0";
305                 else
306                         bb = leftbottomxspin_->get_text() + bbunit;
307
308                 bb += ' ';
309
310                 if (leftbottomyspin_->get_text().empty())
311                         bb += "0";
312                 else
313                         bb += leftbottomyspin_->get_text() + bbunit;
314
315                 bb += ' ';
316
317                 if (righttopxspin_->get_text().empty())
318                         bb += "0";
319                 else
320                         bb += righttopxspin_->get_text() + bbunit;
321
322                 bb += ' ';
323
324                 if (righttopyspin_->get_text().empty())
325                         bb += "0";
326                 else
327                         bb += righttopyspin_->get_text() + bbunit;
328
329                 /// The bounding box with "xLB yLB yRT yRT ", divided by a space!
330                 igp.bb = bb;
331         }
332         igp.clip = clipcheck_->get_active();
333
334         // the extra section
335         igp.rotateAngle = convert<string>(anglespin_->get_adjustment()->get_value());
336
337         int const origin_pos = origincombo_->get_active_row_number();
338         igp.rotateOrigin = origins_[origin_pos];
339
340         igp.subcaption = subfigcheck_->get_active();
341         igp.subcaptionText = subfigentry_->get_text();
342
343         igp.special = latexoptsentry_->get_text();
344 }
345
346
347 void GGraphics::update() {
348         // set the right default unit
349         defaultUnit = getDefaultUnit();
350
351         // Update dialog with details from inset
352         InsetGraphicsParams & igp = controller().params();
353
354         // the file section
355         string const name =
356                 igp.filename.outputFilename(kernel().bufferFilepath());
357         fileentry_->set_text(name);
358         displayscalespin_->get_adjustment()->set_value(igp.lyxscale);
359
360
361         switch (igp.display) {
362         case graphics::NoDisplay:
363                 displaycombo_->set_active(4);
364                 break;
365         case graphics::ColorDisplay:
366                 displaycombo_->set_active(3);
367                 break;
368         case graphics::GrayscaleDisplay:
369                 displaycombo_->set_active(2);
370                 break;
371         case graphics::MonochromeDisplay:
372                 displaycombo_->set_active(1);
373                 break;
374         case graphics::DefaultDisplay:
375                 displaycombo_->set_active(0);
376         }
377
378         outputscalespin_->get_adjustment()->set_value(convert<double>(igp.scale));
379
380         setWidgetsFromLength(*widthspin_->get_adjustment(), widthunitscombo_, igp.width);
381         setWidgetsFromLength(*heightspin_->get_adjustment(), heightunitscombo_, igp.height);
382
383         if (!igp.scale.empty()
384                 && !float_equal(convert<double>(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(convert<double>(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