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 "CheckedLineEdit.h"
20 #include "LengthCombo.h"
21 #include "lengthcommon.h"
24 #include "qt_helpers.h"
25 #include "Validator.h"
27 #include "controllers/frontend_helpers.h"
29 #include "insets/InsetGraphicsParams.h"
31 #include "support/convert.h"
32 #include "support/lstrings.h"
33 #include "support/lyxlib.h"
34 #include "support/os.h"
38 #include "insets/InsetGraphicsParams.h"
41 #include <QCloseEvent>
44 #include <QPushButton>
48 using lyx::support::float_equal;
49 using lyx::support::token;
51 using lyx::support::os::internal_path;
53 #ifndef CXX_GLOBAL_CSTD
65 GuiGraphicsDialog::GuiGraphicsDialog(GuiGraphics * form)
70 connect(okPB, SIGNAL(clicked()),
71 form, SLOT(slotOK()));
72 connect(applyPB, SIGNAL(clicked()),
73 form, SLOT(slotApply()));
74 connect(closePB, SIGNAL(clicked()),
75 form, SLOT(slotClose()));
76 connect(restorePB, SIGNAL(clicked()),
77 form, SLOT(slotRestore()));
80 connect(filename, SIGNAL(textChanged(const QString &)),
81 this, SLOT(change_adaptor()));
82 connect(WidthCB, SIGNAL( clicked()),
83 this, SLOT(change_adaptor()));
84 connect(HeightCB, SIGNAL( clicked()),
85 this, SLOT(change_adaptor()));
86 connect(Width, SIGNAL(textChanged(const QString &)),
87 this, SLOT(change_adaptor()));
88 connect(Height, SIGNAL(textChanged(const QString &)),
89 this, SLOT(change_adaptor()));
90 connect(heightUnit, SIGNAL(selectionChanged(lyx::Length::UNIT)),
91 this, SLOT(change_adaptor()));
92 connect(widthUnit, SIGNAL(selectionChanged(lyx::Length::UNIT)),
93 this, SLOT(change_adaptor()));
94 connect(aspectratio, SIGNAL(stateChanged(int)),
95 this, SLOT(change_adaptor()));
96 connect(angle, SIGNAL(textChanged(const QString &)),
97 this, SLOT(change_adaptor()));
98 connect(origin, SIGNAL(activated(int)),
99 this, SLOT(change_adaptor()));
100 connect(scaleCB, SIGNAL(clicked()),
101 this, SLOT(change_adaptor()));
102 connect(Scale, SIGNAL(textChanged(const QString &)),
103 this, SLOT(change_adaptor()));
104 connect(rotateOrderCB, SIGNAL(clicked()),
105 this, SLOT(change_adaptor()));
107 filename->setValidator(new PathValidator(true, filename));
108 setFocusProxy(filename);
110 QDoubleValidator * scaleValidator = new DoubleAutoValidator(Scale);
111 scaleValidator->setBottom(0);
112 scaleValidator->setDecimals(256); //I guess that will do
113 Scale->setValidator(scaleValidator);
114 Height->setValidator(unsignedLengthAutoValidator(Height));
115 Width->setValidator(unsignedLengthAutoValidator(Width));
116 angle->setValidator(new QDoubleValidator(-360, 360, 2, angle));
119 connect(clip, SIGNAL(stateChanged(int)),
120 this, SLOT(change_adaptor()));
121 connect(lbY, SIGNAL(textChanged(const QString&)),
122 this, SLOT(change_bb()));
123 connect(lbYunit, SIGNAL(activated(int)),
124 this, SLOT(change_bb()));
125 connect(rtY, SIGNAL(textChanged(const QString&)),
126 this, SLOT(change_bb()));
127 connect(rtYunit, SIGNAL(activated(int)),
128 this, SLOT(change_bb()));
129 connect(lbX, SIGNAL(textChanged(const QString&)),
130 this, SLOT(change_bb()));
131 connect(lbXunit, SIGNAL(activated(int)),
132 this, SLOT(change_bb()));
133 connect(rtX, SIGNAL(textChanged(const QString&)),
134 this, SLOT(change_bb()));
135 connect(rtXunit, SIGNAL(activated(int)),
136 this, SLOT(change_bb()));
137 connect(getPB, SIGNAL(clicked()),
138 this, SLOT(change_adaptor()));
140 lbX->setValidator(new QDoubleValidator(lbX));
141 lbY->setValidator(new QDoubleValidator(lbY));
142 rtX->setValidator(new QDoubleValidator(rtX));
143 rtY->setValidator(new QDoubleValidator(rtY));
146 connect(latexoptions, SIGNAL(textChanged(const QString&)),
147 this, SLOT(change_adaptor()));
148 connect(draftCB, SIGNAL(stateChanged(int)),
149 this, SLOT(change_adaptor()));
150 connect(unzipCB, SIGNAL(stateChanged(int)),
151 this, SLOT(change_adaptor()));
152 // FIXME: we should connect to clicked() when we move to Qt 4.2 because
153 // the toggled(bool) signal is also trigged when we update the widgets
154 // (rgh-4/07) this isn't as much or a problem as it was, because we're now
155 // using blockSignals() to keep from triggering that signal when we call
156 // setChecked(). Note, too, that clicked() would get called whenever it
157 // is clicked, even right clicked (I think), not just whenever it is
159 connect(subfigure, SIGNAL(toggled(bool)),
160 this, SLOT(change_adaptor()));
161 connect(subcaption, SIGNAL(textChanged(const QString&)),
162 this, SLOT(change_adaptor()));
163 connect(displayGB, SIGNAL(toggled(bool)),
164 this, SLOT(change_adaptor()));
165 connect(showCB, SIGNAL(currentIndexChanged(int)),
166 this, SLOT(change_adaptor()));
167 connect(displayscale, SIGNAL(textChanged(const QString&)),
168 this, SLOT(change_adaptor()));
169 displayscale->setValidator(new QIntValidator(displayscale));
173 void GuiGraphicsDialog::show()
179 void GuiGraphicsDialog::change_adaptor()
185 void GuiGraphicsDialog::change_bb()
187 form_->controller().bbChanged = true;
188 LYXERR(Debug::GRAPHICS)
189 << "[controller().bb_Changed set to true]\n";
194 void GuiGraphicsDialog::closeEvent(QCloseEvent * e)
201 void GuiGraphicsDialog::on_browsePB_clicked()
203 docstring const str =
204 form_->controller().browse(qstring_to_ucs4(filename->text()));
206 filename->setText(toqstr(str));
212 void GuiGraphicsDialog::on_getPB_clicked()
218 void GuiGraphicsDialog::on_editPB_clicked()
220 form_->controller().editGraphics();
224 void GuiGraphicsDialog::on_filename_textChanged(const QString & filename)
226 editPB->setDisabled(filename.isEmpty());
230 void GuiGraphicsDialog::setAutoText()
232 if (scaleCB->isChecked()) return;
233 if (!Scale->isEnabled() && Scale->text() != "100")
234 Scale->setText(QString("auto"));
236 setAutoTextCB(WidthCB, Width, widthUnit);
237 setAutoTextCB(HeightCB, Height, heightUnit);
241 void GuiGraphicsDialog::on_scaleCB_toggled(bool setScale)
243 Scale->setEnabled(setScale);
245 Scale->setText("100");
246 Scale->setFocus(Qt::OtherFocusReason);
249 WidthCB->setDisabled(setScale);
250 WidthCB->blockSignals(true);
251 WidthCB->setChecked(false);
252 WidthCB->blockSignals(false);
253 Width->setEnabled(false);
254 widthUnit->setEnabled(false);
256 HeightCB->setDisabled(setScale);
257 HeightCB->blockSignals(true);
258 HeightCB->setChecked(false);
259 HeightCB->blockSignals(false);
260 Height->setEnabled(false);
261 heightUnit->setEnabled(false);
263 aspectratio->setDisabled(true);
264 aspectratio->setChecked(true);
266 rotateOrderCB->setEnabled((WidthCB->isChecked() ||
267 HeightCB->isChecked() ||
268 scaleCB->isChecked()) &&
269 (angle->text() != "0"));
274 void GuiGraphicsDialog::on_WidthCB_toggled(bool setWidth)
276 Width->setEnabled(setWidth);
277 widthUnit->setEnabled(setWidth);
279 Width->setFocus(Qt::OtherFocusReason);
281 bool const setHeight = HeightCB->isChecked();
282 aspectratio->setEnabled(setWidth && setHeight);
283 aspectratio->blockSignals(true);
284 aspectratio->setChecked(!(setWidth && setHeight));
285 aspectratio->blockSignals(false);
287 scaleCB->setEnabled(!setWidth && !setHeight);
288 //already will be unchecked, so don't need to do that
289 Scale->setEnabled((!setWidth && !setHeight) //=scaleCB->isEnabled()
290 && scaleCB->isChecked()); //should be false, but let's check
291 rotateOrderCB->setEnabled((setWidth || setHeight ||
292 scaleCB->isChecked()) &&
293 (angle->text() != "0"));
298 void GuiGraphicsDialog::on_HeightCB_toggled(bool setHeight)
300 Height->setEnabled(setHeight);
301 heightUnit->setEnabled(setHeight);
303 Height->setFocus(Qt::OtherFocusReason);
305 bool const setWidth = WidthCB->isChecked();
306 aspectratio->setEnabled(setWidth && setHeight);
307 aspectratio->blockSignals(true);
308 aspectratio->setChecked(!(setWidth && setHeight));
309 aspectratio->blockSignals(false);
311 scaleCB->setEnabled(!setWidth && !setHeight);
313 Scale->setEnabled((!setWidth && !setHeight) //=scaleCB->isEnabled()
314 && scaleCB->isChecked()); //should be false
315 rotateOrderCB->setEnabled((setWidth || setHeight ||
316 scaleCB->isChecked()) &&
317 (angle->text() != "0"));
323 void GuiGraphicsDialog::on_angle_textChanged(const QString & filename)
325 rotateOrderCB->setEnabled((WidthCB->isChecked() ||
326 HeightCB->isChecked() ||
327 scaleCB->isChecked()) &&
332 GuiGraphics::GuiGraphics(Dialog & parent)
333 : GuiView<GuiGraphicsDialog>(parent, _("Graphics"))
338 void GuiGraphics::build_dialog()
340 dialog_.reset(new GuiGraphicsDialog(this));
342 bcview().setOK(dialog_->okPB);
343 bcview().setApply(dialog_->applyPB);
344 bcview().setRestore(dialog_->restorePB);
345 bcview().setCancel(dialog_->closePB);
347 bcview().addReadOnly(dialog_->latexoptions);
348 bcview().addReadOnly(dialog_->subfigure);
349 bcview().addReadOnly(dialog_->filenameL);
350 bcview().addReadOnly(dialog_->filename);
351 bcview().addReadOnly(dialog_->browsePB);
352 bcview().addReadOnly(dialog_->unzipCB);
353 bcview().addReadOnly(dialog_->bbFrame);
354 bcview().addReadOnly(dialog_->draftCB);
355 bcview().addReadOnly(dialog_->clip);
356 bcview().addReadOnly(dialog_->unzipCB);
357 bcview().addReadOnly(dialog_->displayGB);
358 bcview().addReadOnly(dialog_->sizeGB);
359 bcview().addReadOnly(dialog_->rotationGB);
360 bcview().addReadOnly(dialog_->latexoptions);
361 bcview().addReadOnly(dialog_->getPB);
362 bcview().addReadOnly(dialog_->rotateOrderCB);
364 // initialize the length validator
365 addCheckedLineEdit(bcview(), dialog_->Scale, dialog_->scaleCB);
366 addCheckedLineEdit(bcview(), dialog_->Width, dialog_->WidthCB);
367 addCheckedLineEdit(bcview(), dialog_->Height, dialog_->HeightCB);
368 addCheckedLineEdit(bcview(), dialog_->displayscale, dialog_->scaleLA);
369 addCheckedLineEdit(bcview(), dialog_->angle, dialog_->angleL);
370 addCheckedLineEdit(bcview(), dialog_->lbX, dialog_->xL);
371 addCheckedLineEdit(bcview(), dialog_->lbY, dialog_->yL);
372 addCheckedLineEdit(bcview(), dialog_->rtX, dialog_->xL_2);
373 addCheckedLineEdit(bcview(), dialog_->rtY, dialog_->yL_2);
374 addCheckedLineEdit(bcview(), dialog_->filename, dialog_->filenameL);
380 // returns the number of the string s in the vector v
381 int getItemNo(vector<string> v, string const & s) {
382 vector<string>::const_iterator cit =
383 find(v.begin(), v.end(), s);
384 return (cit != v.end()) ? int(cit - v.begin()) : 0;
390 void GuiGraphics::update_contents()
392 // clear and fill in the comboboxes
393 vector<string> const bb_units = frontend::getBBUnits();
394 dialog_->lbXunit->clear();
395 dialog_->lbYunit->clear();
396 dialog_->rtXunit->clear();
397 dialog_->rtYunit->clear();
398 for (vector<string>::const_iterator it = bb_units.begin();
399 it != bb_units.end(); ++it) {
400 dialog_->lbXunit->addItem(toqstr(*it));
401 dialog_->lbYunit->addItem(toqstr(*it));
402 dialog_->rtXunit->addItem(toqstr(*it));
403 dialog_->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 dialog_->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 dialog_->lbX->setText(toqstr(token(bb, ' ', 0)));
429 dialog_->lbY->setText(toqstr(token(bb, ' ', 1)));
430 dialog_->rtX->setText(toqstr(token(bb, ' ', 2)));
431 dialog_->rtY->setText(toqstr(token(bb, ' ', 3)));
432 dialog_->lbXunit->setCurrentIndex(0);
433 dialog_->lbYunit->setCurrentIndex(0);
434 dialog_->rtXunit->setCurrentIndex(0);
435 dialog_->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 dialog_->lbX->setText(toqstr(convert<string>(anyLength.value())));
446 string const unit(unit_name[anyLength.unit()]);
447 dialog_->lbXunit->setCurrentIndex(getItemNo(bb_units, unit));
449 dialog_->lbX->setText(toqstr(xl));
451 if (isValidLength(yl, &anyLength)) {
452 dialog_->lbY->setText(toqstr(convert<string>(anyLength.value())));
453 string const unit(unit_name[anyLength.unit()]);
454 dialog_->lbYunit->setCurrentIndex(getItemNo(bb_units, unit));
456 dialog_->lbY->setText(toqstr(xl));
458 if (isValidLength(xr, &anyLength)) {
459 dialog_->rtX->setText(toqstr(convert<string>(anyLength.value())));
460 string const unit(unit_name[anyLength.unit()]);
461 dialog_->rtXunit->setCurrentIndex(getItemNo(bb_units, unit));
463 dialog_->rtX->setText(toqstr(xl));
465 if (isValidLength(yr, &anyLength)) {
466 dialog_->rtY->setText(toqstr(convert<string>(anyLength.value())));
467 string const unit(unit_name[anyLength.unit()]);
468 dialog_->rtYunit->setCurrentIndex(getItemNo(bb_units, unit));
470 dialog_->rtY->setText(toqstr(xl));
472 controller().bbChanged = true;
475 // Update the draft and clip mode
476 dialog_->draftCB->setChecked(igp.draft);
477 dialog_->clip->setChecked(igp.clip);
478 dialog_->unzipCB->setChecked(igp.noUnzip);
480 // Update the subcaption check button and input field
481 dialog_->subfigure->setChecked(igp.subcaption);
482 dialog_->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 dialog_->showCB->setCurrentIndex(item);
493 dialog_->displayscale->setText(toqstr(convert<string>(igp.lyxscale)));
494 dialog_->displayGB->setChecked(igp.display != graphics::NoDisplay);
496 // the output section (width/height)
498 dialog_->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 dialog_->scaleCB->blockSignals(true);
502 dialog_->scaleCB->setChecked(scaleChecked);
503 dialog_->scaleCB->blockSignals(false);
504 dialog_->Scale->setEnabled(scaleChecked);
506 lengthAutoToWidgets(dialog_->Width, dialog_->widthUnit, igp.width,
508 bool const widthChecked = !dialog_->Width->text().isEmpty() &&
509 dialog_->Width->text() != "auto";
510 dialog_->WidthCB->blockSignals(true);
511 dialog_->WidthCB->setChecked(widthChecked);
512 dialog_->WidthCB->blockSignals(false);
513 dialog_->Width->setEnabled(widthChecked);
514 dialog_->widthUnit->setEnabled(widthChecked);
516 lengthAutoToWidgets(dialog_->Height, dialog_->heightUnit, igp.height,
518 bool const heightChecked = !dialog_->Height->text().isEmpty()
519 && dialog_->Height->text() != "auto";
520 dialog_->HeightCB->blockSignals(true);
521 dialog_->HeightCB->setChecked(heightChecked);
522 dialog_->HeightCB->blockSignals(false);
523 dialog_->Height->setEnabled(heightChecked);
524 dialog_->heightUnit->setEnabled(heightChecked);
526 dialog_->scaleCB->setEnabled(!widthChecked && !heightChecked);
527 dialog_->WidthCB->setEnabled(!scaleChecked);
528 dialog_->HeightCB->setEnabled(!scaleChecked);
529 dialog_->aspectratio->setEnabled(widthChecked && heightChecked);
531 dialog_->setAutoText();
533 dialog_->angle->setText(toqstr(igp.rotateAngle));
534 dialog_->rotateOrderCB->setChecked(igp.scaleBeforeRotation);
536 dialog_->rotateOrderCB->setEnabled((widthChecked ||
539 (igp.rotateAngle != "0"));
541 dialog_->origin->clear();
543 vector<RotationOriginPair> origindata = getRotationOriginData();
544 vector<docstring> const origin_lang = getFirst(origindata);
545 GuiGraphics::origin_ltx = getSecond(origindata);
547 for (vector<docstring>::const_iterator it = origin_lang.begin();
548 it != origin_lang.end(); ++it)
549 dialog_->origin->addItem(toqstr(*it));
551 if (!igp.rotateOrigin.empty())
552 dialog_->origin->setCurrentIndex(
553 getItemNo(origin_ltx, igp.rotateOrigin));
555 dialog_->origin->setCurrentIndex(0);
557 // disable edit button when no filename is present
558 dialog_->editPB->setDisabled(dialog_->filename->text().isEmpty());
561 dialog_->latexoptions->setText(toqstr(igp.special));
565 void GuiGraphics::apply()
567 InsetGraphicsParams & igp = controller().params();
569 igp.filename.set(internal_path(fromqstr(dialog_->filename->text())),
570 kernel().bufferFilepath());
574 if (controller().bbChanged) {
576 string lbX(fromqstr(dialog_->lbX->text()));
577 string lbY(fromqstr(dialog_->lbY->text()));
578 string rtX(fromqstr(dialog_->rtX->text()));
579 string rtY(fromqstr(dialog_->rtY->text()));
581 convert<int>(lbX) + convert<int>(lbY) +
582 convert<int>(rtX) + convert<int>(rtX);
587 bb = lbX + fromqstr(dialog_->lbXunit->currentText()) + ' ';
591 bb += (lbY + fromqstr(dialog_->lbYunit->currentText()) + ' ');
595 bb += (rtX + fromqstr(dialog_->rtXunit->currentText()) + ' ');
599 bb += (rtY + fromqstr(dialog_->rtYunit->currentText()));
604 igp.draft = dialog_->draftCB->isChecked();
605 igp.clip = dialog_->clip->isChecked();
606 igp.subcaption = dialog_->subfigure->isChecked();
607 igp.subcaptionText = fromqstr(dialog_->subcaption->text());
609 switch (dialog_->showCB->currentIndex()) {
610 case 0: igp.display = graphics::DefaultDisplay; break;
611 case 1: igp.display = graphics::MonochromeDisplay; break;
612 case 2: igp.display = graphics::GrayscaleDisplay; break;
613 case 3: igp.display = graphics::ColorDisplay; break;
617 if (!dialog_->displayGB->isChecked())
618 igp.display = graphics::NoDisplay;
620 //the graphics section
621 if (dialog_->scaleCB->isChecked() && !dialog_->Scale->text().isEmpty()) {
622 igp.scale = fromqstr(dialog_->Scale->text());
623 igp.width = Length("0pt");
624 igp.height = Length("0pt");
625 igp.keepAspectRatio = false;
627 igp.scale = string();
628 igp.width = dialog_->WidthCB->isChecked() ?
629 //Note that this works even if dialog_->Width is "auto", since in
630 //that case we get "0pt".
631 Length(widgetsToLength(dialog_->Width, dialog_->widthUnit)):
633 igp.height = dialog_->HeightCB->isChecked() ?
634 Length(widgetsToLength(dialog_->Height, dialog_->heightUnit)) :
636 igp.keepAspectRatio = dialog_->aspectratio->isEnabled() &&
637 dialog_->aspectratio->isChecked() &&
638 igp.width.value() > 0 && igp.height.value() > 0;
641 igp.noUnzip = dialog_->unzipCB->isChecked();
643 igp.lyxscale = convert<int>(fromqstr(dialog_->displayscale->text()));
645 igp.rotateAngle = fromqstr(dialog_->angle->text());
647 double rotAngle = convert<double>(igp.rotateAngle);
648 if (std::abs(rotAngle) > 360.0) {
649 rotAngle -= 360.0 * floor(rotAngle / 360.0);
650 igp.rotateAngle = convert<string>(rotAngle);
653 // save the latex name for the origin. If it is the default
654 // then origin_ltx returns ""
656 GuiGraphics::origin_ltx[dialog_->origin->currentIndex()];
658 igp.scaleBeforeRotation = dialog_->rotateOrderCB->isChecked();
660 // more latex options
661 igp.special = fromqstr(dialog_->latexoptions->text());
665 void GuiGraphics::getBB()
667 string const filename(fromqstr(dialog_->filename->text()));
668 if (!filename.empty()) {
669 string const bb(controller().readBB(filename));
671 dialog_->lbX->setText(toqstr(token(bb, ' ', 0)));
672 dialog_->lbY->setText(toqstr(token(bb, ' ', 1)));
673 dialog_->rtX->setText(toqstr(token(bb, ' ', 2)));
674 dialog_->rtY->setText(toqstr(token(bb, ' ', 3)));
675 // the default units for the bb values when reading
677 dialog_->lbXunit->setCurrentIndex(0);
678 dialog_->lbYunit->setCurrentIndex(0);
679 dialog_->rtXunit->setCurrentIndex(0);
680 dialog_->rtYunit->setCurrentIndex(0);
682 controller().bbChanged = false;
687 bool GuiGraphics::isValid()
689 return !dialog_->filename->text().isEmpty();
692 } // namespace frontend
696 #include "GuiGraphics_moc.cpp"