Linux/cups: Better advanced options UI
Previously we were using a treeview that wasn't very clear it was editable, now we simply use QComboBoxes that are much more clear the user can change the values Change-Id: Ied9bca195f4cb275115213631e21cd6a15544311 Reviewed-by: Michael Weghorn <m.weghorn@posteo.de> Reviewed-by: Frederik Gladhorn <frederik.gladhorn@qt.io>
This commit is contained in:
parent
f1b0ce40d6
commit
67adf1e0dd
@ -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,8 +133,6 @@ public:
|
||||
|
||||
void setupPrinter() const;
|
||||
|
||||
void showEvent(QShowEvent *event) override;
|
||||
|
||||
private slots:
|
||||
void reject() override;
|
||||
void accept() override;
|
||||
@ -154,9 +147,15 @@ private:
|
||||
#endif
|
||||
|
||||
#if QT_CONFIG(cups)
|
||||
void setCupsOptionsFromItems(QOptionTreeItem *parent) const;
|
||||
bool createAdvancedOptionsWidget();
|
||||
void setPrinterAdvancedCupsOptions() const;
|
||||
void revertAdvancedOptionsToSavedValues() const;
|
||||
void advancedOptionsUpdateSavedValues() const;
|
||||
bool anyAdvancedOptionConflict() const;
|
||||
|
||||
QPPDOptionsModel *m_cupsOptionsModel;
|
||||
QPrintDevice *m_currentPrintDevice;
|
||||
QTextCodec *m_cupsCodec;
|
||||
QVector<QComboBox*> m_advancedOptionsCombos;
|
||||
#endif
|
||||
};
|
||||
|
||||
@ -240,101 +239,6 @@ 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
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@ -373,24 +277,11 @@ QPrintPropertiesDialog::QPrintPropertiesDialog(QPrinter *printer, QPrintDevice *
|
||||
|
||||
const int advancedTabIndex = widget.tabs->indexOf(widget.cupsPropertiesPage);
|
||||
#if QT_CONFIG(cups)
|
||||
m_cupsOptionsModel = new QPPDOptionsModel(currentPrintDevice, this);
|
||||
m_currentPrintDevice = currentPrintDevice;
|
||||
const bool anyWidgetCreated = createAdvancedOptionsWidget();
|
||||
|
||||
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);
|
||||
widget.tabs->setTabEnabled(advancedTabIndex, anyWidgetCreated);
|
||||
widget.conflictsLabel->setVisible(anyAdvancedOptionConflict());
|
||||
#else
|
||||
Q_UNUSED(currentPrintDevice)
|
||||
widget.tabs->setTabEnabled(advancedTabIndex, false);
|
||||
@ -416,16 +307,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();
|
||||
@ -435,7 +320,7 @@ void QPrintPropertiesDialog::reject()
|
||||
#endif
|
||||
|
||||
#if QT_CONFIG(cups)
|
||||
m_cupsOptionsModel->revertToSavedValues();
|
||||
revertAdvancedOptionsToSavedValues();
|
||||
#endif
|
||||
QDialog::reject();
|
||||
}
|
||||
@ -443,7 +328,7 @@ void QPrintPropertiesDialog::reject()
|
||||
void QPrintPropertiesDialog::accept()
|
||||
{
|
||||
#if QT_CONFIG(cups)
|
||||
if (m_cupsOptionsModel->hasConflicts()) {
|
||||
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?"),
|
||||
@ -451,7 +336,7 @@ void QPrintPropertiesDialog::accept()
|
||||
if (answer != QMessageBox::No)
|
||||
return;
|
||||
}
|
||||
m_cupsOptionsModel->updateSavedValues();
|
||||
advancedOptionsUpdateSavedValues();
|
||||
#endif
|
||||
|
||||
#if QT_CONFIG(cupsjobwidget)
|
||||
@ -463,6 +348,195 @@ void QPrintPropertiesDialog::accept()
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
#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();
|
||||
|
||||
bool foundMarkedOption = 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);
|
||||
if (!m_currentPrintDevice->isFeatureAvailable(PDPK_PpdChoiceIsInstallableConflict, values)) {
|
||||
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));
|
||||
foundMarkedOption = true;
|
||||
} else if (!foundMarkedOption && qstrcmp(choice->choice, option->defchoice) == 0) {
|
||||
choicesCb->setCurrentIndex(choicesCb->count() - 1);
|
||||
choicesCb->setProperty(ppdOriginallySelectedChoiceProperty, QVariant(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (choicesCb->count() > 1) {
|
||||
|
||||
connect(choicesCb, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [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(anyAdvancedOptionConflict());
|
||||
});
|
||||
|
||||
// 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(anyAdvancedOptionConflict());
|
||||
}
|
||||
|
||||
void QPrintPropertiesDialog::advancedOptionsUpdateSavedValues() const
|
||||
{
|
||||
for (QComboBox *choicesCb : m_advancedOptionsCombos)
|
||||
choicesCb->setProperty(ppdOriginallySelectedChoiceProperty, choicesCb->currentData());
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@ -1258,436 +1332,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();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
@ -47,10 +47,24 @@
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QTreeView" name="treeView">
|
||||
<property name="alternatingRowColors">
|
||||
<widget class="QScrollArea" name="scrollArea">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="widgetResizable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<widget class="QWidget" name="scrollAreaWidgetContents">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>376</width>
|
||||
<height>217</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout"/>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
|
Loading…
Reference in New Issue
Block a user