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"
19 #include "LengthCombo.h"
20 #include "lengthcommon.h"
22 #include "qt_helpers.h"
23 #include "Validator.h"
25 #include "controllers/frontend_helpers.h"
27 #include "insets/InsetGraphicsParams.h"
29 #include "support/convert.h"
30 #include "support/lstrings.h"
31 #include "support/lyxlib.h"
32 #include "support/os.h"
35 #include <QCloseEvent>
38 #include <QPushButton>
44 using lyx::support::float_equal;
45 using lyx::support::token;
47 using lyx::support::os::internal_path;
49 #ifndef CXX_GLOBAL_CSTD
62 std::vector<typename Pair::first_type> const
63 getFirst(std::vector<Pair> const & pr)
65 std::vector<typename Pair::first_type> tmp(pr.size());
66 std::transform(pr.begin(), pr.end(), tmp.begin(),
67 boost::bind(&Pair::first, _1));
71 GuiGraphicsDialog::GuiGraphicsDialog(GuiGraphics * form)
76 connect(okPB, SIGNAL(clicked()),
77 form, SLOT(slotOK()));
78 connect(applyPB, SIGNAL(clicked()),
79 form, SLOT(slotApply()));
80 connect(closePB, SIGNAL(clicked()),
81 form, SLOT(slotClose()));
82 connect(restorePB, SIGNAL(clicked()),
83 form, 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));
179 void GuiGraphicsDialog::showView()
185 void GuiGraphicsDialog::change_adaptor()
191 void GuiGraphicsDialog::change_bb()
193 form_->controller().bbChanged = true;
194 LYXERR(Debug::GRAPHICS)
195 << "[controller().bb_Changed set to true]\n";
200 void GuiGraphicsDialog::closeEvent(QCloseEvent * e)
207 void GuiGraphicsDialog::on_browsePB_clicked()
209 docstring const str =
210 form_->controller().browse(qstring_to_ucs4(filename->text()));
212 filename->setText(toqstr(str));
218 void GuiGraphicsDialog::on_getPB_clicked()
224 void GuiGraphicsDialog::on_editPB_clicked()
226 form_->controller().editGraphics();
230 void GuiGraphicsDialog::on_filename_textChanged(const QString & filename)
232 editPB->setDisabled(filename.isEmpty());
236 void GuiGraphicsDialog::setAutoText()
238 if (scaleCB->isChecked()) return;
239 if (!Scale->isEnabled() && Scale->text() != "100")
240 Scale->setText(QString("auto"));
242 setAutoTextCB(WidthCB, Width, widthUnit);
243 setAutoTextCB(HeightCB, Height, heightUnit);
247 void GuiGraphicsDialog::on_scaleCB_toggled(bool setScale)
249 Scale->setEnabled(setScale);
251 Scale->setText("100");
252 Scale->setFocus(Qt::OtherFocusReason);
255 WidthCB->setDisabled(setScale);
256 WidthCB->blockSignals(true);
257 WidthCB->setChecked(false);
258 WidthCB->blockSignals(false);
259 Width->setEnabled(false);
260 widthUnit->setEnabled(false);
262 HeightCB->setDisabled(setScale);
263 HeightCB->blockSignals(true);
264 HeightCB->setChecked(false);
265 HeightCB->blockSignals(false);
266 Height->setEnabled(false);
267 heightUnit->setEnabled(false);
269 aspectratio->setDisabled(true);
270 aspectratio->setChecked(true);
272 rotateOrderCB->setEnabled((WidthCB->isChecked() ||
273 HeightCB->isChecked() ||
274 scaleCB->isChecked()) &&
275 (angle->text() != "0"));
280 void GuiGraphicsDialog::on_WidthCB_toggled(bool setWidth)
282 Width->setEnabled(setWidth);
283 widthUnit->setEnabled(setWidth);
285 Width->setFocus(Qt::OtherFocusReason);
287 bool const setHeight = HeightCB->isChecked();
288 aspectratio->setEnabled(setWidth && setHeight);
289 aspectratio->blockSignals(true);
290 aspectratio->setChecked(!(setWidth && setHeight));
291 aspectratio->blockSignals(false);
293 scaleCB->setEnabled(!setWidth && !setHeight);
294 //already will be unchecked, so don't need to do that
295 Scale->setEnabled((!setWidth && !setHeight) //=scaleCB->isEnabled()
296 && scaleCB->isChecked()); //should be false, but let's check
297 rotateOrderCB->setEnabled((setWidth || setHeight ||
298 scaleCB->isChecked()) &&
299 (angle->text() != "0"));
305 void GuiGraphicsDialog::on_HeightCB_toggled(bool setHeight)
307 Height->setEnabled(setHeight);
308 heightUnit->setEnabled(setHeight);
310 Height->setFocus(Qt::OtherFocusReason);
312 bool const setWidth = WidthCB->isChecked();
313 aspectratio->setEnabled(setWidth && setHeight);
314 aspectratio->blockSignals(true);
315 aspectratio->setChecked(!(setWidth && setHeight));
316 aspectratio->blockSignals(false);
318 scaleCB->setEnabled(!setWidth && !setHeight);
320 Scale->setEnabled((!setWidth && !setHeight) //=scaleCB->isEnabled()
321 && scaleCB->isChecked()); //should be false
322 rotateOrderCB->setEnabled((setWidth || setHeight ||
323 scaleCB->isChecked()) &&
324 (angle->text() != "0"));
330 void GuiGraphicsDialog::on_angle_textChanged(const QString & filename)
332 rotateOrderCB->setEnabled((WidthCB->isChecked() ||
333 HeightCB->isChecked() ||
334 scaleCB->isChecked()) &&
339 GuiGraphics::GuiGraphics(GuiDialog & parent)
340 : GuiView<GuiGraphicsDialog>(parent, _("Graphics"))
345 void GuiGraphics::build_dialog()
347 dialog_.reset(new GuiGraphicsDialog(this));
349 bc().setOK(dialog_->okPB);
350 bc().setApply(dialog_->applyPB);
351 bc().setRestore(dialog_->restorePB);
352 bc().setCancel(dialog_->closePB);
354 bc().addReadOnly(dialog_->latexoptions);
355 bc().addReadOnly(dialog_->subfigure);
356 bc().addReadOnly(dialog_->filenameL);
357 bc().addReadOnly(dialog_->filename);
358 bc().addReadOnly(dialog_->browsePB);
359 bc().addReadOnly(dialog_->unzipCB);
360 bc().addReadOnly(dialog_->bbFrame);
361 bc().addReadOnly(dialog_->draftCB);
362 bc().addReadOnly(dialog_->clip);
363 bc().addReadOnly(dialog_->unzipCB);
364 bc().addReadOnly(dialog_->displayGB);
365 bc().addReadOnly(dialog_->sizeGB);
366 bc().addReadOnly(dialog_->rotationGB);
367 bc().addReadOnly(dialog_->latexoptions);
368 bc().addReadOnly(dialog_->getPB);
369 bc().addReadOnly(dialog_->rotateOrderCB);
371 // initialize the length validator
372 bc().addCheckedLineEdit(dialog_->Scale, dialog_->scaleCB);
373 bc().addCheckedLineEdit(dialog_->Width, dialog_->WidthCB);
374 bc().addCheckedLineEdit(dialog_->Height, dialog_->HeightCB);
375 bc().addCheckedLineEdit(dialog_->displayscale, dialog_->scaleLA);
376 bc().addCheckedLineEdit(dialog_->angle, dialog_->angleL);
377 bc().addCheckedLineEdit(dialog_->lbX, dialog_->xL);
378 bc().addCheckedLineEdit(dialog_->lbY, dialog_->yL);
379 bc().addCheckedLineEdit(dialog_->rtX, dialog_->xL_2);
380 bc().addCheckedLineEdit(dialog_->rtY, dialog_->yL_2);
381 bc().addCheckedLineEdit(dialog_->filename, dialog_->filenameL);
385 // returns the number of the string s in the vector v
386 static int getItemNo(const vector<string> & v, string const & s)
388 vector<string>::const_iterator cit =
389 find(v.begin(), v.end(), s);
390 return (cit != v.end()) ? int(cit - v.begin()) : 0;
394 std::vector<typename Pair::second_type> const
395 getSecond(std::vector<Pair> const & pr)
397 std::vector<typename Pair::second_type> tmp(pr.size());
398 std::transform(pr.begin(), pr.end(), tmp.begin(),
399 boost::bind(&Pair::second, _1));
404 void GuiGraphics::update_contents()
406 // clear and fill in the comboboxes
407 vector<string> const bb_units = frontend::getBBUnits();
408 dialog_->lbXunit->clear();
409 dialog_->lbYunit->clear();
410 dialog_->rtXunit->clear();
411 dialog_->rtYunit->clear();
412 for (vector<string>::const_iterator it = bb_units.begin();
413 it != bb_units.end(); ++it) {
414 dialog_->lbXunit->addItem(toqstr(*it));
415 dialog_->lbYunit->addItem(toqstr(*it));
416 dialog_->rtXunit->addItem(toqstr(*it));
417 dialog_->rtYunit->addItem(toqstr(*it));
420 InsetGraphicsParams & igp = controller().params();
422 // set the right default unit
423 Length::UNIT unitDefault = Length::CM;
424 switch (lyxrc.default_papersize) {
427 case PAPER_USEXECUTIVE:
428 unitDefault = Length::IN;
435 igp.filename.outputFilename(kernel().bufferFilepath());
436 dialog_->filename->setText(toqstr(name));
438 // set the bounding box values
439 if (igp.bb.empty()) {
440 string const bb = controller().readBB(igp.filename.absFilename());
441 // the values from the file always have the bigpoint-unit bp
442 dialog_->lbX->setText(toqstr(token(bb, ' ', 0)));
443 dialog_->lbY->setText(toqstr(token(bb, ' ', 1)));
444 dialog_->rtX->setText(toqstr(token(bb, ' ', 2)));
445 dialog_->rtY->setText(toqstr(token(bb, ' ', 3)));
446 dialog_->lbXunit->setCurrentIndex(0);
447 dialog_->lbYunit->setCurrentIndex(0);
448 dialog_->rtXunit->setCurrentIndex(0);
449 dialog_->rtYunit->setCurrentIndex(0);
450 controller().bbChanged = false;
452 // get the values from the inset
454 string const xl(token(igp.bb, ' ', 0));
455 string const yl(token(igp.bb, ' ', 1));
456 string const xr(token(igp.bb, ' ', 2));
457 string const yr(token(igp.bb, ' ', 3));
458 if (isValidLength(xl, &anyLength)) {
459 dialog_->lbX->setText(toqstr(convert<string>(anyLength.value())));
460 string const unit(unit_name[anyLength.unit()]);
461 dialog_->lbXunit->setCurrentIndex(getItemNo(bb_units, unit));
463 dialog_->lbX->setText(toqstr(xl));
465 if (isValidLength(yl, &anyLength)) {
466 dialog_->lbY->setText(toqstr(convert<string>(anyLength.value())));
467 string const unit(unit_name[anyLength.unit()]);
468 dialog_->lbYunit->setCurrentIndex(getItemNo(bb_units, unit));
470 dialog_->lbY->setText(toqstr(xl));
472 if (isValidLength(xr, &anyLength)) {
473 dialog_->rtX->setText(toqstr(convert<string>(anyLength.value())));
474 string const unit(unit_name[anyLength.unit()]);
475 dialog_->rtXunit->setCurrentIndex(getItemNo(bb_units, unit));
477 dialog_->rtX->setText(toqstr(xl));
479 if (isValidLength(yr, &anyLength)) {
480 dialog_->rtY->setText(toqstr(convert<string>(anyLength.value())));
481 string const unit(unit_name[anyLength.unit()]);
482 dialog_->rtYunit->setCurrentIndex(getItemNo(bb_units, unit));
484 dialog_->rtY->setText(toqstr(xl));
486 controller().bbChanged = true;
489 // Update the draft and clip mode
490 dialog_->draftCB->setChecked(igp.draft);
491 dialog_->clip->setChecked(igp.clip);
492 dialog_->unzipCB->setChecked(igp.noUnzip);
494 // Update the subcaption check button and input field
495 dialog_->subfigure->setChecked(igp.subcaption);
496 dialog_->subcaption->setText(toqstr(igp.subcaptionText));
499 switch (igp.display) {
500 case graphics::DefaultDisplay: item = 0; break;
501 case graphics::MonochromeDisplay: item = 1; break;
502 case graphics::GrayscaleDisplay: item = 2; break;
503 case graphics::ColorDisplay: item = 3; break;
504 case graphics::NoDisplay: item = 0; break;
506 dialog_->showCB->setCurrentIndex(item);
507 dialog_->displayscale->setText(toqstr(convert<string>(igp.lyxscale)));
508 dialog_->displayGB->setChecked(igp.display != graphics::NoDisplay);
510 // the output section (width/height)
512 dialog_->Scale->setText(toqstr(igp.scale));
513 //igp.scale defaults to 100, so we treat it as empty
514 bool const scaleChecked = !igp.scale.empty() && igp.scale != "100";
515 dialog_->scaleCB->blockSignals(true);
516 dialog_->scaleCB->setChecked(scaleChecked);
517 dialog_->scaleCB->blockSignals(false);
518 dialog_->Scale->setEnabled(scaleChecked);
520 lengthAutoToWidgets(dialog_->Width, dialog_->widthUnit, igp.width,
522 bool const widthChecked = !dialog_->Width->text().isEmpty() &&
523 dialog_->Width->text() != "auto";
524 dialog_->WidthCB->blockSignals(true);
525 dialog_->WidthCB->setChecked(widthChecked);
526 dialog_->WidthCB->blockSignals(false);
527 dialog_->Width->setEnabled(widthChecked);
528 dialog_->widthUnit->setEnabled(widthChecked);
530 lengthAutoToWidgets(dialog_->Height, dialog_->heightUnit, igp.height,
532 bool const heightChecked = !dialog_->Height->text().isEmpty()
533 && dialog_->Height->text() != "auto";
534 dialog_->HeightCB->blockSignals(true);
535 dialog_->HeightCB->setChecked(heightChecked);
536 dialog_->HeightCB->blockSignals(false);
537 dialog_->Height->setEnabled(heightChecked);
538 dialog_->heightUnit->setEnabled(heightChecked);
540 dialog_->scaleCB->setEnabled(!widthChecked && !heightChecked);
541 dialog_->WidthCB->setEnabled(!scaleChecked);
542 dialog_->HeightCB->setEnabled(!scaleChecked);
543 dialog_->aspectratio->setEnabled(widthChecked && heightChecked);
545 dialog_->setAutoText();
547 dialog_->angle->setText(toqstr(igp.rotateAngle));
548 dialog_->rotateOrderCB->setChecked(igp.scaleBeforeRotation);
550 dialog_->rotateOrderCB->setEnabled((widthChecked ||
553 (igp.rotateAngle != "0"));
555 dialog_->origin->clear();
557 vector<RotationOriginPair> origindata = getRotationOriginData();
558 vector<docstring> const origin_lang = getFirst(origindata);
559 GuiGraphics::origin_ltx = getSecond(origindata);
561 for (vector<docstring>::const_iterator it = origin_lang.begin();
562 it != origin_lang.end(); ++it)
563 dialog_->origin->addItem(toqstr(*it));
565 if (!igp.rotateOrigin.empty())
566 dialog_->origin->setCurrentIndex(
567 getItemNo(origin_ltx, igp.rotateOrigin));
569 dialog_->origin->setCurrentIndex(0);
571 // disable edit button when no filename is present
572 dialog_->editPB->setDisabled(dialog_->filename->text().isEmpty());
575 dialog_->latexoptions->setText(toqstr(igp.special));
579 void GuiGraphics::applyView()
581 InsetGraphicsParams & igp = controller().params();
583 igp.filename.set(internal_path(fromqstr(dialog_->filename->text())),
584 kernel().bufferFilepath());
588 if (controller().bbChanged) {
590 string lbX(fromqstr(dialog_->lbX->text()));
591 string lbY(fromqstr(dialog_->lbY->text()));
592 string rtX(fromqstr(dialog_->rtX->text()));
593 string rtY(fromqstr(dialog_->rtY->text()));
595 convert<int>(lbX) + convert<int>(lbY) +
596 convert<int>(rtX) + convert<int>(rtX);
601 bb = lbX + fromqstr(dialog_->lbXunit->currentText()) + ' ';
605 bb += (lbY + fromqstr(dialog_->lbYunit->currentText()) + ' ');
609 bb += (rtX + fromqstr(dialog_->rtXunit->currentText()) + ' ');
613 bb += (rtY + fromqstr(dialog_->rtYunit->currentText()));
618 igp.draft = dialog_->draftCB->isChecked();
619 igp.clip = dialog_->clip->isChecked();
620 igp.subcaption = dialog_->subfigure->isChecked();
621 igp.subcaptionText = fromqstr(dialog_->subcaption->text());
623 switch (dialog_->showCB->currentIndex()) {
624 case 0: igp.display = graphics::DefaultDisplay; break;
625 case 1: igp.display = graphics::MonochromeDisplay; break;
626 case 2: igp.display = graphics::GrayscaleDisplay; break;
627 case 3: igp.display = graphics::ColorDisplay; break;
631 if (!dialog_->displayGB->isChecked())
632 igp.display = graphics::NoDisplay;
634 //the graphics section
635 if (dialog_->scaleCB->isChecked() && !dialog_->Scale->text().isEmpty()) {
636 igp.scale = fromqstr(dialog_->Scale->text());
637 igp.width = Length("0pt");
638 igp.height = Length("0pt");
639 igp.keepAspectRatio = false;
641 igp.scale = string();
642 igp.width = dialog_->WidthCB->isChecked() ?
643 //Note that this works even if dialog_->Width is "auto", since in
644 //that case we get "0pt".
645 Length(widgetsToLength(dialog_->Width, dialog_->widthUnit)):
647 igp.height = dialog_->HeightCB->isChecked() ?
648 Length(widgetsToLength(dialog_->Height, dialog_->heightUnit)) :
650 igp.keepAspectRatio = dialog_->aspectratio->isEnabled() &&
651 dialog_->aspectratio->isChecked() &&
652 igp.width.value() > 0 && igp.height.value() > 0;
655 igp.noUnzip = dialog_->unzipCB->isChecked();
657 igp.lyxscale = convert<int>(fromqstr(dialog_->displayscale->text()));
659 igp.rotateAngle = fromqstr(dialog_->angle->text());
661 double rotAngle = convert<double>(igp.rotateAngle);
662 if (std::abs(rotAngle) > 360.0) {
663 rotAngle -= 360.0 * floor(rotAngle / 360.0);
664 igp.rotateAngle = convert<string>(rotAngle);
667 // save the latex name for the origin. If it is the default
668 // then origin_ltx returns ""
670 GuiGraphics::origin_ltx[dialog_->origin->currentIndex()];
672 igp.scaleBeforeRotation = dialog_->rotateOrderCB->isChecked();
674 // more latex options
675 igp.special = fromqstr(dialog_->latexoptions->text());
679 void GuiGraphics::getBB()
681 string const filename(fromqstr(dialog_->filename->text()));
682 if (!filename.empty()) {
683 string const bb(controller().readBB(filename));
685 dialog_->lbX->setText(toqstr(token(bb, ' ', 0)));
686 dialog_->lbY->setText(toqstr(token(bb, ' ', 1)));
687 dialog_->rtX->setText(toqstr(token(bb, ' ', 2)));
688 dialog_->rtY->setText(toqstr(token(bb, ' ', 3)));
689 // the default units for the bb values when reading
691 dialog_->lbXunit->setCurrentIndex(0);
692 dialog_->lbYunit->setCurrentIndex(0);
693 dialog_->rtXunit->setCurrentIndex(0);
694 dialog_->rtYunit->setCurrentIndex(0);
696 controller().bbChanged = false;
701 bool GuiGraphics::isValid()
703 return !dialog_->filename->text().isEmpty();
706 } // namespace frontend
710 #include "GuiGraphics_moc.cpp"