diff options
Diffstat (limited to 'src/printsupport/dialogs/qprintdialog_unix.cpp')
-rw-r--r-- | src/printsupport/dialogs/qprintdialog_unix.cpp | 909 |
1 files changed, 342 insertions, 567 deletions
diff --git a/src/printsupport/dialogs/qprintdialog_unix.cpp b/src/printsupport/dialogs/qprintdialog_unix.cpp index 86daea3b02..5390a8b2f2 100644 --- a/src/printsupport/dialogs/qprintdialog_unix.cpp +++ b/src/printsupport/dialogs/qprintdialog_unix.cpp @@ -50,12 +50,14 @@ #endif #include <QtCore/qdebug.h> #include <QtCore/qdir.h> +#include <QtCore/qglobal.h> #include <QtCore/qtextcodec.h> #include <QtGui/qevent.h> #if QT_CONFIG(filesystemmodel) #include <QtWidgets/qfilesystemmodel.h> #endif #include <QtWidgets/qstyleditemdelegate.h> +#include <QtWidgets/qformlayout.h> #include <QtPrintSupport/qprinter.h> #include <qpa/qplatformprintplugin.h> @@ -73,6 +75,7 @@ #include "ui_qprintwidget.h" #if QT_CONFIG(cups) +Q_DECLARE_METATYPE(const ppd_option_t *) #include <private/qcups_p.h> #if QT_CONFIG(cupsjobwidget) #include "qcupsjobwidget_p.h" @@ -110,11 +113,6 @@ Print dialog class declarations allow editing of Page and Advanced tabs. Layout in qprintpropertieswidget.ui - - QPPDOptionsModel: Holds the PPD Options for the printer. - - QPPDOptionsEditor: Edits the PPD Options for the printer. - */ static void initResources() @@ -124,9 +122,6 @@ static void initResources() QT_BEGIN_NAMESPACE -class QOptionTreeItem; -class QPPDOptionsModel; - class QPrintPropertiesDialog : public QDialog { Q_OBJECT @@ -138,13 +133,13 @@ public: void setupPrinter() const; - void showEvent(QShowEvent *event) override; - private slots: void reject() override; void accept() override; private: + void showEvent(QShowEvent *event) override; + friend class QUnixPrintWidgetPrivate; #if QT_CONFIG(cups) QPrinter *m_printer; @@ -156,9 +151,16 @@ private: #endif #if QT_CONFIG(cups) - void setCupsOptionsFromItems(QOptionTreeItem *parent) const; + bool createAdvancedOptionsWidget(); + void setPrinterAdvancedCupsOptions() const; + void revertAdvancedOptionsToSavedValues() const; + void advancedOptionsUpdateSavedValues() const; + bool anyPpdOptionConflict() const; + bool anyAdvancedOptionConflict() const; - QPPDOptionsModel *m_cupsOptionsModel; + QPrintDevice *m_currentPrintDevice; + QTextCodec *m_cupsCodec = nullptr; + QVector<QComboBox*> m_advancedOptionsCombos; #endif }; @@ -174,6 +176,7 @@ public: void updatePrinter(); private: + friend class QPrintDialog; friend class QPrintDialogPrivate; friend class QUnixPrintWidgetPrivate; QUnixPrintWidgetPrivate *d; @@ -206,6 +209,11 @@ public: void updateWidget(); +#if QT_CONFIG(cups) + void setPpdDuplex(QPrinter::DuplexMode mode); + ppd_option_t *m_duplexPpdOption; +#endif + private: QPrintDialogPrivate *optionsPane; bool filePrintersAdded; @@ -229,6 +237,9 @@ public: #endif void _q_collapseOrExpandDialog(); +#if QT_CONFIG(cups) + void updatePpdDuplexOption(QRadioButton *radio); +#endif void setupPrinter(); void updateWidgets(); @@ -240,103 +251,12 @@ public: QDialogButtonBox *buttons; QPushButton *collapseButton; QPrinter::OutputFormat printerOutputFormat; -}; - -#if QT_CONFIG(cups) -class QOptionTreeItem -{ -public: - enum ItemType { Root, Group, Option, Choice }; - - QOptionTreeItem(ItemType t, int i, const void *p, QOptionTreeItem *pi) - : type(t), - index(i), - ptr(p), - parentItem(pi) {} - - ~QOptionTreeItem() { - qDeleteAll(childItems); - } - - ItemType type; - int index; - const void *ptr; - QOptionTreeItem *parentItem; - QList<QOptionTreeItem*> childItems; -}; - -class QOptionTreeItemOption : public QOptionTreeItem -{ -public: - QOptionTreeItemOption (int i, const void *p, QOptionTreeItem *pi) - : QOptionTreeItem(Option, i, p, pi) - { - } - - // These indices are related to ppd_option_t::choices not to childItems - int selected; - int originallySelected; -}; - -class QPPDOptionsModel : public QAbstractItemModel -{ - Q_OBJECT - -public: - explicit QPPDOptionsModel(QPrintDevice *currentPrintDevice, QObject *parent); - - int columnCount(const QModelIndex &parent = QModelIndex()) const override; - int rowCount(const QModelIndex &parent = QModelIndex()) const override; - QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; - QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; - QModelIndex parent(const QModelIndex &index) const override; - Qt::ItemFlags flags(const QModelIndex &index) const override; - QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole ) const override; - - void setCupsOptionsFromItems(QPrinter *printer) const; - void reject(); - void updateSavedValues(); - void revertToSavedValues(); - - QPrintDevice *currentPrintDevice() const; - QTextCodec *cupsCodec() const; - - void emitConflictsChanged(); - bool hasConflicts() const; - -signals: - void hasConflictsChanged(bool conflicts); - private: - void parseGroups(QOptionTreeItem *parent); - void parseOptions(QOptionTreeItem *parent); - void parseChoices(QOptionTreeItemOption *parent); - - void setCupsOptionsFromItems(QPrinter *printer, QOptionTreeItem *parent) const; - void reject(QOptionTreeItem *item); - void updateSavedValues(QOptionTreeItem *item); - void revertToSavedValues(QOptionTreeItem *item); - void emitDataChanged(QOptionTreeItem *item, const QModelIndex &itemIndex, bool *conflictsFound); - bool hasConflicts(QOptionTreeItem *item) const; - - QPrintDevice *m_currentPrintDevice; - QTextCodec *m_cupsCodec; - QOptionTreeItem *m_rootItem; + void setExplicitDuplexMode(QPrint::DuplexMode duplexMode); + // duplex mode explicitly set by user, QPrint::DuplexAuto otherwise + QPrint::DuplexMode explicitDuplexMode; }; -class QPPDOptionsEditor : public QStyledItemDelegate -{ - Q_OBJECT -public: - explicit QPPDOptionsEditor(QObject *parent) : QStyledItemDelegate(parent) {} - - QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override; - void setEditorData(QWidget *editor, const QModelIndex &index) const override; - void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override; -}; - -#endif - //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// @@ -377,24 +297,15 @@ QPrintPropertiesDialog::QPrintPropertiesDialog(QPrinter *printer, QPrintDevice * const int advancedTabIndex = widget.tabs->indexOf(widget.cupsPropertiesPage); #if QT_CONFIG(cups) - m_cupsOptionsModel = new QPPDOptionsModel(currentPrintDevice, this); - - widget.treeView->setItemDelegate(new QPPDOptionsEditor(this)); + m_currentPrintDevice = currentPrintDevice; + const bool anyWidgetCreated = createAdvancedOptionsWidget(); - if (m_cupsOptionsModel->rowCount() > 0) { - widget.treeView->setModel(m_cupsOptionsModel); + widget.tabs->setTabEnabled(advancedTabIndex, anyWidgetCreated); - for (int i = 0; i < m_cupsOptionsModel->rowCount(); ++i) - widget.treeView->expand(m_cupsOptionsModel->index(i, 0)); + connect(widget.pageSetup, &QPageSetupWidget::ppdOptionChanged, this, [this] { + widget.conflictsLabel->setVisible(anyPpdOptionConflict()); + }); - widget.tabs->setTabEnabled(advancedTabIndex, true); - } else { - widget.treeView->setModel(nullptr); - widget.tabs->setTabEnabled(advancedTabIndex, false); - } - - widget.conflictsLabel->setVisible(m_cupsOptionsModel->hasConflicts()); - connect(m_cupsOptionsModel, &QPPDOptionsModel::hasConflictsChanged, widget.conflictsLabel, &QLabel::setVisible); #else Q_UNUSED(currentPrintDevice) widget.tabs->setTabEnabled(advancedTabIndex, false); @@ -420,16 +331,10 @@ void QPrintPropertiesDialog::setupPrinter() const // Set Color by default, that will change if the "ColorModel" property is available m_printer->setColorMode(QPrinter::Color); - m_cupsOptionsModel->setCupsOptionsFromItems(m_printer); + setPrinterAdvancedCupsOptions(); #endif } -void QPrintPropertiesDialog::showEvent(QShowEvent *event) -{ - widget.treeView->resizeColumnToContents(0); - QDialog::showEvent(event); -} - void QPrintPropertiesDialog::reject() { widget.pageSetup->revertToSavedValues(); @@ -439,7 +344,7 @@ void QPrintPropertiesDialog::reject() #endif #if QT_CONFIG(cups) - m_cupsOptionsModel->revertToSavedValues(); + revertAdvancedOptionsToSavedValues(); #endif QDialog::reject(); } @@ -447,7 +352,14 @@ void QPrintPropertiesDialog::reject() void QPrintPropertiesDialog::accept() { #if QT_CONFIG(cups) - if (m_cupsOptionsModel->hasConflicts()) { + if (widget.pageSetup->hasPpdConflict()) { + widget.tabs->setCurrentWidget(widget.tabPage); + const QMessageBox::StandardButton answer = QMessageBox::warning(this, tr("Page Setup Conflicts"), + tr("There are conflicts in page setup options. Do you want to fix them?"), + QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); + if (answer != QMessageBox::No) + return; + } else if (anyAdvancedOptionConflict()) { widget.tabs->setCurrentWidget(widget.cupsPropertiesPage); const QMessageBox::StandardButton answer = QMessageBox::warning(this, tr("Advanced Option Conflicts"), tr("There are conflicts in some advanced options. Do you want to fix them?"), @@ -455,7 +367,7 @@ void QPrintPropertiesDialog::accept() if (answer != QMessageBox::No) return; } - m_cupsOptionsModel->updateSavedValues(); + advancedOptionsUpdateSavedValues(); #endif #if QT_CONFIG(cupsjobwidget) @@ -467,6 +379,224 @@ void QPrintPropertiesDialog::accept() QDialog::accept(); } +void QPrintPropertiesDialog::showEvent(QShowEvent *event) +{ +#if QT_CONFIG(cups) + widget.conflictsLabel->setVisible(anyPpdOptionConflict()); +#endif + QDialog::showEvent(event); +} + +#if QT_CONFIG(cups) + +// Used to store the ppd_option_t for each QComboBox that represents an advanced option +static const char *ppdOptionProperty = "_q_ppd_option"; + +// Used to store the originally selected choice index for each QComboBox that represents an advanced option +static const char *ppdOriginallySelectedChoiceProperty = "_q_ppd_originally_selected_choice"; + +// Used to store the warning label pointer for each QComboBox that represents an advanced option +static const char *warningLabelProperty = "_q_warning_label"; + +static bool isBlacklistedGroup(const ppd_group_t *group) Q_DECL_NOTHROW +{ + return qstrcmp(group->name, "InstallableOptions") == 0; +}; + +static bool isBlacklistedOption(const char *keyword) Q_DECL_NOTHROW +{ + // We already let the user set these options elsewhere + const char *cupsOptionBlacklist[] = { + "Collate", + "Copies", + "OutputOrder", + "PageRegion", + "PageSize", + "Duplex" // handled by the main dialog + }; + auto equals = [](const char *keyword) { + return [keyword](const char *candidate) { + return qstrcmp(keyword, candidate) == 0; + }; + }; + return std::any_of(std::begin(cupsOptionBlacklist), std::end(cupsOptionBlacklist), equals(keyword)); +}; + +bool QPrintPropertiesDialog::createAdvancedOptionsWidget() +{ + bool anyWidgetCreated = false; + + ppd_file_t *ppd = m_currentPrintDevice->property(PDPK_PpdFile).value<ppd_file_t*>(); + + if (ppd) { + m_cupsCodec = QTextCodec::codecForName(ppd->lang_encoding); + + QWidget *holdingWidget = new QWidget(); + QVBoxLayout *layout = new QVBoxLayout(holdingWidget); + + for (int i = 0; i < ppd->num_groups; ++i) { + const ppd_group_t *group = &ppd->groups[i]; + + if (!isBlacklistedGroup(group)) { + QFormLayout *groupLayout = new QFormLayout(); + + for (int i = 0; i < group->num_options; ++i) { + const ppd_option_t *option = &group->options[i]; + + if (!isBlacklistedOption(option->keyword)) { + QComboBox *choicesCb = new QComboBox(); + + const auto setPpdOptionFromCombo = [this, choicesCb, option] { + // We can't use choicesCb->currentIndex() to know the index of the option in the choices[] array + // because some of them may not be present in the list because they conflict with the + // installable options so use the index passed on addItem + const int selectedChoiceIndex = choicesCb->currentData().toInt(); + const auto values = QStringList{} << QString::fromLatin1(option->keyword) + << QString::fromLatin1(option->choices[selectedChoiceIndex].choice); + m_currentPrintDevice->setProperty(PDPK_PpdOption, values); + widget.conflictsLabel->setVisible(anyPpdOptionConflict()); + }; + + bool foundMarkedChoice = false; + bool markedChoiceNotAvailable = false; + for (int i = 0; i < option->num_choices; ++i) { + const ppd_choice_t *choice = &option->choices[i]; + const auto values = QStringList{} << QString::fromLatin1(option->keyword) << QString::fromLatin1(choice->choice); + const bool choiceIsInstallableConflict = m_currentPrintDevice->isFeatureAvailable(PDPK_PpdChoiceIsInstallableConflict, values); + if (choiceIsInstallableConflict && static_cast<int>(choice->marked) == 1) { + markedChoiceNotAvailable = true; + } else if (!choiceIsInstallableConflict) { + choicesCb->addItem(m_cupsCodec->toUnicode(choice->text), i); + if (static_cast<int>(choice->marked) == 1) { + choicesCb->setCurrentIndex(choicesCb->count() - 1); + choicesCb->setProperty(ppdOriginallySelectedChoiceProperty, QVariant(i)); + foundMarkedChoice = true; + } else if (!foundMarkedChoice && qstrcmp(choice->choice, option->defchoice) == 0) { + choicesCb->setCurrentIndex(choicesCb->count() - 1); + choicesCb->setProperty(ppdOriginallySelectedChoiceProperty, QVariant(i)); + } + } + } + + if (markedChoiceNotAvailable) { + // If the user default option is not available because of it conflicting with + // the installed options, we need to set the internal ppd value to the value + // being shown in the combo + setPpdOptionFromCombo(); + } + + if (choicesCb->count() > 1) { + + connect(choicesCb, QOverload<int>::of(&QComboBox::currentIndexChanged), this, setPpdOptionFromCombo); + + // We need an extra label at the end to show the conflict warning + QWidget *choicesCbWithLabel = new QWidget(); + QHBoxLayout *choicesCbWithLabelLayout = new QHBoxLayout(choicesCbWithLabel); + choicesCbWithLabelLayout->setContentsMargins(0, 0, 0, 0); + QLabel *warningLabel = new QLabel(); + choicesCbWithLabelLayout->addWidget(choicesCb); + choicesCbWithLabelLayout->addWidget(warningLabel); + + QLabel *optionLabel = new QLabel(m_cupsCodec->toUnicode(option->text)); + groupLayout->addRow(optionLabel, choicesCbWithLabel); + anyWidgetCreated = true; + choicesCb->setProperty(ppdOptionProperty, QVariant::fromValue(option)); + choicesCb->setProperty(warningLabelProperty, QVariant::fromValue(warningLabel)); + m_advancedOptionsCombos << choicesCb; + } else { + delete choicesCb; + } + } + } + + if (groupLayout->rowCount() > 0) { + QGroupBox *groupBox = new QGroupBox(m_cupsCodec->toUnicode(group->text)); + groupBox->setLayout(groupLayout); + layout->addWidget(groupBox); + } else { + delete groupLayout; + } + } + } + + layout->addStretch(); + widget.scrollArea->setWidget(holdingWidget); + } + + if (!m_cupsCodec) + m_cupsCodec = QTextCodec::codecForLocale(); + + return anyWidgetCreated; +} + +void QPrintPropertiesDialog::setPrinterAdvancedCupsOptions() const +{ + for (const QComboBox *choicesCb : m_advancedOptionsCombos) { + const ppd_option_t *option = choicesCb->property(ppdOptionProperty).value<const ppd_option_t *>(); + + // We can't use choicesCb->currentIndex() to know the index of the option in the choices[] array + // because some of them may not be present in the list because they conflict with the + // installable options so use the index passed on addItem + const int selectedChoiceIndex = choicesCb->currentData().toInt(); + const char *selectedChoice = option->choices[selectedChoiceIndex].choice; + + if (qstrcmp(option->keyword, "ColorModel") == 0) + m_printer->setColorMode(qstrcmp(selectedChoice, "Gray") == 0 ? QPrinter::GrayScale : QPrinter::Color); + + if (qstrcmp(option->defchoice, selectedChoice) != 0) + QCUPSSupport::setCupsOption(m_printer, QString::fromLatin1(option->keyword), QString::fromLatin1(selectedChoice)); + } +} + +void QPrintPropertiesDialog::revertAdvancedOptionsToSavedValues() const +{ + for (QComboBox *choicesCb : m_advancedOptionsCombos) { + const int originallySelectedChoice = choicesCb->property(ppdOriginallySelectedChoiceProperty).value<int>(); + const int newComboIndexToSelect = choicesCb->findData(originallySelectedChoice); + choicesCb->setCurrentIndex(newComboIndexToSelect); + // The currentIndexChanged lambda takes care of resetting the ppd option + } + widget.conflictsLabel->setVisible(anyPpdOptionConflict()); +} + +void QPrintPropertiesDialog::advancedOptionsUpdateSavedValues() const +{ + for (QComboBox *choicesCb : m_advancedOptionsCombos) + choicesCb->setProperty(ppdOriginallySelectedChoiceProperty, choicesCb->currentData()); +} + +bool QPrintPropertiesDialog::anyPpdOptionConflict() const +{ + // we need to execute both since besides returning true/false they update the warning icons + const bool pageSetupConflicts = widget.pageSetup->hasPpdConflict(); + const bool advancedOptionConflicts = anyAdvancedOptionConflict(); + return pageSetupConflicts || advancedOptionConflicts; +} + +bool QPrintPropertiesDialog::anyAdvancedOptionConflict() const +{ + const QIcon warning = QApplication::style()->standardIcon(QStyle::SP_MessageBoxWarning, nullptr, nullptr); + + bool anyConflicted = false; + + for (const QComboBox *choicesCb : m_advancedOptionsCombos) { + const ppd_option_t *option = choicesCb->property(ppdOptionProperty).value<const ppd_option_t *>(); + QLabel *warningLabel = choicesCb->property(warningLabelProperty).value<QLabel *>(); + if (option->conflicted) { + anyConflicted = true; + const int pixmap_size = choicesCb->sizeHint().height() * .75; + warningLabel->setPixmap(warning.pixmap(pixmap_size, pixmap_size)); + } else { + warningLabel->setPixmap(QPixmap()); + } + } + + return anyConflicted; +} + +#endif + + //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// @@ -479,7 +609,8 @@ void QPrintPropertiesDialog::accept() */ QPrintDialogPrivate::QPrintDialogPrivate() - : top(nullptr), bottom(nullptr), buttons(nullptr), collapseButton(nullptr) + : top(nullptr), bottom(nullptr), buttons(nullptr), collapseButton(nullptr), + explicitDuplexMode(QPrint::DuplexAuto) { initResources(); } @@ -540,6 +671,16 @@ void QPrintDialogPrivate::init() q, SLOT(_q_togglePageSetCombo(bool))); QObject::connect(collapseButton, SIGNAL(released()), q, SLOT(_q_collapseOrExpandDialog())); + + QObject::connect(options.noDuplex, &QAbstractButton::clicked, q, [this] { setExplicitDuplexMode(QPrint::DuplexNone); }); + QObject::connect(options.duplexLong, &QAbstractButton::clicked, q, [this] { setExplicitDuplexMode(QPrint::DuplexLongSide); }); + QObject::connect(options.duplexShort, &QAbstractButton::clicked, q, [this] { setExplicitDuplexMode(QPrint::DuplexShortSide); }); + +#if QT_CONFIG(cups) + QObject::connect(options.noDuplex, &QAbstractButton::toggled, q, [this] { updatePpdDuplexOption(options.noDuplex); }); + QObject::connect(options.duplexLong, &QAbstractButton::toggled, q, [this] { updatePpdDuplexOption(options.duplexLong); }); + QObject::connect(options.duplexShort, &QAbstractButton::toggled, q, [this] { updatePpdDuplexOption(options.duplexShort); }); +#endif } // initialize printer options @@ -559,13 +700,20 @@ void QPrintDialogPrivate::selectPrinter(const QPrinter::OutputFormat outputForma else options.grayscale->setChecked(true); - switch (p->duplex()) { - case QPrinter::DuplexNone: + // keep duplex value explicitly set by user, if any, and selected printer supports it; + // use device default otherwise + QPrint::DuplexMode duplex; + if (explicitDuplexMode != QPrint::DuplexAuto && supportedDuplexMode.contains(explicitDuplexMode)) + duplex = explicitDuplexMode; + else + duplex = top->d->m_currentPrintDevice.defaultDuplexMode(); + switch (duplex) { + case QPrint::DuplexNone: options.noDuplex->setChecked(true); break; - case QPrinter::DuplexLongSide: - case QPrinter::DuplexAuto: + case QPrint::DuplexLongSide: + case QPrint::DuplexAuto: options.duplexLong->setChecked(true); break; - case QPrinter::DuplexShortSide: + case QPrint::DuplexShortSide: options.duplexShort->setChecked(true); break; } options.copies->setValue(p->copyCount()); @@ -665,8 +813,26 @@ static bool isValidPagesString(const QString &pagesString) Q_DECL_NOTHROW auto pagesRanges = pageRangesFromString(pagesString); return !pagesRanges.empty(); } + +void QPrintDialogPrivate::updatePpdDuplexOption(QRadioButton *radio) +{ + const bool checked = radio->isChecked(); + if (checked) { + if (radio == options.noDuplex) top->d->setPpdDuplex(QPrinter::DuplexNone); + else if (radio == options.duplexLong) top->d->setPpdDuplex(QPrinter::DuplexLongSide); + else if (radio == options.duplexShort) top->d->setPpdDuplex(QPrinter::DuplexShortSide); + } + const bool conflict = checked && top->d->m_duplexPpdOption && top->d->m_duplexPpdOption->conflicted; + radio->setIcon(conflict ? QApplication::style()->standardIcon(QStyle::SP_MessageBoxWarning, nullptr, nullptr) : QIcon()); +} + #endif +void QPrintDialogPrivate::setExplicitDuplexMode(const QPrint::DuplexMode duplexMode) +{ + explicitDuplexMode = duplexMode; +} + void QPrintDialogPrivate::setupPrinter() { // First setup the requested OutputFormat, Printer and Page Size first @@ -931,6 +1097,13 @@ void QPrintDialog::accept() QMessageBox::Ok, QMessageBox::Ok); return; } + if (d->top->d->m_duplexPpdOption && d->top->d->m_duplexPpdOption->conflicted) { + const QMessageBox::StandardButton answer = QMessageBox::warning(this, tr("Duplex Settings Conflicts"), + tr("There are conflicts in duplex settings. Do you want to fix them?"), + QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); + if (answer != QMessageBox::No) + return; + } #endif d->setupPrinter(); QDialog::accept(); @@ -952,8 +1125,11 @@ void QPrintDialog::accept() /*! \internal */ QUnixPrintWidgetPrivate::QUnixPrintWidgetPrivate(QUnixPrintWidget *p, QPrinter *prn) - : parent(p), propertiesDialog(nullptr), printer(prn), optionsPane(0), - filePrintersAdded(false) + : parent(p), propertiesDialog(nullptr), printer(prn), +#if QT_CONFIG(cups) + m_duplexPpdOption(nullptr), +#endif + optionsPane(nullptr), filePrintersAdded(false) { q = nullptr; if (parent) @@ -1043,6 +1219,10 @@ void QUnixPrintWidgetPrivate::_q_printerChanged(int index) propertiesDialog = nullptr; } +#if QT_CONFIG(cups) + m_duplexPpdOption = nullptr; +#endif + if (filePrintersAdded) { Q_ASSERT(index != printerCount - 2); // separator if (index == printerCount - 1) { // PDF @@ -1077,6 +1257,10 @@ void QUnixPrintWidgetPrivate::_q_printerChanged(int index) if (optionsPane) optionsPane->selectPrinter(QPrinter::NativeFormat); } + +#if QT_CONFIG(cups) + m_duplexPpdOption = QCUPSSupport::findPpdOption("Duplex", &m_currentPrintDevice); +#endif } void QUnixPrintWidgetPrivate::setOptionsPane(QPrintDialogPrivate *pane) @@ -1172,11 +1356,30 @@ void QUnixPrintWidgetPrivate::setupPrinterProperties() propertiesDialog = new QPrintPropertiesDialog(q->printer(), &m_currentPrintDevice, outputFormat, printerName, q); } +#if QT_CONFIG(cups) +void QUnixPrintWidgetPrivate::setPpdDuplex(QPrinter::DuplexMode mode) +{ + auto values = QStringList{} << QStringLiteral("Duplex"); + if (mode == QPrinter::DuplexNone) values << QStringLiteral("None"); + else if (mode == QPrinter::DuplexLongSide) values << QStringLiteral("DuplexNoTumble"); + else if (mode == QPrinter::DuplexShortSide) values << QStringLiteral("DuplexTumble"); + + m_currentPrintDevice.setProperty(PDPK_PpdOption, values); +} +#endif + void QUnixPrintWidgetPrivate::_q_btnPropertiesClicked() { if (!propertiesDialog) setupPrinterProperties(); propertiesDialog->exec(); + +#if QT_CONFIG(cups) + // update the warning icon on the duplex options if needed + optionsPane->updatePpdDuplexOption(optionsPane->options.noDuplex); + optionsPane->updatePpdDuplexOption(optionsPane->options.duplexLong); + optionsPane->updatePpdDuplexOption(optionsPane->options.duplexShort); +#endif } void QUnixPrintWidgetPrivate::setupPrinter() @@ -1262,436 +1465,8 @@ void QUnixPrintWidget::updatePrinter() d->setupPrinter(); } -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// - -/* - - QPPDOptionsModel - - Holds the PPD Options for the printer. - -*/ - #if QT_CONFIG(cups) -static bool isBlacklistedGroup(ppd_group_t *group) Q_DECL_NOTHROW -{ - return qstrcmp(group->name, "InstallableOptions") == 0; -}; - -QPPDOptionsModel::QPPDOptionsModel(QPrintDevice *currentPrintDevice, QObject *parent) - : QAbstractItemModel(parent) - , m_currentPrintDevice(currentPrintDevice) - , m_cupsCodec(nullptr) -{ - ppd_file_t *ppd = m_currentPrintDevice->property(PDPK_PpdFile).value<ppd_file_t*>(); - m_rootItem = new QOptionTreeItem(QOptionTreeItem::Root, 0, ppd, nullptr); - - if (ppd) { - m_cupsCodec = QTextCodec::codecForName(ppd->lang_encoding); - for (int i = 0; i < ppd->num_groups; ++i) { - if (!isBlacklistedGroup(&ppd->groups[i])) { - QOptionTreeItem *group = new QOptionTreeItem(QOptionTreeItem::Group, i, &ppd->groups[i], m_rootItem); - m_rootItem->childItems.append(group); - parseGroups(group); // parse possible subgroups - parseOptions(group); // parse options - } - } - } - - if (!m_cupsCodec) - m_cupsCodec = QTextCodec::codecForLocale(); -} - -int QPPDOptionsModel::columnCount(const QModelIndex &) const -{ - return 2; -} - -int QPPDOptionsModel::rowCount(const QModelIndex &parent) const -{ - QOptionTreeItem *itm; - if (!parent.isValid()) - itm = m_rootItem; - else - itm = static_cast<QOptionTreeItem*>(parent.internalPointer()); - - if (itm->type == QOptionTreeItem::Option) - return 0; - - return itm->childItems.count(); -} - -QVariant QPPDOptionsModel::data(const QModelIndex &index, int role) const -{ - if (!index.isValid()) - return QVariant(); - - QOptionTreeItem *itm = static_cast<QOptionTreeItem*>(index.internalPointer()); - - switch (role) { - case Qt::FontRole: { - if (itm->type == QOptionTreeItem::Group){ - QFont font; - font.setBold(true); - return QVariant(font); - } - return QVariant(); - } - break; - - case Qt::DisplayRole: { - if (index.column() == 0) { - if (itm->type == QOptionTreeItem::Option) { - const ppd_option_t *option = static_cast<const ppd_option_t*>(itm->ptr); - return m_cupsCodec->toUnicode(option->text); - } else if (itm->type == QOptionTreeItem::Group) { - const ppd_group_t *group = static_cast<const ppd_group_t*>(itm->ptr); - return m_cupsCodec->toUnicode(group->text); - } - } else if (itm->type == QOptionTreeItem::Option) { - QOptionTreeItemOption *itmOption = static_cast<QOptionTreeItemOption *>(itm); - const ppd_option_t *option = static_cast<const ppd_option_t*>(itm->ptr); - if (itmOption->selected > -1) - return m_cupsCodec->toUnicode(option->choices[itmOption->selected].text); - } - - return QVariant(); - } - break; - - case Qt::DecorationRole: { - if (itm->type == QOptionTreeItem::Option && index.column() == 1) { - const ppd_option_t *option = static_cast<const ppd_option_t*>(itm->ptr); - if (option->conflicted) { - const QIcon warning = QApplication::style()->standardIcon(QStyle::SP_MessageBoxWarning, nullptr, nullptr); - if (!warning.isNull()) - return warning; - - qWarning() << "Current application style returned a null icon for SP_MessageBoxWarning."; - return QColor(Qt::red); - } - } - return QVariant(); - } - break; - - } - - return QVariant(); -} - -QModelIndex QPPDOptionsModel::index(int row, int column, const QModelIndex &parent) const -{ - QOptionTreeItem *itm; - if (!parent.isValid()) - itm = m_rootItem; - else - itm = static_cast<QOptionTreeItem*>(parent.internalPointer()); - - return createIndex(row, column, itm->childItems.at(row)); -} - - -QModelIndex QPPDOptionsModel::parent(const QModelIndex &index) const -{ - if (!index.isValid()) - return QModelIndex(); - - QOptionTreeItem *itm = static_cast<QOptionTreeItem*>(index.internalPointer()); - - if (itm->parentItem && itm->parentItem != m_rootItem) - return createIndex(itm->parentItem->index, 0, itm->parentItem); - - return QModelIndex(); -} - -Qt::ItemFlags QPPDOptionsModel::flags(const QModelIndex &index) const -{ - if (!index.isValid() || static_cast<QOptionTreeItem*>(index.internalPointer())->type == QOptionTreeItem::Group) - return Qt::ItemIsEnabled; - - if (index.column() == 1) - return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable; - - return Qt::ItemIsEnabled | Qt::ItemIsSelectable; -} - -QPrintDevice *QPPDOptionsModel::currentPrintDevice() const -{ - return m_currentPrintDevice; -} - -QTextCodec *QPPDOptionsModel::cupsCodec() const -{ - return m_cupsCodec; -} - -void QPPDOptionsModel::setCupsOptionsFromItems(QPrinter *printer) const -{ - setCupsOptionsFromItems(printer, m_rootItem); -} - -void QPPDOptionsModel::setCupsOptionsFromItems(QPrinter *printer, QOptionTreeItem *parent) const -{ - for (QOptionTreeItem *itm : qAsConst(parent->childItems)) { - if (itm->type == QOptionTreeItem::Option) { - QOptionTreeItemOption *itmOption = static_cast<QOptionTreeItemOption *>(itm); - const ppd_option_t *opt = static_cast<const ppd_option_t*>(itm->ptr); - - if (qstrcmp(opt->keyword, "ColorModel") == 0) - printer->setColorMode(qstrcmp(opt->choices[itmOption->selected].choice, "Gray") == 0 ? QPrinter::GrayScale : QPrinter::Color); - - if (qstrcmp(opt->defchoice, opt->choices[itmOption->selected].choice) != 0) { - QCUPSSupport::setCupsOption(printer, QString::fromLatin1(opt->keyword), QString::fromLatin1(opt->choices[itmOption->selected].choice)); - } - } else { - setCupsOptionsFromItems(printer, itm); - } - } -} - -void QPPDOptionsModel::parseGroups(QOptionTreeItem *parent) -{ - const ppd_group_t *group = static_cast<const ppd_group_t*>(parent->ptr); - - if (group) { - for (int i = 0; i < group->num_subgroups; ++i) { - if (!isBlacklistedGroup(&group->subgroups[i])) { - QOptionTreeItem *subgroup = new QOptionTreeItem(QOptionTreeItem::Group, i, &group->subgroups[i], parent); - parent->childItems.append(subgroup); - parseGroups(subgroup); // parse possible subgroups - parseOptions(subgroup); // parse options - } - } - } -} - -static bool isBlacklistedOption(const char *keyword) Q_DECL_NOTHROW -{ - // We already let the user set these options elsewhere - const char *cupsOptionBlacklist[] = { - "Collate", - "Copies", - "OutputOrder", - "PageRegion", - "PageSize", - "Duplex" // handled by the main dialog - }; - auto equals = [](const char *keyword) { - return [keyword](const char *candidate) { - return qstrcmp(keyword, candidate) == 0; - }; - }; - return std::any_of(std::begin(cupsOptionBlacklist), std::end(cupsOptionBlacklist), equals(keyword)); -}; - -void QPPDOptionsModel::parseOptions(QOptionTreeItem *parent) -{ - const ppd_group_t *group = static_cast<const ppd_group_t*>(parent->ptr); - for (int i = 0; i < group->num_options; ++i) { - if (!isBlacklistedOption(group->options[i].keyword)) { - QOptionTreeItemOption *opt = new QOptionTreeItemOption(i, &group->options[i], parent); - parseChoices(opt); - - // Don't show options that are actually not options at all - // because they don't give the user any choice - if (opt->childItems.count() > 1) - parent->childItems.append(opt); - else - delete opt; - } - } -} - -void QPPDOptionsModel::parseChoices(QOptionTreeItemOption *parent) -{ - const ppd_option_t *option = static_cast<const ppd_option_t*>(parent->ptr); - bool marked = false; - for (int i = 0; i < option->num_choices; ++i) { - const auto values = QStringList{} << QString::fromLatin1(option->keyword) << QString::fromLatin1(option->choices[i].choice); - if (!m_currentPrintDevice->isFeatureAvailable(PDPK_PpdChoiceIsInstallableConflict, values)) { - QOptionTreeItem *choice = new QOptionTreeItem(QOptionTreeItem::Choice, i, &option->choices[i], parent); - if (static_cast<int>(option->choices[i].marked) == 1) { - parent->selected = i; - marked = true; - } else if (!marked && qstrcmp(option->choices[i].choice, option->defchoice) == 0) { - parent->selected = i; - } - parent->originallySelected = parent->selected; - parent->childItems.append(choice); - } - } -} - -bool QPPDOptionsModel::hasConflicts() const -{ - return hasConflicts(m_rootItem); -} - -bool QPPDOptionsModel::hasConflicts(QOptionTreeItem *item) const -{ - if (item->type == QOptionTreeItem::Option) { - const ppd_option_t *option = static_cast<const ppd_option_t*>(item->ptr); - return option->conflicted; - } - - for (QOptionTreeItem *child : qAsConst(item->childItems)) { - if (hasConflicts(child)) - return true; - } - - return false; -} - -void QPPDOptionsModel::emitConflictsChanged() -{ - bool conflictsFound = false; - emitDataChanged(m_rootItem, QModelIndex(), &conflictsFound); - - emit hasConflictsChanged(conflictsFound); -} - -void QPPDOptionsModel::emitDataChanged(QOptionTreeItem *item, const QModelIndex &itemIndex, bool *conflictsFound) -{ - if (item->type == QOptionTreeItem::Option) { - // We just emit DecorationRole dataChanged for all the leaves - // and let the view requery the value - const QModelIndex secondColItem = index(itemIndex.row(), 1, itemIndex.parent()); - emit dataChanged(secondColItem, secondColItem, QVector<int>() << Qt::DecorationRole); - - if (conflictsFound && *conflictsFound == false) { - const ppd_option_t *option = static_cast<const ppd_option_t*>(item->ptr); - if (option->conflicted && conflictsFound) - *conflictsFound = true; - } - } - - for (int i = 0; i < item->childItems.count(); ++i) { - QOptionTreeItem *child = item->childItems.at(i); - emitDataChanged(child, index(i, 0, itemIndex), conflictsFound); - } -} - -QVariant QPPDOptionsModel::headerData(int section, Qt::Orientation, int role) const -{ - if (role != Qt::DisplayRole) - return QVariant(); - - switch (section) { - case 0: - return QVariant(tr("Name")); - case 1: - return QVariant(tr("Value")); - } - - return QVariant(); -} - -void QPPDOptionsModel::revertToSavedValues() -{ - revertToSavedValues(m_rootItem); - emitConflictsChanged(); -} - -void QPPDOptionsModel::revertToSavedValues(QOptionTreeItem *item) -{ - if (item->type == QOptionTreeItem::Option) { - QOptionTreeItemOption *itemOption = static_cast<QOptionTreeItemOption *>(item); - - const ppd_option_t *option = static_cast<const ppd_option_t*>(item->ptr); - const char *choice = itemOption->originallySelected != -1 ? option->choices[itemOption->originallySelected].choice - : option->defchoice; - const auto values = QStringList{} << QString::fromLatin1(option->keyword) << QString::fromLatin1(choice); - m_currentPrintDevice->setProperty(PDPK_PpdOption, values); - itemOption->selected = itemOption->originallySelected; - } - - for (QOptionTreeItem *child : qAsConst(item->childItems)) - revertToSavedValues(child); -} - -void QPPDOptionsModel::updateSavedValues() -{ - updateSavedValues(m_rootItem); -} - -void QPPDOptionsModel::updateSavedValues(QOptionTreeItem *item) -{ - if (item->type == QOptionTreeItem::Option) { - QOptionTreeItemOption *itemOption = static_cast<QOptionTreeItemOption *>(item); - itemOption->originallySelected = itemOption->selected; - } - - for (QOptionTreeItem *child : qAsConst(item->childItems)) - updateSavedValues(child); -} - -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// - -/* - - QPPDOptionsEditor - - Edits the PPD Options for the printer. - -*/ - -QWidget *QPPDOptionsEditor::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const -{ - Q_UNUSED(option) - - if (index.column() == 1 && static_cast<QOptionTreeItem*>(index.internalPointer())->type == QOptionTreeItem::Option) - return new QComboBox(parent); - - return nullptr; -} - -void QPPDOptionsEditor::setEditorData(QWidget *editor, const QModelIndex &index) const -{ - if (index.column() != 1) - return; - - QComboBox *cb = static_cast<QComboBox*>(editor); - QOptionTreeItemOption *itm = static_cast<QOptionTreeItemOption*>(index.internalPointer()); - - if (itm->selected == -1) - cb->addItem(QString()); - - const QPPDOptionsModel *m = static_cast<const QPPDOptionsModel*>(index.model()); - for (auto *childItem : qAsConst(itm->childItems)) { - const ppd_choice_t *choice = static_cast<const ppd_choice_t*>(childItem->ptr); - cb->addItem(m->cupsCodec()->toUnicode(choice->text), childItem->index); - if (childItem->index == itm->selected) - cb->setCurrentIndex(cb->count() - 1); - } -} - -void QPPDOptionsEditor::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const -{ - QComboBox *cb = static_cast<QComboBox*>(editor); - QOptionTreeItemOption *itm = static_cast<QOptionTreeItemOption*>(index.internalPointer()); - - // We can't use cb->currentIndex() to know the index of the option in the choices[] array - // because some of them may not be present in the list because they conflict with the - // installable options so use the index passed on addItem - const int selectedChoiceIndex = cb->currentData().toInt(); - - if (itm->selected == selectedChoiceIndex || selectedChoiceIndex < 0) - return; - - const ppd_option_t *opt = static_cast<const ppd_option_t*>(itm->ptr); - QPPDOptionsModel *m = static_cast<QPPDOptionsModel*>(model); - - const auto values = QStringList{} << QString::fromLatin1(opt->keyword) << QString::fromLatin1(opt->choices[selectedChoiceIndex].choice); - m->currentPrintDevice()->setProperty(PDPK_PpdOption, values); - itm->selected = selectedChoiceIndex; - - m->emitConflictsChanged(); -} - //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// |