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