diff options
Diffstat (limited to 'src/printsupport/dialogs/qprintdialog_unix.cpp')
-rw-r--r-- | src/printsupport/dialogs/qprintdialog_unix.cpp | 533 |
1 files changed, 453 insertions, 80 deletions
diff --git a/src/printsupport/dialogs/qprintdialog_unix.cpp b/src/printsupport/dialogs/qprintdialog_unix.cpp index 0b36dec003..63d23cc162 100644 --- a/src/printsupport/dialogs/qprintdialog_unix.cpp +++ b/src/printsupport/dialogs/qprintdialog_unix.cpp @@ -49,6 +49,7 @@ #include "qfiledialog.h" #endif #include <QtCore/qdir.h> +#include <QtCore/qtextcodec.h> #include <QtGui/qevent.h> #if QT_CONFIG(filesystemmodel) #include <QtWidgets/qfilesystemmodel.h> @@ -108,6 +109,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 +130,29 @@ class QPrintPropertiesDialog : public QDialog { Q_OBJECT public: - QPrintPropertiesDialog(QAbstractPrintDialog *parent = nullptr); + 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: 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; @@ -168,8 +181,6 @@ public: QUnixPrintWidgetPrivate(QUnixPrintWidget *q, QPrinter *prn); ~QUnixPrintWidgetPrivate(); - /// copy printer properties to the widget - void applyPrinterProperties(); bool checkFields(); void setupPrinter(); void setOptionsPane(QPrintDialogPrivate *pane); @@ -203,8 +214,6 @@ public: ~QPrintDialogPrivate(); void init(); - /// copy printer properties to the widget - void applyPrinterProperties(); void selectPrinter(const QPrinter::OutputFormat outputFormat); @@ -217,7 +226,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 +236,78 @@ 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, const char *desc, QOptionTreeItem *pi) + : type(t), + index(i), + ptr(p), + description(desc), + parentItem(pi) {} + + ~QOptionTreeItem() { + qDeleteAll(childItems); + } + + ItemType type; + int index; + const void *ptr; + const char *description; + QOptionTreeItem *parentItem; + QList<QOptionTreeItem*> childItems; +}; + +class QOptionTreeItemOption : public QOptionTreeItem +{ +public: + QOptionTreeItemOption (int i, const void *p, const char *desc, QOptionTreeItem *pi) + : QOptionTreeItem(Option, i, p, desc, pi) + { + } + + int selected; + const char *selDescription; +}; + +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; + + QPrintDevice *m_currentPrintDevice; + QTextCodec *cupsCodec; + QOptionTreeItem *rootItem; + void parseGroups(QOptionTreeItem *parent); + void parseOptions(QOptionTreeItem *parent); + void parseChoices(QOptionTreeItemOption *parent); +}; + +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,8 +321,11 @@ 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); @@ -254,22 +338,38 @@ QPrintPropertiesDialog::QPrintPropertiesDialog(QAbstractPrintDialog *parent) 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 -} -QPrintPropertiesDialog::~QPrintPropertiesDialog() -{ + 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); + } +#else + Q_UNUSED(currentPrintDevice) + widget.tabs->setTabEnabled(advancedTabIndex, false); +#endif } -void QPrintPropertiesDialog::applyPrinterProperties(QPrinter *p) +QPrintPropertiesDialog::~QPrintPropertiesDialog() { - widget.pageSetup->setPrinter(p); -#if QT_CONFIG(cupsjobwidget) - m_jobOptions->setPrinter(p); -#endif } void QPrintPropertiesDialog::setupPrinter() const @@ -278,13 +378,37 @@ void QPrintPropertiesDialog::setupPrinter() const #if QT_CONFIG(cupsjobwidget) m_jobOptions->setupPrinter(); #endif + +#if QT_CONFIG(cups) + setCupsOptionsFromItems(m_cupsOptionsModel->rootItem); +#endif } -void QPrintPropertiesDialog::selectPrinter(QPrinter::OutputFormat outputFormat, const QString &printerName) +void QPrintPropertiesDialog::showEvent(QShowEvent *event) { - widget.pageSetup->selectPrinter(outputFormat, printerName); + widget.treeView->resizeColumnToContents(0); + QDialog::showEvent(event); } +#if QT_CONFIG(cups) +void QPrintPropertiesDialog::setCupsOptionsFromItems(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->defchoice, opt->choices[itmOption->selected].choice) != 0) { + QStringList cupsOptions = QCUPSSupport::cupsOptionsList(m_printer); + QCUPSSupport::setCupsOption(cupsOptions, QString::fromLatin1(opt->keyword), QString::fromLatin1(opt->choices[itmOption->selected].choice)); + QCUPSSupport::setCupsOptions(m_printer, cupsOptions); + } + } else { + setCupsOptionsFromItems(itm); + } + } +} +#endif + //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// @@ -390,12 +514,6 @@ void QPrintDialogPrivate::selectPrinter(const QPrinter::OutputFormat outputForma options.pageSetCombo->setEnabled(true); } -void QPrintDialogPrivate::applyPrinterProperties() -{ - // apply printer options to property dialog - top->d->applyPrinterProperties(); -} - void QPrintDialogPrivate::setupPrinter() { // First setup the requested OutputFormat, Printer and Page Size first @@ -800,48 +918,6 @@ void QUnixPrintWidgetPrivate::_q_btnBrowseClicked() } } -void QUnixPrintWidgetPrivate::applyPrinterProperties() -{ - if (printer == nullptr) - 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() { @@ -899,18 +975,20 @@ void QUnixPrintWidgetPrivate::setupPrinterProperties() { 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); + propertiesDialog->setResult(QDialog::Rejected); + propertiesDialogShown = false; } void QUnixPrintWidgetPrivate::_q_btnPropertiesClicked() @@ -960,7 +1038,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 @@ -982,6 +1094,267 @@ void QUnixPrintWidget::updatePrinter() //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// +/* + + QPPDOptionsModel + + Holds the PPD Options for the printer. + +*/ + +#if QT_CONFIG(cups) + +QPPDOptionsModel::QPPDOptionsModel(QPrintDevice *currentPrintDevice, QObject *parent) + : QAbstractItemModel(parent) + , m_currentPrintDevice(currentPrintDevice) +{ + ppd_file_t *ppd = m_currentPrintDevice->property(PDPK_PpdFile).value<ppd_file_t*>(); + rootItem = new QOptionTreeItem(QOptionTreeItem::Root, 0, ppd, "Root Item", 0); + + if (ppd) { + cupsCodec = QTextCodec::codecForName(ppd->lang_encoding); + for (int i = 0; i < ppd->num_groups; ++i) { + QOptionTreeItem *group = new QOptionTreeItem(QOptionTreeItem::Group, i, &ppd->groups[i], ppd->groups[i].text, rootItem); + rootItem->childItems.append(group); + parseGroups(group); // parse possible subgroups + parseOptions(group); // parse options + } + } + + if (!cupsCodec) + cupsCodec = QTextCodec::codecForLocale(); +} + +int QPPDOptionsModel::columnCount(const QModelIndex &) const +{ + return 2; +} + +int QPPDOptionsModel::rowCount(const QModelIndex &parent) const +{ + QOptionTreeItem *itm; + if (!parent.isValid()) + itm = 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) { + return cupsCodec->toUnicode(itm->description); + } else if (itm->type == QOptionTreeItem::Option) { + QOptionTreeItemOption *itmOption = static_cast<QOptionTreeItemOption *>(itm); + if (itmOption->selected > -1) + return cupsCodec->toUnicode(itmOption->selDescription); + } + + return QVariant(); + } + break; + + } + + return QVariant(); +} + +QModelIndex QPPDOptionsModel::index(int row, int column, const QModelIndex &parent) const +{ + QOptionTreeItem *itm; + if (!parent.isValid()) + itm = 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 != 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; +} + +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) { + QOptionTreeItem *subgroup = new QOptionTreeItem(QOptionTreeItem::Group, i, &group->subgroups[i], group->subgroups[i].text, 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" + }; + 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], group->options[i].text, parent); + parent->childItems.append(opt); + parseChoices(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) { + QOptionTreeItem *choice = new QOptionTreeItem(QOptionTreeItem::Choice, i, &option->choices[i], option->choices[i].text, parent); + if (static_cast<int>(option->choices[i].marked) == 1) { + parent->selected = i; + parent->selDescription = option->choices[i].text; + marked = true; + } else if (!marked && qstrcmp(option->choices[i].choice, option->defchoice) == 0) { + parent->selected = i; + parent->selDescription = option->choices[i].text; + } + parent->childItems.append(choice); + } +} + +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(); +} + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +/* + + 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)) + cb->addItem(m->cupsCodec->toUnicode(childItem->description)); + + if (itm->selected > -1) + cb->setCurrentIndex(itm->selected); +} + +void QPPDOptionsEditor::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const +{ + QComboBox *cb = static_cast<QComboBox*>(editor); + QOptionTreeItemOption *itm = static_cast<QOptionTreeItemOption*>(index.internalPointer()); + + if (itm->selected == cb->currentIndex()) + 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[cb->currentIndex()].choice); + if (m->m_currentPrintDevice->setProperty(PDPK_PpdOption, values)) { + itm->selected = cb->currentIndex(); + itm->selDescription = static_cast<const ppd_option_t*>(itm->ptr)->choices[itm->selected].text; + } +} + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +#endif // QT_CONFIG(cups) #endif // defined (Q_OS_UNIX) QT_END_NAMESPACE |