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

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