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(lyx::Length::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()),
191 fileED->setText(toqstr(str));
197 void QExternalDialog::change_adaptor()
203 void QExternalDialog::closeEvent(QCloseEvent * e)
210 void QExternalDialog::editClicked()
212 form_->controller().editExternal();
217 void QExternalDialog::extraChanged(const QString& text)
219 std::string const format = fromqstr(extraFormatCO->currentText());
220 form_->extra_[format] = text;
225 void QExternalDialog::formatChanged(const QString& format)
227 extraED->setText(form_->extra_[fromqstr(format)]);
231 void QExternalDialog::getbbClicked()
237 void QExternalDialog::sizeChanged()
239 aspectratioCB->setEnabled(activateAspectratio());
244 void QExternalDialog::templateChanged()
246 form_->updateTemplate();
251 void QExternalDialog::widthUnitChanged()
253 bool useHeight = (widthUnitCO->currentIndex() > 0);
256 widthED->setValidator(unsignedLengthValidator(widthED));
258 widthED->setValidator(new QDoubleValidator(0, 1000, 2, widthED));
260 heightED->setEnabled(useHeight);
261 heightUnitCO->setEnabled(useHeight);
266 /////////////////////////////////////////////////////////////////////
270 /////////////////////////////////////////////////////////////////////
274 Length::UNIT defaultUnit()
276 Length::UNIT default_unit = Length::CM;
277 switch (lyxrc.default_papersize) {
280 case PAPER_USEXECUTIVE:
281 default_unit = Length::IN;
290 void setDisplay(QCheckBox & displayCB, QComboBox & showCO, QLineEdit & scaleED,
291 external::DisplayType display, unsigned int scale,
296 case external::DefaultDisplay:
299 case external::MonochromeDisplay:
302 case external::GrayscaleDisplay:
305 case external::ColorDisplay:
308 case external::PreviewDisplay:
311 case external::NoDisplay:
316 showCO.setCurrentIndex(item);
317 bool const no_display = display == external::NoDisplay;
318 showCO.setEnabled(!no_display && !read_only);
319 displayCB.setChecked(!no_display);
320 scaleED.setEnabled(!no_display && !read_only);
321 scaleED.setText(toqstr(convert<string>(scale)));
325 void getDisplay(external::DisplayType & display,
326 unsigned int & scale,
327 QCheckBox const & displayCB,
328 QComboBox const & showCO,
329 QLineEdit const & scaleED)
331 switch (showCO.currentIndex()) {
333 display = external::DefaultDisplay;
336 display = external::MonochromeDisplay;
339 display = external::GrayscaleDisplay;
342 display = external::ColorDisplay;
345 display = external::PreviewDisplay;
349 if (!displayCB.isChecked())
350 display = external::NoDisplay;
352 scale = convert<int>(fromqstr(scaleED.text()));
356 void setRotation(QLineEdit & angleED, QComboBox & originCO,
357 external::RotationData const & data)
359 originCO.setCurrentIndex(int(data.origin()));
360 angleED.setText(toqstr(data.angle));
364 void getRotation(external::RotationData & data,
365 QLineEdit const & angleED, QComboBox const & originCO)
367 typedef external::RotationData::OriginType OriginType;
369 data.origin(static_cast<OriginType>(originCO.currentIndex()));
370 data.angle = fromqstr(angleED.text());
374 void setSize(QLineEdit & widthED, QComboBox & widthUnitCO,
375 QLineEdit & heightED, LengthCombo & heightUnitCO,
376 QCheckBox & aspectratioCB,
377 external::ResizeData const & data)
379 bool using_scale = data.usingScale();
380 std::string scale = data.scale;
381 if (data.no_resize()) {
382 // Everything is zero, so default to this!
388 widthED.setText(toqstr(scale));
389 widthUnitCO.setCurrentIndex(0);
391 widthED.setText(toqstr(convert<string>(data.width.value())));
392 // Because 'Scale' is position 0...
393 // Note also that width cannot be zero here, so
394 // we don't need to worry about the default unit.
395 widthUnitCO.setCurrentIndex(data.width.unit() + 1);
398 string const h = data.height.zero() ? string() : data.height.asString();
399 Length::UNIT default_unit = data.width.zero() ?
400 defaultUnit() : data.width.unit();
401 lengthToWidgets(&heightED, &heightUnitCO, h, default_unit);
403 heightED.setEnabled(!using_scale);
404 heightUnitCO.setEnabled(!using_scale);
406 aspectratioCB.setChecked(data.keepAspectRatio);
408 bool const disable_aspectRatio = using_scale ||
409 data.width.zero() || data.height.zero();
410 aspectratioCB.setEnabled(!disable_aspectRatio);
414 void getSize(external::ResizeData & data,
415 QLineEdit const & widthED, QComboBox const & widthUnitCO,
416 QLineEdit const & heightED, LengthCombo const & heightUnitCO,
417 QCheckBox const & aspectratioCB)
419 string const width = fromqstr(widthED.text());
421 if (widthUnitCO.currentIndex() > 0) {
422 // Subtract one, because scale is 0.
423 int const unit = widthUnitCO.currentIndex() - 1;
426 if (isValidLength(width, &w))
428 else if (isStrDbl(width))
429 data.width = Length(convert<double>(width),
430 static_cast<Length::UNIT>(unit));
432 data.width = Length();
434 data.scale = string();
437 // scaling instead of a width
439 data.width = Length();
442 data.height = Length(widgetsToLength(&heightED, &heightUnitCO));
444 data.keepAspectRatio = aspectratioCB.isChecked();
448 void setCrop(QCheckBox & clipCB,
449 QLineEdit & xlED, QLineEdit & ybED,
450 QLineEdit & xrED, QLineEdit & ytED,
451 external::ClipData const & data)
453 clipCB.setChecked(data.clip);
454 graphics::BoundingBox const & bbox = data.bbox;
455 xlED.setText(toqstr(convert<string>(bbox.xl)));
456 ybED.setText(toqstr(convert<string>(bbox.yb)));
457 xrED.setText(toqstr(convert<string>(bbox.xr)));
458 ytED.setText(toqstr(convert<string>(bbox.yt)));
462 void getCrop(external::ClipData & data,
463 QCheckBox const & clipCB,
464 QLineEdit const & xlED, QLineEdit const & ybED,
465 QLineEdit const & xrED, QLineEdit const & ytED,
468 data.clip = clipCB.isChecked();
473 data.bbox.xl = convert<int>(fromqstr(xlED.text()));
474 data.bbox.yb = convert<int>(fromqstr(ybED.text()));
475 data.bbox.xr = convert<int>(fromqstr(xrED.text()));
476 data.bbox.yt = convert<int>(fromqstr(ytED.text()));
480 void getExtra(external::ExtraData & data,
481 QExternal::MapType const & extra)
483 typedef QExternal::MapType MapType;
484 MapType::const_iterator it = extra.begin();
485 MapType::const_iterator const end = extra.end();
486 for (; it != end; ++it)
487 data.set(it->first, trim(fromqstr(it->second)));
493 typedef QController<ControlExternal, QView<QExternalDialog> >
496 QExternal::QExternal(Dialog & parent)
497 : ExternalBase(parent, _("External Material"))
501 void QExternal::build_dialog()
503 dialog_.reset(new QExternalDialog(this));
505 bcview().setOK(dialog_->okPB);
506 bcview().setApply(dialog_->applyPB);
507 bcview().setCancel(dialog_->closePB);
509 bcview().addReadOnly(dialog_->fileED);
510 bcview().addReadOnly(dialog_->browsePB);
511 bcview().addReadOnly(dialog_->editPB);
512 bcview().addReadOnly(dialog_->externalCO);
513 bcview().addReadOnly(dialog_->draftCB);
514 bcview().addReadOnly(dialog_->displayscaleED);
515 bcview().addReadOnly(dialog_->showCO);
516 bcview().addReadOnly(dialog_->displayCB);
517 bcview().addReadOnly(dialog_->angleED);
518 bcview().addReadOnly(dialog_->originCO);
519 bcview().addReadOnly(dialog_->heightUnitCO);
520 bcview().addReadOnly(dialog_->heightED);
521 bcview().addReadOnly(dialog_->aspectratioCB);
522 bcview().addReadOnly(dialog_->widthUnitCO);
523 bcview().addReadOnly(dialog_->widthED);
524 bcview().addReadOnly(dialog_->clipCB);
525 bcview().addReadOnly(dialog_->getbbPB);
526 bcview().addReadOnly(dialog_->ytED);
527 bcview().addReadOnly(dialog_->xlED);
528 bcview().addReadOnly(dialog_->xrED);
529 bcview().addReadOnly(dialog_->ybED);
530 bcview().addReadOnly(dialog_->extraFormatCO);
531 bcview().addReadOnly(dialog_->extraED);
533 addCheckedLineEdit(bcview(), dialog_->angleED, dialog_->angleLA);
534 addCheckedLineEdit(bcview(), dialog_->displayscaleED, dialog_->scaleLA);
535 addCheckedLineEdit(bcview(), dialog_->heightED, dialog_->heightLA);
536 addCheckedLineEdit(bcview(), dialog_->widthED, dialog_->widthLA);
537 addCheckedLineEdit(bcview(), dialog_->xlED, dialog_->lbLA);
538 addCheckedLineEdit(bcview(), dialog_->ybED, dialog_->lbLA);
539 addCheckedLineEdit(bcview(), dialog_->xrED, dialog_->rtLA);
540 addCheckedLineEdit(bcview(), dialog_->ytED, dialog_->rtLA);
541 addCheckedLineEdit(bcview(), dialog_->fileED, dialog_->fileLA);
543 std::vector<string> templates(controller().getTemplates());
545 for (std::vector<string>::const_iterator cit = templates.begin();
546 cit != templates.end(); ++cit) {
547 dialog_->externalCO->addItem(toqstr(*cit));
550 // Fill the origins combo
551 typedef vector<external::RotationDataType> Origins;
552 Origins const & all_origins = external::all_origins();
553 for (Origins::size_type i = 0; i != all_origins.size(); ++i)
554 dialog_->originCO->addItem(toqstr(external::origin_gui_str(i)));
556 // Fill the width combo
557 dialog_->widthUnitCO->addItem(qt_("Scale%"));
558 for (int i = 0; i < num_units; i++)
559 dialog_->widthUnitCO->addItem(qt_(unit_name_gui[i]));
563 void QExternal::update_contents()
565 PathValidator * path_validator = getPathValidator(dialog_->fileED);
567 path_validator->setChecker(kernel().docType(), lyxrc);
569 dialog_->tab->setCurrentIndex(0);
570 InsetExternalParams const & params = controller().params();
573 params.filename.outputFilename(kernel().bufferFilepath());
574 dialog_->fileED->setText(toqstr(name));
576 dialog_->externalCO->setCurrentIndex(
577 controller().getTemplateNumber(params.templatename()));
580 dialog_->draftCB->setChecked(params.draft);
582 setDisplay(*dialog_->displayCB, *dialog_->showCO,
583 *dialog_->displayscaleED,
584 params.display, params.lyxscale, readOnly());
586 setRotation(*dialog_->angleED, *dialog_->originCO, params.rotationdata);
588 setSize(*dialog_->widthED, *dialog_->widthUnitCO,
589 *dialog_->heightED, *dialog_->heightUnitCO,
590 *dialog_->aspectratioCB,
593 setCrop(*dialog_->clipCB,
594 *dialog_->xlED, *dialog_->ybED,
595 *dialog_->xrED, *dialog_->ytED,
597 controller().bbChanged(!params.clipdata.bbox.empty());
603 void QExternal::updateTemplate()
605 external::Template templ =
606 controller().getTemplate(dialog_->externalCO->currentIndex());
607 dialog_->externalTB->setPlainText(toqstr(templ.helpText));
609 // Ascertain which (if any) transformations the template supports
610 // and disable tabs hosting unsupported transforms.
611 typedef vector<external::TransformID> TransformIDs;
612 TransformIDs const transformIds = templ.transformIds;
613 TransformIDs::const_iterator tr_begin = transformIds.begin();
614 TransformIDs::const_iterator const tr_end = transformIds.end();
616 bool found = find(tr_begin, tr_end, external::Rotate) != tr_end;
617 dialog_->tab->setTabEnabled(
618 dialog_->tab->indexOf(dialog_->rotatetab), found);
619 found = find(tr_begin, tr_end, external::Resize) != tr_end;
620 dialog_->tab->setTabEnabled(
621 dialog_->tab->indexOf(dialog_->scaletab), found);
623 found = find(tr_begin, tr_end, external::Clip) != tr_end;
624 dialog_->tab->setTabEnabled(
625 dialog_->tab->indexOf(dialog_->croptab), found);
627 found = find(tr_begin, tr_end, external::Extra) != tr_end;
628 dialog_->tab->setTabEnabled(
629 dialog_->tab->indexOf(dialog_->optionstab), found);
634 // Ascertain whether the template has any formats supporting
635 // the 'Extra' option
636 QLineEdit * const extraED = dialog_->extraED;
637 QComboBox * const extraCB = dialog_->extraFormatCO;
643 external::Template::Formats::const_iterator it = templ.formats.begin();
644 external::Template::Formats::const_iterator end = templ.formats.end();
645 for (; it != end; ++it) {
646 if (it->second.option_transformers.find(external::Extra) ==
647 it->second.option_transformers.end())
649 string const format = it->first;
650 string const opt = controller().params().extradata.get(format);
651 extraCB->addItem(toqstr(format));
652 extra_[format] = toqstr(opt);
655 bool const enabled = extraCB->count() > 0;
657 dialog_->tab->setTabEnabled(
658 dialog_->tab->indexOf(dialog_->optionstab), enabled);
659 extraED->setEnabled(enabled && !kernel().isBufferReadonly());
660 extraCB->setEnabled(enabled);
663 extraCB->setCurrentIndex(0);
664 extraED->setText(extra_[fromqstr(extraCB->currentText())]);
669 void QExternal::apply()
671 InsetExternalParams params = controller().params();
673 params.filename.set(internal_path(fromqstr(dialog_->fileED->text())),
674 kernel().bufferFilepath());
676 params.settemplate(controller().getTemplate(
677 dialog_->externalCO->currentIndex()).lyxName);
679 params.draft = dialog_->draftCB->isChecked();
681 getDisplay(params.display, params.lyxscale,
682 *dialog_->displayCB, *dialog_->showCO,
683 *dialog_->displayscaleED);
685 if (dialog_->tab->isTabEnabled(
686 dialog_->tab->indexOf(dialog_->rotatetab)))
687 getRotation(params.rotationdata,
688 *dialog_->angleED, *dialog_->originCO);
690 if (dialog_->tab->isTabEnabled(
691 dialog_->tab->indexOf(dialog_->scaletab)))
692 getSize(params.resizedata,
693 *dialog_->widthED, *dialog_->widthUnitCO,
694 *dialog_->heightED, *dialog_->heightUnitCO,
695 *dialog_->aspectratioCB);
697 if (dialog_->tab->isTabEnabled(
698 dialog_->tab->indexOf(dialog_->croptab)))
699 getCrop(params.clipdata,
701 *dialog_->xlED, *dialog_->ybED,
702 *dialog_->xrED, *dialog_->ytED,
703 controller().bbChanged());
705 if (dialog_->tab->isTabEnabled(
706 dialog_->tab->indexOf(dialog_->optionstab)))
707 getExtra(params.extradata, extra_);
709 controller().setParams(params);
713 void QExternal::getBB()
715 dialog_->xlED->setText("0");
716 dialog_->ybED->setText("0");
717 dialog_->xrED->setText("0");
718 dialog_->ytED->setText("0");
720 string const filename = fromqstr(dialog_->fileED->text());
721 if (filename.empty())
724 string const bb = controller().readBB(filename);
728 dialog_->xlED->setText(toqstr(token(bb, ' ', 0)));
729 dialog_->ybED->setText(toqstr(token(bb, ' ', 1)));
730 dialog_->xrED->setText(toqstr(token(bb, ' ', 2)));
731 dialog_->ytED->setText(toqstr(token(bb, ' ', 3)));
733 controller().bbChanged(false);
736 } // namespace frontend
739 #include "QExternal_moc.cpp"