2 * \file GuiExternal.cpp
3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
7 * \author Angus Leeming
9 * Full author contact details are available in file CREDITS.
14 // Qt defines a macro 'signals' that clashes with a boost namespace.
15 // All is well if the namespace is visible first.
16 #include "lengthcommon.h"
19 #include "controllers/ButtonController.h"
21 #include "insets/ExternalTemplate.h"
22 #include "insets/InsetExternal.h"
24 #include "support/lstrings.h"
25 #include "support/convert.h"
26 #include "support/os.h"
27 #include "support/lyxlib.h"
29 #include "GuiExternal.h"
32 #include "CheckedLineEdit.h"
33 #include "LengthCombo.h"
34 #include "qt_helpers.h"
35 #include "Validator.h"
38 #include <QPushButton>
41 #include <QTextBrowser>
43 namespace external = lyx::external;
45 using lyx::support::isStrDbl;
46 using lyx::support::token;
47 using lyx::support::trim;
48 using lyx::support::float_equal;
50 using lyx::support::os::internal_path;
60 /////////////////////////////////////////////////////////////////////
64 /////////////////////////////////////////////////////////////////////
67 GuiExternalDialog::GuiExternalDialog(GuiExternal * form)
71 connect(okPB, SIGNAL(clicked()), form, SLOT(slotOK()));
72 connect(applyPB, SIGNAL(clicked()), form, SLOT(slotApply()));
73 connect(closePB, SIGNAL(clicked()), form, SLOT(slotClose()));
75 connect(displayCB, SIGNAL(toggled(bool)),
76 showCO, SLOT(setEnabled(bool)));
77 connect(displayCB, SIGNAL(toggled(bool)),
78 displayscaleED, SLOT(setEnabled(bool)));
79 connect(showCO, SIGNAL(activated(const QString&)),
80 this, SLOT(change_adaptor()));
81 connect(originCO, SIGNAL(activated(int)),
82 this, SLOT(change_adaptor()));
83 connect(aspectratioCB, SIGNAL(stateChanged(int)),
84 this, SLOT(change_adaptor()));
85 connect(browsePB, SIGNAL(clicked()),
86 this, SLOT(browseClicked()));
87 connect(editPB, SIGNAL(clicked()),
88 this, SLOT(editClicked()));
89 connect(externalCO, SIGNAL(activated(const QString &)),
90 this, SLOT(templateChanged()));
91 connect(extraED, SIGNAL(textChanged(const QString &)),
92 this, SLOT(extraChanged(const QString&)));
93 connect(extraFormatCO, SIGNAL(activated(const QString &)),
94 this, SLOT(formatChanged(const QString&)));
95 connect(widthUnitCO, SIGNAL(activated(int)),
96 this, SLOT(widthUnitChanged()));
97 connect(heightUnitCO, SIGNAL(selectionChanged(lyx::Length::UNIT)),
98 this, SLOT(change_adaptor()));
99 connect(displayCB, SIGNAL(stateChanged(int)),
100 this, SLOT(change_adaptor()));
101 connect(displayscaleED, SIGNAL(textChanged(const QString &)),
102 this, SLOT(change_adaptor()));
103 connect(angleED, SIGNAL(textChanged(const QString &)),
104 this, SLOT(change_adaptor()));
105 connect(widthED, SIGNAL(textChanged(const QString &)),
106 this, SLOT(sizeChanged()));
107 connect(heightED, SIGNAL(textChanged(const QString &)),
108 this, SLOT(sizeChanged()));
109 connect(fileED, SIGNAL(textChanged(const QString &)),
110 this, SLOT(change_adaptor()));
111 connect(clipCB, SIGNAL(stateChanged(int)),
112 this, SLOT(change_adaptor()));
113 connect(getbbPB, SIGNAL(clicked()), this, SLOT(getbbClicked()));
114 connect(xrED, SIGNAL(textChanged(const QString &)), this, SLOT(bbChanged()));
115 connect(ytED, SIGNAL(textChanged(const QString &)), this, SLOT(bbChanged()));
116 connect(xlED, SIGNAL(textChanged(const QString &)), this, SLOT(bbChanged()));
117 connect(ybED, SIGNAL(textChanged(const QString &)), this, SLOT(bbChanged()));
118 connect(draftCB, SIGNAL(clicked()), this, SLOT(change_adaptor()));
120 QIntValidator * validator = new QIntValidator(displayscaleED);
121 validator->setBottom(1);
122 displayscaleED->setValidator(validator);
124 angleED->setValidator(new QDoubleValidator(-360, 360, 2, angleED));
126 xlED->setValidator(new QIntValidator(xlED));
127 ybED->setValidator(new QIntValidator(ybED));
128 xrED->setValidator(new QIntValidator(xrED));
129 ytED->setValidator(new QIntValidator(ytED));
131 widthED->setValidator(unsignedLengthValidator(widthED));
132 heightED->setValidator(unsignedLengthValidator(heightED));
134 setFocusProxy(fileED);
138 void GuiExternalDialog::show()
145 bool GuiExternalDialog::activateAspectratio() const
147 if (widthUnitCO->currentIndex() == 0)
150 string const wstr = fromqstr(widthED->text());
153 bool const wIsDbl = isStrDbl(wstr);
154 if (wIsDbl && float_equal(convert<double>(wstr), 0.0, 0.05))
157 if (!wIsDbl && (!isValidLength(wstr, &l) || l.zero()))
160 string const hstr = fromqstr(heightED->text());
163 bool const hIsDbl = isStrDbl(hstr);
164 if (hIsDbl && float_equal(convert<double>(hstr), 0.0, 0.05))
166 if (!hIsDbl && (!isValidLength(hstr, &l) || l.zero()))
173 void GuiExternalDialog::bbChanged()
175 form_->controller().bbChanged(true);
180 void GuiExternalDialog::browseClicked()
182 int const choice = externalCO->currentIndex();
183 docstring const template_name =
184 from_utf8(form_->controller().getTemplate(choice).lyxName);
185 docstring const str =
186 form_->controller().browse(qstring_to_ucs4(fileED->text()),
189 fileED->setText(toqstr(str));
195 void GuiExternalDialog::change_adaptor()
201 void GuiExternalDialog::closeEvent(QCloseEvent * e)
208 void GuiExternalDialog::editClicked()
210 form_->controller().editExternal();
215 void GuiExternalDialog::extraChanged(const QString& text)
217 std::string const format = fromqstr(extraFormatCO->currentText());
218 form_->extra_[format] = text;
223 void GuiExternalDialog::formatChanged(const QString& format)
225 extraED->setText(form_->extra_[fromqstr(format)]);
229 void GuiExternalDialog::getbbClicked()
235 void GuiExternalDialog::sizeChanged()
237 aspectratioCB->setEnabled(activateAspectratio());
242 void GuiExternalDialog::templateChanged()
244 form_->updateTemplate();
249 void GuiExternalDialog::widthUnitChanged()
251 bool useHeight = (widthUnitCO->currentIndex() > 0);
254 widthED->setValidator(unsignedLengthValidator(widthED));
256 widthED->setValidator(new QDoubleValidator(0, 1000, 2, widthED));
258 heightED->setEnabled(useHeight);
259 heightUnitCO->setEnabled(useHeight);
264 /////////////////////////////////////////////////////////////////////
268 /////////////////////////////////////////////////////////////////////
272 Length::UNIT defaultUnit()
274 Length::UNIT default_unit = Length::CM;
275 switch (lyxrc.default_papersize) {
278 case PAPER_USEXECUTIVE:
279 default_unit = Length::IN;
288 void setDisplay(QCheckBox & displayCB, QComboBox & showCO, QLineEdit & scaleED,
289 external::DisplayType display, unsigned int scale,
294 case external::DefaultDisplay:
297 case external::MonochromeDisplay:
300 case external::GrayscaleDisplay:
303 case external::ColorDisplay:
306 case external::PreviewDisplay:
309 case external::NoDisplay:
314 showCO.setCurrentIndex(item);
315 bool const no_display = display == external::NoDisplay;
316 showCO.setEnabled(!no_display && !read_only);
317 displayCB.setChecked(!no_display);
318 scaleED.setEnabled(!no_display && !read_only);
319 scaleED.setText(toqstr(convert<string>(scale)));
323 void getDisplay(external::DisplayType & display,
324 unsigned int & scale,
325 QCheckBox const & displayCB,
326 QComboBox const & showCO,
327 QLineEdit const & scaleED)
329 switch (showCO.currentIndex()) {
331 display = external::DefaultDisplay;
334 display = external::MonochromeDisplay;
337 display = external::GrayscaleDisplay;
340 display = external::ColorDisplay;
343 display = external::PreviewDisplay;
347 if (!displayCB.isChecked())
348 display = external::NoDisplay;
350 scale = convert<int>(fromqstr(scaleED.text()));
354 void setRotation(QLineEdit & angleED, QComboBox & originCO,
355 external::RotationData const & data)
357 originCO.setCurrentIndex(int(data.origin()));
358 angleED.setText(toqstr(data.angle));
362 void getRotation(external::RotationData & data,
363 QLineEdit const & angleED, QComboBox const & originCO)
365 typedef external::RotationData::OriginType OriginType;
367 data.origin(static_cast<OriginType>(originCO.currentIndex()));
368 data.angle = fromqstr(angleED.text());
372 void setSize(QLineEdit & widthED, QComboBox & widthUnitCO,
373 QLineEdit & heightED, LengthCombo & heightUnitCO,
374 QCheckBox & aspectratioCB,
375 external::ResizeData const & data)
377 bool using_scale = data.usingScale();
378 std::string scale = data.scale;
379 if (data.no_resize()) {
380 // Everything is zero, so default to this!
386 widthED.setText(toqstr(scale));
387 widthUnitCO.setCurrentIndex(0);
389 widthED.setText(toqstr(convert<string>(data.width.value())));
390 // Because 'Scale' is position 0...
391 // Note also that width cannot be zero here, so
392 // we don't need to worry about the default unit.
393 widthUnitCO.setCurrentIndex(data.width.unit() + 1);
396 string const h = data.height.zero() ? string() : data.height.asString();
397 Length::UNIT default_unit = data.width.zero() ?
398 defaultUnit() : data.width.unit();
399 lengthToWidgets(&heightED, &heightUnitCO, h, default_unit);
401 heightED.setEnabled(!using_scale);
402 heightUnitCO.setEnabled(!using_scale);
404 aspectratioCB.setChecked(data.keepAspectRatio);
406 bool const disable_aspectRatio = using_scale ||
407 data.width.zero() || data.height.zero();
408 aspectratioCB.setEnabled(!disable_aspectRatio);
412 void getSize(external::ResizeData & data,
413 QLineEdit const & widthED, QComboBox const & widthUnitCO,
414 QLineEdit const & heightED, LengthCombo const & heightUnitCO,
415 QCheckBox const & aspectratioCB)
417 string const width = fromqstr(widthED.text());
419 if (widthUnitCO.currentIndex() > 0) {
420 // Subtract one, because scale is 0.
421 int const unit = widthUnitCO.currentIndex() - 1;
424 if (isValidLength(width, &w))
426 else if (isStrDbl(width))
427 data.width = Length(convert<double>(width),
428 static_cast<Length::UNIT>(unit));
430 data.width = Length();
432 data.scale = string();
435 // scaling instead of a width
437 data.width = Length();
440 data.height = Length(widgetsToLength(&heightED, &heightUnitCO));
442 data.keepAspectRatio = aspectratioCB.isChecked();
446 void setCrop(QCheckBox & clipCB,
447 QLineEdit & xlED, QLineEdit & ybED,
448 QLineEdit & xrED, QLineEdit & ytED,
449 external::ClipData const & data)
451 clipCB.setChecked(data.clip);
452 graphics::BoundingBox const & bbox = data.bbox;
453 xlED.setText(toqstr(convert<string>(bbox.xl)));
454 ybED.setText(toqstr(convert<string>(bbox.yb)));
455 xrED.setText(toqstr(convert<string>(bbox.xr)));
456 ytED.setText(toqstr(convert<string>(bbox.yt)));
460 void getCrop(external::ClipData & data,
461 QCheckBox const & clipCB,
462 QLineEdit const & xlED, QLineEdit const & ybED,
463 QLineEdit const & xrED, QLineEdit const & ytED,
466 data.clip = clipCB.isChecked();
471 data.bbox.xl = convert<int>(fromqstr(xlED.text()));
472 data.bbox.yb = convert<int>(fromqstr(ybED.text()));
473 data.bbox.xr = convert<int>(fromqstr(xrED.text()));
474 data.bbox.yt = convert<int>(fromqstr(ytED.text()));
478 void getExtra(external::ExtraData & data,
479 GuiExternal::MapType const & extra)
481 typedef GuiExternal::MapType MapType;
482 MapType::const_iterator it = extra.begin();
483 MapType::const_iterator const end = extra.end();
484 for (; it != end; ++it)
485 data.set(it->first, trim(fromqstr(it->second)));
491 GuiExternal::GuiExternal(Dialog & parent)
492 : GuiView<GuiExternalDialog>(parent, _("External Material"))
496 void GuiExternal::build_dialog()
498 dialog_.reset(new GuiExternalDialog(this));
500 bcview().setOK(dialog_->okPB);
501 bcview().setApply(dialog_->applyPB);
502 bcview().setCancel(dialog_->closePB);
504 bcview().addReadOnly(dialog_->fileED);
505 bcview().addReadOnly(dialog_->browsePB);
506 bcview().addReadOnly(dialog_->editPB);
507 bcview().addReadOnly(dialog_->externalCO);
508 bcview().addReadOnly(dialog_->draftCB);
509 bcview().addReadOnly(dialog_->displayscaleED);
510 bcview().addReadOnly(dialog_->showCO);
511 bcview().addReadOnly(dialog_->displayCB);
512 bcview().addReadOnly(dialog_->angleED);
513 bcview().addReadOnly(dialog_->originCO);
514 bcview().addReadOnly(dialog_->heightUnitCO);
515 bcview().addReadOnly(dialog_->heightED);
516 bcview().addReadOnly(dialog_->aspectratioCB);
517 bcview().addReadOnly(dialog_->widthUnitCO);
518 bcview().addReadOnly(dialog_->widthED);
519 bcview().addReadOnly(dialog_->clipCB);
520 bcview().addReadOnly(dialog_->getbbPB);
521 bcview().addReadOnly(dialog_->ytED);
522 bcview().addReadOnly(dialog_->xlED);
523 bcview().addReadOnly(dialog_->xrED);
524 bcview().addReadOnly(dialog_->ybED);
525 bcview().addReadOnly(dialog_->extraFormatCO);
526 bcview().addReadOnly(dialog_->extraED);
528 addCheckedLineEdit(bcview(), dialog_->angleED, dialog_->angleLA);
529 addCheckedLineEdit(bcview(), dialog_->displayscaleED, dialog_->scaleLA);
530 addCheckedLineEdit(bcview(), dialog_->heightED, dialog_->heightLA);
531 addCheckedLineEdit(bcview(), dialog_->widthED, dialog_->widthLA);
532 addCheckedLineEdit(bcview(), dialog_->xlED, dialog_->lbLA);
533 addCheckedLineEdit(bcview(), dialog_->ybED, dialog_->lbLA);
534 addCheckedLineEdit(bcview(), dialog_->xrED, dialog_->rtLA);
535 addCheckedLineEdit(bcview(), dialog_->ytED, dialog_->rtLA);
536 addCheckedLineEdit(bcview(), dialog_->fileED, dialog_->fileLA);
538 std::vector<string> templates(controller().getTemplates());
540 for (std::vector<string>::const_iterator cit = templates.begin();
541 cit != templates.end(); ++cit) {
542 dialog_->externalCO->addItem(qt_(*cit));
545 // Fill the origins combo
546 typedef vector<external::RotationDataType> Origins;
547 Origins const & all_origins = external::all_origins();
548 for (Origins::size_type i = 0; i != all_origins.size(); ++i)
549 dialog_->originCO->addItem(toqstr(external::origin_gui_str(i)));
551 // Fill the width combo
552 dialog_->widthUnitCO->addItem(qt_("Scale%"));
553 for (int i = 0; i < num_units; i++)
554 dialog_->widthUnitCO->addItem(qt_(unit_name_gui[i]));
558 void GuiExternal::update_contents()
560 dialog_->tab->setCurrentIndex(0);
561 InsetExternalParams const & params = controller().params();
564 params.filename.outputFilename(kernel().bufferFilepath());
565 dialog_->fileED->setText(toqstr(name));
567 dialog_->externalCO->setCurrentIndex(
568 controller().getTemplateNumber(params.templatename()));
571 dialog_->draftCB->setChecked(params.draft);
573 setDisplay(*dialog_->displayCB, *dialog_->showCO,
574 *dialog_->displayscaleED,
575 params.display, params.lyxscale, readOnly());
577 setRotation(*dialog_->angleED, *dialog_->originCO, params.rotationdata);
579 setSize(*dialog_->widthED, *dialog_->widthUnitCO,
580 *dialog_->heightED, *dialog_->heightUnitCO,
581 *dialog_->aspectratioCB,
584 setCrop(*dialog_->clipCB,
585 *dialog_->xlED, *dialog_->ybED,
586 *dialog_->xrED, *dialog_->ytED,
588 controller().bbChanged(!params.clipdata.bbox.empty());
594 void GuiExternal::updateTemplate()
596 external::Template templ =
597 controller().getTemplate(dialog_->externalCO->currentIndex());
598 dialog_->externalTB->setPlainText(qt_(templ.helpText));
600 // Ascertain which (if any) transformations the template supports
601 // and disable tabs hosting unsupported transforms.
602 typedef vector<external::TransformID> TransformIDs;
603 TransformIDs const transformIds = templ.transformIds;
604 TransformIDs::const_iterator tr_begin = transformIds.begin();
605 TransformIDs::const_iterator const tr_end = transformIds.end();
607 bool found = find(tr_begin, tr_end, external::Rotate) != tr_end;
608 dialog_->tab->setTabEnabled(
609 dialog_->tab->indexOf(dialog_->rotatetab), found);
610 found = find(tr_begin, tr_end, external::Resize) != tr_end;
611 dialog_->tab->setTabEnabled(
612 dialog_->tab->indexOf(dialog_->scaletab), found);
614 found = find(tr_begin, tr_end, external::Clip) != tr_end;
615 dialog_->tab->setTabEnabled(
616 dialog_->tab->indexOf(dialog_->croptab), found);
618 found = find(tr_begin, tr_end, external::Extra) != tr_end;
619 dialog_->tab->setTabEnabled(
620 dialog_->tab->indexOf(dialog_->optionstab), found);
625 // Ascertain whether the template has any formats supporting
626 // the 'Extra' option
627 QLineEdit * const extraED = dialog_->extraED;
628 QComboBox * const extraCB = dialog_->extraFormatCO;
634 external::Template::Formats::const_iterator it = templ.formats.begin();
635 external::Template::Formats::const_iterator end = templ.formats.end();
636 for (; it != end; ++it) {
637 if (it->second.option_transformers.find(external::Extra) ==
638 it->second.option_transformers.end())
640 string const format = it->first;
641 string const opt = controller().params().extradata.get(format);
642 extraCB->addItem(toqstr(format));
643 extra_[format] = toqstr(opt);
646 bool const enabled = extraCB->count() > 0;
648 dialog_->tab->setTabEnabled(
649 dialog_->tab->indexOf(dialog_->optionstab), enabled);
650 extraED->setEnabled(enabled && !kernel().isBufferReadonly());
651 extraCB->setEnabled(enabled);
654 extraCB->setCurrentIndex(0);
655 extraED->setText(extra_[fromqstr(extraCB->currentText())]);
660 void GuiExternal::apply()
662 InsetExternalParams params = controller().params();
664 params.filename.set(internal_path(fromqstr(dialog_->fileED->text())),
665 kernel().bufferFilepath());
667 params.settemplate(controller().getTemplate(
668 dialog_->externalCO->currentIndex()).lyxName);
670 params.draft = dialog_->draftCB->isChecked();
672 getDisplay(params.display, params.lyxscale,
673 *dialog_->displayCB, *dialog_->showCO,
674 *dialog_->displayscaleED);
676 if (dialog_->tab->isTabEnabled(
677 dialog_->tab->indexOf(dialog_->rotatetab)))
678 getRotation(params.rotationdata,
679 *dialog_->angleED, *dialog_->originCO);
681 if (dialog_->tab->isTabEnabled(
682 dialog_->tab->indexOf(dialog_->scaletab)))
683 getSize(params.resizedata,
684 *dialog_->widthED, *dialog_->widthUnitCO,
685 *dialog_->heightED, *dialog_->heightUnitCO,
686 *dialog_->aspectratioCB);
688 if (dialog_->tab->isTabEnabled(
689 dialog_->tab->indexOf(dialog_->croptab)))
690 getCrop(params.clipdata,
692 *dialog_->xlED, *dialog_->ybED,
693 *dialog_->xrED, *dialog_->ytED,
694 controller().bbChanged());
696 if (dialog_->tab->isTabEnabled(
697 dialog_->tab->indexOf(dialog_->optionstab)))
698 getExtra(params.extradata, extra_);
700 controller().setParams(params);
704 void GuiExternal::getBB()
706 dialog_->xlED->setText("0");
707 dialog_->ybED->setText("0");
708 dialog_->xrED->setText("0");
709 dialog_->ytED->setText("0");
711 string const filename = fromqstr(dialog_->fileED->text());
712 if (filename.empty())
715 string const bb = controller().readBB(filename);
719 dialog_->xlED->setText(toqstr(token(bb, ' ', 0)));
720 dialog_->ybED->setText(toqstr(token(bb, ' ', 1)));
721 dialog_->xrED->setText(toqstr(token(bb, ' ', 2)));
722 dialog_->ytED->setText(toqstr(token(bb, ' ', 3)));
724 controller().bbChanged(false);
727 } // namespace frontend
730 #include "GuiExternal_moc.cpp"