1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtGui module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qplatformdefs.h"
41#include <QtPrintSupport/private/qtprintsupportglobal_p.h>
42
43#include "private/qabstractprintdialog_p.h"
44#if QT_CONFIG(messagebox)
45#include <QtWidgets/qmessagebox.h>
46#endif
47#include "qprintdialog.h"
48#if QT_CONFIG(filedialog)
49#include "qfiledialog.h"
50#endif
51#include <QtCore/qdebug.h>
52#include <QtCore/qdir.h>
53#include <QtCore/qglobal.h>
54#include <QtCore/qtextcodec.h>
55#include <QtGui/qevent.h>
56#if QT_CONFIG(filesystemmodel)
57#include <QtWidgets/qfilesystemmodel.h>
58#endif
59#include <QtWidgets/qstyleditemdelegate.h>
60#include <QtWidgets/qformlayout.h>
61#include <QtPrintSupport/qprinter.h>
62
63#include <qpa/qplatformprintplugin.h>
64#include <qpa/qplatformprintersupport.h>
65
66#include <private/qprintdevice_p.h>
67
68#include <QtWidgets/qdialogbuttonbox.h>
69
70#if QT_CONFIG(completer)
71#include <private/qcompleter_p.h>
72#endif
73#include "ui_qprintpropertieswidget.h"
74#include "ui_qprintsettingsoutput.h"
75#include "ui_qprintwidget.h"
76
77#if QT_CONFIG(cups)
78Q_DECLARE_METATYPE(const ppd_option_t *)
79#include <private/qcups_p.h>
80#if QT_CONFIG(cupsjobwidget)
81#include "qcupsjobwidget_p.h"
82#endif
83#endif
84
85/*
86
87Print dialog class declarations
88
89 QPrintDialog: The main Print Dialog, nothing really held here.
90
91 QUnixPrintWidget:
92 QUnixPrintWidgetPrivate: The real Unix Print Dialog implementation.
93
94 Directly includes the upper half of the Print Dialog
95 containing the Printer Selection widgets and
96 Properties button.
97
98 Embeds the Properties pop-up dialog from
99 QPrintPropertiesDialog
100
101 Embeds the lower half from separate widget class
102 QPrintDialogPrivate
103
104 Layout in qprintwidget.ui
105
106 QPrintDialogPrivate: The lower half of the Print Dialog containing the
107 Copies and Options tabs that expands when the
108 Options button is selected.
109
110 Layout in qprintsettingsoutput.ui
111
112 QPrintPropertiesDialog: Dialog displayed when clicking on Properties button to
113 allow editing of Page and Advanced tabs.
114
115 Layout in qprintpropertieswidget.ui
116*/
117
118static void initResources()
119{
120 Q_INIT_RESOURCE(qprintdialog);
121}
122
123QT_BEGIN_NAMESPACE
124
125class QPrintPropertiesDialog : public QDialog
126{
127 Q_OBJECT
128public:
129 QPrintPropertiesDialog(QPrinter *printer, QPrintDevice *currentPrintDevice,
130 QPrinter::OutputFormat outputFormat, const QString &printerName,
131 QAbstractPrintDialog *parent);
132 ~QPrintPropertiesDialog();
133
134 void setupPrinter() const;
135
136private slots:
137 void reject() override;
138 void accept() override;
139
140private:
141 void showEvent(QShowEvent *event) override;
142
143 friend class QUnixPrintWidgetPrivate;
144#if QT_CONFIG(cups)
145 QPrinter *m_printer;
146#endif
147 Ui::QPrintPropertiesWidget widget;
148 QDialogButtonBox *m_buttons;
149#if QT_CONFIG(cupsjobwidget)
150 QCupsJobWidget *m_jobOptions;
151#endif
152
153#if QT_CONFIG(cups)
154 bool createAdvancedOptionsWidget();
155 void setPrinterAdvancedCupsOptions() const;
156 void revertAdvancedOptionsToSavedValues() const;
157 void advancedOptionsUpdateSavedValues() const;
158 bool anyPpdOptionConflict() const;
159 bool anyAdvancedOptionConflict() const;
160
161 QPrintDevice *m_currentPrintDevice;
162 QTextCodec *m_cupsCodec = nullptr;
163 QVector<QComboBox*> m_advancedOptionsCombos;
164#endif
165};
166
167class QUnixPrintWidgetPrivate;
168
169class QUnixPrintWidget : public QWidget
170{
171 Q_OBJECT
172
173public:
174 explicit QUnixPrintWidget(QPrinter *printer, QWidget *parent = nullptr);
175 ~QUnixPrintWidget();
176 void updatePrinter();
177
178private:
179 friend class QPrintDialog;
180 friend class QPrintDialogPrivate;
181 friend class QUnixPrintWidgetPrivate;
182 QUnixPrintWidgetPrivate *d;
183 Q_PRIVATE_SLOT(d, void _q_printerChanged(int))
184 Q_PRIVATE_SLOT(d, void _q_btnBrowseClicked())
185 Q_PRIVATE_SLOT(d, void _q_btnPropertiesClicked())
186};
187
188class QUnixPrintWidgetPrivate
189{
190public:
191 QUnixPrintWidgetPrivate(QUnixPrintWidget *q, QPrinter *prn);
192 ~QUnixPrintWidgetPrivate();
193
194 bool checkFields();
195 void setupPrinter();
196 void setOptionsPane(QPrintDialogPrivate *pane);
197 void setupPrinterProperties();
198// slots
199 void _q_printerChanged(int index);
200 void _q_btnPropertiesClicked();
201 void _q_btnBrowseClicked();
202
203 QUnixPrintWidget * const parent;
204 QPrintPropertiesDialog *propertiesDialog;
205 Ui::QPrintWidget widget;
206 QAbstractPrintDialog * q;
207 QPrinter *printer;
208 QPrintDevice m_currentPrintDevice;
209
210 void updateWidget();
211
212#if QT_CONFIG(cups)
213 void setPpdDuplex(QPrinter::DuplexMode mode);
214 ppd_option_t *m_duplexPpdOption;
215#endif
216
217private:
218 QPrintDialogPrivate *optionsPane;
219 bool filePrintersAdded;
220};
221
222class QPrintDialogPrivate : public QAbstractPrintDialogPrivate
223{
224 Q_DECLARE_PUBLIC(QPrintDialog)
225 Q_DECLARE_TR_FUNCTIONS(QPrintDialog)
226public:
227 QPrintDialogPrivate();
228 ~QPrintDialogPrivate();
229
230 void init();
231
232 void selectPrinter(const QPrinter::OutputFormat outputFormat);
233
234 void _q_togglePageSetCombo(bool);
235#if QT_CONFIG(messagebox)
236 void _q_checkFields();
237#endif
238 void _q_collapseOrExpandDialog();
239
240#if QT_CONFIG(cups)
241 void updatePpdDuplexOption(QRadioButton *radio);
242#endif
243 void setupPrinter();
244 void updateWidgets();
245
246 virtual void setTabs(const QList<QWidget*> &tabs) override;
247
248 Ui::QPrintSettingsOutput options;
249 QUnixPrintWidget *top;
250 QWidget *bottom;
251 QDialogButtonBox *buttons;
252 QPushButton *collapseButton;
253 QPrinter::OutputFormat printerOutputFormat;
254private:
255 void setExplicitDuplexMode(QPrint::DuplexMode duplexMode);
256 // duplex mode explicitly set by user, QPrint::DuplexAuto otherwise
257 QPrint::DuplexMode explicitDuplexMode;
258};
259
260////////////////////////////////////////////////////////////////////////////////
261////////////////////////////////////////////////////////////////////////////////
262
263/*
264
265 QPrintPropertiesDialog
266
267 Dialog displayed when clicking on Properties button to allow editing of Page
268 and Advanced tabs.
269
270*/
271
272QPrintPropertiesDialog::QPrintPropertiesDialog(QPrinter *printer, QPrintDevice *currentPrintDevice,
273 QPrinter::OutputFormat outputFormat, const QString &printerName,
274 QAbstractPrintDialog *parent)
275 : QDialog(parent)
276#if QT_CONFIG(cups)
277 , m_printer(printer)
278#endif
279{
280 setWindowTitle(tr("Printer Properties"));
281 QVBoxLayout *lay = new QVBoxLayout(this);
282 QWidget *content = new QWidget(this);
283 widget.setupUi(content);
284 m_buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, this);
285 lay->addWidget(content);
286 lay->addWidget(m_buttons);
287
288 connect(m_buttons->button(QDialogButtonBox::Ok), &QPushButton::clicked, this, &QPrintPropertiesDialog::accept);
289 connect(m_buttons->button(QDialogButtonBox::Cancel), &QPushButton::clicked, this, &QPrintPropertiesDialog::reject);
290
291 widget.pageSetup->setPrinter(printer, currentPrintDevice, outputFormat, printerName);
292
293#if QT_CONFIG(cupsjobwidget)
294 m_jobOptions = new QCupsJobWidget(printer, currentPrintDevice);
295 widget.tabs->insertTab(1, m_jobOptions, tr("Job Options"));
296#endif
297
298 const int advancedTabIndex = widget.tabs->indexOf(widget.cupsPropertiesPage);
299#if QT_CONFIG(cups)
300 m_currentPrintDevice = currentPrintDevice;
301 const bool anyWidgetCreated = createAdvancedOptionsWidget();
302
303 widget.tabs->setTabEnabled(advancedTabIndex, anyWidgetCreated);
304
305 connect(widget.pageSetup, &QPageSetupWidget::ppdOptionChanged, this, [this] {
306 widget.conflictsLabel->setVisible(anyPpdOptionConflict());
307 });
308
309#else
310 Q_UNUSED(currentPrintDevice)
311 widget.tabs->setTabEnabled(advancedTabIndex, false);
312#endif
313}
314
315QPrintPropertiesDialog::~QPrintPropertiesDialog()
316{
317}
318
319void QPrintPropertiesDialog::setupPrinter() const
320{
321#if QT_CONFIG(cups)
322 QCUPSSupport::clearCupsOptions(m_printer);
323#endif
324
325 widget.pageSetup->setupPrinter();
326#if QT_CONFIG(cupsjobwidget)
327 m_jobOptions->setupPrinter();
328#endif
329
330#if QT_CONFIG(cups)
331 // Set Color by default, that will change if the "ColorModel" property is available
332 m_printer->setColorMode(QPrinter::Color);
333
334 setPrinterAdvancedCupsOptions();
335#endif
336}
337
338void QPrintPropertiesDialog::reject()
339{
340 widget.pageSetup->revertToSavedValues();
341
342#if QT_CONFIG(cupsjobwidget)
343 m_jobOptions->revertToSavedValues();
344#endif
345
346#if QT_CONFIG(cups)
347 revertAdvancedOptionsToSavedValues();
348#endif
349 QDialog::reject();
350}
351
352void QPrintPropertiesDialog::accept()
353{
354#if QT_CONFIG(cups)
355 if (widget.pageSetup->hasPpdConflict()) {
356 widget.tabs->setCurrentWidget(widget.tabPage);
357 const QMessageBox::StandardButton answer = QMessageBox::warning(this, tr("Page Setup Conflicts"),
358 tr("There are conflicts in page setup options. Do you want to fix them?"),
359 QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
360 if (answer != QMessageBox::No)
361 return;
362 } else if (anyAdvancedOptionConflict()) {
363 widget.tabs->setCurrentWidget(widget.cupsPropertiesPage);
364 const QMessageBox::StandardButton answer = QMessageBox::warning(this, tr("Advanced Option Conflicts"),
365 tr("There are conflicts in some advanced options. Do you want to fix them?"),
366 QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
367 if (answer != QMessageBox::No)
368 return;
369 }
370 advancedOptionsUpdateSavedValues();
371#endif
372
373#if QT_CONFIG(cupsjobwidget)
374 m_jobOptions->updateSavedValues();
375#endif
376
377 widget.pageSetup->updateSavedValues();
378
379 QDialog::accept();
380}
381
382void QPrintPropertiesDialog::showEvent(QShowEvent *event)
383{
384#if QT_CONFIG(cups)
385 widget.conflictsLabel->setVisible(anyPpdOptionConflict());
386#endif
387 QDialog::showEvent(event);
388}
389
390#if QT_CONFIG(cups)
391
392// Used to store the ppd_option_t for each QComboBox that represents an advanced option
393static const char *ppdOptionProperty = "_q_ppd_option";
394
395// Used to store the originally selected choice index for each QComboBox that represents an advanced option
396static const char *ppdOriginallySelectedChoiceProperty = "_q_ppd_originally_selected_choice";
397
398// Used to store the warning label pointer for each QComboBox that represents an advanced option
399static const char *warningLabelProperty = "_q_warning_label";
400
401static bool isBlacklistedGroup(const ppd_group_t *group) noexcept
402{
403 return qstrcmp(group->name, "InstallableOptions") == 0;
404};
405
406static bool isBlacklistedOption(const char *keyword) noexcept
407{
408 // We already let the user set these options elsewhere
409 const char *cupsOptionBlacklist[] = {
410 "Collate",
411 "Copies",
412 "OutputOrder",
413 "PageRegion",
414 "PageSize",
415 "Duplex" // handled by the main dialog
416 };
417 auto equals = [](const char *keyword) {
418 return [keyword](const char *candidate) {
419 return qstrcmp(keyword, candidate) == 0;
420 };
421 };
422 return std::any_of(std::begin(cupsOptionBlacklist), std::end(cupsOptionBlacklist), equals(keyword));
423};
424
425bool QPrintPropertiesDialog::createAdvancedOptionsWidget()
426{
427 bool anyWidgetCreated = false;
428
429 ppd_file_t *ppd = m_currentPrintDevice->property(PDPK_PpdFile).value<ppd_file_t*>();
430
431 if (ppd) {
432 m_cupsCodec = QTextCodec::codecForName(ppd->lang_encoding);
433
434 QWidget *holdingWidget = new QWidget();
435 QVBoxLayout *layout = new QVBoxLayout(holdingWidget);
436
437 for (int i = 0; i < ppd->num_groups; ++i) {
438 const ppd_group_t *group = &ppd->groups[i];
439
440 if (!isBlacklistedGroup(group)) {
441 QFormLayout *groupLayout = new QFormLayout();
442
443 for (int i = 0; i < group->num_options; ++i) {
444 const ppd_option_t *option = &group->options[i];
445
446 if (!isBlacklistedOption(option->keyword)) {
447 QComboBox *choicesCb = new QComboBox();
448
449 const auto setPpdOptionFromCombo = [this, choicesCb, option] {
450 // We can't use choicesCb->currentIndex() to know the index of the option in the choices[] array
451 // because some of them may not be present in the list because they conflict with the
452 // installable options so use the index passed on addItem
453 const int selectedChoiceIndex = choicesCb->currentData().toInt();
454 const auto values = QStringList{} << QString::fromLatin1(option->keyword)
455 << QString::fromLatin1(option->choices[selectedChoiceIndex].choice);
456 m_currentPrintDevice->setProperty(PDPK_PpdOption, values);
457 widget.conflictsLabel->setVisible(anyPpdOptionConflict());
458 };
459
460 bool foundMarkedChoice = false;
461 bool markedChoiceNotAvailable = false;
462 for (int i = 0; i < option->num_choices; ++i) {
463 const ppd_choice_t *choice = &option->choices[i];
464 const auto values = QStringList{} << QString::fromLatin1(option->keyword) << QString::fromLatin1(choice->choice);
465 const bool choiceIsInstallableConflict = m_currentPrintDevice->isFeatureAvailable(PDPK_PpdChoiceIsInstallableConflict, values);
466 if (choiceIsInstallableConflict && static_cast<int>(choice->marked) == 1) {
467 markedChoiceNotAvailable = true;
468 } else if (!choiceIsInstallableConflict) {
469 choicesCb->addItem(m_cupsCodec->toUnicode(choice->text), i);
470 if (static_cast<int>(choice->marked) == 1) {
471 choicesCb->setCurrentIndex(choicesCb->count() - 1);
472 choicesCb->setProperty(ppdOriginallySelectedChoiceProperty, QVariant(i));
473 foundMarkedChoice = true;
474 } else if (!foundMarkedChoice && qstrcmp(choice->choice, option->defchoice) == 0) {
475 choicesCb->setCurrentIndex(choicesCb->count() - 1);
476 choicesCb->setProperty(ppdOriginallySelectedChoiceProperty, QVariant(i));
477 }
478 }
479 }
480
481 if (markedChoiceNotAvailable) {
482 // If the user default option is not available because of it conflicting with
483 // the installed options, we need to set the internal ppd value to the value
484 // being shown in the combo
485 setPpdOptionFromCombo();
486 }
487
488 if (choicesCb->count() > 1) {
489
490 connect(choicesCb, QOverload<int>::of(&QComboBox::currentIndexChanged), this, setPpdOptionFromCombo);
491
492 // We need an extra label at the end to show the conflict warning
493 QWidget *choicesCbWithLabel = new QWidget();
494 QHBoxLayout *choicesCbWithLabelLayout = new QHBoxLayout(choicesCbWithLabel);
495 choicesCbWithLabelLayout->setContentsMargins(0, 0, 0, 0);
496 QLabel *warningLabel = new QLabel();
497 choicesCbWithLabelLayout->addWidget(choicesCb);
498 choicesCbWithLabelLayout->addWidget(warningLabel);
499
500 QLabel *optionLabel = new QLabel(m_cupsCodec->toUnicode(option->text));
501 groupLayout->addRow(optionLabel, choicesCbWithLabel);
502 anyWidgetCreated = true;
503 choicesCb->setProperty(ppdOptionProperty, QVariant::fromValue(option));
504 choicesCb->setProperty(warningLabelProperty, QVariant::fromValue(warningLabel));
505 m_advancedOptionsCombos << choicesCb;
506 } else {
507 delete choicesCb;
508 }
509 }
510 }
511
512 if (groupLayout->rowCount() > 0) {
513 QGroupBox *groupBox = new QGroupBox(m_cupsCodec->toUnicode(group->text));
514 groupBox->setLayout(groupLayout);
515 layout->addWidget(groupBox);
516 } else {
517 delete groupLayout;
518 }
519 }
520 }
521
522 layout->addStretch();
523 widget.scrollArea->setWidget(holdingWidget);
524 }
525
526 if (!m_cupsCodec)
527 m_cupsCodec = QTextCodec::codecForLocale();
528
529 return anyWidgetCreated;
530}
531
532void QPrintPropertiesDialog::setPrinterAdvancedCupsOptions() const
533{
534 for (const QComboBox *choicesCb : m_advancedOptionsCombos) {
535 const ppd_option_t *option = choicesCb->property(ppdOptionProperty).value<const ppd_option_t *>();
536
537 // We can't use choicesCb->currentIndex() to know the index of the option in the choices[] array
538 // because some of them may not be present in the list because they conflict with the
539 // installable options so use the index passed on addItem
540 const int selectedChoiceIndex = choicesCb->currentData().toInt();
541 const char *selectedChoice = option->choices[selectedChoiceIndex].choice;
542
543 if (qstrcmp(option->keyword, "ColorModel") == 0)
544 m_printer->setColorMode(qstrcmp(selectedChoice, "Gray") == 0 ? QPrinter::GrayScale : QPrinter::Color);
545
546 if (qstrcmp(option->defchoice, selectedChoice) != 0)
547 QCUPSSupport::setCupsOption(m_printer, QString::fromLatin1(option->keyword), QString::fromLatin1(selectedChoice));
548 }
549}
550
551void QPrintPropertiesDialog::revertAdvancedOptionsToSavedValues() const
552{
553 for (QComboBox *choicesCb : m_advancedOptionsCombos) {
554 const int originallySelectedChoice = choicesCb->property(ppdOriginallySelectedChoiceProperty).value<int>();
555 const int newComboIndexToSelect = choicesCb->findData(originallySelectedChoice);
556 choicesCb->setCurrentIndex(newComboIndexToSelect);
557 // The currentIndexChanged lambda takes care of resetting the ppd option
558 }
559 widget.conflictsLabel->setVisible(anyPpdOptionConflict());
560}
561
562void QPrintPropertiesDialog::advancedOptionsUpdateSavedValues() const
563{
564 for (QComboBox *choicesCb : m_advancedOptionsCombos)
565 choicesCb->setProperty(ppdOriginallySelectedChoiceProperty, choicesCb->currentData());
566}
567
568bool QPrintPropertiesDialog::anyPpdOptionConflict() const
569{
570 // we need to execute both since besides returning true/false they update the warning icons
571 const bool pageSetupConflicts = widget.pageSetup->hasPpdConflict();
572 const bool advancedOptionConflicts = anyAdvancedOptionConflict();
573 return pageSetupConflicts || advancedOptionConflicts;
574}
575
576bool QPrintPropertiesDialog::anyAdvancedOptionConflict() const
577{
578 const QIcon warning = QApplication::style()->standardIcon(QStyle::SP_MessageBoxWarning, nullptr, nullptr);
579
580 bool anyConflicted = false;
581
582 for (const QComboBox *choicesCb : m_advancedOptionsCombos) {
583 const ppd_option_t *option = choicesCb->property(ppdOptionProperty).value<const ppd_option_t *>();
584 QLabel *warningLabel = choicesCb->property(warningLabelProperty).value<QLabel *>();
585 if (option->conflicted) {
586 anyConflicted = true;
587 const int pixmap_size = choicesCb->sizeHint().height() * .75;
588 warningLabel->setPixmap(warning.pixmap(pixmap_size, pixmap_size));
589 } else {
590 warningLabel->setPixmap(QPixmap());
591 }
592 }
593
594 return anyConflicted;
595}
596
597#endif
598
599
600////////////////////////////////////////////////////////////////////////////////
601////////////////////////////////////////////////////////////////////////////////
602
603/*
604
605 QPrintDialogPrivate
606
607 The lower half of the Print Dialog containing the Copies and Options
608 tabs that expands when the Options button is selected.
609
610*/
611QPrintDialogPrivate::QPrintDialogPrivate()
612 : top(nullptr), bottom(nullptr), buttons(nullptr), collapseButton(nullptr),
613 explicitDuplexMode(QPrint::DuplexAuto)
614{
615 initResources();
616}
617
618QPrintDialogPrivate::~QPrintDialogPrivate()
619{
620}
621
622void QPrintDialogPrivate::init()
623{
624 Q_Q(QPrintDialog);
625
626 top = new QUnixPrintWidget(q->printer(), q);
627 bottom = new QWidget(q);
628 options.setupUi(bottom);
629 options.color->setIconSize(QSize(32, 32));
630 options.color->setIcon(QIcon(QLatin1String(":/qt-project.org/dialogs/qprintdialog/images/status-color.png")));
631 options.grayscale->setIconSize(QSize(32, 32));
632 options.grayscale->setIcon(QIcon(QLatin1String(":/qt-project.org/dialogs/qprintdialog/images/status-gray-scale.png")));
633
634#if QT_CONFIG(cups)
635 // Add Page Set widget if CUPS is available
636 options.pageSetCombo->addItem(tr("All Pages"), QVariant::fromValue(QCUPSSupport::AllPages));
637 options.pageSetCombo->addItem(tr("Odd Pages"), QVariant::fromValue(QCUPSSupport::OddPages));
638 options.pageSetCombo->addItem(tr("Even Pages"), QVariant::fromValue(QCUPSSupport::EvenPages));
639#else
640 for (int i = options.pagesLayout->count() - 1; i >= 0; --i)
641 delete options.pagesLayout->itemAt(i)->widget();
642#endif
643
644 top->d->setOptionsPane(this);
645
646 buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, q);
647 collapseButton = new QPushButton(QPrintDialog::tr("&Options >>"), buttons);
648 buttons->addButton(collapseButton, QDialogButtonBox::ResetRole);
649 bottom->setVisible(false);
650
651 QPushButton *printButton = buttons->button(QDialogButtonBox::Ok);
652 printButton->setText(QPrintDialog::tr("&Print"));
653 printButton->setDefault(true);
654
655 QVBoxLayout *lay = new QVBoxLayout(q);
656 lay->addWidget(top);
657 lay->addWidget(bottom);
658 lay->addWidget(buttons);
659
660#if !QT_CONFIG(messagebox)
661 QObject::connect(buttons, SIGNAL(accepted()), q, SLOT(accept()));
662#else
663 QObject::connect(buttons, SIGNAL(accepted()), q, SLOT(_q_checkFields()));
664#endif
665 QObject::connect(buttons, SIGNAL(rejected()), q, SLOT(reject()));
666
667 QObject::connect(options.printSelection, SIGNAL(toggled(bool)),
668 q, SLOT(_q_togglePageSetCombo(bool)));
669
670 QObject::connect(options.printCurrentPage, SIGNAL(toggled(bool)),
671 q, SLOT(_q_togglePageSetCombo(bool)));
672
673 QObject::connect(collapseButton, SIGNAL(released()), q, SLOT(_q_collapseOrExpandDialog()));
674
675 QObject::connect(options.noDuplex, &QAbstractButton::clicked, q, [this] { setExplicitDuplexMode(QPrint::DuplexNone); });
676 QObject::connect(options.duplexLong, &QAbstractButton::clicked, q, [this] { setExplicitDuplexMode(QPrint::DuplexLongSide); });
677 QObject::connect(options.duplexShort, &QAbstractButton::clicked, q, [this] { setExplicitDuplexMode(QPrint::DuplexShortSide); });
678
679#if QT_CONFIG(cups)
680 QObject::connect(options.noDuplex, &QAbstractButton::toggled, q, [this] { updatePpdDuplexOption(options.noDuplex); });
681 QObject::connect(options.duplexLong, &QAbstractButton::toggled, q, [this] { updatePpdDuplexOption(options.duplexLong); });
682 QObject::connect(options.duplexShort, &QAbstractButton::toggled, q, [this] { updatePpdDuplexOption(options.duplexShort); });
683#endif
684}
685
686// initialize printer options
687void QPrintDialogPrivate::selectPrinter(const QPrinter::OutputFormat outputFormat)
688{
689 Q_Q(QPrintDialog);
690 QPrinter *p = q->printer();
691 printerOutputFormat = outputFormat;
692
693 // printer supports duplex mode?
694 const auto supportedDuplexMode = top->d->m_currentPrintDevice.supportedDuplexModes();
695 options.duplexLong->setEnabled(supportedDuplexMode.contains(QPrint::DuplexLongSide));
696 options.duplexShort->setEnabled(supportedDuplexMode.contains(QPrint::DuplexShortSide));
697
698 if (p->colorMode() == QPrinter::Color)
699 options.color->setChecked(true);
700 else
701 options.grayscale->setChecked(true);
702
703 // keep duplex value explicitly set by user, if any, and selected printer supports it;
704 // use device default otherwise
705 QPrint::DuplexMode duplex;
706 if (explicitDuplexMode != QPrint::DuplexAuto && supportedDuplexMode.contains(explicitDuplexMode))
707 duplex = explicitDuplexMode;
708 else
709 duplex = top->d->m_currentPrintDevice.defaultDuplexMode();
710 switch (duplex) {
711 case QPrint::DuplexNone:
712 options.noDuplex->setChecked(true); break;
713 case QPrint::DuplexLongSide:
714 case QPrint::DuplexAuto:
715 options.duplexLong->setChecked(true); break;
716 case QPrint::DuplexShortSide:
717 options.duplexShort->setChecked(true); break;
718 }
719 options.copies->setValue(p->copyCount());
720 options.collate->setChecked(p->collateCopies());
721 options.reverse->setChecked(p->pageOrder() == QPrinter::LastPageFirst);
722
723 if (outputFormat == QPrinter::PdfFormat || options.printSelection->isChecked()
724 || options.printCurrentPage->isChecked())
725
726 options.pageSetCombo->setEnabled(false);
727 else
728 options.pageSetCombo->setEnabled(true);
729
730#if QT_CONFIG(cups)
731 // Disable color options on main dialog if not printing to file, it will be handled by CUPS advanced dialog
732 options.colorMode->setVisible(outputFormat == QPrinter::PdfFormat);
733#endif
734}
735
736#if QT_CONFIG(cups)
737static std::vector<std::pair<int, int>> pageRangesFromString(const QString &pagesString) noexcept
738{
739 std::vector<std::pair<int, int>> result;
740 const QStringList items = pagesString.split(',');
741 for (const QString &item : items) {
742 if (item.isEmpty())
743 return {};
744
745 if (item.contains(QLatin1Char('-'))) {
746 const QStringList rangeItems = item.split('-');
747 if (rangeItems.count() != 2)
748 return {};
749
750 bool ok;
751 const int number1 = rangeItems[0].toInt(&ok);
752 if (!ok)
753 return {};
754
755 const int number2 = rangeItems[1].toInt(&ok);
756 if (!ok)
757 return {};
758
759 if (number1 < 1 || number2 < 1 || number2 < number1)
760 return {};
761
762 result.push_back(std::make_pair(number1, number2));
763
764 } else {
765 bool ok;
766 const int number = item.toInt(&ok);
767 if (!ok)
768 return {};
769
770 if (number < 1)
771 return {};
772
773 result.push_back(std::make_pair(number, number));
774 }
775 }
776
777 // check no range intersects with the next
778 std::sort(result.begin(), result.end(),
779 [](const std::pair<int, int> &it1, const std::pair<int, int> &it2) { return it1.first < it2.first; });
780 int previousSecond = -1;
781 for (auto pair : result) {
782 if (pair.first <= previousSecond)
783 return {};
784
785 previousSecond = pair.second;
786 }
787
788 return result;
789}
790
791static QString stringFromPageRanges(const std::vector<std::pair<int, int>> &pageRanges) noexcept
792{
793 QString result;
794
795 for (auto pair : pageRanges) {
796 if (!result.isEmpty())
797 result += QLatin1Char(',');
798
799 if (pair.first == pair.second)
800 result += QString::number(pair.first);
801 else
802 result += QStringLiteral("%1-%2").arg(pair.first).arg(pair.second);
803 }
804
805 return result;
806}
807
808static bool isValidPagesString(const QString &pagesString) noexcept
809{
810 if (pagesString.isEmpty())
811 return false;
812
813 auto pagesRanges = pageRangesFromString(pagesString);
814 return !pagesRanges.empty();
815}
816
817void QPrintDialogPrivate::updatePpdDuplexOption(QRadioButton *radio)
818{
819 const bool checked = radio->isChecked();
820 if (checked) {
821 if (radio == options.noDuplex) top->d->setPpdDuplex(QPrinter::DuplexNone);
822 else if (radio == options.duplexLong) top->d->setPpdDuplex(QPrinter::DuplexLongSide);
823 else if (radio == options.duplexShort) top->d->setPpdDuplex(QPrinter::DuplexShortSide);
824 }
825 const bool conflict = checked && top->d->m_duplexPpdOption && top->d->m_duplexPpdOption->conflicted;
826 radio->setIcon(conflict ? QApplication::style()->standardIcon(QStyle::SP_MessageBoxWarning, nullptr, nullptr) : QIcon());
827}
828
829#endif
830
831void QPrintDialogPrivate::setExplicitDuplexMode(const QPrint::DuplexMode duplexMode)
832{
833 explicitDuplexMode = duplexMode;
834}
835
836void QPrintDialogPrivate::setupPrinter()
837{
838 // First setup the requested OutputFormat, Printer and Page Size first
839 top->d->setupPrinter();
840
841 // Then setup Print Job options
842 Q_Q(QPrintDialog);
843 QPrinter* p = q->printer();
844
845 if (options.duplex->isEnabled()) {
846 if (options.noDuplex->isChecked())
847 p->setDuplex(QPrinter::DuplexNone);
848 else if (options.duplexLong->isChecked())
849 p->setDuplex(QPrinter::DuplexLongSide);
850 else
851 p->setDuplex(QPrinter::DuplexShortSide);
852 }
853
854#if QT_CONFIG(cups)
855 // When printing to a device the colorMode will be set by the advanced panel
856 if (p->outputFormat() == QPrinter::PdfFormat)
857#endif
858 p->setColorMode(options.color->isChecked() ? QPrinter::Color : QPrinter::GrayScale);
859
860 p->setPageOrder(options.reverse->isChecked() ? QPrinter::LastPageFirst : QPrinter::FirstPageFirst);
861
862 // print range
863 if (options.printAll->isChecked()) {
864 p->setPrintRange(QPrinter::AllPages);
865 p->setFromTo(0,0);
866 } else if (options.printSelection->isChecked()) {
867 p->setPrintRange(QPrinter::Selection);
868 p->setFromTo(0,0);
869 } else if (options.printCurrentPage->isChecked()) {
870 p->setPrintRange(QPrinter::CurrentPage);
871 p->setFromTo(0,0);
872 } else if (options.printRange->isChecked()) {
873 if (q->isOptionEnabled(QPrintDialog::PrintPageRange)) {
874 p->setPrintRange(QPrinter::PageRange);
875 p->setFromTo(options.from->value(), qMax(options.from->value(), options.to->value()));
876 } else {
877 // This case happens when CUPS server-side page range is enabled
878 // Setting the range to the printer occurs below
879 p->setPrintRange(QPrinter::AllPages);
880 p->setFromTo(0,0);
881 }
882 }
883
884#if QT_CONFIG(cups)
885 if (options.pagesRadioButton->isChecked()) {
886 auto pageRanges = pageRangesFromString(options.pagesLineEdit->text());
887
888 p->setPrintRange(QPrinter::AllPages);
889 p->setFromTo(0, 0);
890
891 // server-side page filtering
892 QCUPSSupport::setPageRange(p, stringFromPageRanges(pageRanges));
893 }
894
895 // page set
896 if (p->printRange() == QPrinter::AllPages || p->printRange() == QPrinter::PageRange) {
897 //If the application is selecting pages and the first page number is even then need to adjust the odd-even accordingly
898 QCUPSSupport::PageSet pageSet = options.pageSetCombo->itemData(options.pageSetCombo->currentIndex()).value<QCUPSSupport::PageSet>();
899 if (q->isOptionEnabled(QPrintDialog::PrintPageRange)
900 && p->printRange() == QPrinter::PageRange
901 && (q->fromPage() % 2 == 0)) {
902
903 switch (pageSet) {
904 case QCUPSSupport::AllPages:
905 break;
906 case QCUPSSupport::OddPages:
907 QCUPSSupport::setPageSet(p, QCUPSSupport::EvenPages);
908 break;
909 case QCUPSSupport::EvenPages:
910 QCUPSSupport::setPageSet(p, QCUPSSupport::OddPages);
911 break;
912 }
913 } else if (pageSet != QCUPSSupport::AllPages) {
914 QCUPSSupport::setPageSet(p, pageSet);
915 }
916
917 // server-side page range, since we set the page range on the printer to 0-0/AllPages above,
918 // we need to take the values directly from the widget as q->fromPage() will return 0
919 if (!q->isOptionEnabled(QPrintDialog::PrintPageRange) && options.printRange->isChecked())
920 QCUPSSupport::setPageRange(p, options.from->value(), qMax(options.from->value(), options.to->value()));
921 }
922#endif
923
924 // copies
925 p->setCopyCount(options.copies->value());
926 p->setCollateCopies(options.collate->isChecked());
927}
928
929void QPrintDialogPrivate::_q_togglePageSetCombo(bool checked)
930{
931 if (printerOutputFormat == QPrinter::PdfFormat)
932 return;
933
934 options.pageSetCombo->setDisabled(checked);
935}
936
937void QPrintDialogPrivate::_q_collapseOrExpandDialog()
938{
939 int collapseHeight = 0;
940 Q_Q(QPrintDialog);
941 QWidget *widgetToHide = bottom;
942 if (widgetToHide->isVisible()) {
943 collapseButton->setText(QPrintDialog::tr("&Options >>"));
944 collapseHeight = widgetToHide->y() + widgetToHide->height() - (top->y() + top->height());
945 }
946 else
947 collapseButton->setText(QPrintDialog::tr("&Options <<"));
948 widgetToHide->setVisible(! widgetToHide->isVisible());
949 if (! widgetToHide->isVisible()) { // make it shrink
950 q->layout()->activate();
951 q->resize( QSize(q->width(), q->height() - collapseHeight) );
952 }
953}
954
955#if QT_CONFIG(messagebox)
956void QPrintDialogPrivate::_q_checkFields()
957{
958 Q_Q(QPrintDialog);
959 if (top->d->checkFields())
960 q->accept();
961}
962#endif // QT_CONFIG(messagebox)
963
964
965void QPrintDialogPrivate::updateWidgets()
966{
967 Q_Q(QPrintDialog);
968 options.gbPrintRange->setVisible(q->isOptionEnabled(QPrintDialog::PrintPageRange) ||
969 q->isOptionEnabled(QPrintDialog::PrintSelection) ||
970 q->isOptionEnabled(QPrintDialog::PrintCurrentPage));
971
972 options.printRange->setEnabled(q->isOptionEnabled(QPrintDialog::PrintPageRange));
973 options.printSelection->setVisible(q->isOptionEnabled(QPrintDialog::PrintSelection));
974 options.printCurrentPage->setVisible(q->isOptionEnabled(QPrintDialog::PrintCurrentPage));
975 options.collate->setVisible(q->isOptionEnabled(QPrintDialog::PrintCollateCopies));
976
977#if QT_CONFIG(cups)
978 // Don't display Page Set if only Selection or Current Page are enabled
979 if (!q->isOptionEnabled(QPrintDialog::PrintPageRange)
980 && (q->isOptionEnabled(QPrintDialog::PrintSelection) || q->isOptionEnabled(QPrintDialog::PrintCurrentPage))) {
981 options.pageSetCombo->setVisible(false);
982 options.pageSetLabel->setVisible(false);
983 } else {
984 options.pageSetCombo->setVisible(true);
985 options.pageSetLabel->setVisible(true);
986 }
987
988 if (!q->isOptionEnabled(QPrintDialog::PrintPageRange)) {
989 // If we can do CUPS server side pages selection,
990 // display the page range widgets
991 options.gbPrintRange->setVisible(true);
992 options.printRange->setEnabled(true);
993 }
994#endif
995
996 switch (q->printRange()) {
997 case QPrintDialog::AllPages:
998 options.printAll->setChecked(true);
999 options.pageSetCombo->setEnabled(true);
1000 break;
1001 case QPrintDialog::Selection:
1002 options.printSelection->setChecked(true);
1003 options.pageSetCombo->setEnabled(false);
1004 break;
1005 case QPrintDialog::PageRange:
1006 options.printRange->setChecked(true);
1007 options.pageSetCombo->setEnabled(true);
1008 break;
1009 case QPrintDialog::CurrentPage:
1010 if (q->isOptionEnabled(QPrintDialog::PrintCurrentPage)) {
1011 options.printCurrentPage->setChecked(true);
1012 options.pageSetCombo->setEnabled(false);
1013 }
1014 break;
1015 default:
1016 break;
1017 }
1018 const int minPage = qMax(1, qMin(q->minPage() , q->maxPage()));
1019 const int maxPage = qMax(1, q->maxPage() == INT_MAX ? 9999 : q->maxPage());
1020
1021 options.from->setMinimum(minPage);
1022 options.to->setMinimum(minPage);
1023 options.from->setMaximum(maxPage);
1024 options.to->setMaximum(maxPage);
1025
1026 options.from->setValue(q->fromPage());
1027 options.to->setValue(q->toPage());
1028 top->d->updateWidget();
1029}
1030
1031void QPrintDialogPrivate::setTabs(const QList<QWidget*> &tabWidgets)
1032{
1033 QList<QWidget*>::ConstIterator iter = tabWidgets.begin();
1034 while(iter != tabWidgets.constEnd()) {
1035 QWidget *tab = *iter;
1036 options.tabs->addTab(tab, tab->windowTitle());
1037 ++iter;
1038 }
1039}
1040
1041////////////////////////////////////////////////////////////////////////////////
1042////////////////////////////////////////////////////////////////////////////////
1043
1044/*
1045
1046 QPrintDialog
1047
1048 The main Print Dialog.
1049
1050*/
1051
1052QPrintDialog::QPrintDialog(QPrinter *printer, QWidget *parent)
1053 : QAbstractPrintDialog(*(new QPrintDialogPrivate), printer, parent)
1054{
1055 Q_D(QPrintDialog);
1056 d->init();
1057}
1058
1059/*!
1060 Constructs a print dialog with the given \a parent.
1061*/
1062QPrintDialog::QPrintDialog(QWidget *parent)
1063 : QAbstractPrintDialog(*(new QPrintDialogPrivate), nullptr, parent)
1064{
1065 Q_D(QPrintDialog);
1066 d->init();
1067}
1068
1069QPrintDialog::~QPrintDialog()
1070{
1071}
1072
1073void QPrintDialog::setVisible(bool visible)
1074{
1075 Q_D(QPrintDialog);
1076
1077 if (visible)
1078 d->updateWidgets();
1079
1080 QAbstractPrintDialog::setVisible(visible);
1081}
1082
1083int QPrintDialog::exec()
1084{
1085 return QAbstractPrintDialog::exec();
1086}
1087
1088void QPrintDialog::accept()
1089{
1090 Q_D(QPrintDialog);
1091#if QT_CONFIG(cups)
1092 if (d->options.pagesRadioButton->isChecked() && !isValidPagesString(d->options.pagesLineEdit->text())) {
1093 QMessageBox::critical(this, tr("Invalid Pages Definition"),
1094 tr("%1 does not follow the correct syntax. Please use ',' to separate "
1095 "ranges and pages, '-' to define ranges and make sure ranges do "
1096 "not intersect with each other.").arg(d->options.pagesLineEdit->text()),
1097 QMessageBox::Ok, QMessageBox::Ok);
1098 return;
1099 }
1100 if (d->top->d->m_duplexPpdOption && d->top->d->m_duplexPpdOption->conflicted) {
1101 const QMessageBox::StandardButton answer = QMessageBox::warning(this, tr("Duplex Settings Conflicts"),
1102 tr("There are conflicts in duplex settings. Do you want to fix them?"),
1103 QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
1104 if (answer != QMessageBox::No)
1105 return;
1106 }
1107#endif
1108 d->setupPrinter();
1109 QDialog::accept();
1110}
1111
1112////////////////////////////////////////////////////////////////////////////////
1113////////////////////////////////////////////////////////////////////////////////
1114
1115/*
1116
1117 QUnixPrintWidget && QUnixPrintWidgetPrivate
1118
1119 The upper half of the Print Dialog containing the Printer Selection widgets
1120
1121*/
1122
1123#if defined (Q_OS_UNIX)
1124
1125/*! \internal
1126*/
1127QUnixPrintWidgetPrivate::QUnixPrintWidgetPrivate(QUnixPrintWidget *p, QPrinter *prn)
1128 : parent(p), propertiesDialog(nullptr), printer(prn),
1129#if QT_CONFIG(cups)
1130 m_duplexPpdOption(nullptr),
1131#endif
1132 optionsPane(nullptr), filePrintersAdded(false)
1133{
1134 q = nullptr;
1135 if (parent)
1136 q = qobject_cast<QAbstractPrintDialog*> (parent->parent());
1137
1138 widget.setupUi(parent);
1139
1140 int currentPrinterIndex = 0;
1141 QPlatformPrinterSupport *ps = QPlatformPrinterSupportPlugin::get();
1142 if (ps) {
1143 const QStringList printers = ps->availablePrintDeviceIds();
1144 const QString defaultPrinter = ps->defaultPrintDeviceId();
1145
1146 widget.printers->addItems(printers);
1147
1148 const QString selectedPrinter = prn && !prn->printerName().isEmpty() ? prn->printerName() : defaultPrinter;
1149 const int idx = printers.indexOf(selectedPrinter);
1150
1151 if (idx >= 0)
1152 currentPrinterIndex = idx;
1153 }
1154 widget.properties->setEnabled(true);
1155
1156#if QT_CONFIG(filesystemmodel) && QT_CONFIG(completer)
1157 QFileSystemModel *fsm = new QFileSystemModel(widget.filename);
1158 fsm->setRootPath(QDir::homePath());
1159 widget.filename->setCompleter(new QCompleter(fsm, widget.filename));
1160#endif
1161 _q_printerChanged(currentPrinterIndex);
1162
1163 QObject::connect(widget.printers, SIGNAL(currentIndexChanged(int)),
1164 parent, SLOT(_q_printerChanged(int)));
1165 QObject::connect(widget.fileBrowser, SIGNAL(clicked()), parent, SLOT(_q_btnBrowseClicked()));
1166 QObject::connect(widget.properties, SIGNAL(clicked()), parent, SLOT(_q_btnPropertiesClicked()));
1167
1168 // disable features that QPrinter does not yet support.
1169 widget.preview->setVisible(false);
1170}
1171
1172void QUnixPrintWidgetPrivate::updateWidget()
1173{
1174 const bool printToFile = q == 0 || q->isOptionEnabled(QPrintDialog::PrintToFile);
1175 if (printToFile && !filePrintersAdded) {
1176 if (widget.printers->count())
1177 widget.printers->insertSeparator(widget.printers->count());
1178 widget.printers->addItem(QPrintDialog::tr("Print to File (PDF)"));
1179 filePrintersAdded = true;
1180 }
1181 if (!printToFile && filePrintersAdded) {
1182 widget.printers->removeItem(widget.printers->count()-1);
1183 widget.printers->removeItem(widget.printers->count()-1);
1184 if (widget.printers->count())
1185 widget.printers->removeItem(widget.printers->count()-1); // remove separator
1186 filePrintersAdded = false;
1187 }
1188 if (printer && filePrintersAdded && (printer->outputFormat() != QPrinter::NativeFormat
1189 || printer->printerName().isEmpty()))
1190 {
1191 if (printer->outputFormat() == QPrinter::PdfFormat)
1192 widget.printers->setCurrentIndex(widget.printers->count() - 1);
1193 widget.filename->setEnabled(true);
1194 widget.lOutput->setEnabled(true);
1195 }
1196
1197 widget.filename->setVisible(printToFile);
1198 widget.lOutput->setVisible(printToFile);
1199 widget.fileBrowser->setVisible(printToFile);
1200
1201 widget.properties->setVisible(q->isOptionEnabled(QAbstractPrintDialog::PrintShowPageSize));
1202}
1203
1204QUnixPrintWidgetPrivate::~QUnixPrintWidgetPrivate()
1205{
1206}
1207
1208void QUnixPrintWidgetPrivate::_q_printerChanged(int index)
1209{
1210 if (index < 0)
1211 return;
1212 const int printerCount = widget.printers->count();
1213 widget.filename->setEnabled(false);
1214 widget.lOutput->setEnabled(false);
1215
1216 // Reset properties dialog when printer is changed
1217 if (propertiesDialog){
1218 delete propertiesDialog;
1219 propertiesDialog = nullptr;
1220 }
1221
1222#if QT_CONFIG(cups)
1223 m_duplexPpdOption = nullptr;
1224#endif
1225
1226 if (filePrintersAdded) {
1227 Q_ASSERT(index != printerCount - 2); // separator
1228 if (index == printerCount - 1) { // PDF
1229 widget.location->setText(QPrintDialog::tr("Local file"));
1230 widget.type->setText(QPrintDialog::tr("Write PDF file"));
1231 widget.properties->setEnabled(true);
1232 widget.filename->setEnabled(true);
1233 QString filename = widget.filename->text();
1234 widget.filename->setText(filename);
1235 widget.lOutput->setEnabled(true);
1236 if (optionsPane)
1237 optionsPane->selectPrinter(QPrinter::PdfFormat);
1238 printer->setOutputFormat(QPrinter::PdfFormat);
1239 m_currentPrintDevice = QPrintDevice();
1240 return;
1241 }
1242 }
1243
1244 if (printer) {
1245 printer->setOutputFormat(QPrinter::NativeFormat);
1246
1247 QPlatformPrinterSupport *ps = QPlatformPrinterSupportPlugin::get();
1248 if (ps)
1249 m_currentPrintDevice = ps->createPrintDevice(widget.printers->itemText(index));
1250 else
1251 m_currentPrintDevice = QPrintDevice();
1252
1253 printer->setPrinterName(m_currentPrintDevice.id());
1254
1255 widget.location->setText(m_currentPrintDevice.location());
1256 widget.type->setText(m_currentPrintDevice.makeAndModel());
1257 if (optionsPane)
1258 optionsPane->selectPrinter(QPrinter::NativeFormat);
1259 }
1260
1261#if QT_CONFIG(cups)
1262 m_duplexPpdOption = QCUPSSupport::findPpdOption("Duplex", &m_currentPrintDevice);
1263#endif
1264}
1265
1266void QUnixPrintWidgetPrivate::setOptionsPane(QPrintDialogPrivate *pane)
1267{
1268 optionsPane = pane;
1269 if (optionsPane)
1270 optionsPane->selectPrinter(QPrinter::NativeFormat);
1271}
1272
1273void QUnixPrintWidgetPrivate::_q_btnBrowseClicked()
1274{
1275 QString filename = widget.filename->text();
1276#if QT_CONFIG(filedialog)
1277 filename = QFileDialog::getSaveFileName(parent, QPrintDialog::tr("Print To File ..."), filename,
1278 QString(), nullptr, QFileDialog::DontConfirmOverwrite);
1279#else
1280 filename.clear();
1281#endif
1282 if (!filename.isEmpty()) {
1283 widget.filename->setText(filename);
1284 widget.printers->setCurrentIndex(widget.printers->count() - 1); // the pdf one
1285 }
1286}
1287
1288#if QT_CONFIG(messagebox)
1289bool QUnixPrintWidgetPrivate::checkFields()
1290{
1291 if (widget.filename->isEnabled()) {
1292 QString file = widget.filename->text();
1293 QFile f(file);
1294 QFileInfo fi(f);
1295 bool exists = fi.exists();
1296 bool opened = false;
1297 if (exists && fi.isDir()) {
1298 QMessageBox::warning(q, q->windowTitle(),
1299 QPrintDialog::tr("%1 is a directory.\nPlease choose a different file name.").arg(file));
1300 return false;
1301 } else if ((exists && !fi.isWritable()) || !(opened = f.open(QFile::Append))) {
1302 QMessageBox::warning(q, q->windowTitle(),
1303 QPrintDialog::tr("File %1 is not writable.\nPlease choose a different file name.").arg(file));
1304 return false;
1305 } else if (exists) {
1306 int ret = QMessageBox::question(q, q->windowTitle(),
1307 QPrintDialog::tr("%1 already exists.\nDo you want to overwrite it?").arg(file),
1308 QMessageBox::Yes|QMessageBox::No, QMessageBox::No);
1309 if (ret == QMessageBox::No)
1310 return false;
1311 }
1312 if (opened) {
1313 f.close();
1314 if (!exists)
1315 f.remove();
1316 }
1317 }
1318
1319#if QT_CONFIG(cups)
1320 if (propertiesDialog) {
1321 QCUPSSupport::PagesPerSheet pagesPerSheet = propertiesDialog->widget.pageSetup->m_ui.pagesPerSheetCombo
1322 ->currentData().value<QCUPSSupport::PagesPerSheet>();
1323
1324 QCUPSSupport::PageSet pageSet = optionsPane->options.pageSetCombo->currentData().value<QCUPSSupport::PageSet>();
1325
1326
1327 if (pagesPerSheet != QCUPSSupport::OnePagePerSheet
1328 && pageSet != QCUPSSupport::AllPages) {
1329 QMessageBox::warning(q, q->windowTitle(),
1330 QPrintDialog::tr("Options 'Pages Per Sheet' and 'Page Set' cannot be used together.\nPlease turn one of those options off."));
1331 return false;
1332 }
1333 }
1334#endif
1335
1336 // Every test passed. Accept the dialog.
1337 return true;
1338}
1339#endif // QT_CONFIG(messagebox)
1340
1341void QUnixPrintWidgetPrivate::setupPrinterProperties()
1342{
1343 delete propertiesDialog;
1344
1345 QPrinter::OutputFormat outputFormat;
1346 QString printerName;
1347
1348 if (q->isOptionEnabled(QPrintDialog::PrintToFile)
1349 && (widget.printers->currentIndex() == widget.printers->count() - 1)) {// PDF
1350 outputFormat = QPrinter::PdfFormat;
1351 } else {
1352 outputFormat = QPrinter::NativeFormat;
1353 printerName = widget.printers->currentText();
1354 }
1355
1356 propertiesDialog = new QPrintPropertiesDialog(q->printer(), &m_currentPrintDevice, outputFormat, printerName, q);
1357}
1358
1359#if QT_CONFIG(cups)
1360void QUnixPrintWidgetPrivate::setPpdDuplex(QPrinter::DuplexMode mode)
1361{
1362 auto values = QStringList{} << QStringLiteral("Duplex");
1363 if (mode == QPrinter::DuplexNone) values << QStringLiteral("None");
1364 else if (mode == QPrinter::DuplexLongSide) values << QStringLiteral("DuplexNoTumble");
1365 else if (mode == QPrinter::DuplexShortSide) values << QStringLiteral("DuplexTumble");
1366
1367 m_currentPrintDevice.setProperty(PDPK_PpdOption, values);
1368}
1369#endif
1370
1371void QUnixPrintWidgetPrivate::_q_btnPropertiesClicked()
1372{
1373 if (!propertiesDialog)
1374 setupPrinterProperties();
1375 propertiesDialog->exec();
1376
1377#if QT_CONFIG(cups)
1378 // update the warning icon on the duplex options if needed
1379 optionsPane->updatePpdDuplexOption(optionsPane->options.noDuplex);
1380 optionsPane->updatePpdDuplexOption(optionsPane->options.duplexLong);
1381 optionsPane->updatePpdDuplexOption(optionsPane->options.duplexShort);
1382#endif
1383}
1384
1385void QUnixPrintWidgetPrivate::setupPrinter()
1386{
1387 const int printerCount = widget.printers->count();
1388 const int index = widget.printers->currentIndex();
1389
1390 if (filePrintersAdded && index == printerCount - 1) { // PDF
1391 printer->setPrinterName(QString());
1392 Q_ASSERT(index != printerCount - 2); // separator
1393 printer->setOutputFormat(QPrinter::PdfFormat);
1394 QString path = widget.filename->text();
1395 if (QDir::isRelativePath(path))
1396 path = QDir::homePath() + QDir::separator() + path;
1397 printer->setOutputFileName(path);
1398 }
1399 else {
1400 printer->setPrinterName(widget.printers->currentText());
1401 printer->setOutputFileName(QString());
1402 }
1403
1404 if (!propertiesDialog)
1405 setupPrinterProperties();
1406
1407 propertiesDialog->setupPrinter();
1408}
1409
1410/*! \internal
1411*/
1412QUnixPrintWidget::QUnixPrintWidget(QPrinter *printer, QWidget *parent)
1413 : QWidget(parent), d(new QUnixPrintWidgetPrivate(this, printer))
1414{
1415 if (printer == nullptr)
1416 return;
1417 if (printer->outputFileName().isEmpty()) {
1418 QString home = QDir::homePath();
1419 QString cur = QDir::currentPath();
1420 if (!home.endsWith(QLatin1Char('/')))
1421 home += QLatin1Char('/');
1422 if (!cur.startsWith(home))
1423 cur = home;
1424 else if (!cur.endsWith(QLatin1Char('/')))
1425 cur += QLatin1Char('/');
1426 if (QGuiApplication::platformName() == QStringLiteral("xcb")) {
1427 if (printer->docName().isEmpty()) {
1428 cur += QStringLiteral("print.pdf");
1429 } else {
1430 const QRegExp re(QStringLiteral("(.*)\\.\\S+"));
1431 if (re.exactMatch(printer->docName()))
1432 cur += re.cap(1);
1433 else
1434 cur += printer->docName();
1435 cur += QStringLiteral(".pdf");
1436 }
1437 } // xcb
1438
1439 d->widget.filename->setText(cur);
1440 }
1441 else
1442 d->widget.filename->setText(printer->outputFileName());
1443 const QString printerName = printer->printerName();
1444 if (!printerName.isEmpty()) {
1445 const int i = d->widget.printers->findText(printerName);
1446 if (i >= 0)
1447 d->widget.printers->setCurrentIndex(i);
1448 }
1449 // PDF printer not added to the dialog yet, we'll handle those cases in QUnixPrintWidgetPrivate::updateWidget
1450}
1451
1452/*! \internal
1453*/
1454QUnixPrintWidget::~QUnixPrintWidget()
1455{
1456 delete d;
1457}
1458
1459/*! \internal
1460
1461 Updates the printer with the states held in the QUnixPrintWidget.
1462*/
1463void QUnixPrintWidget::updatePrinter()
1464{
1465 d->setupPrinter();
1466}
1467
1468#if QT_CONFIG(cups)
1469
1470////////////////////////////////////////////////////////////////////////////////
1471////////////////////////////////////////////////////////////////////////////////
1472
1473#endif // QT_CONFIG(cups)
1474#endif // defined (Q_OS_UNIX)
1475
1476QT_END_NAMESPACE
1477
1478#include "moc_qprintdialog.cpp"
1479#include "qprintdialog_unix.moc"
1480