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