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

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

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