2 * \file GuiGraphics.cpp
3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
11 * Full author contact details are available in file CREDITS.
16 #include "GuiGraphics.h"
18 #include "ControlGraphics.h"
20 #include "LengthCombo.h"
21 #include "lengthcommon.h"
23 #include "qt_helpers.h"
24 #include "Validator.h"
26 #include "controllers/frontend_helpers.h"
28 #include "insets/InsetGraphicsParams.h"
30 #include "support/convert.h"
31 #include "support/lstrings.h"
32 #include "support/lyxlib.h"
33 #include "support/os.h"
36 #include <QCloseEvent>
39 #include <QPushButton>
45 using lyx::support::float_equal;
46 using lyx::support::token;
48 using lyx::support::os::internal_path;
50 #ifndef CXX_GLOBAL_CSTD
63 std::vector<typename Pair::first_type> const
64 getFirst(std::vector<Pair> const & pr)
66 std::vector<typename Pair::first_type> tmp(pr.size());
67 std::transform(pr.begin(), pr.end(), tmp.begin(),
68 boost::bind(&Pair::first, _1));
72 GuiGraphicsDialog::GuiGraphicsDialog(LyXView & lv)
73 : GuiDialog(lv, "graphics")
76 setViewTitle(_("Graphics"));
77 setController(new ControlGraphics(*this));
80 connect(okPB, SIGNAL(clicked()), this, SLOT(slotOK()));
81 connect(applyPB, SIGNAL(clicked()), this, SLOT(slotApply()));
82 connect(closePB, SIGNAL(clicked()), this, SLOT(slotClose()));
83 connect(restorePB, SIGNAL(clicked()), this, SLOT(slotRestore()));
86 connect(filename, SIGNAL(textChanged(const QString &)),
87 this, SLOT(change_adaptor()));
88 connect(WidthCB, SIGNAL( clicked()),
89 this, SLOT(change_adaptor()));
90 connect(HeightCB, SIGNAL( clicked()),
91 this, SLOT(change_adaptor()));
92 connect(Width, SIGNAL(textChanged(const QString &)),
93 this, SLOT(change_adaptor()));
94 connect(Height, SIGNAL(textChanged(const QString &)),
95 this, SLOT(change_adaptor()));
96 connect(heightUnit, SIGNAL(selectionChanged(lyx::Length::UNIT)),
97 this, SLOT(change_adaptor()));
98 connect(widthUnit, SIGNAL(selectionChanged(lyx::Length::UNIT)),
99 this, SLOT(change_adaptor()));
100 connect(aspectratio, SIGNAL(stateChanged(int)),
101 this, SLOT(change_adaptor()));
102 connect(angle, SIGNAL(textChanged(const QString &)),
103 this, SLOT(change_adaptor()));
104 connect(origin, SIGNAL(activated(int)),
105 this, SLOT(change_adaptor()));
106 connect(scaleCB, SIGNAL(clicked()),
107 this, SLOT(change_adaptor()));
108 connect(Scale, SIGNAL(textChanged(const QString &)),
109 this, SLOT(change_adaptor()));
110 connect(rotateOrderCB, SIGNAL(clicked()),
111 this, SLOT(change_adaptor()));
113 filename->setValidator(new PathValidator(true, filename));
114 setFocusProxy(filename);
116 QDoubleValidator * scaleValidator = new DoubleAutoValidator(Scale);
117 scaleValidator->setBottom(0);
118 scaleValidator->setDecimals(256); //I guess that will do
119 Scale->setValidator(scaleValidator);
120 Height->setValidator(unsignedLengthAutoValidator(Height));
121 Width->setValidator(unsignedLengthAutoValidator(Width));
122 angle->setValidator(new QDoubleValidator(-360, 360, 2, angle));
125 connect(clip, SIGNAL(stateChanged(int)),
126 this, SLOT(change_adaptor()));
127 connect(lbY, SIGNAL(textChanged(const QString&)),
128 this, SLOT(change_bb()));
129 connect(lbYunit, SIGNAL(activated(int)),
130 this, SLOT(change_bb()));
131 connect(rtY, SIGNAL(textChanged(const QString&)),
132 this, SLOT(change_bb()));
133 connect(rtYunit, SIGNAL(activated(int)),
134 this, SLOT(change_bb()));
135 connect(lbX, SIGNAL(textChanged(const QString&)),
136 this, SLOT(change_bb()));
137 connect(lbXunit, SIGNAL(activated(int)),
138 this, SLOT(change_bb()));
139 connect(rtX, SIGNAL(textChanged(const QString&)),
140 this, SLOT(change_bb()));
141 connect(rtXunit, SIGNAL(activated(int)),
142 this, SLOT(change_bb()));
143 connect(getPB, SIGNAL(clicked()),
144 this, SLOT(change_adaptor()));
146 lbX->setValidator(new QDoubleValidator(lbX));
147 lbY->setValidator(new QDoubleValidator(lbY));
148 rtX->setValidator(new QDoubleValidator(rtX));
149 rtY->setValidator(new QDoubleValidator(rtY));
152 connect(latexoptions, SIGNAL(textChanged(const QString&)),
153 this, SLOT(change_adaptor()));
154 connect(draftCB, SIGNAL(stateChanged(int)),
155 this, SLOT(change_adaptor()));
156 connect(unzipCB, SIGNAL(stateChanged(int)),
157 this, SLOT(change_adaptor()));
158 // FIXME: we should connect to clicked() when we move to Qt 4.2 because
159 // the toggled(bool) signal is also trigged when we update the widgets
160 // (rgh-4/07) this isn't as much or a problem as it was, because we're now
161 // using blockSignals() to keep from triggering that signal when we call
162 // setChecked(). Note, too, that clicked() would get called whenever it
163 // is clicked, even right clicked (I think), not just whenever it is
165 connect(subfigure, SIGNAL(toggled(bool)),
166 this, SLOT(change_adaptor()));
167 connect(subcaption, SIGNAL(textChanged(const QString&)),
168 this, SLOT(change_adaptor()));
169 connect(displayGB, SIGNAL(toggled(bool)),
170 this, SLOT(change_adaptor()));
171 connect(showCB, SIGNAL(currentIndexChanged(int)),
172 this, SLOT(change_adaptor()));
173 connect(displayscale, SIGNAL(textChanged(const QString&)),
174 this, SLOT(change_adaptor()));
175 displayscale->setValidator(new QIntValidator(displayscale));
177 bc().setPolicy(ButtonPolicy::NoRepeatedApplyReadOnlyPolicy);
179 bc().setApply(applyPB);
180 bc().setRestore(restorePB);
181 bc().setCancel(closePB);
183 bc().addReadOnly(latexoptions);
184 bc().addReadOnly(subfigure);
185 bc().addReadOnly(filenameL);
186 bc().addReadOnly(filename);
187 bc().addReadOnly(browsePB);
188 bc().addReadOnly(unzipCB);
189 bc().addReadOnly(bbFrame);
190 bc().addReadOnly(draftCB);
191 bc().addReadOnly(clip);
192 bc().addReadOnly(unzipCB);
193 bc().addReadOnly(displayGB);
194 bc().addReadOnly(sizeGB);
195 bc().addReadOnly(rotationGB);
196 bc().addReadOnly(latexoptions);
197 bc().addReadOnly(getPB);
198 bc().addReadOnly(rotateOrderCB);
200 // initialize the length validator
201 bc().addCheckedLineEdit(Scale, scaleCB);
202 bc().addCheckedLineEdit(Width, WidthCB);
203 bc().addCheckedLineEdit(Height, HeightCB);
204 bc().addCheckedLineEdit(displayscale, scaleLA);
205 bc().addCheckedLineEdit(angle, angleL);
206 bc().addCheckedLineEdit(lbX, xL);
207 bc().addCheckedLineEdit(lbY, yL);
208 bc().addCheckedLineEdit(rtX, xL_2);
209 bc().addCheckedLineEdit(rtY, yL_2);
210 bc().addCheckedLineEdit(filename, filenameL);
214 ControlGraphics & GuiGraphicsDialog::controller() const
216 return static_cast<ControlGraphics &>(GuiDialog::controller());
220 void GuiGraphicsDialog::showView()
226 void GuiGraphicsDialog::change_adaptor()
232 void GuiGraphicsDialog::change_bb()
234 controller().bbChanged = true;
235 LYXERR(Debug::GRAPHICS)
236 << "[controller().bb_Changed set to true]\n";
241 void GuiGraphicsDialog::closeEvent(QCloseEvent * e)
248 void GuiGraphicsDialog::on_browsePB_clicked()
250 docstring const str =
251 controller().browse(qstring_to_ucs4(filename->text()));
253 filename->setText(toqstr(str));
259 void GuiGraphicsDialog::on_getPB_clicked()
265 void GuiGraphicsDialog::on_editPB_clicked()
267 controller().editGraphics();
271 void GuiGraphicsDialog::on_filename_textChanged(const QString & filename)
273 editPB->setDisabled(filename.isEmpty());
277 void GuiGraphicsDialog::setAutoText()
279 if (scaleCB->isChecked())
281 if (!Scale->isEnabled() && Scale->text() != "100")
282 Scale->setText(QString("auto"));
284 setAutoTextCB(WidthCB, Width, widthUnit);
285 setAutoTextCB(HeightCB, Height, heightUnit);
289 void GuiGraphicsDialog::on_scaleCB_toggled(bool setScale)
291 Scale->setEnabled(setScale);
293 Scale->setText("100");
294 Scale->setFocus(Qt::OtherFocusReason);
297 WidthCB->setDisabled(setScale);
298 WidthCB->blockSignals(true);
299 WidthCB->setChecked(false);
300 WidthCB->blockSignals(false);
301 Width->setEnabled(false);
302 widthUnit->setEnabled(false);
304 HeightCB->setDisabled(setScale);
305 HeightCB->blockSignals(true);
306 HeightCB->setChecked(false);
307 HeightCB->blockSignals(false);
308 Height->setEnabled(false);
309 heightUnit->setEnabled(false);
311 aspectratio->setDisabled(true);
312 aspectratio->setChecked(true);
314 rotateOrderCB->setEnabled((WidthCB->isChecked() ||
315 HeightCB->isChecked() ||
316 scaleCB->isChecked()) &&
317 (angle->text() != "0"));
323 void GuiGraphicsDialog::on_WidthCB_toggled(bool setWidth)
325 Width->setEnabled(setWidth);
326 widthUnit->setEnabled(setWidth);
328 Width->setFocus(Qt::OtherFocusReason);
330 bool const setHeight = HeightCB->isChecked();
331 aspectratio->setEnabled(setWidth && setHeight);
332 aspectratio->blockSignals(true);
333 aspectratio->setChecked(!(setWidth && setHeight));
334 aspectratio->blockSignals(false);
336 scaleCB->setEnabled(!setWidth && !setHeight);
337 //already will be unchecked, so don't need to do that
338 Scale->setEnabled((!setWidth && !setHeight) //=scaleCB->isEnabled()
339 && scaleCB->isChecked()); //should be false, but let's check
340 rotateOrderCB->setEnabled((setWidth || setHeight ||
341 scaleCB->isChecked()) &&
342 (angle->text() != "0"));
348 void GuiGraphicsDialog::on_HeightCB_toggled(bool setHeight)
350 Height->setEnabled(setHeight);
351 heightUnit->setEnabled(setHeight);
353 Height->setFocus(Qt::OtherFocusReason);
355 bool const setWidth = WidthCB->isChecked();
356 aspectratio->setEnabled(setWidth && setHeight);
357 aspectratio->blockSignals(true);
358 aspectratio->setChecked(!(setWidth && setHeight));
359 aspectratio->blockSignals(false);
361 scaleCB->setEnabled(!setWidth && !setHeight);
363 Scale->setEnabled((!setWidth && !setHeight) //=scaleCB->isEnabled()
364 && scaleCB->isChecked()); //should be false
365 rotateOrderCB->setEnabled((setWidth || setHeight ||
366 scaleCB->isChecked()) &&
367 (angle->text() != "0"));
373 void GuiGraphicsDialog::on_angle_textChanged(const QString & filename)
375 rotateOrderCB->setEnabled((WidthCB->isChecked() ||
376 HeightCB->isChecked() ||
377 scaleCB->isChecked()) &&
381 // returns the number of the string s in the vector v
382 static int getItemNo(const vector<string> & v, string const & s)
384 vector<string>::const_iterator cit =
385 find(v.begin(), v.end(), s);
386 return (cit != v.end()) ? int(cit - v.begin()) : 0;
390 void GuiGraphicsDialog::update_contents()
392 // clear and fill in the comboboxes
393 vector<string> const bb_units = frontend::getBBUnits();
398 for (vector<string>::const_iterator it = bb_units.begin();
399 it != bb_units.end(); ++it) {
400 lbXunit->addItem(toqstr(*it));
401 lbYunit->addItem(toqstr(*it));
402 rtXunit->addItem(toqstr(*it));
403 rtYunit->addItem(toqstr(*it));
406 InsetGraphicsParams & igp = controller().params();
408 // set the right default unit
409 Length::UNIT unitDefault = Length::CM;
410 switch (lyxrc.default_papersize) {
413 case PAPER_USEXECUTIVE:
414 unitDefault = Length::IN;
421 igp.filename.outputFilename(kernel().bufferFilepath());
422 filename->setText(toqstr(name));
424 // set the bounding box values
425 if (igp.bb.empty()) {
426 string const bb = controller().readBB(igp.filename.absFilename());
427 // the values from the file always have the bigpoint-unit bp
428 lbX->setText(toqstr(token(bb, ' ', 0)));
429 lbY->setText(toqstr(token(bb, ' ', 1)));
430 rtX->setText(toqstr(token(bb, ' ', 2)));
431 rtY->setText(toqstr(token(bb, ' ', 3)));
432 lbXunit->setCurrentIndex(0);
433 lbYunit->setCurrentIndex(0);
434 rtXunit->setCurrentIndex(0);
435 rtYunit->setCurrentIndex(0);
436 controller().bbChanged = false;
438 // get the values from the inset
440 string const xl(token(igp.bb, ' ', 0));
441 string const yl(token(igp.bb, ' ', 1));
442 string const xr(token(igp.bb, ' ', 2));
443 string const yr(token(igp.bb, ' ', 3));
444 if (isValidLength(xl, &anyLength)) {
445 lbX->setText(toqstr(convert<string>(anyLength.value())));
446 string const unit(unit_name[anyLength.unit()]);
447 lbXunit->setCurrentIndex(getItemNo(bb_units, unit));
449 lbX->setText(toqstr(xl));
451 if (isValidLength(yl, &anyLength)) {
452 lbY->setText(toqstr(convert<string>(anyLength.value())));
453 string const unit(unit_name[anyLength.unit()]);
454 lbYunit->setCurrentIndex(getItemNo(bb_units, unit));
456 lbY->setText(toqstr(xl));
458 if (isValidLength(xr, &anyLength)) {
459 rtX->setText(toqstr(convert<string>(anyLength.value())));
460 string const unit(unit_name[anyLength.unit()]);
461 rtXunit->setCurrentIndex(getItemNo(bb_units, unit));
463 rtX->setText(toqstr(xl));
465 if (isValidLength(yr, &anyLength)) {
466 rtY->setText(toqstr(convert<string>(anyLength.value())));
467 string const unit(unit_name[anyLength.unit()]);
468 rtYunit->setCurrentIndex(getItemNo(bb_units, unit));
470 rtY->setText(toqstr(xl));
472 controller().bbChanged = true;
475 // Update the draft and clip mode
476 draftCB->setChecked(igp.draft);
477 clip->setChecked(igp.clip);
478 unzipCB->setChecked(igp.noUnzip);
480 // Update the subcaption check button and input field
481 subfigure->setChecked(igp.subcaption);
482 subcaption->setText(toqstr(igp.subcaptionText));
485 switch (igp.display) {
486 case graphics::DefaultDisplay: item = 0; break;
487 case graphics::MonochromeDisplay: item = 1; break;
488 case graphics::GrayscaleDisplay: item = 2; break;
489 case graphics::ColorDisplay: item = 3; break;
490 case graphics::NoDisplay: item = 0; break;
492 showCB->setCurrentIndex(item);
493 displayscale->setText(toqstr(convert<string>(igp.lyxscale)));
494 displayGB->setChecked(igp.display != graphics::NoDisplay);
496 // the output section (width/height)
498 Scale->setText(toqstr(igp.scale));
499 //igp.scale defaults to 100, so we treat it as empty
500 bool const scaleChecked = !igp.scale.empty() && igp.scale != "100";
501 scaleCB->blockSignals(true);
502 scaleCB->setChecked(scaleChecked);
503 scaleCB->blockSignals(false);
504 Scale->setEnabled(scaleChecked);
506 lengthAutoToWidgets(Width, widthUnit, igp.width,
508 bool const widthChecked = !Width->text().isEmpty() &&
509 Width->text() != "auto";
510 WidthCB->blockSignals(true);
511 WidthCB->setChecked(widthChecked);
512 WidthCB->blockSignals(false);
513 Width->setEnabled(widthChecked);
514 widthUnit->setEnabled(widthChecked);
516 lengthAutoToWidgets(Height, heightUnit, igp.height,
518 bool const heightChecked = !Height->text().isEmpty()
519 && Height->text() != "auto";
520 HeightCB->blockSignals(true);
521 HeightCB->setChecked(heightChecked);
522 HeightCB->blockSignals(false);
523 Height->setEnabled(heightChecked);
524 heightUnit->setEnabled(heightChecked);
526 scaleCB->setEnabled(!widthChecked && !heightChecked);
527 WidthCB->setEnabled(!scaleChecked);
528 HeightCB->setEnabled(!scaleChecked);
529 aspectratio->setEnabled(widthChecked && heightChecked);
533 angle->setText(toqstr(igp.rotateAngle));
534 rotateOrderCB->setChecked(igp.scaleBeforeRotation);
536 rotateOrderCB->setEnabled( (widthChecked || heightChecked || scaleChecked)
537 && igp.rotateAngle != "0");
541 vector<RotationOriginPair> origindata = getRotationOriginData();
542 vector<docstring> const origin_lang = getFirst(origindata);
543 origin_ltx = getSecond(origindata);
545 for (vector<docstring>::const_iterator it = origin_lang.begin();
546 it != origin_lang.end(); ++it)
547 origin->addItem(toqstr(*it));
549 if (!igp.rotateOrigin.empty())
550 origin->setCurrentIndex(
551 getItemNo(origin_ltx, igp.rotateOrigin));
553 origin->setCurrentIndex(0);
555 // disable edit button when no filename is present
556 editPB->setDisabled(filename->text().isEmpty());
559 latexoptions->setText(toqstr(igp.special));
563 void GuiGraphicsDialog::applyView()
565 InsetGraphicsParams & igp = controller().params();
567 igp.filename.set(internal_path(fromqstr(filename->text())),
568 kernel().bufferFilepath());
572 if (controller().bbChanged) {
574 string lbXs = fromqstr(lbX->text());
575 string lbYs = fromqstr(lbY->text());
576 string rtXs = fromqstr(rtX->text());
577 string rtYs = fromqstr(rtY->text());
579 convert<int>(lbXs) + convert<int>(lbYs) +
580 convert<int>(rtXs) + convert<int>(rtXs);
585 bb = lbXs + fromqstr(lbXunit->currentText()) + ' ';
589 bb += (lbYs + fromqstr(lbYunit->currentText()) + ' ');
593 bb += (rtXs + fromqstr(rtXunit->currentText()) + ' ');
597 bb += (rtYs + fromqstr(rtYunit->currentText()));
602 igp.draft = draftCB->isChecked();
603 igp.clip = clip->isChecked();
604 igp.subcaption = subfigure->isChecked();
605 igp.subcaptionText = fromqstr(subcaption->text());
607 switch (showCB->currentIndex()) {
608 case 0: igp.display = graphics::DefaultDisplay; break;
609 case 1: igp.display = graphics::MonochromeDisplay; break;
610 case 2: igp.display = graphics::GrayscaleDisplay; break;
611 case 3: igp.display = graphics::ColorDisplay; break;
615 if (!displayGB->isChecked())
616 igp.display = graphics::NoDisplay;
618 //the graphics section
619 if (scaleCB->isChecked() && !Scale->text().isEmpty()) {
620 igp.scale = fromqstr(Scale->text());
621 igp.width = Length("0pt");
622 igp.height = Length("0pt");
623 igp.keepAspectRatio = false;
625 igp.scale = string();
626 igp.width = WidthCB->isChecked() ?
627 //Note that this works even if Width is "auto", since in
628 //that case we get "0pt".
629 Length(widgetsToLength(Width, widthUnit)):
631 igp.height = HeightCB->isChecked() ?
632 Length(widgetsToLength(Height, heightUnit)) :
634 igp.keepAspectRatio = aspectratio->isEnabled() &&
635 aspectratio->isChecked() &&
636 igp.width.value() > 0 && igp.height.value() > 0;
639 igp.noUnzip = unzipCB->isChecked();
640 igp.lyxscale = displayscale->text().toInt();
641 igp.rotateAngle = fromqstr(angle->text());
643 double rotAngle = convert<double>(igp.rotateAngle);
644 if (std::abs(rotAngle) > 360.0) {
645 rotAngle -= 360.0 * floor(rotAngle / 360.0);
646 igp.rotateAngle = convert<string>(rotAngle);
649 // save the latex name for the origin. If it is the default
650 // then origin_ltx returns ""
651 igp.rotateOrigin = origin_ltx[origin->currentIndex()];
652 igp.scaleBeforeRotation = rotateOrderCB->isChecked();
654 // more latex options
655 igp.special = fromqstr(latexoptions->text());
659 void GuiGraphicsDialog::getBB()
661 string const fn = fromqstr(filename->text());
663 string const bb = controller().readBB(fn);
665 lbX->setText(toqstr(token(bb, ' ', 0)));
666 lbY->setText(toqstr(token(bb, ' ', 1)));
667 rtX->setText(toqstr(token(bb, ' ', 2)));
668 rtY->setText(toqstr(token(bb, ' ', 3)));
669 // the default units for the bb values when reading
671 lbXunit->setCurrentIndex(0);
672 lbYunit->setCurrentIndex(0);
673 rtXunit->setCurrentIndex(0);
674 rtYunit->setCurrentIndex(0);
676 controller().bbChanged = false;
681 bool GuiGraphicsDialog::isValid()
683 return !filename->text().isEmpty();
686 } // namespace frontend
690 #include "GuiGraphics_moc.cpp"