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

source code of qtbase/src/printsupport/dialogs/qprintdialog_unix.cpp