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 setFocusProxy(fileED);
139 void QExternalDialog::show()
146 bool QExternalDialog::activateAspectratio() const
148 if (widthUnitCO->currentIndex() == 0)
151 string const wstr = fromqstr(widthED->text());
154 bool const wIsDbl = isStrDbl(wstr);
155 if (wIsDbl && float_equal(convert<double>(wstr), 0.0, 0.05))
158 if (!wIsDbl && (!isValidLength(wstr, &l) || l.zero()))
161 string const hstr = fromqstr(heightED->text());
164 bool const hIsDbl = isStrDbl(hstr);
165 if (hIsDbl && float_equal(convert<double>(hstr), 0.0, 0.05))
167 if (!hIsDbl && (!isValidLength(hstr, &l) || l.zero()))
174 void QExternalDialog::bbChanged()
176 form_->controller().bbChanged(true);
181 void QExternalDialog::browseClicked()
183 int const choice = externalCO->currentIndex();
184 docstring const template_name =
185 from_utf8(form_->controller().getTemplate(choice).lyxName);
186 docstring const str =
187 form_->controller().browse(qstring_to_ucs4(fileED->text()),
190 fileED->setText(toqstr(str));
196 void QExternalDialog::change_adaptor()
202 void QExternalDialog::closeEvent(QCloseEvent * e)
209 void QExternalDialog::editClicked()
211 form_->controller().editExternal();
216 void QExternalDialog::extraChanged(const QString& text)
218 std::string const format = fromqstr(extraFormatCO->currentText());
219 form_->extra_[format] = text;
224 void QExternalDialog::formatChanged(const QString& format)
226 extraED->setText(form_->extra_[fromqstr(format)]);
230 void QExternalDialog::getbbClicked()
236 void QExternalDialog::sizeChanged()
238 aspectratioCB->setEnabled(activateAspectratio());
243 void QExternalDialog::templateChanged()
245 form_->updateTemplate();
250 void QExternalDialog::widthUnitChanged()
252 bool useHeight = (widthUnitCO->currentIndex() > 0);
255 widthED->setValidator(unsignedLengthValidator(widthED));
257 widthED->setValidator(new QDoubleValidator(0, 1000, 2, widthED));
259 heightED->setEnabled(useHeight);
260 heightUnitCO->setEnabled(useHeight);
265 /////////////////////////////////////////////////////////////////////
269 /////////////////////////////////////////////////////////////////////
273 Length::UNIT defaultUnit()
275 Length::UNIT default_unit = Length::CM;
276 switch (lyxrc.default_papersize) {
279 case PAPER_USEXECUTIVE:
280 default_unit = Length::IN;
289 void setDisplay(QCheckBox & displayCB, QComboBox & showCO, QLineEdit & scaleED,
290 external::DisplayType display, unsigned int scale,
295 case external::DefaultDisplay:
298 case external::MonochromeDisplay:
301 case external::GrayscaleDisplay:
304 case external::ColorDisplay:
307 case external::PreviewDisplay:
310 case external::NoDisplay:
315 showCO.setCurrentIndex(item);
316 bool const no_display = display == external::NoDisplay;
317 showCO.setEnabled(!no_display && !read_only);
318 displayCB.setChecked(!no_display);
319 scaleED.setEnabled(!no_display && !read_only);
320 scaleED.setText(toqstr(convert<string>(scale)));
324 void getDisplay(external::DisplayType & display,
325 unsigned int & scale,
326 QCheckBox const & displayCB,
327 QComboBox const & showCO,
328 QLineEdit const & scaleED)
330 switch (showCO.currentIndex()) {
332 display = external::DefaultDisplay;
335 display = external::MonochromeDisplay;
338 display = external::GrayscaleDisplay;
341 display = external::ColorDisplay;
344 display = external::PreviewDisplay;
348 if (!displayCB.isChecked())
349 display = external::NoDisplay;
351 scale = convert<int>(fromqstr(scaleED.text()));
355 void setRotation(QLineEdit & angleED, QComboBox & originCO,
356 external::RotationData const & data)
358 originCO.setCurrentIndex(int(data.origin()));
359 angleED.setText(toqstr(data.angle));
363 void getRotation(external::RotationData & data,
364 QLineEdit const & angleED, QComboBox const & originCO)
366 typedef external::RotationData::OriginType OriginType;
368 data.origin(static_cast<OriginType>(originCO.currentIndex()));
369 data.angle = fromqstr(angleED.text());
373 void setSize(QLineEdit & widthED, QComboBox & widthUnitCO,
374 QLineEdit & heightED, LengthCombo & heightUnitCO,
375 QCheckBox & aspectratioCB,
376 external::ResizeData const & data)
378 bool using_scale = data.usingScale();
379 std::string scale = data.scale;
380 if (data.no_resize()) {
381 // Everything is zero, so default to this!
387 widthED.setText(toqstr(scale));
388 widthUnitCO.setCurrentIndex(0);
390 widthED.setText(toqstr(convert<string>(data.width.value())));
391 // Because 'Scale' is position 0...
392 // Note also that width cannot be zero here, so
393 // we don't need to worry about the default unit.
394 widthUnitCO.setCurrentIndex(data.width.unit() + 1);
397 string const h = data.height.zero() ? string() : data.height.asString();
398 Length::UNIT default_unit = data.width.zero() ?
399 defaultUnit() : data.width.unit();
400 lengthToWidgets(&heightED, &heightUnitCO, h, default_unit);
402 heightED.setEnabled(!using_scale);
403 heightUnitCO.setEnabled(!using_scale);
405 aspectratioCB.setChecked(data.keepAspectRatio);
407 bool const disable_aspectRatio = using_scale ||
408 data.width.zero() || data.height.zero();
409 aspectratioCB.setEnabled(!disable_aspectRatio);
413 void getSize(external::ResizeData & data,
414 QLineEdit const & widthED, QComboBox const & widthUnitCO,
415 QLineEdit const & heightED, LengthCombo const & heightUnitCO,
416 QCheckBox const & aspectratioCB)
418 string const width = fromqstr(widthED.text());
420 if (widthUnitCO.currentIndex() > 0) {
421 // Subtract one, because scale is 0.
422 int const unit = widthUnitCO.currentIndex() - 1;
425 if (isValidLength(width, &w))
427 else if (isStrDbl(width))
428 data.width = Length(convert<double>(width),
429 static_cast<Length::UNIT>(unit));
431 data.width = Length();
433 data.scale = string();
436 // scaling instead of a width
438 data.width = Length();
441 data.height = Length(widgetsToLength(&heightED, &heightUnitCO));
443 data.keepAspectRatio = aspectratioCB.isChecked();
447 void setCrop(QCheckBox & clipCB,
448 QLineEdit & xlED, QLineEdit & ybED,
449 QLineEdit & xrED, QLineEdit & ytED,
450 external::ClipData const & data)
452 clipCB.setChecked(data.clip);
453 graphics::BoundingBox const & bbox = data.bbox;
454 xlED.setText(toqstr(convert<string>(bbox.xl)));
455 ybED.setText(toqstr(convert<string>(bbox.yb)));
456 xrED.setText(toqstr(convert<string>(bbox.xr)));
457 ytED.setText(toqstr(convert<string>(bbox.yt)));
461 void getCrop(external::ClipData & data,
462 QCheckBox const & clipCB,
463 QLineEdit const & xlED, QLineEdit const & ybED,
464 QLineEdit const & xrED, QLineEdit const & ytED,
467 data.clip = clipCB.isChecked();
472 data.bbox.xl = convert<int>(fromqstr(xlED.text()));
473 data.bbox.yb = convert<int>(fromqstr(ybED.text()));
474 data.bbox.xr = convert<int>(fromqstr(xrED.text()));
475 data.bbox.yt = convert<int>(fromqstr(ytED.text()));
479 void getExtra(external::ExtraData & data,
480 QExternal::MapType const & extra)
482 typedef QExternal::MapType MapType;
483 MapType::const_iterator it = extra.begin();
484 MapType::const_iterator const end = extra.end();
485 for (; it != end; ++it)
486 data.set(it->first, trim(fromqstr(it->second)));
492 typedef QController<ControlExternal, QView<QExternalDialog> >
495 QExternal::QExternal(Dialog & parent)
496 : ExternalBase(parent, _("External Material"))
500 void QExternal::build_dialog()
502 dialog_.reset(new QExternalDialog(this));
504 bcview().setOK(dialog_->okPB);
505 bcview().setApply(dialog_->applyPB);
506 bcview().setCancel(dialog_->closePB);
508 bcview().addReadOnly(dialog_->fileED);
509 bcview().addReadOnly(dialog_->browsePB);
510 bcview().addReadOnly(dialog_->editPB);
511 bcview().addReadOnly(dialog_->externalCO);
512 bcview().addReadOnly(dialog_->draftCB);
513 bcview().addReadOnly(dialog_->displayscaleED);
514 bcview().addReadOnly(dialog_->showCO);
515 bcview().addReadOnly(dialog_->displayCB);
516 bcview().addReadOnly(dialog_->angleED);
517 bcview().addReadOnly(dialog_->originCO);
518 bcview().addReadOnly(dialog_->heightUnitCO);
519 bcview().addReadOnly(dialog_->heightED);
520 bcview().addReadOnly(dialog_->aspectratioCB);
521 bcview().addReadOnly(dialog_->widthUnitCO);
522 bcview().addReadOnly(dialog_->widthED);
523 bcview().addReadOnly(dialog_->clipCB);
524 bcview().addReadOnly(dialog_->getbbPB);
525 bcview().addReadOnly(dialog_->ytED);
526 bcview().addReadOnly(dialog_->xlED);
527 bcview().addReadOnly(dialog_->xrED);
528 bcview().addReadOnly(dialog_->ybED);
529 bcview().addReadOnly(dialog_->extraFormatCO);
530 bcview().addReadOnly(dialog_->extraED);
532 addCheckedLineEdit(bcview(), dialog_->angleED, dialog_->angleLA);
533 addCheckedLineEdit(bcview(), dialog_->displayscaleED, dialog_->scaleLA);
534 addCheckedLineEdit(bcview(), dialog_->heightED, dialog_->heightLA);
535 addCheckedLineEdit(bcview(), dialog_->widthED, dialog_->widthLA);
536 addCheckedLineEdit(bcview(), dialog_->xlED, dialog_->lbLA);
537 addCheckedLineEdit(bcview(), dialog_->ybED, dialog_->lbLA);
538 addCheckedLineEdit(bcview(), dialog_->xrED, dialog_->rtLA);
539 addCheckedLineEdit(bcview(), dialog_->ytED, dialog_->rtLA);
540 addCheckedLineEdit(bcview(), dialog_->fileED, dialog_->fileLA);
542 std::vector<string> templates(controller().getTemplates());
544 for (std::vector<string>::const_iterator cit = templates.begin();
545 cit != templates.end(); ++cit) {
546 dialog_->externalCO->addItem(qt_(*cit));
549 // Fill the origins combo
550 typedef vector<external::RotationDataType> Origins;
551 Origins const & all_origins = external::all_origins();
552 for (Origins::size_type i = 0; i != all_origins.size(); ++i)
553 dialog_->originCO->addItem(toqstr(external::origin_gui_str(i)));
555 // Fill the width combo
556 dialog_->widthUnitCO->addItem(qt_("Scale%"));
557 for (int i = 0; i < num_units; i++)
558 dialog_->widthUnitCO->addItem(qt_(unit_name_gui[i]));
562 void QExternal::update_contents()
564 dialog_->tab->setCurrentIndex(0);
565 InsetExternalParams const & params = controller().params();
568 params.filename.outputFilename(kernel().bufferFilepath());
569 dialog_->fileED->setText(toqstr(name));
571 dialog_->externalCO->setCurrentIndex(
572 controller().getTemplateNumber(params.templatename()));
575 dialog_->draftCB->setChecked(params.draft);
577 setDisplay(*dialog_->displayCB, *dialog_->showCO,
578 *dialog_->displayscaleED,
579 params.display, params.lyxscale, readOnly());
581 setRotation(*dialog_->angleED, *dialog_->originCO, params.rotationdata);
583 setSize(*dialog_->widthED, *dialog_->widthUnitCO,
584 *dialog_->heightED, *dialog_->heightUnitCO,
585 *dialog_->aspectratioCB,
588 setCrop(*dialog_->clipCB,
589 *dialog_->xlED, *dialog_->ybED,
590 *dialog_->xrED, *dialog_->ytED,
592 controller().bbChanged(!params.clipdata.bbox.empty());
598 void QExternal::updateTemplate()
600 external::Template templ =
601 controller().getTemplate(dialog_->externalCO->currentIndex());
602 dialog_->externalTB->setPlainText(qt_(templ.helpText));
604 // Ascertain which (if any) transformations the template supports
605 // and disable tabs hosting unsupported transforms.
606 typedef vector<external::TransformID> TransformIDs;
607 TransformIDs const transformIds = templ.transformIds;
608 TransformIDs::const_iterator tr_begin = transformIds.begin();
609 TransformIDs::const_iterator const tr_end = transformIds.end();
611 bool found = find(tr_begin, tr_end, external::Rotate) != tr_end;
612 dialog_->tab->setTabEnabled(
613 dialog_->tab->indexOf(dialog_->rotatetab), found);
614 found = find(tr_begin, tr_end, external::Resize) != tr_end;
615 dialog_->tab->setTabEnabled(
616 dialog_->tab->indexOf(dialog_->scaletab), found);
618 found = find(tr_begin, tr_end, external::Clip) != tr_end;
619 dialog_->tab->setTabEnabled(
620 dialog_->tab->indexOf(dialog_->croptab), found);
622 found = find(tr_begin, tr_end, external::Extra) != tr_end;
623 dialog_->tab->setTabEnabled(
624 dialog_->tab->indexOf(dialog_->optionstab), found);
629 // Ascertain whether the template has any formats supporting
630 // the 'Extra' option
631 QLineEdit * const extraED = dialog_->extraED;
632 QComboBox * const extraCB = dialog_->extraFormatCO;
638 external::Template::Formats::const_iterator it = templ.formats.begin();
639 external::Template::Formats::const_iterator end = templ.formats.end();
640 for (; it != end; ++it) {
641 if (it->second.option_transformers.find(external::Extra) ==
642 it->second.option_transformers.end())
644 string const format = it->first;
645 string const opt = controller().params().extradata.get(format);
646 extraCB->addItem(toqstr(format));
647 extra_[format] = toqstr(opt);
650 bool const enabled = extraCB->count() > 0;
652 dialog_->tab->setTabEnabled(
653 dialog_->tab->indexOf(dialog_->optionstab), enabled);
654 extraED->setEnabled(enabled && !kernel().isBufferReadonly());
655 extraCB->setEnabled(enabled);
658 extraCB->setCurrentIndex(0);
659 extraED->setText(extra_[fromqstr(extraCB->currentText())]);
664 void QExternal::apply()
666 InsetExternalParams params = controller().params();
668 params.filename.set(internal_path(fromqstr(dialog_->fileED->text())),
669 kernel().bufferFilepath());
671 params.settemplate(controller().getTemplate(
672 dialog_->externalCO->currentIndex()).lyxName);
674 params.draft = dialog_->draftCB->isChecked();
676 getDisplay(params.display, params.lyxscale,
677 *dialog_->displayCB, *dialog_->showCO,
678 *dialog_->displayscaleED);
680 if (dialog_->tab->isTabEnabled(
681 dialog_->tab->indexOf(dialog_->rotatetab)))
682 getRotation(params.rotationdata,
683 *dialog_->angleED, *dialog_->originCO);
685 if (dialog_->tab->isTabEnabled(
686 dialog_->tab->indexOf(dialog_->scaletab)))
687 getSize(params.resizedata,
688 *dialog_->widthED, *dialog_->widthUnitCO,
689 *dialog_->heightED, *dialog_->heightUnitCO,
690 *dialog_->aspectratioCB);
692 if (dialog_->tab->isTabEnabled(
693 dialog_->tab->indexOf(dialog_->croptab)))
694 getCrop(params.clipdata,
696 *dialog_->xlED, *dialog_->ybED,
697 *dialog_->xrED, *dialog_->ytED,
698 controller().bbChanged());
700 if (dialog_->tab->isTabEnabled(
701 dialog_->tab->indexOf(dialog_->optionstab)))
702 getExtra(params.extradata, extra_);
704 controller().setParams(params);
708 void QExternal::getBB()
710 dialog_->xlED->setText("0");
711 dialog_->ybED->setText("0");
712 dialog_->xrED->setText("0");
713 dialog_->ytED->setText("0");
715 string const filename = fromqstr(dialog_->fileED->text());
716 if (filename.empty())
719 string const bb = controller().readBB(filename);
723 dialog_->xlED->setText(toqstr(token(bb, ' ', 0)));
724 dialog_->ybED->setText(toqstr(token(bb, ' ', 1)));
725 dialog_->xrED->setText(toqstr(token(bb, ' ', 2)));
726 dialog_->ytED->setText(toqstr(token(bb, ' ', 3)));
728 controller().bbChanged(false);
731 } // namespace frontend
734 #include "QExternal_moc.cpp"