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/ControlExternal.h"
20 #include "controllers/ButtonController.h"
22 #include "insets/ExternalTemplate.h"
23 #include "insets/InsetExternal.h"
25 #include "support/lstrings.h"
26 #include "support/convert.h"
27 #include "support/os.h"
28 #include "support/lyxlib.h"
30 #include "QExternal.h"
33 #include "CheckedLineEdit.h"
34 #include "LengthCombo.h"
35 #include "qt_helpers.h"
36 #include "Validator.h"
39 #include <QPushButton>
42 #include <QTextBrowser>
44 namespace external = lyx::external;
46 using lyx::support::isStrDbl;
47 using lyx::support::token;
48 using lyx::support::trim;
49 using lyx::support::float_equal;
51 using lyx::support::os::internal_path;
61 /////////////////////////////////////////////////////////////////////
65 /////////////////////////////////////////////////////////////////////
68 QExternalDialog::QExternalDialog(QExternal * form)
72 connect(okPB, SIGNAL(clicked()), form, SLOT(slotOK()));
73 connect(applyPB, SIGNAL(clicked()), form, SLOT(slotApply()));
74 connect(closePB, SIGNAL(clicked()), form, SLOT(slotClose()));
76 connect(displayCB, SIGNAL(toggled(bool)),
77 showCO, SLOT(setEnabled(bool)));
78 connect(displayCB, SIGNAL(toggled(bool)),
79 displayscaleED, SLOT(setEnabled(bool)));
80 connect(showCO, SIGNAL(activated(const QString&)),
81 this, SLOT(change_adaptor()));
82 connect(originCO, SIGNAL(activated(int)),
83 this, SLOT(change_adaptor()));
84 connect(aspectratioCB, SIGNAL(stateChanged(int)),
85 this, SLOT(change_adaptor()));
86 connect(browsePB, SIGNAL(clicked()),
87 this, SLOT(browseClicked()));
88 connect(editPB, SIGNAL(clicked()),
89 this, SLOT(editClicked()));
90 connect(externalCO, SIGNAL(activated(const QString&)),
91 this, SLOT(templateChanged()));
92 connect(extraED, SIGNAL(textChanged(const QString&)),
93 this, SLOT(extraChanged(const QString&)));
94 connect(extraFormatCO, SIGNAL(activated(const QString&)),
95 this, SLOT(formatChanged(const QString&)));
96 connect(widthUnitCO, SIGNAL(activated(int)),
97 this, SLOT(widthUnitChanged()));
98 connect(heightUnitCO, SIGNAL(selectionChanged(LyXLength::UNIT)),
99 this, SLOT(change_adaptor()));
100 connect(displayCB, SIGNAL(stateChanged(int)),
101 this, SLOT(change_adaptor()));
102 connect(displayscaleED, SIGNAL(textChanged(const QString&)),
103 this, SLOT(change_adaptor()));
104 connect(angleED, SIGNAL(textChanged(const QString&)),
105 this, SLOT(change_adaptor()));
106 connect(widthED, SIGNAL(textChanged(const QString&)),
107 this, SLOT(sizeChanged()));
108 connect(heightED, SIGNAL(textChanged(const QString&)),
109 this, SLOT(sizeChanged()));
110 connect(fileED, SIGNAL(textChanged(const QString&)),
111 this, SLOT(change_adaptor()));
112 connect(clipCB, SIGNAL(stateChanged(int)),
113 this, SLOT(change_adaptor()));
114 connect(getbbPB, SIGNAL(clicked()), this, SLOT(getbbClicked()));
115 connect(xrED, SIGNAL(textChanged(const QString&)), this, SLOT(bbChanged()));
116 connect(ytED, SIGNAL(textChanged(const QString&)), this, SLOT(bbChanged()));
117 connect(xlED, SIGNAL(textChanged(const QString&)), this, SLOT(bbChanged()));
118 connect(ybED, SIGNAL(textChanged(const QString&)), this, SLOT(bbChanged()));
119 connect(draftCB, SIGNAL(clicked()), this, SLOT(change_adaptor()));
121 QIntValidator * validator = new QIntValidator(displayscaleED);
122 validator->setBottom(1);
123 displayscaleED->setValidator(validator);
125 angleED->setValidator(new QDoubleValidator(-360, 360, 2, angleED));
127 xlED->setValidator(new QIntValidator(xlED));
128 ybED->setValidator(new QIntValidator(ybED));
129 xrED->setValidator(new QIntValidator(xrED));
130 ytED->setValidator(new QIntValidator(ytED));
132 widthED->setValidator(unsignedLengthValidator(widthED));
133 heightED->setValidator(unsignedLengthValidator(heightED));
135 fileED->setValidator(new PathValidator(true, fileED));
136 setFocusProxy(fileED);
140 void QExternalDialog::show()
147 bool QExternalDialog::activateAspectratio() const
149 if (widthUnitCO->currentIndex() == 0)
152 string const wstr = fromqstr(widthED->text());
155 bool const wIsDbl = isStrDbl(wstr);
156 if (wIsDbl && float_equal(convert<double>(wstr), 0.0, 0.05))
159 if (!wIsDbl && (!isValidLength(wstr, &l) || l.zero()))
162 string const hstr = fromqstr(heightED->text());
165 bool const hIsDbl = isStrDbl(hstr);
166 if (hIsDbl && float_equal(convert<double>(hstr), 0.0, 0.05))
168 if (!hIsDbl && (!isValidLength(hstr, &l) || l.zero()))
175 void QExternalDialog::bbChanged()
177 form_->controller().bbChanged(true);
182 void QExternalDialog::browseClicked()
184 int const choice = externalCO->currentIndex();
185 docstring const template_name =
186 from_utf8(form_->controller().getTemplate(choice).lyxName);
187 docstring const str =
188 form_->controller().browse(qstring_to_ucs4(fileED->text()),
190 fileED->setText(toqstr(str));
195 void QExternalDialog::change_adaptor()
201 void QExternalDialog::closeEvent(QCloseEvent * e)
208 void QExternalDialog::editClicked()
210 form_->controller().editExternal();
215 void QExternalDialog::extraChanged(const QString& text)
217 std::string const format = fromqstr(extraFormatCO->currentText());
218 form_->extra_[format] = text;
223 void QExternalDialog::formatChanged(const QString& format)
225 extraED->setText(form_->extra_[fromqstr(format)]);
229 void QExternalDialog::getbbClicked()
235 void QExternalDialog::sizeChanged()
237 aspectratioCB->setEnabled(activateAspectratio());
242 void QExternalDialog::templateChanged()
244 form_->updateTemplate();
249 void QExternalDialog::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 LyXLength::UNIT defaultUnit()
274 LyXLength::UNIT default_unit = LyXLength::CM;
275 switch (lyxrc.default_papersize) {
278 case PAPER_USEXECUTIVE:
279 default_unit = LyXLength::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 LyXLength::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 = LyXLength(convert<double>(width),
428 static_cast<LyXLength::UNIT>(unit));
430 data.width = LyXLength();
432 data.scale = string();
435 // scaling instead of a width
437 data.width = LyXLength();
440 data.height = LyXLength(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 QExternal::MapType const & extra)
481 typedef QExternal::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 typedef QController<ControlExternal, QView<QExternalDialog> >
494 QExternal::QExternal(Dialog & parent)
495 : ExternalBase(parent, _("External Material"))
499 void QExternal::build_dialog()
501 dialog_.reset(new QExternalDialog(this));
503 bcview().setOK(dialog_->okPB);
504 bcview().setApply(dialog_->applyPB);
505 bcview().setCancel(dialog_->closePB);
507 bcview().addReadOnly(dialog_->fileED);
508 bcview().addReadOnly(dialog_->browsePB);
509 bcview().addReadOnly(dialog_->editPB);
510 bcview().addReadOnly(dialog_->externalCO);
511 bcview().addReadOnly(dialog_->draftCB);
512 bcview().addReadOnly(dialog_->displayscaleED);
513 bcview().addReadOnly(dialog_->showCO);
514 bcview().addReadOnly(dialog_->displayCB);
515 bcview().addReadOnly(dialog_->angleED);
516 bcview().addReadOnly(dialog_->originCO);
517 bcview().addReadOnly(dialog_->heightUnitCO);
518 bcview().addReadOnly(dialog_->heightED);
519 bcview().addReadOnly(dialog_->aspectratioCB);
520 bcview().addReadOnly(dialog_->widthUnitCO);
521 bcview().addReadOnly(dialog_->widthED);
522 bcview().addReadOnly(dialog_->clipCB);
523 bcview().addReadOnly(dialog_->getbbPB);
524 bcview().addReadOnly(dialog_->ytED);
525 bcview().addReadOnly(dialog_->xlED);
526 bcview().addReadOnly(dialog_->xrED);
527 bcview().addReadOnly(dialog_->ybED);
528 bcview().addReadOnly(dialog_->extraFormatCO);
529 bcview().addReadOnly(dialog_->extraED);
531 addCheckedLineEdit(bcview(), dialog_->angleED, dialog_->angleLA);
532 addCheckedLineEdit(bcview(), dialog_->displayscaleED, dialog_->scaleLA);
533 addCheckedLineEdit(bcview(), dialog_->heightED, dialog_->heightLA);
534 addCheckedLineEdit(bcview(), dialog_->widthED, dialog_->widthLA);
535 addCheckedLineEdit(bcview(), dialog_->xlED, dialog_->lbLA);
536 addCheckedLineEdit(bcview(), dialog_->ybED, dialog_->lbLA);
537 addCheckedLineEdit(bcview(), dialog_->xrED, dialog_->rtLA);
538 addCheckedLineEdit(bcview(), dialog_->ytED, dialog_->rtLA);
539 addCheckedLineEdit(bcview(), dialog_->fileED, dialog_->fileLA);
541 std::vector<string> templates(controller().getTemplates());
543 for (std::vector<string>::const_iterator cit = templates.begin();
544 cit != templates.end(); ++cit) {
545 dialog_->externalCO->addItem(toqstr(*cit));
548 // Fill the origins combo
549 typedef vector<external::RotationDataType> Origins;
550 Origins const & all_origins = external::all_origins();
551 for (Origins::size_type i = 0; i != all_origins.size(); ++i)
552 dialog_->originCO->addItem(toqstr(external::origin_gui_str(i)));
554 // Fill the width combo
555 dialog_->widthUnitCO->addItem(qt_("Scale%"));
556 for (int i = 0; i < num_units; i++)
557 dialog_->widthUnitCO->addItem(qt_(unit_name_gui[i]));
561 void QExternal::update_contents()
563 PathValidator * path_validator = getPathValidator(dialog_->fileED);
565 path_validator->setChecker(kernel().docType(), lyxrc);
567 dialog_->tab->setCurrentIndex(0);
568 InsetExternalParams const & params = controller().params();
571 params.filename.outputFilename(kernel().bufferFilepath());
572 dialog_->fileED->setText(toqstr(name));
574 dialog_->externalCO->setCurrentIndex(
575 controller().getTemplateNumber(params.templatename()));
578 dialog_->draftCB->setChecked(params.draft);
580 setDisplay(*dialog_->displayCB, *dialog_->showCO,
581 *dialog_->displayscaleED,
582 params.display, params.lyxscale, readOnly());
584 setRotation(*dialog_->angleED, *dialog_->originCO, params.rotationdata);
586 setSize(*dialog_->widthED, *dialog_->widthUnitCO,
587 *dialog_->heightED, *dialog_->heightUnitCO,
588 *dialog_->aspectratioCB,
591 setCrop(*dialog_->clipCB,
592 *dialog_->xlED, *dialog_->ybED,
593 *dialog_->xrED, *dialog_->ytED,
595 controller().bbChanged(!params.clipdata.bbox.empty());
601 void QExternal::updateTemplate()
603 external::Template templ =
604 controller().getTemplate(dialog_->externalCO->currentIndex());
605 dialog_->externalTB->setPlainText(toqstr(templ.helpText));
607 // Ascertain which (if any) transformations the template supports
608 // and disable tabs hosting unsupported transforms.
609 typedef vector<external::TransformID> TransformIDs;
610 TransformIDs const transformIds = templ.transformIds;
611 TransformIDs::const_iterator tr_begin = transformIds.begin();
612 TransformIDs::const_iterator const tr_end = transformIds.end();
614 bool found = find(tr_begin, tr_end, external::Rotate) != tr_end;
615 dialog_->tab->setTabEnabled(
616 dialog_->tab->indexOf(dialog_->rotatetab), found);
617 found = find(tr_begin, tr_end, external::Resize) != tr_end;
618 dialog_->tab->setTabEnabled(
619 dialog_->tab->indexOf(dialog_->scaletab), found);
621 found = find(tr_begin, tr_end, external::Clip) != tr_end;
622 dialog_->tab->setTabEnabled(
623 dialog_->tab->indexOf(dialog_->croptab), found);
625 found = find(tr_begin, tr_end, external::Extra) != tr_end;
626 dialog_->tab->setTabEnabled(
627 dialog_->tab->indexOf(dialog_->optionstab), found);
632 // Ascertain whether the template has any formats supporting
633 // the 'Extra' option
634 QLineEdit * const extraED = dialog_->extraED;
635 QComboBox * const extraCB = dialog_->extraFormatCO;
641 external::Template::Formats::const_iterator it = templ.formats.begin();
642 external::Template::Formats::const_iterator end = templ.formats.end();
643 for (; it != end; ++it) {
644 if (it->second.option_transformers.find(external::Extra) ==
645 it->second.option_transformers.end())
647 string const format = it->first;
648 string const opt = controller().params().extradata.get(format);
649 extraCB->addItem(toqstr(format));
650 extra_[format] = toqstr(opt);
653 bool const enabled = extraCB->count() > 0;
655 dialog_->tab->setTabEnabled(
656 dialog_->tab->indexOf(dialog_->optionstab), enabled);
657 extraED->setEnabled(enabled && !kernel().isBufferReadonly());
658 extraCB->setEnabled(enabled);
661 extraCB->setCurrentIndex(0);
662 extraED->setText(extra_[fromqstr(extraCB->currentText())]);
667 void QExternal::apply()
669 InsetExternalParams params = controller().params();
671 params.filename.set(internal_path(fromqstr(dialog_->fileED->text())),
672 kernel().bufferFilepath());
674 params.settemplate(controller().getTemplate(
675 dialog_->externalCO->currentIndex()).lyxName);
677 params.draft = dialog_->draftCB->isChecked();
679 getDisplay(params.display, params.lyxscale,
680 *dialog_->displayCB, *dialog_->showCO,
681 *dialog_->displayscaleED);
683 if (dialog_->tab->isTabEnabled(
684 dialog_->tab->indexOf(dialog_->rotatetab)))
685 getRotation(params.rotationdata,
686 *dialog_->angleED, *dialog_->originCO);
688 if (dialog_->tab->isTabEnabled(
689 dialog_->tab->indexOf(dialog_->scaletab)))
690 getSize(params.resizedata,
691 *dialog_->widthED, *dialog_->widthUnitCO,
692 *dialog_->heightED, *dialog_->heightUnitCO,
693 *dialog_->aspectratioCB);
695 if (dialog_->tab->isTabEnabled(
696 dialog_->tab->indexOf(dialog_->croptab)))
697 getCrop(params.clipdata,
699 *dialog_->xlED, *dialog_->ybED,
700 *dialog_->xrED, *dialog_->ytED,
701 controller().bbChanged());
703 if (dialog_->tab->isTabEnabled(
704 dialog_->tab->indexOf(dialog_->optionstab)))
705 getExtra(params.extradata, extra_);
707 controller().setParams(params);
711 void QExternal::getBB()
713 dialog_->xlED->setText("0");
714 dialog_->ybED->setText("0");
715 dialog_->xrED->setText("0");
716 dialog_->ytED->setText("0");
718 string const filename = fromqstr(dialog_->fileED->text());
719 if (filename.empty())
722 string const bb = controller().readBB(filename);
726 dialog_->xlED->setText(toqstr(token(bb, ' ', 0)));
727 dialog_->ybED->setText(toqstr(token(bb, ' ', 1)));
728 dialog_->xrED->setText(toqstr(token(bb, ' ', 2)));
729 dialog_->ytED->setText(toqstr(token(bb, ' ', 3)));
731 controller().bbChanged(false);
734 } // namespace frontend
737 #include "QExternal_moc.cpp"