File indexing completed on 2025-01-05 05:09:31

0001 /*
0002     SPDX-FileCopyrightText: 2010-2018 Daniel Nicoletti <dantti12@gmail.com>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "SelectMakeModel.h"
0008 #include "ui_SelectMakeModel.h"
0009 
0010 #include "KCupsRequest.h"
0011 #include "NoSelectionRectDelegate.h"
0012 #include "kcupslib_log.h"
0013 
0014 #include <QItemSelection>
0015 #include <QLineEdit>
0016 #include <QStandardItemModel>
0017 
0018 #include <QDBusConnection>
0019 #include <QDBusMetaType>
0020 #include <QDBusReply>
0021 
0022 #include <KLocalizedString>
0023 #include <KMessageBox>
0024 
0025 // Marshall the MyStructure data into a D-Bus argument
0026 QDBusArgument &operator<<(QDBusArgument &argument, const DriverMatch &driverMatch)
0027 {
0028     argument.beginStructure();
0029     argument << driverMatch.ppd << driverMatch.match;
0030     argument.endStructure();
0031     return argument;
0032 }
0033 
0034 // Retrieve the MyStructure data from the D-Bus argument
0035 const QDBusArgument &operator>>(const QDBusArgument &argument, DriverMatch &driverMatch)
0036 {
0037     argument.beginStructure();
0038     argument >> driverMatch.ppd >> driverMatch.match;
0039     argument.endStructure();
0040     return argument;
0041 }
0042 
0043 SelectMakeModel::SelectMakeModel(QWidget *parent)
0044     : QWidget(parent)
0045     , ui(new Ui::SelectMakeModel)
0046 {
0047     ui->setupUi(this);
0048 
0049     // Configure the error message widget
0050     ui->messageWidget->setWordWrap(true);
0051     ui->messageWidget->setMessageType(KMessageWidget::Error);
0052     ui->messageWidget->hide();
0053 
0054     m_sourceModel = new PPDModel(this);
0055 
0056     ui->makeView->setModel(m_sourceModel);
0057     ui->makeView->setItemDelegate(new NoSelectionRectDelegate(this));
0058     // Updates the PPD view to the selected Make
0059     connect(ui->makeView->selectionModel(), &QItemSelectionModel::currentChanged, ui->ppdsLV, &QListView::setRootIndex);
0060 
0061     ui->ppdsLV->setModel(m_sourceModel);
0062     ui->ppdsLV->setItemDelegate(new NoSelectionRectDelegate(this));
0063     connect(m_sourceModel, &PPDModel::dataChanged, this, &SelectMakeModel::checkChanged);
0064 
0065     // Clear the PPD view selection, so the Next/Finish button gets disabled
0066     connect(ui->makeView->selectionModel(), &QItemSelectionModel::currentChanged, ui->ppdsLV->selectionModel(), &QItemSelectionModel::clearSelection);
0067 
0068     // Make sure we update the Next/Finish button if a PPD is selected
0069     connect(ui->ppdsLV->selectionModel(), &QItemSelectionModel::selectionChanged, this, &SelectMakeModel::checkChanged);
0070 
0071     // When the radio button changes the signal must be emitted
0072     connect(ui->ppdFileRB, &QRadioButton::toggled, this, &SelectMakeModel::checkChanged);
0073     connect(ui->ppdFilePathUrl, &KUrlRequester::textChanged, this, &SelectMakeModel::checkChanged);
0074 
0075     qDBusRegisterMetaType<DriverMatch>();
0076     qDBusRegisterMetaType<DriverMatchList>();
0077 }
0078 
0079 SelectMakeModel::~SelectMakeModel()
0080 {
0081     delete ui;
0082 }
0083 
0084 void SelectMakeModel::setDeviceInfo(const QString &deviceId, const QString &make, const QString &makeAndModel, const QString &deviceUri)
0085 {
0086     qCDebug(LIBKCUPS) << "===================================" << deviceId << makeAndModel << deviceUri;
0087     m_gotBestDrivers = false;
0088     m_hasRecommended = false;
0089     m_make = make;
0090     m_makeAndModel = makeAndModel;
0091 
0092     // Get the best drivers
0093     QDBusMessage message;
0094     message = QDBusMessage::createMethodCall(QLatin1String("org.fedoraproject.Config.Printing"),
0095                                              QLatin1String("/org/fedoraproject/Config/Printing"),
0096                                              QLatin1String("org.fedoraproject.Config.Printing"),
0097                                              QLatin1String("GetBestDrivers"));
0098     message << deviceId;
0099     message << makeAndModel;
0100     message << deviceUri;
0101     QDBusConnection::sessionBus().callWithCallback(message,
0102                                                    this,
0103                                                    SLOT(getBestDriversFinished(QDBusMessage)),
0104                                                    SLOT(getBestDriversFailed(QDBusError, QDBusMessage)));
0105 
0106     if (!m_ppdRequest) {
0107         m_ppdRequest = new KCupsRequest;
0108         connect(m_ppdRequest, &KCupsRequest::finished, this, &SelectMakeModel::ppdsLoaded);
0109         m_ppdRequest->getPPDS();
0110     }
0111 }
0112 
0113 void SelectMakeModel::setMakeModel(const QString &make, const QString &makeAndModel)
0114 {
0115     ui->radioButtonSelectDriver->setText(i18n("Choose the driver for %1", makeAndModel));
0116     if (!m_ppdRequest) {
0117         // We won't try to get the best driver
0118         // we should be we need more info and testing
0119         // TODO
0120         m_gotBestDrivers = true;
0121         m_hasRecommended = false;
0122         m_make = make;
0123         m_makeAndModel = makeAndModel;
0124 
0125         m_ppdRequest = new KCupsRequest;
0126         connect(m_ppdRequest, &KCupsRequest::finished, this, &SelectMakeModel::ppdsLoaded);
0127         m_ppdRequest->getPPDS();
0128     } else {
0129         // TODO test this
0130         setModelData();
0131     }
0132 }
0133 
0134 void SelectMakeModel::ppdsLoaded(KCupsRequest *request)
0135 {
0136     if (request->hasError()) {
0137         qCWarning(LIBKCUPS) << "Failed to get PPDs" << request->errorMsg();
0138         ui->messageWidget->setText(i18n("Failed to get a list of drivers: '%1'", request->errorMsg()));
0139         ui->messageWidget->animatedShow();
0140 
0141         // Force the changed signal to be sent
0142         checkChanged();
0143 
0144     } else {
0145         m_ppds = request->ppds();
0146 
0147         // Try to show the PPDs
0148         setModelData();
0149     }
0150     m_ppdRequest = nullptr;
0151     request->deleteLater();
0152 }
0153 
0154 void SelectMakeModel::checkChanged()
0155 {
0156     qCDebug(LIBKCUPS);
0157     if (isFileSelected()) {
0158         Q_EMIT changed(!selectedPPDFileName().isNull());
0159     } else {
0160         // enable or disable the job action buttons if something is selected
0161         Q_EMIT changed(!selectedPPDName().isNull());
0162 
0163         selectFirstMake();
0164     }
0165 }
0166 
0167 QString SelectMakeModel::selectedPPDName() const
0168 {
0169     QItemSelection ppdSelection = ui->ppdsLV->selectionModel()->selection();
0170     if (!isFileSelected() && !ppdSelection.indexes().isEmpty()) {
0171         QModelIndex index = ppdSelection.indexes().first();
0172         return index.data(PPDModel::PPDName).toString();
0173     }
0174     return QString();
0175 }
0176 
0177 QString SelectMakeModel::selectedPPDMakeAndModel() const
0178 {
0179     QItemSelection ppdSelection = ui->ppdsLV->selectionModel()->selection();
0180     if (!isFileSelected() && !ppdSelection.indexes().isEmpty()) {
0181         QModelIndex index = ppdSelection.indexes().first();
0182         return index.data(PPDModel::PPDMakeAndModel).toString();
0183     }
0184     return QString();
0185 }
0186 
0187 QString SelectMakeModel::selectedPPDFileName() const
0188 {
0189     if (isFileSelected()) {
0190         QFileInfo file = QFileInfo(ui->ppdFilePathUrl->url().toLocalFile());
0191         qCDebug(LIBKCUPS) << ui->ppdFilePathUrl->url().toLocalFile() << file.isFile() << file.filePath();
0192         if (file.isFile()) {
0193             return file.filePath();
0194         }
0195     }
0196     return QString();
0197 }
0198 
0199 bool SelectMakeModel::isFileSelected() const
0200 {
0201     qCDebug(LIBKCUPS) << ui->ppdFileRB->isChecked();
0202     return ui->ppdFileRB->isChecked();
0203 }
0204 
0205 void SelectMakeModel::getBestDriversFinished(const QDBusMessage &message)
0206 {
0207     if (message.type() == QDBusMessage::ReplyMessage && message.arguments().size() == 1) {
0208         QDBusArgument arg = message.arguments().first().value<QDBusArgument>();
0209         const DriverMatchList driverMatchList = qdbus_cast<DriverMatchList>(arg);
0210         m_driverMatchList = driverMatchList;
0211         m_hasRecommended = !m_driverMatchList.isEmpty();
0212         for (const DriverMatch &driverMatch : driverMatchList) {
0213             qCDebug(LIBKCUPS) << driverMatch.ppd << driverMatch.match;
0214         }
0215     } else {
0216         qCWarning(LIBKCUPS) << "Unexpected message" << message;
0217     }
0218     m_gotBestDrivers = true;
0219     setModelData();
0220 }
0221 
0222 void SelectMakeModel::getBestDriversFailed(const QDBusError &error, const QDBusMessage &message)
0223 {
0224     qCWarning(LIBKCUPS) << "Failed to get best drivers" << error << message;
0225 
0226     // Show the PPDs anyway
0227     m_gotBestDrivers = true;
0228     ui->messageWidget->setText(i18n("Failed to search for a recommended driver: '%1'", error.message()));
0229     ui->messageWidget->animatedShow();
0230     setModelData();
0231 }
0232 
0233 void SelectMakeModel::setModelData()
0234 {
0235     if (!m_ppds.isEmpty() && m_gotBestDrivers) {
0236         m_sourceModel->setPPDs(m_ppds, m_driverMatchList);
0237 
0238         // Pre-select the first Recommended PPD
0239         if (m_hasRecommended) {
0240             selectRecommendedPPD();
0241         } else if (!m_ppds.isEmpty() && !m_make.isEmpty()) {
0242             selectMakeModelPPD();
0243         }
0244 
0245         // Force changed signal to be emitted
0246         checkChanged();
0247     }
0248 }
0249 
0250 void SelectMakeModel::selectFirstMake()
0251 {
0252     QItemSelection selection;
0253     selection = ui->makeView->selectionModel()->selection();
0254     // Make sure the first make is selected
0255     if (selection.indexes().isEmpty() && m_sourceModel->rowCount() > 0) {
0256         ui->makeView->selectionModel()->setCurrentIndex(m_sourceModel->index(0, 0), QItemSelectionModel::SelectCurrent);
0257     }
0258 }
0259 
0260 void SelectMakeModel::selectMakeModelPPD()
0261 {
0262     const QList<QStandardItem *> makes = m_sourceModel->findItems(m_make);
0263     for (QStandardItem *make : makes) {
0264         // Check if the item is in this make
0265         for (int i = 0; i < make->rowCount(); i++) {
0266             if (make->child(i)->data(PPDModel::PPDMakeAndModel).toString() == m_makeAndModel) {
0267                 ui->makeView->selectionModel()->setCurrentIndex(make->index(), QItemSelectionModel::SelectCurrent);
0268                 ui->ppdsLV->selectionModel()->setCurrentIndex(make->child(i)->index(), QItemSelectionModel::SelectCurrent);
0269                 return;
0270             }
0271         }
0272     }
0273 
0274     // the exact PPD wasn't found try to select just the make
0275     if (!makes.isEmpty()) {
0276         ui->makeView->selectionModel()->setCurrentIndex(makes.first()->index(), QItemSelectionModel::SelectCurrent);
0277     }
0278 }
0279 
0280 void SelectMakeModel::selectRecommendedPPD()
0281 {
0282     // Force the first make to be selected
0283     selectFirstMake();
0284 
0285     QItemSelection ppdSelection = ui->ppdsLV->selectionModel()->selection();
0286     if (ppdSelection.indexes().isEmpty()) {
0287         QItemSelection makeSelection = ui->makeView->selectionModel()->selection();
0288         QModelIndex parent = makeSelection.indexes().first();
0289         if (parent.isValid()) {
0290             ui->ppdsLV->selectionModel()->setCurrentIndex(m_sourceModel->index(0, 0, parent), QItemSelectionModel::SelectCurrent);
0291         }
0292     }
0293 }
0294 
0295 #include "moc_SelectMakeModel.cpp"