]> git.lyx.org Git - lyx.git/blob - src/frontends/qt4/GuiExternal.cpp
This is the last of a series of patches that merges the layout modules development...
[lyx.git] / src / frontends / qt4 / GuiExternal.cpp
1 /**
2  * \file GuiExternal.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author John Levon
7  * \author Angus Leeming
8  *
9  * Full author contact details are available in file CREDITS.
10  */
11
12 #include <config.h>
13
14 #include "GuiExternal.h"
15
16 #include "ControlExternal.h"
17 #include "lengthcommon.h"
18 #include "LyXRC.h"
19
20 #include "insets/ExternalTemplate.h"
21 #include "insets/InsetExternal.h"
22
23 #include "support/lstrings.h"
24 #include "support/convert.h"
25 #include "support/os.h"
26 #include "support/lyxlib.h"
27
28 #include "LengthCombo.h"
29 #include "qt_helpers.h"
30 #include "Validator.h"
31
32 #include <QCloseEvent>
33 #include <QCheckBox>
34 #include <QLineEdit>
35 #include <QPushButton>
36 #include <QTabWidget>
37 #include <QTextBrowser>
38
39 namespace external = lyx::external;
40
41 using lyx::support::isStrDbl;
42 using lyx::support::token;
43 using lyx::support::trim;
44 using lyx::support::float_equal;
45
46 using lyx::support::os::internal_path;
47
48 using std::string;
49 using std::vector;
50
51
52 namespace lyx {
53 namespace frontend {
54
55 GuiExternalDialog::GuiExternalDialog(LyXView & lv)
56         : GuiDialog(lv, "external")
57 {
58         setupUi(this);
59         setViewTitle(_("External Material"));
60         setController(new ControlExternal(*this));
61
62         connect(okPB, SIGNAL(clicked()), this, SLOT(slotOK()));
63         connect(applyPB, SIGNAL(clicked()), this, SLOT(slotApply()));
64         connect(closePB, SIGNAL(clicked()), this, SLOT(slotClose()));
65
66         connect(displayCB, SIGNAL(toggled(bool)),
67                 showCO, SLOT(setEnabled(bool)));
68         connect(displayCB, SIGNAL(toggled(bool)),
69                 displayscaleED, SLOT(setEnabled(bool)));
70         connect(showCO, SIGNAL(activated(const QString&)),
71                 this, SLOT(change_adaptor()));
72         connect(originCO, SIGNAL(activated(int)),
73                 this, SLOT(change_adaptor()));
74         connect(aspectratioCB, SIGNAL(stateChanged(int)),
75                 this, SLOT(change_adaptor()));
76         connect(browsePB, SIGNAL(clicked()),
77                 this, SLOT(browseClicked()));
78         connect(editPB, SIGNAL(clicked()),
79                 this, SLOT(editClicked()));
80         connect(externalCO, SIGNAL(activated(const QString &)),
81                 this, SLOT(templateChanged()));
82         connect(extraED, SIGNAL(textChanged(const QString &)),
83                 this, SLOT(extraChanged(const QString&)));
84         connect(extraFormatCO, SIGNAL(activated(const QString &)),
85                 this, SLOT(formatChanged(const QString&)));
86         connect(widthUnitCO, SIGNAL(activated(int)),
87                 this, SLOT(widthUnitChanged()));
88         connect(heightUnitCO, SIGNAL(selectionChanged(lyx::Length::UNIT)),
89                 this, SLOT(change_adaptor()));
90         connect(displayCB, SIGNAL(stateChanged(int)),
91                 this, SLOT(change_adaptor()));
92         connect(displayscaleED, SIGNAL(textChanged(const QString &)),
93                 this, SLOT(change_adaptor()));
94         connect(angleED, SIGNAL(textChanged(const QString &)),
95                 this, SLOT(change_adaptor()));
96         connect(widthED, SIGNAL(textChanged(const QString &)),
97                 this, SLOT(sizeChanged()));
98         connect(heightED, SIGNAL(textChanged(const QString &)),
99                 this, SLOT(sizeChanged()));
100         connect(fileED, SIGNAL(textChanged(const QString &)),
101                 this, SLOT(change_adaptor()));
102         connect(clipCB, SIGNAL(stateChanged(int)),
103                 this, SLOT(change_adaptor()));
104         connect(getbbPB, SIGNAL(clicked()), this, SLOT(getbbClicked()));
105         connect(xrED, SIGNAL(textChanged(const QString &)), this, SLOT(bbChanged()));
106         connect(ytED, SIGNAL(textChanged(const QString &)), this, SLOT(bbChanged()));
107         connect(xlED, SIGNAL(textChanged(const QString &)), this, SLOT(bbChanged()));
108         connect(ybED, SIGNAL(textChanged(const QString &)), this, SLOT(bbChanged()));
109         connect(draftCB, SIGNAL(clicked()), this, SLOT(change_adaptor()));
110
111         QIntValidator * validator = new QIntValidator(displayscaleED);
112         validator->setBottom(1);
113         displayscaleED->setValidator(validator);
114
115         angleED->setValidator(new QDoubleValidator(-360, 360, 2, angleED));
116
117         xlED->setValidator(new QIntValidator(xlED));
118         ybED->setValidator(new QIntValidator(ybED));
119         xrED->setValidator(new QIntValidator(xrED));
120         ytED->setValidator(new QIntValidator(ytED));
121
122         widthED->setValidator(unsignedLengthValidator(widthED));
123         heightED->setValidator(unsignedLengthValidator(heightED));
124
125         setFocusProxy(fileED);
126
127         bc().setPolicy(ButtonPolicy::NoRepeatedApplyReadOnlyPolicy);
128
129         bc().setOK(okPB);
130         bc().setApply(applyPB);
131         bc().setCancel(closePB);
132
133         bc().addReadOnly(fileED);
134         bc().addReadOnly(browsePB);
135         bc().addReadOnly(editPB);
136         bc().addReadOnly(externalCO);
137         bc().addReadOnly(draftCB);
138         bc().addReadOnly(displayscaleED);
139         bc().addReadOnly(showCO);
140         bc().addReadOnly(displayCB);
141         bc().addReadOnly(angleED);
142         bc().addReadOnly(originCO);
143         bc().addReadOnly(heightUnitCO);
144         bc().addReadOnly(heightED);
145         bc().addReadOnly(aspectratioCB);
146         bc().addReadOnly(widthUnitCO);
147         bc().addReadOnly(widthED);
148         bc().addReadOnly(clipCB);
149         bc().addReadOnly(getbbPB);
150         bc().addReadOnly(ytED);
151         bc().addReadOnly(xlED);
152         bc().addReadOnly(xrED);
153         bc().addReadOnly(ybED);
154         bc().addReadOnly(extraFormatCO);
155         bc().addReadOnly(extraED);
156
157         bc().addCheckedLineEdit(angleED, angleLA);
158         bc().addCheckedLineEdit(displayscaleED, scaleLA);
159         bc().addCheckedLineEdit(heightED, heightLA);
160         bc().addCheckedLineEdit(widthED, widthLA);
161         bc().addCheckedLineEdit(xlED, lbLA);
162         bc().addCheckedLineEdit(ybED, lbLA);
163         bc().addCheckedLineEdit(xrED, rtLA);
164         bc().addCheckedLineEdit(ytED, rtLA);
165         bc().addCheckedLineEdit(fileED, fileLA);
166
167         std::vector<string> templates(controller().getTemplates());
168
169         for (std::vector<string>::const_iterator cit = templates.begin();
170                 cit != templates.end(); ++cit) {
171                 externalCO->addItem(qt_(*cit));
172         }
173
174         // Fill the origins combo
175         typedef vector<external::RotationDataType> Origins;
176         Origins const & all_origins = external::all_origins();
177         for (Origins::size_type i = 0; i != all_origins.size(); ++i)
178                 originCO->addItem(toqstr(external::origin_gui_str(i)));
179
180         // Fill the width combo
181         widthUnitCO->addItem(qt_("Scale%"));
182         for (int i = 0; i < num_units; i++)
183                 widthUnitCO->addItem(qt_(unit_name_gui[i]));
184 }
185
186
187 ControlExternal & GuiExternalDialog::controller()
188 {
189         return static_cast<ControlExternal &>(GuiDialog::controller());
190 }
191
192
193 bool GuiExternalDialog::activateAspectratio() const
194 {
195         if (widthUnitCO->currentIndex() == 0)
196                 return false;
197
198         string const wstr = fromqstr(widthED->text());
199         if (wstr.empty())
200                 return false;
201         bool const wIsDbl = isStrDbl(wstr);
202         if (wIsDbl && float_equal(convert<double>(wstr), 0.0, 0.05))
203                 return false;
204         Length l;
205         if (!wIsDbl && (!isValidLength(wstr, &l) || l.zero()))
206                 return false;
207
208         string const hstr = fromqstr(heightED->text());
209         if (hstr.empty())
210                 return false;
211         bool const hIsDbl = isStrDbl(hstr);
212         if (hIsDbl && float_equal(convert<double>(hstr), 0.0, 0.05))
213                 return false;
214         if (!hIsDbl && (!isValidLength(hstr, &l) || l.zero()))
215                 return false;
216
217         return true;
218 }
219
220
221 void GuiExternalDialog::bbChanged()
222 {
223         controller().bbChanged(true);
224         changed();
225 }
226
227
228 void GuiExternalDialog::browseClicked()
229 {
230         int const choice =  externalCO->currentIndex();
231         docstring const template_name =
232                 from_utf8(controller().getTemplate(choice).lyxName);
233         docstring const str =
234                 controller().browse(qstring_to_ucs4(fileED->text()),
235                                            template_name);
236         if (!str.empty()) {
237                 fileED->setText(toqstr(str));
238                 changed();
239         }
240 }
241
242
243 void GuiExternalDialog::change_adaptor()
244 {
245         changed();
246 }
247
248
249 void GuiExternalDialog::closeEvent(QCloseEvent * e)
250 {
251         slotClose();
252         e->accept();
253 }
254
255
256 void GuiExternalDialog::editClicked()
257 {
258         controller().editExternal();
259 }
260
261
262
263 void GuiExternalDialog::extraChanged(const QString& text)
264 {
265         std::string const format = fromqstr(extraFormatCO->currentText());
266         extra_[format] = text;
267         changed();
268 }
269
270
271 void GuiExternalDialog::formatChanged(const QString& format)
272 {
273         extraED->setText(extra_[fromqstr(format)]);
274 }
275
276
277 void GuiExternalDialog::getbbClicked()
278 {
279         getBB();
280 }
281
282
283 void GuiExternalDialog::sizeChanged()
284 {
285         aspectratioCB->setEnabled(activateAspectratio());
286         changed();
287 }
288
289
290 void GuiExternalDialog::templateChanged()
291 {
292         updateTemplate();
293         changed();
294 }
295
296
297 void GuiExternalDialog::widthUnitChanged()
298 {
299         bool useHeight = (widthUnitCO->currentIndex() > 0);
300
301         if (useHeight)
302                 widthED->setValidator(unsignedLengthValidator(widthED));
303         else
304                 widthED->setValidator(new QDoubleValidator(0, 1000, 2, widthED));
305
306         heightED->setEnabled(useHeight);
307         heightUnitCO->setEnabled(useHeight);
308         changed();
309 }
310
311
312
313 namespace {
314
315 Length::UNIT defaultUnit()
316 {
317         Length::UNIT default_unit = Length::CM;
318         switch (lyxrc.default_papersize) {
319         case PAPER_USLETTER:
320         case PAPER_USLEGAL:
321         case PAPER_USEXECUTIVE:
322                 default_unit = Length::IN;
323                 break;
324         default:
325                 break;
326         }
327         return default_unit;
328 }
329
330
331 void setDisplay(QCheckBox & displayCB, QComboBox & showCO, QLineEdit & scaleED,
332                 external::DisplayType display, unsigned int scale,
333                 bool read_only)
334 {
335         int item = 0;
336         switch (display) {
337         case external::DefaultDisplay:
338                 item = 0;
339                 break;
340         case external::MonochromeDisplay:
341                 item = 1;
342                 break;
343         case external::GrayscaleDisplay:
344                 item = 2;
345                 break;
346         case external::ColorDisplay:
347                 item = 3;
348                 break;
349         case external::PreviewDisplay:
350                 item = 4;
351                 break;
352         case external::NoDisplay:
353                 item = 0;
354                 break;
355         }
356
357         showCO.setCurrentIndex(item);
358         bool const no_display = display == external::NoDisplay;
359         showCO.setEnabled(!no_display && !read_only);
360         displayCB.setChecked(!no_display);
361         scaleED.setEnabled(!no_display && !read_only);
362         scaleED.setText(QString::number(scale));
363 }
364
365
366 void getDisplay(external::DisplayType & display,
367                 unsigned int & scale,
368                 QCheckBox const & displayCB,
369                 QComboBox const & showCO,
370                 QLineEdit const & scaleED)
371 {
372         switch (showCO.currentIndex()) {
373         case 0:
374                 display = external::DefaultDisplay;
375                 break;
376         case 1:
377                 display = external::MonochromeDisplay;
378                 break;
379         case 2:
380                 display = external::GrayscaleDisplay;
381                 break;
382         case 3:
383                 display = external::ColorDisplay;
384                 break;
385         case 4:
386                 display = external::PreviewDisplay;
387                 break;
388         }
389
390         if (!displayCB.isChecked())
391                 display = external::NoDisplay;
392
393         scale = scaleED.text().toInt();
394 }
395
396
397 void setRotation(QLineEdit & angleED, QComboBox & originCO,
398                  external::RotationData const & data)
399 {
400         originCO.setCurrentIndex(int(data.origin()));
401         angleED.setText(toqstr(data.angle));
402 }
403
404
405 void getRotation(external::RotationData & data,
406                  QLineEdit const & angleED, QComboBox const & originCO)
407 {
408         typedef external::RotationData::OriginType OriginType;
409
410         data.origin(static_cast<OriginType>(originCO.currentIndex()));
411         data.angle = fromqstr(angleED.text());
412 }
413
414
415 void setSize(QLineEdit & widthED, QComboBox & widthUnitCO,
416              QLineEdit & heightED, LengthCombo & heightUnitCO,
417              QCheckBox & aspectratioCB,
418              external::ResizeData const & data)
419 {
420         bool using_scale = data.usingScale();
421         std::string scale = data.scale;
422         if (data.no_resize()) {
423                 // Everything is zero, so default to this!
424                 using_scale = true;
425                 scale = "100";
426         }
427
428         if (using_scale) {
429                 widthED.setText(toqstr(scale));
430                 widthUnitCO.setCurrentIndex(0);
431         } else {
432                 widthED.setText(QString::number(data.width.value()));
433                 // Because 'Scale' is position 0...
434                 // Note also that width cannot be zero here, so
435                 // we don't need to worry about the default unit.
436                 widthUnitCO.setCurrentIndex(data.width.unit() + 1);
437         }
438
439         string const h = data.height.zero() ? string() : data.height.asString();
440         Length::UNIT default_unit = data.width.zero() ?
441                 defaultUnit() : data.width.unit();
442         lengthToWidgets(&heightED, &heightUnitCO, h, default_unit);
443
444         heightED.setEnabled(!using_scale);
445         heightUnitCO.setEnabled(!using_scale);
446
447         aspectratioCB.setChecked(data.keepAspectRatio);
448
449         bool const disable_aspectRatio = using_scale ||
450                 data.width.zero() || data.height.zero();
451         aspectratioCB.setEnabled(!disable_aspectRatio);
452 }
453
454
455 void getSize(external::ResizeData & data,
456              QLineEdit const & widthED, QComboBox const & widthUnitCO,
457              QLineEdit const & heightED, LengthCombo const & heightUnitCO,
458              QCheckBox const & aspectratioCB)
459 {
460         string const width = fromqstr(widthED.text());
461
462         if (widthUnitCO.currentIndex() > 0) {
463                 // Subtract one, because scale is 0.
464                 int const unit = widthUnitCO.currentIndex() - 1;
465
466                 Length w;
467                 if (isValidLength(width, &w))
468                         data.width = w;
469                 else if (isStrDbl(width))
470                         data.width = Length(convert<double>(width),
471                                            static_cast<Length::UNIT>(unit));
472                 else
473                         data.width = Length();
474
475                 data.scale = string();
476
477         } else {
478                 // scaling instead of a width
479                 data.scale = width;
480                 data.width = Length();
481         }
482
483         data.height = Length(widgetsToLength(&heightED, &heightUnitCO));
484
485         data.keepAspectRatio = aspectratioCB.isChecked();
486 }
487
488
489 void setCrop(QCheckBox & clipCB,
490              QLineEdit & xlED, QLineEdit & ybED,
491              QLineEdit & xrED, QLineEdit & ytED,
492              external::ClipData const & data)
493 {
494         clipCB.setChecked(data.clip);
495         graphics::BoundingBox const & bbox = data.bbox;
496         xlED.setText(QString::number(bbox.xl));
497         ybED.setText(QString::number(bbox.yb));
498         xrED.setText(QString::number(bbox.xr));
499         ytED.setText(QString::number(bbox.yt));
500 }
501
502
503 void getCrop(external::ClipData & data,
504              QCheckBox const & clipCB,
505              QLineEdit const & xlED, QLineEdit const & ybED,
506              QLineEdit const & xrED, QLineEdit const & ytED,
507              bool bb_changed)
508 {
509         data.clip = clipCB.isChecked();
510
511         if (!bb_changed)
512                 return;
513
514         data.bbox.xl = xlED.text().toInt();
515         data.bbox.yb = ybED.text().toInt();
516         data.bbox.xr = xrED.text().toInt();
517         data.bbox.yt = ytED.text().toInt();
518 }
519
520
521 void getExtra(external::ExtraData & data,
522               GuiExternalDialog::MapType const & extra)
523 {
524         typedef GuiExternalDialog::MapType MapType;
525         MapType::const_iterator it = extra.begin();
526         MapType::const_iterator const end = extra.end();
527         for (; it != end; ++it)
528                 data.set(it->first, trim(fromqstr(it->second)));
529 }
530
531 } // namespace anon
532
533
534
535 void GuiExternalDialog::updateContents()
536 {
537         tab->setCurrentIndex(0);
538         InsetExternalParams const & params = controller().params();
539
540         string const name =
541                 params.filename.outputFilename(controller().bufferFilepath());
542         fileED->setText(toqstr(name));
543
544         externalCO->setCurrentIndex(
545                 controller().getTemplateNumber(params.templatename()));
546         updateTemplate();
547
548         draftCB->setChecked(params.draft);
549
550         setDisplay(*displayCB, *showCO,
551                    *displayscaleED,
552                    params.display, params.lyxscale, controller().isBufferReadonly());
553
554         setRotation(*angleED, *originCO, params.rotationdata);
555
556         setSize(*widthED, *widthUnitCO,
557                 *heightED, *heightUnitCO,
558                 *aspectratioCB,
559                 params.resizedata);
560
561         setCrop(*clipCB,
562                 *xlED, *ybED,
563                 *xrED, *ytED,
564                 params.clipdata);
565         controller().bbChanged(!params.clipdata.bbox.empty());
566
567         isValid();
568 }
569
570
571 void GuiExternalDialog::updateTemplate()
572 {
573         external::Template templ =
574                 controller().getTemplate(externalCO->currentIndex());
575         externalTB->setPlainText(qt_(templ.helpText));
576
577         // Ascertain which (if any) transformations the template supports
578         // and disable tabs hosting unsupported transforms.
579         typedef vector<external::TransformID> TransformIDs;
580         TransformIDs const transformIds = templ.transformIds;
581         TransformIDs::const_iterator tr_begin = transformIds.begin();
582         TransformIDs::const_iterator const tr_end = transformIds.end();
583
584         bool found = std::find(tr_begin, tr_end, external::Rotate) != tr_end;
585         tab->setTabEnabled(tab->indexOf(rotatetab), found);
586         found = std::find(tr_begin, tr_end, external::Resize) != tr_end;
587         tab->setTabEnabled(tab->indexOf(scaletab), found);
588
589         found = std::find(tr_begin, tr_end, external::Clip) != tr_end;
590         tab->setTabEnabled(tab->indexOf(croptab), found);
591
592         found = std::find(tr_begin, tr_end, external::Extra) != tr_end;
593         tab->setTabEnabled(tab->indexOf(optionstab), found);
594
595         if (!found)
596                 return;
597
598         // Ascertain whether the template has any formats supporting
599         // the 'Extra' option
600         extra_.clear();
601         extraED->clear();
602         extraFormatCO->clear();
603
604         external::Template::Formats::const_iterator it  = templ.formats.begin();
605         external::Template::Formats::const_iterator end = templ.formats.end();
606         for (; it != end; ++it) {
607                 if (it->second.option_transformers.find(external::Extra) ==
608                     it->second.option_transformers.end())
609                         continue;
610                 string const format = it->first;
611                 string const opt = controller().params().extradata.get(format);
612                 extraFormatCO->addItem(toqstr(format));
613                 extra_[format] = toqstr(opt);
614         }
615
616         bool const enabled = extraFormatCO->count()  > 0;
617
618         tab->setTabEnabled(
619                 tab->indexOf(optionstab), enabled);
620         extraED->setEnabled(enabled && !controller().isBufferReadonly());
621         extraFormatCO->setEnabled(enabled);
622
623         if (enabled) {
624                 extraFormatCO->setCurrentIndex(0);
625                 extraED->setText(extra_[fromqstr(extraFormatCO->currentText())]);
626         }
627 }
628
629
630 void GuiExternalDialog::applyView()
631 {
632         InsetExternalParams params = controller().params();
633
634         params.filename.set(internal_path(fromqstr(fileED->text())),
635                             controller().bufferFilepath());
636
637         params.settemplate(controller().getTemplate(
638                                    externalCO->currentIndex()).lyxName);
639
640         params.draft = draftCB->isChecked();
641
642         getDisplay(params.display, params.lyxscale,
643                    *displayCB, *showCO,
644                    *displayscaleED);
645
646         if (tab->isTabEnabled(tab->indexOf(rotatetab)))
647                 getRotation(params.rotationdata, *angleED, *originCO);
648
649         if (tab->isTabEnabled(tab->indexOf(scaletab)))
650                 getSize(params.resizedata, *widthED, *widthUnitCO,
651                         *heightED, *heightUnitCO, *aspectratioCB);
652
653         if (tab->isTabEnabled(tab->indexOf(croptab)))
654                 getCrop(params.clipdata, *clipCB, *xlED, *ybED,
655                         *xrED, *ytED, controller().bbChanged());
656
657         if (tab->isTabEnabled(tab->indexOf(optionstab)))
658                 getExtra(params.extradata, extra_);
659
660         controller().setParams(params);
661 }
662
663
664 void GuiExternalDialog::getBB()
665 {
666         xlED->setText("0");
667         ybED->setText("0");
668         xrED->setText("0");
669         ytED->setText("0");
670
671         string const filename = fromqstr(fileED->text());
672         if (filename.empty())
673                 return;
674
675         string const bb = controller().readBB(filename);
676         if (bb.empty())
677                 return;
678
679         xlED->setText(toqstr(token(bb, ' ', 0)));
680         ybED->setText(toqstr(token(bb, ' ', 1)));
681         xrED->setText(toqstr(token(bb, ' ', 2)));
682         ytED->setText(toqstr(token(bb, ' ', 3)));
683
684         controller().bbChanged(false);
685 }
686
687 } // namespace frontend
688 } // namespace lyx
689
690 #include "GuiExternal_moc.cpp"