diff options
Diffstat (limited to 'src/printsupport/dialogs/qprintdialog_unix.cpp')
-rw-r--r-- | src/printsupport/dialogs/qprintdialog_unix.cpp | 915 |
1 files changed, 811 insertions, 104 deletions
diff --git a/src/printsupport/dialogs/qprintdialog_unix.cpp b/src/printsupport/dialogs/qprintdialog_unix.cpp index a05c9ac83a..caab7867dc 100644 --- a/src/printsupport/dialogs/qprintdialog_unix.cpp +++ b/src/printsupport/dialogs/qprintdialog_unix.cpp @@ -48,7 +48,9 @@ #if QT_CONFIG(filedialog) #include "qfiledialog.h" #endif +#include <QtCore/qdebug.h> #include <QtCore/qdir.h> +#include <QtCore/qtextcodec.h> #include <QtGui/qevent.h> #if QT_CONFIG(filesystemmodel) #include <QtWidgets/qfilesystemmodel.h> @@ -108,6 +110,11 @@ 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,22 +131,33 @@ class QPrintPropertiesDialog : public QDialog { Q_OBJECT public: - QPrintPropertiesDialog(QAbstractPrintDialog *parent = 0); + QPrintPropertiesDialog(QPrinter *printer, QPrintDevice *currentPrintDevice, + QPrinter::OutputFormat outputFormat, const QString &printerName, + QAbstractPrintDialog *parent); ~QPrintPropertiesDialog(); - void selectPrinter(QPrinter::OutputFormat outputFormat, const QString &printerName); - - /// copy printer properties to the widget - void applyPrinterProperties(QPrinter *p); void setupPrinter() const; + void showEvent(QShowEvent *event) override; + +private slots: + void reject() override; + void accept() override; + private: friend class QUnixPrintWidgetPrivate; + QPrinter *m_printer; Ui::QPrintPropertiesWidget widget; QDialogButtonBox *m_buttons; #if QT_CONFIG(cupsjobwidget) QCupsJobWidget *m_jobOptions; #endif + +#if QT_CONFIG(cups) + void setCupsOptionsFromItems(QOptionTreeItem *parent) const; + + QPPDOptionsModel *m_cupsOptionsModel; +#endif }; class QUnixPrintWidgetPrivate; @@ -149,7 +167,7 @@ class QUnixPrintWidget : public QWidget Q_OBJECT public: - explicit QUnixPrintWidget(QPrinter *printer, QWidget *parent = 0); + explicit QUnixPrintWidget(QPrinter *printer, QWidget *parent = nullptr); ~QUnixPrintWidget(); void updatePrinter(); @@ -168,8 +186,6 @@ public: QUnixPrintWidgetPrivate(QUnixPrintWidget *q, QPrinter *prn); ~QUnixPrintWidgetPrivate(); - /// copy printer properties to the widget - void applyPrinterProperties(); bool checkFields(); void setupPrinter(); void setOptionsPane(QPrintDialogPrivate *pane); @@ -191,7 +207,6 @@ public: private: QPrintDialogPrivate *optionsPane; bool filePrintersAdded; - bool propertiesDialogShown; }; class QPrintDialogPrivate : public QAbstractPrintDialogPrivate @@ -203,8 +218,6 @@ public: ~QPrintDialogPrivate(); void init(); - /// copy printer properties to the widget - void applyPrinterProperties(); void selectPrinter(const QPrinter::OutputFormat outputFormat); @@ -217,7 +230,7 @@ public: void setupPrinter(); void updateWidgets(); - virtual void setTabs(const QList<QWidget*> &tabs) Q_DECL_OVERRIDE; + virtual void setTabs(const QList<QWidget*> &tabs) override; Ui::QPrintSettingsOutput options; QUnixPrintWidget *top; @@ -227,6 +240,100 @@ public: 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; +}; + +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 //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// @@ -240,24 +347,53 @@ public: */ -QPrintPropertiesDialog::QPrintPropertiesDialog(QAbstractPrintDialog *parent) +QPrintPropertiesDialog::QPrintPropertiesDialog(QPrinter *printer, QPrintDevice *currentPrintDevice, + QPrinter::OutputFormat outputFormat, const QString &printerName, + QAbstractPrintDialog *parent) : QDialog(parent) + , m_printer(printer) { setWindowTitle(tr("Printer Properties")); QVBoxLayout *lay = new QVBoxLayout(this); - this->setLayout(lay); QWidget *content = new QWidget(this); widget.setupUi(content); m_buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, this); lay->addWidget(content); lay->addWidget(m_buttons); - connect(m_buttons->button(QDialogButtonBox::Ok), SIGNAL(clicked()), this, SLOT(accept())); - connect(m_buttons->button(QDialogButtonBox::Cancel), SIGNAL(clicked()), this, SLOT(reject())); + connect(m_buttons->button(QDialogButtonBox::Ok), &QPushButton::clicked, this, &QPrintPropertiesDialog::accept); + connect(m_buttons->button(QDialogButtonBox::Cancel), &QPushButton::clicked, this, &QPrintPropertiesDialog::reject); + + widget.pageSetup->setPrinter(printer, currentPrintDevice, outputFormat, printerName); #if QT_CONFIG(cupsjobwidget) - m_jobOptions = new QCupsJobWidget(); - widget.tabs->addTab(m_jobOptions, tr("Job Options")); + m_jobOptions = new QCupsJobWidget(printer, currentPrintDevice); + widget.tabs->insertTab(1, m_jobOptions, tr("Job Options")); +#endif + + const int advancedTabIndex = widget.tabs->indexOf(widget.cupsPropertiesPage); +#if QT_CONFIG(cups) + m_cupsOptionsModel = new QPPDOptionsModel(currentPrintDevice, this); + + widget.treeView->setItemDelegate(new QPPDOptionsEditor(this)); + + if (m_cupsOptionsModel->rowCount() > 0) { + widget.treeView->setModel(m_cupsOptionsModel); + + for (int i = 0; i < m_cupsOptionsModel->rowCount(); ++i) + widget.treeView->expand(m_cupsOptionsModel->index(i, 0)); + + 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); #endif } @@ -265,25 +401,66 @@ QPrintPropertiesDialog::~QPrintPropertiesDialog() { } -void QPrintPropertiesDialog::applyPrinterProperties(QPrinter *p) +void QPrintPropertiesDialog::setupPrinter() const { - widget.pageSetup->setPrinter(p); +#if QT_CONFIG(cups) + QCUPSSupport::clearCupsOptions(m_printer); +#endif + + widget.pageSetup->setupPrinter(); #if QT_CONFIG(cupsjobwidget) - m_jobOptions->setPrinter(p); + m_jobOptions->setupPrinter(); +#endif + +#if QT_CONFIG(cups) + // Set Color by default, that will change if the "ColorModel" property is available + m_printer->setColorMode(QPrinter::Color); + + m_cupsOptionsModel->setCupsOptionsFromItems(m_printer); #endif } -void QPrintPropertiesDialog::setupPrinter() const +void QPrintPropertiesDialog::showEvent(QShowEvent *event) { - widget.pageSetup->setupPrinter(); + widget.treeView->resizeColumnToContents(0); + QDialog::showEvent(event); +} + +void QPrintPropertiesDialog::reject() +{ + widget.pageSetup->revertToSavedValues(); + #if QT_CONFIG(cupsjobwidget) - m_jobOptions->setupPrinter(); + m_jobOptions->revertToSavedValues(); #endif + +#if QT_CONFIG(cups) + m_cupsOptionsModel->revertToSavedValues(); +#endif + QDialog::reject(); } -void QPrintPropertiesDialog::selectPrinter(QPrinter::OutputFormat outputFormat, const QString &printerName) +void QPrintPropertiesDialog::accept() { - widget.pageSetup->selectPrinter(outputFormat, printerName); +#if QT_CONFIG(cups) + if (m_cupsOptionsModel->hasConflicts()) { + 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?"), + QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); + if (answer != QMessageBox::No) + return; + } + m_cupsOptionsModel->updateSavedValues(); +#endif + +#if QT_CONFIG(cupsjobwidget) + m_jobOptions->updateSavedValues(); +#endif + + widget.pageSetup->updateSavedValues(); + + QDialog::accept(); } //////////////////////////////////////////////////////////////////////////////// @@ -298,7 +475,7 @@ void QPrintPropertiesDialog::selectPrinter(QPrinter::OutputFormat outputFormat, */ QPrintDialogPrivate::QPrintDialogPrivate() - : top(0), bottom(0), buttons(0), collapseButton(0) + : top(nullptr), bottom(nullptr), buttons(nullptr), collapseButton(nullptr) { initResources(); } @@ -324,6 +501,9 @@ void QPrintDialogPrivate::init() options.pageSetCombo->addItem(tr("All Pages"), QVariant::fromValue(QCUPSSupport::AllPages)); options.pageSetCombo->addItem(tr("Odd Pages"), QVariant::fromValue(QCUPSSupport::OddPages)); options.pageSetCombo->addItem(tr("Even Pages"), QVariant::fromValue(QCUPSSupport::EvenPages)); +#else + for (int i = options.pagesLayout->count() - 1; i >= 0; --i) + delete options.pagesLayout->itemAt(i)->widget(); #endif top->d->setOptionsPane(this); @@ -338,7 +518,6 @@ void QPrintDialogPrivate::init() printButton->setDefault(true); QVBoxLayout *lay = new QVBoxLayout(q); - q->setLayout(lay); lay->addWidget(top); lay->addWidget(bottom); lay->addWidget(buttons); @@ -366,6 +545,11 @@ void QPrintDialogPrivate::selectPrinter(const QPrinter::OutputFormat outputForma QPrinter *p = q->printer(); printerOutputFormat = outputFormat; + // printer supports duplex mode? + const auto supportedDuplexMode = top->d->m_currentPrintDevice.supportedDuplexModes(); + options.duplexLong->setEnabled(supportedDuplexMode.contains(QPrint::DuplexLongSide)); + options.duplexShort->setEnabled(supportedDuplexMode.contains(QPrint::DuplexShortSide)); + if (p->colorMode() == QPrinter::Color) options.color->setChecked(true); else @@ -390,13 +574,94 @@ void QPrintDialogPrivate::selectPrinter(const QPrinter::OutputFormat outputForma options.pageSetCombo->setEnabled(false); else options.pageSetCombo->setEnabled(true); + +#if QT_CONFIG(cups) + // Disable color options on main dialog if not printing to file, it will be handled by CUPS advanced dialog + options.colorMode->setVisible(outputFormat == QPrinter::PdfFormat); +#endif +} + +#if QT_CONFIG(cups) +static std::vector<std::pair<int, int>> pageRangesFromString(const QString &pagesString) Q_DECL_NOTHROW +{ + std::vector<std::pair<int, int>> result; + const QStringList items = pagesString.split(','); + for (const QString item : items) { + if (item.isEmpty()) + return {}; + + if (item.contains(QLatin1Char('-'))) { + const QStringList rangeItems = item.split('-'); + if (rangeItems.count() != 2) + return {}; + + bool ok; + const int number1 = rangeItems[0].toInt(&ok); + if (!ok) + return {}; + + const int number2 = rangeItems[1].toInt(&ok); + if (!ok) + return {}; + + if (number1 < 1 || number2 < 1 || number2 < number1) + return {}; + + result.push_back(std::make_pair(number1, number2)); + + } else { + bool ok; + const int number = item.toInt(&ok); + if (!ok) + return {}; + + if (number < 1) + return {}; + + result.push_back(std::make_pair(number, number)); + } + } + + // check no range intersects with the next + std::sort(result.begin(), result.end(), + [](const std::pair<int, int> &it1, const std::pair<int, int> &it2) { return it1.first < it2.first; }); + int previousSecond = -1; + for (auto pair : result) { + if (pair.first <= previousSecond) + return {}; + + previousSecond = pair.second; + } + + return result; +} + +static QString stringFromPageRanges(const std::vector<std::pair<int, int>> &pageRanges) Q_DECL_NOTHROW +{ + QString result; + + for (auto pair : pageRanges) { + if (!result.isEmpty()) + result += QLatin1Char(','); + + if (pair.first == pair.second) + result += QString::number(pair.first); + else + result += QStringLiteral("%1-%2").arg(pair.first).arg(pair.second); + } + + return result; } -void QPrintDialogPrivate::applyPrinterProperties() +static bool isValidPagesString(const QString &pagesString) Q_DECL_NOTHROW { - // apply printer options to property dialog - top->d->applyPrinterProperties(); + if (pagesString.isEmpty()) + return false; + + auto pagesRanges = pageRangesFromString(pagesString); + return !pagesRanges.empty(); } +#endif void QPrintDialogPrivate::setupPrinter() { @@ -416,7 +681,12 @@ void QPrintDialogPrivate::setupPrinter() p->setDuplex(QPrinter::DuplexShortSide); } - p->setColorMode(options.color->isChecked() ? QPrinter::Color : QPrinter::GrayScale); +#if QT_CONFIG(cups) + // When printing to a device the colorMode will be set by the advanced panel + if (p->outputFormat() == QPrinter::PdfFormat) +#endif + p->setColorMode(options.color->isChecked() ? QPrinter::Color : QPrinter::GrayScale); + p->setPageOrder(options.reverse->isChecked() ? QPrinter::LastPageFirst : QPrinter::FirstPageFirst); // print range @@ -442,6 +712,16 @@ void QPrintDialogPrivate::setupPrinter() } #if QT_CONFIG(cups) + if (options.pagesRadioButton->isChecked()) { + auto pageRanges = pageRangesFromString(options.pagesLineEdit->text()); + + p->setPrintRange(QPrinter::AllPages); + p->setFromTo(0, 0); + + // server-side page filtering + QCUPSSupport::setPageRange(p, stringFromPageRanges(pageRanges)); + } + // page set if (p->printRange() == QPrinter::AllPages || p->printRange() == QPrinter::PageRange) { //If the application is selecting pages and the first page number is even then need to adjust the odd-even accordingly @@ -610,7 +890,7 @@ QPrintDialog::QPrintDialog(QPrinter *printer, QWidget *parent) Constructs a print dialog with the given \a parent. */ QPrintDialog::QPrintDialog(QWidget *parent) - : QAbstractPrintDialog(*(new QPrintDialogPrivate), 0, parent) + : QAbstractPrintDialog(*(new QPrintDialogPrivate), nullptr, parent) { Q_D(QPrintDialog); d->init(); @@ -638,6 +918,16 @@ int QPrintDialog::exec() void QPrintDialog::accept() { Q_D(QPrintDialog); +#if QT_CONFIG(cups) + if (d->options.pagesRadioButton->isChecked() && !isValidPagesString(d->options.pagesLineEdit->text())) { + QMessageBox::critical(this, tr("Invalid Pages Definition"), + tr("%1 does not follow the correct syntax. Please use ',' to separate " + "ranges and pages, '-' to define ranges and make sure ranges do " + "not intersect with each other.").arg(d->options.pagesLineEdit->text()), + QMessageBox::Ok, QMessageBox::Ok); + return; + } +#endif d->setupPrinter(); QDialog::accept(); } @@ -658,10 +948,10 @@ void QPrintDialog::accept() /*! \internal */ QUnixPrintWidgetPrivate::QUnixPrintWidgetPrivate(QUnixPrintWidget *p, QPrinter *prn) - : parent(p), propertiesDialog(0), printer(prn), optionsPane(0), - filePrintersAdded(false), propertiesDialogShown(false) + : parent(p), propertiesDialog(nullptr), printer(prn), optionsPane(0), + filePrintersAdded(false) { - q = 0; + q = nullptr; if (parent) q = qobject_cast<QAbstractPrintDialog*> (parent->parent()); @@ -746,8 +1036,7 @@ void QUnixPrintWidgetPrivate::_q_printerChanged(int index) // Reset properties dialog when printer is changed if (propertiesDialog){ delete propertiesDialog; - propertiesDialog = 0; - propertiesDialogShown = false; + propertiesDialog = nullptr; } if (filePrintersAdded) { @@ -762,14 +1051,20 @@ void QUnixPrintWidgetPrivate::_q_printerChanged(int index) widget.lOutput->setEnabled(true); if (optionsPane) optionsPane->selectPrinter(QPrinter::PdfFormat); + printer->setOutputFormat(QPrinter::PdfFormat); + m_currentPrintDevice = QPrintDevice(); return; } } if (printer) { + printer->setOutputFormat(QPrinter::NativeFormat); + QPlatformPrinterSupport *ps = QPlatformPrinterSupportPlugin::get(); if (ps) m_currentPrintDevice = ps->createPrintDevice(widget.printers->itemText(index)); + else + m_currentPrintDevice = QPrintDevice(); printer->setPrinterName(m_currentPrintDevice.id()); @@ -792,7 +1087,7 @@ void QUnixPrintWidgetPrivate::_q_btnBrowseClicked() QString filename = widget.filename->text(); #if QT_CONFIG(filedialog) filename = QFileDialog::getSaveFileName(parent, QPrintDialog::tr("Print To File ..."), filename, - QString(), 0, QFileDialog::DontConfirmOverwrite); + QString(), nullptr, QFileDialog::DontConfirmOverwrite); #else filename.clear(); #endif @@ -802,48 +1097,6 @@ void QUnixPrintWidgetPrivate::_q_btnBrowseClicked() } } -void QUnixPrintWidgetPrivate::applyPrinterProperties() -{ - if (printer == 0) - return; - if (printer->outputFileName().isEmpty()) { - QString home = QDir::homePath(); - QString cur = QDir::currentPath(); - if (home.at(home.length()-1) != QLatin1Char('/')) - home += QLatin1Char('/'); - if (!cur.isEmpty() && cur.at(cur.length()-1) != QLatin1Char('/')) - cur += QLatin1Char('/'); - if (!cur.startsWith(home)) - cur = home; - if (QGuiApplication::platformName() == QLatin1String("xcb")) { - if (printer->docName().isEmpty()) { - cur += QLatin1String("print.pdf"); - } else { - QRegExp re(QString::fromLatin1("(.*)\\.\\S+")); - if (re.exactMatch(printer->docName())) - cur += re.cap(1); - else - cur += printer->docName(); - cur += QLatin1String(".pdf"); - } - } // xcb - - widget.filename->setText(cur); - } - else - widget.filename->setText( printer->outputFileName() ); - QString printerName = printer->printerName(); - if (!printerName.isEmpty()) { - const int i = widget.printers->findText(printerName); - if (i >= 0) - widget.printers->setCurrentIndex(i); - } - // PDF printer not added to the dialog yet, we'll handle those cases in QUnixPrintWidgetPrivate::updateWidget - - if (propertiesDialog) - propertiesDialog->applyPrinterProperties(printer); -} - #if QT_CONFIG(messagebox) bool QUnixPrintWidgetPrivate::checkFields() { @@ -876,7 +1129,7 @@ bool QUnixPrintWidgetPrivate::checkFields() } #if QT_CONFIG(cups) - if (propertiesDialogShown) { + if (propertiesDialog) { QCUPSSupport::PagesPerSheet pagesPerSheet = propertiesDialog->widget.pageSetup->m_ui.pagesPerSheetCombo ->currentData().value<QCUPSSupport::PagesPerSheet>(); @@ -899,21 +1152,20 @@ bool QUnixPrintWidgetPrivate::checkFields() void QUnixPrintWidgetPrivate::setupPrinterProperties() { - if (propertiesDialog) - delete propertiesDialog; + delete propertiesDialog; - propertiesDialog = new QPrintPropertiesDialog(q); - propertiesDialog->setResult(QDialog::Rejected); - propertiesDialogShown = false; - - propertiesDialog->applyPrinterProperties(q->printer()); + QPrinter::OutputFormat outputFormat; + QString printerName; if (q->isOptionEnabled(QPrintDialog::PrintToFile) && (widget.printers->currentIndex() == widget.printers->count() - 1)) {// PDF - propertiesDialog->selectPrinter(QPrinter::PdfFormat, QString()); + outputFormat = QPrinter::PdfFormat; + } else { + outputFormat = QPrinter::NativeFormat; + printerName = widget.printers->currentText(); } - else - propertiesDialog->selectPrinter(QPrinter::NativeFormat, widget.printers->currentText()); + + propertiesDialog = new QPrintPropertiesDialog(q->printer(), &m_currentPrintDevice, outputFormat, printerName, q); } void QUnixPrintWidgetPrivate::_q_btnPropertiesClicked() @@ -921,15 +1173,6 @@ void QUnixPrintWidgetPrivate::_q_btnPropertiesClicked() if (!propertiesDialog) setupPrinterProperties(); propertiesDialog->exec(); - if (!propertiesDialogShown && propertiesDialog->result() == QDialog::Rejected) { - // If properties dialog was rejected the dialog is deleted and - // the properties are set to defaults when printer is setup - delete propertiesDialog; - propertiesDialog = 0; - propertiesDialogShown = false; - } else - // properties dialog was shown and accepted - propertiesDialogShown = true; } void QUnixPrintWidgetPrivate::setupPrinter() @@ -954,8 +1197,7 @@ void QUnixPrintWidgetPrivate::setupPrinter() if (!propertiesDialog) setupPrinterProperties(); - if (propertiesDialog->result() == QDialog::Accepted || !propertiesDialogShown) - propertiesDialog->setupPrinter(); + propertiesDialog->setupPrinter(); } /*! \internal @@ -963,7 +1205,41 @@ void QUnixPrintWidgetPrivate::setupPrinter() QUnixPrintWidget::QUnixPrintWidget(QPrinter *printer, QWidget *parent) : QWidget(parent), d(new QUnixPrintWidgetPrivate(this, printer)) { - d->applyPrinterProperties(); + if (printer == nullptr) + return; + if (printer->outputFileName().isEmpty()) { + QString home = QDir::homePath(); + QString cur = QDir::currentPath(); + if (!home.endsWith(QLatin1Char('/'))) + home += QLatin1Char('/'); + if (!cur.startsWith(home)) + cur = home; + else if (!cur.endsWith(QLatin1Char('/'))) + cur += QLatin1Char('/'); + if (QGuiApplication::platformName() == QStringLiteral("xcb")) { + if (printer->docName().isEmpty()) { + cur += QStringLiteral("print.pdf"); + } else { + const QRegExp re(QStringLiteral("(.*)\\.\\S+")); + if (re.exactMatch(printer->docName())) + cur += re.cap(1); + else + cur += printer->docName(); + cur += QStringLiteral(".pdf"); + } + } // xcb + + d->widget.filename->setText(cur); + } + else + d->widget.filename->setText(printer->outputFileName()); + const QString printerName = printer->printerName(); + if (!printerName.isEmpty()) { + const int i = d->widget.printers->findText(printerName); + if (i >= 0) + d->widget.printers->setCurrentIndex(i); + } + // PDF printer not added to the dialog yet, we'll handle those cases in QUnixPrintWidgetPrivate::updateWidget } /*! \internal @@ -985,6 +1261,437 @@ void QUnixPrintWidget::updatePrinter() //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// +/* + + 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(); +} + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +#endif // QT_CONFIG(cups) #endif // defined (Q_OS_UNIX) QT_END_NAMESPACE |