diff --git a/CMakeLists.txt b/CMakeLists.txt index a7b6a4d..82dab5d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,63 +1,66 @@ project(print-manager) cmake_minimum_required(VERSION 2.8.12 FATAL_ERROR) set(QT_MIN_VERSION "5.3.0") # Print-Manager version set(PM_VERSION "0.3.0" CACHE STRING "Print Manager version") ################# set KDE specific information ################# find_package(ECM 1.3.0 REQUIRED NO_MODULE) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR}) include(KDEInstallDirs) include(KDECMakeSettings) include(KDECompilerSettings NO_POLICY_SCOPE) include(ECMPackageConfigHelpers) include(ECMOptionalAddSubdirectory) include(FeatureSummary) find_package(Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS Core DBus Network Widgets Qml Quick ) find_package(CUPS "1.5" REQUIRED) find_package(KF5 REQUIRED Config ConfigWidgets CoreAddons DBusAddons IconThemes I18n KCMUtils KIO Notifications Plasma WidgetsAddons WindowSystem) -add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0) -add_definitions(-DQT_USE_FAST_CONCATENATION -DQT_USE_FAST_OPERATOR_PLUS) -add_definitions(-DQT_NO_URL_CAST_FROM_STRING) +add_definitions( + -DQT_DISABLE_DEPRECATED_BEFORE=0x050900 + -DQT_USE_FAST_CONCATENATION + -DQT_USE_FAST_OPERATOR_PLUS + -DQT_NO_URL_CAST_FROM_STRING +) remove_definitions(-DQT_NO_CAST_FROM_ASCII -DQT_NO_CAST_FROM_BYTEARRAY -DQT_NO_KEYWORDS) # Generate config.h configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config.h) include_directories(${CMAKE_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/libkcups ${CUPS_INCLUDE_DIR} ) add_definitions(-DTRANSLATION_DOMAIN="print-manager") add_subdirectory(libkcups) add_subdirectory(configure-printer) add_subdirectory(add-printer) add_subdirectory(printer-manager-kcm) add_subdirectory(printqueue) add_subdirectory(print-manager-kded) add_subdirectory(plasmoid) add_subdirectory(declarative-plugins) diff --git a/libkcups/JobModel.cpp b/libkcups/JobModel.cpp index 6ea7b1e..5bf3278 100644 --- a/libkcups/JobModel.cpp +++ b/libkcups/JobModel.cpp @@ -1,628 +1,632 @@ /*************************************************************************** * Copyright (C) 2010-2018 by Daniel Nicoletti * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; see the file COPYING. If not, write to * * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301, USA. * ***************************************************************************/ #include "JobModel.h" #include #include #include #include #include #include #include #include #include #include #include #include JobModel::JobModel(QObject *parent) : QStandardItemModel(parent), m_jobRequest(0), m_whichjobs(CUPS_WHICHJOBS_ACTIVE), m_parentId(0) { setHorizontalHeaderItem(ColStatus, new QStandardItem(i18n("Status"))); setHorizontalHeaderItem(ColName, new QStandardItem(i18n("Name"))); setHorizontalHeaderItem(ColUser, new QStandardItem(i18n("User"))); setHorizontalHeaderItem(ColCreated, new QStandardItem(i18n("Created"))); setHorizontalHeaderItem(ColCompleted, new QStandardItem(i18n("Completed"))); setHorizontalHeaderItem(ColPages, new QStandardItem(i18n("Pages"))); setHorizontalHeaderItem(ColProcessed, new QStandardItem(i18n("Processed"))); setHorizontalHeaderItem(ColSize, new QStandardItem(i18n("Size"))); setHorizontalHeaderItem(ColStatusMessage, new QStandardItem(i18n("Status Message"))); setHorizontalHeaderItem(ColPrinter, new QStandardItem(i18n("Printer"))); setHorizontalHeaderItem(ColFromHost, new QStandardItem(i18n("From Hostname"))); // Setup the attributes we want from jobs m_jobAttributes << KCUPS_JOB_ID; m_jobAttributes << KCUPS_JOB_NAME; m_jobAttributes << KCUPS_JOB_K_OCTETS; m_jobAttributes << KCUPS_JOB_K_OCTETS_PROCESSED; m_jobAttributes << KCUPS_JOB_STATE; m_jobAttributes << KCUPS_TIME_AT_COMPLETED; m_jobAttributes << KCUPS_TIME_AT_CREATION; m_jobAttributes << KCUPS_TIME_AT_PROCESSING; m_jobAttributes << KCUPS_JOB_PRINTER_URI; m_jobAttributes << KCUPS_JOB_ORIGINATING_USER_NAME; m_jobAttributes << KCUPS_JOB_ORIGINATING_HOST_NAME; m_jobAttributes << KCUPS_JOB_MEDIA_PROGRESS; m_jobAttributes << KCUPS_JOB_MEDIA_SHEETS; m_jobAttributes << KCUPS_JOB_MEDIA_SHEETS_COMPLETED; m_jobAttributes << KCUPS_JOB_PRINTER_STATE_MESSAGE; m_jobAttributes << KCUPS_JOB_PRESERVED; - QHash roles = roleNames(); - roles[RoleJobId] = "jobId"; - roles[RoleJobState] = "jobState"; - roles[RoleJobName] = "jobName"; - roles[RoleJobPages] = "jobPages"; - roles[RoleJobSize] = "jobSize"; - roles[RoleJobOwner] = "jobOwner"; - roles[RoleJobCreatedAt] = "jobCreatedAt"; - roles[RoleJobIconName] = "jobIconName"; - roles[RoleJobCancelEnabled] = "jobCancelEnabled"; - roles[RoleJobHoldEnabled] = "jobHoldEnabled"; - roles[RoleJobReleaseEnabled] = "jobReleaseEnabled"; - roles[RoleJobRestartEnabled] = "jobRestartEnabled"; - roles[RoleJobPrinter] = "jobPrinter"; - roles[RoleJobOriginatingHostName] = "jobFrom"; - setRoleNames(roles); + m_roles = QStandardItemModel::roleNames(); + m_roles[RoleJobId] = "jobId"; + m_roles[RoleJobState] = "jobState"; + m_roles[RoleJobName] = "jobName"; + m_roles[RoleJobPages] = "jobPages"; + m_roles[RoleJobSize] = "jobSize"; + m_roles[RoleJobOwner] = "jobOwner"; + m_roles[RoleJobCreatedAt] = "jobCreatedAt"; + m_roles[RoleJobIconName] = "jobIconName"; + m_roles[RoleJobCancelEnabled] = "jobCancelEnabled"; + m_roles[RoleJobHoldEnabled] = "jobHoldEnabled"; + m_roles[RoleJobReleaseEnabled] = "jobReleaseEnabled"; + m_roles[RoleJobRestartEnabled] = "jobRestartEnabled"; + m_roles[RoleJobPrinter] = "jobPrinter"; + m_roles[RoleJobOriginatingHostName] = "jobFrom"; // This is emitted when a job change it's state connect(KCupsConnection::global(), &KCupsConnection::jobState, this, &JobModel::insertUpdateJob); // This is emitted when a job is created connect(KCupsConnection::global(), &KCupsConnection::jobCreated, this, &JobModel::insertUpdateJob); // This is emitted when a job is stopped connect(KCupsConnection::global(), &KCupsConnection::jobStopped, this, &JobModel::insertUpdateJob); // This is emitted when a job has it's config changed connect(KCupsConnection::global(), &KCupsConnection::jobConfigChanged, this, &JobModel::insertUpdateJob); // This is emitted when a job change it's progress connect(KCupsConnection::global(), &KCupsConnection::jobProgress, this, &JobModel::insertUpdateJob); // This is emitted when a printer is removed connect(KCupsConnection::global(), &KCupsConnection::jobCompleted, this, &JobModel::jobCompleted); connect(KCupsConnection::global(), &KCupsConnection::serverAudit, this, &JobModel::getJobs); connect(KCupsConnection::global(), &KCupsConnection::serverStarted, this, &JobModel::getJobs); connect(KCupsConnection::global(), &KCupsConnection::serverStopped, this, &JobModel::getJobs); connect(KCupsConnection::global(), &KCupsConnection::serverRestarted, this, &JobModel::getJobs); } void JobModel::setParentWId(WId parentId) { m_parentId = parentId; } void JobModel::init(const QString &destName) { m_destName = destName; // Get all jobs getJobs(); } void JobModel::hold(const QString &printerName, int jobId) { QPointer request = new KCupsRequest; request->holdJob(printerName, jobId); request->waitTillFinished(); if (request) { request->deleteLater(); } } void JobModel::release(const QString &printerName, int jobId) { QPointer request = new KCupsRequest; request->releaseJob(printerName, jobId); request->waitTillFinished(); if (request) { request->deleteLater(); } } void JobModel::cancel(const QString &printerName, int jobId) { QPointer request = new KCupsRequest; request->cancelJob(printerName, jobId); request->waitTillFinished(); if (request) { request->deleteLater(); } } void JobModel::move(const QString &printerName, int jobId, const QString &toPrinterName) { QPointer request = new KCupsRequest; request->moveJob(printerName, jobId, toPrinterName); request->waitTillFinished(); if (request) { request->deleteLater(); } } void JobModel::getJobs() { if (m_jobRequest) { return; } m_jobRequest = new KCupsRequest; connect(m_jobRequest, &KCupsRequest::finished, this, &JobModel::getJobFinished); m_jobRequest->getJobs(m_destName, false, m_whichjobs, m_jobAttributes); m_processingJob.clear(); } void JobModel::getJobFinished(KCupsRequest *request) { if (request) { if (request->hasError()) { // clear the model after so that the proper widget can be shown clear(); } else { KCupsJobs jobs = request->jobs(); qCDebug(LIBKCUPS) << jobs.size(); for (int i = 0; i < jobs.size(); ++i) { if (jobs.at(i).state() == IPP_JOB_PROCESSING) { m_processingJob = jobs.at(i).name(); } // try to find the job row int job_row = jobRow(jobs.at(i).id()); if (job_row == -1) { // not found, insert new one insertJob(i, jobs.at(i)); } else if (job_row == i) { // update the job updateJob(i, jobs.at(i)); } else { // found at wrong position // take it and insert on the right position QList row = takeRow(job_row); insertRow(i, row); updateJob(i, jobs.at(i)); } } // remove old printers // The above code starts from 0 and make sure // dest == modelIndex(x) and if it's not the // case it either inserts or moves it. // so any item > num_jobs can be safely deleted while (rowCount() > jobs.size()) { removeRow(rowCount() - 1); } } request->deleteLater(); } else { qCWarning(LIBKCUPS) << "Should not be called from a non KCupsRequest class" << sender(); } m_jobRequest = 0; } void JobModel::jobCompleted(const QString &text, const QString &printerUri, const QString &printerName, uint printerState, const QString &printerStateReasons, bool printerIsAcceptingJobs, uint jobId, uint jobState, const QString &jobStateReasons, const QString &jobName, uint jobImpressionsCompleted) { // REALLY? all these parameters just to say foo was deleted?? Q_UNUSED(text) Q_UNUSED(printerUri) Q_UNUSED(printerName) Q_UNUSED(printerState) Q_UNUSED(printerStateReasons) Q_UNUSED(printerIsAcceptingJobs) Q_UNUSED(jobId) Q_UNUSED(jobState) Q_UNUSED(jobStateReasons) Q_UNUSED(jobName) Q_UNUSED(jobImpressionsCompleted) // We grab all jobs again getJobs(); } void JobModel::insertUpdateJob(const QString &text, const QString &printerUri, const QString &printerName, uint printerState, const QString &printerStateReasons, bool printerIsAcceptingJobs, uint jobId, uint jobState, const QString &jobStateReasons, const QString &jobName, uint jobImpressionsCompleted) { // REALLY? all these parameters just to say foo was created?? Q_UNUSED(text) Q_UNUSED(printerUri) Q_UNUSED(printerName) Q_UNUSED(printerState) Q_UNUSED(printerStateReasons) Q_UNUSED(printerIsAcceptingJobs) Q_UNUSED(jobId) Q_UNUSED(jobState) Q_UNUSED(jobStateReasons) Q_UNUSED(jobName) Q_UNUSED(jobImpressionsCompleted) // We grab all jobs again getJobs(); } void JobModel::insertJob(int pos, const KCupsJob &job) { // insert the first column which has the job state and id QList row; ipp_jstate_e jobState = job.state(); auto statusItem = new QStandardItem(jobStatus(jobState)); statusItem->setData(jobState, RoleJobState); statusItem->setData(job.id(), RoleJobId); statusItem->setData(job.name(), RoleJobName); statusItem->setData(job.originatingUserName(), RoleJobOwner); statusItem->setData(job.originatingHostName(), RoleJobOriginatingHostName); QString size = KFormat().formatByteSize(job.size()); statusItem->setData(size, RoleJobSize); QString createdAt = QLocale().toString(job.createdAt()); statusItem->setData(createdAt, RoleJobCreatedAt); // TODO move the update code before the insert and reuse some code... statusItem->setData(KCupsJob::iconName(jobState), RoleJobIconName); statusItem->setData(KCupsJob::cancelEnabled(jobState), RoleJobCancelEnabled); statusItem->setData(KCupsJob::holdEnabled(jobState), RoleJobHoldEnabled); statusItem->setData(KCupsJob::releaseEnabled(jobState), RoleJobReleaseEnabled); statusItem->setData(job.reprintEnabled(), RoleJobRestartEnabled); QString pages = QString::number(job.pages()); if (job.processedPages()) { pages = QString::number(job.processedPages()) % QLatin1Char('/') % QString::number(job.processedPages()); } if (statusItem->data(RoleJobPages) != pages) { statusItem->setData(pages, RoleJobPages); } row << statusItem; for (int i = ColName; i < LastColumn; i++) { // adds all Items to the model row << new QStandardItem; } // insert the whole row insertRow(pos, row); // update the items updateJob(pos, job); } void JobModel::updateJob(int pos, const KCupsJob &job) { // Job Status & internal dataipp_jstate_e ipp_jstate_e jobState = job.state(); if (item(pos, ColStatus)->data(RoleJobState).toInt() != jobState) { item(pos, ColStatus)->setText(jobStatus(jobState)); item(pos, ColStatus)->setData(static_cast(jobState), RoleJobState); item(pos, ColStatus)->setData(KCupsJob::iconName(jobState), RoleJobIconName); item(pos, ColStatus)->setData(KCupsJob::cancelEnabled(jobState), RoleJobCancelEnabled); item(pos, ColStatus)->setData(KCupsJob::holdEnabled(jobState), RoleJobHoldEnabled); item(pos, ColStatus)->setData(KCupsJob::releaseEnabled(jobState), RoleJobReleaseEnabled); item(pos, ColStatus)->setData(job.reprintEnabled(), RoleJobRestartEnabled); } QString pages = QString::number(job.pages()); if (job.processedPages()) { pages = QString::number(job.processedPages()) % QLatin1Char('/') % QString::number(job.processedPages()); } if (item(pos, ColStatus)->data(RoleJobPages) != pages) { item(pos, ColStatus)->setData(pages, RoleJobPages); } // internal dest name & column QString destName = job.printer(); if (item(pos, ColStatus)->data(RoleJobPrinter).toString() != destName) { item(pos, ColStatus)->setData(destName, RoleJobPrinter); // Column job printer Name item(pos, ColPrinter)->setText(destName); } // job name QString jobName = job.name(); if (item(pos, ColName)->text() != jobName) { item(pos, ColStatus)->setData(jobName, RoleJobName); item(pos, ColName)->setText(jobName); } // owner of the job // try to get the full user name QString userString = job.originatingUserName(); KUser user(userString); if (user.isValid() && !user.property(KUser::FullName).toString().isEmpty()) { userString = user.property(KUser::FullName).toString(); } // user name if (item(pos, ColUser)->text() != userString) { item(pos, ColUser)->setText(userString); } // when it was created QDateTime timeAtCreation = job.createdAt(); if (item(pos, ColCreated)->data(Qt::DisplayRole).toDateTime() != timeAtCreation) { item(pos, ColCreated)->setData(timeAtCreation, Qt::DisplayRole); } // when it was completed QDateTime completedAt = job.completedAt(); if (item(pos, ColCompleted)->data(Qt::DisplayRole).toDateTime() != completedAt) { if (!completedAt.isNull()) { item(pos, ColCompleted)->setData(completedAt, Qt::DisplayRole); } else { // Clean the data might happen when the job is restarted item(pos, ColCompleted)->setText(QString()); } } // job pages int completedPages = job.processedPages(); if (item(pos, ColPages)->data(Qt::UserRole) != completedPages) { item(pos, ColPages)->setData(completedPages, Qt::UserRole); item(pos, ColPages)->setText(QString::number(completedPages)); } // when it was precessed QDateTime timeAtProcessing = job.processedAt(); if (item(pos, ColProcessed)->data(Qt::DisplayRole).toDateTime() != timeAtProcessing) { if (!timeAtProcessing.isNull()) { item(pos, ColProcessed)->setData(timeAtProcessing, Qt::DisplayRole); } else { // Clean the data might happen when the job is restarted item(pos, ColCompleted)->setText(QString()); } } int jobSize = job.size(); if (item(pos, ColSize)->data(Qt::UserRole) != jobSize) { item(pos, ColSize)->setData(jobSize, Qt::UserRole); item(pos, ColSize)->setText(KFormat().formatByteSize(jobSize)); } // job printer state message QString stateMessage = job.stateMsg(); if (item(pos, ColStatusMessage)->text() != stateMessage) { item(pos, ColStatusMessage)->setText(stateMessage); } // owner of the job // try to get the full user name QString originatingHostName = job.originatingHostName(); if (item(pos, ColFromHost)->text() != originatingHostName) { item(pos, ColFromHost)->setText(originatingHostName); } } QStringList JobModel::mimeTypes() const { return QStringList("application/x-cupsjobs"); } Qt::DropActions JobModel::supportedDropActions() const { return Qt::MoveAction; } QMimeData* JobModel::mimeData(const QModelIndexList &indexes) const { auto mimeData = new QMimeData(); QByteArray encodedData; QDataStream stream(&encodedData, QIODevice::WriteOnly); for (const QModelIndex &index : indexes) { if (index.isValid() && index.column() == 0) { // serialize the jobId and fromDestName stream << data(index, RoleJobId).toInt() << data(index, RoleJobPrinter).toString() << item(index.row(), ColName)->text(); } } mimeData->setData("application/x-cupsjobs", encodedData); return mimeData; } bool JobModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) { Q_UNUSED(row) Q_UNUSED(column) Q_UNUSED(parent) if (action == Qt::IgnoreAction) { return true; } if (!data->hasFormat("application/x-cupsjobs")) { return false; } QByteArray encodedData = data->data("application/x-cupsjobs"); QDataStream stream(&encodedData, QIODevice::ReadOnly); bool ret = false; while (!stream.atEnd()) { QString fromDestName, displayName; int jobId; // get the jobid and the from dest name stream >> jobId >> fromDestName >> displayName; if (fromDestName == m_destName) { continue; } QPointer request = new KCupsRequest; request->moveJob(fromDestName, jobId, m_destName); request->waitTillFinished(); if (request) { if (request->hasError()) { // failed to move one job // we return here to avoid more password tries KMessageBox::detailedSorryWId(m_parentId, i18n("Failed to move '%1' to '%2'", displayName, m_destName), request->errorMsg(), i18n("Failed")); } request->deleteLater(); ret = !request->hasError(); } } return ret; } +QHash JobModel::roleNames() const +{ + return m_roles; +} + KCupsRequest* JobModel::modifyJob(int row, JobAction action, const QString &newDestName, const QModelIndex &parent) { Q_UNUSED(parent) if (row < 0 || row >= rowCount()) { qCWarning(LIBKCUPS) << "Row number is invalid:" << row; return 0; } QStandardItem *job = item(row, ColStatus); int jobId = job->data(RoleJobId).toInt(); QString destName = job->data(RoleJobPrinter).toString(); // ignore some jobs ipp_jstate_t state = static_cast(job->data(RoleJobState).toInt()); if ((state == IPP_JOB_HELD && action == Hold) || (state == IPP_JOB_CANCELED && action == Cancel) || (state != IPP_JOB_HELD && action == Release)) { return 0; } auto request = new KCupsRequest; switch (action) { case Cancel: request->cancelJob(destName, jobId); break; case Hold: request->holdJob(destName, jobId); break; case Release: request->releaseJob(destName, jobId); break; case Reprint: request->restartJob(destName, jobId); break; case Move: request->moveJob(destName, jobId, newDestName); break; default: qCWarning(LIBKCUPS) << "Unknown ACTION called!!!" << action; return 0; } return request; } int JobModel::jobRow(int jobId) { // find the position of the jobId inside the model for (int i = 0; i < rowCount(); i++) { if (jobId == item(i)->data(RoleJobId).toInt()) { return i; } } // -1 if not found return -1; } QString JobModel::jobStatus(ipp_jstate_e job_state) { switch (job_state) { case IPP_JOB_PENDING : return i18n("Pending"); case IPP_JOB_HELD : return i18n("On hold"); case IPP_JOB_PROCESSING : return "-"; case IPP_JOB_STOPPED : return i18n("Stopped"); case IPP_JOB_CANCELED : return i18n("Canceled"); case IPP_JOB_ABORTED : return i18n("Aborted"); case IPP_JOB_COMPLETED : return i18n("Completed"); } return "-"; } void JobModel::clear() { removeRows(0, rowCount()); } void JobModel::setWhichJobs(WhichJobs whichjobs) { switch (whichjobs) { case WhichActive: m_whichjobs = CUPS_WHICHJOBS_ACTIVE; break; case WhichCompleted: m_whichjobs = CUPS_WHICHJOBS_COMPLETED; break; case WhichAll: m_whichjobs = CUPS_WHICHJOBS_ALL; break; } getJobs(); } Qt::ItemFlags JobModel::flags(const QModelIndex &index) const { if (index.isValid()) { ipp_jstate_t state = static_cast(item(index.row(), ColStatus)->data(RoleJobState).toInt()); if (state == IPP_JOB_PENDING || state == IPP_JOB_PROCESSING) { return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled; } } return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDropEnabled; } QString JobModel::processingJob() const { return m_processingJob; } diff --git a/libkcups/JobModel.h b/libkcups/JobModel.h index 4fd7bee..8bbe449 100644 --- a/libkcups/JobModel.h +++ b/libkcups/JobModel.h @@ -1,149 +1,151 @@ /*************************************************************************** * Copyright (C) 2010 by Daniel Nicoletti * * dantti12@gmail.com * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; see the file COPYING. If not, write to * * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301, USA. * ***************************************************************************/ #ifndef JOB_MODEL_H #define JOB_MODEL_H #include #include class KCupsJob; class KCupsRequest; class Q_DECL_EXPORT JobModel : public QStandardItemModel { Q_OBJECT Q_ENUMS(JobAction) Q_ENUMS(Role) Q_ENUMS(WhichJobs) public: enum Role { RoleJobId = Qt::UserRole + 2, RoleJobState, RoleJobName, RoleJobPages, RoleJobSize, RoleJobOwner, RoleJobCreatedAt, RoleJobIconName, RoleJobCancelEnabled, RoleJobHoldEnabled, RoleJobReleaseEnabled, RoleJobRestartEnabled, RoleJobPrinter, RoleJobOriginatingHostName }; enum JobAction { Cancel, Hold, Release, Move, Reprint }; enum WhichJobs { WhichAll, WhichActive, WhichCompleted }; enum Columns { ColStatus = 0, ColName, ColUser, ColCreated, ColCompleted, ColPages, ColProcessed, ColSize, ColStatusMessage, ColPrinter, ColFromHost, LastColumn }; explicit JobModel(QObject *parent = 0); void setParentWId(WId parentId); Q_INVOKABLE void init(const QString &destName = QString()); Q_INVOKABLE void hold(const QString &printerName, int jobId); Q_INVOKABLE void release(const QString &printerName, int jobId); Q_INVOKABLE void cancel(const QString &printerName, int jobId); Q_INVOKABLE void move(const QString &printerName, int jobId, const QString &toPrinterName); QString processingJob() const; Qt::ItemFlags flags(const QModelIndex &index) const Q_DECL_OVERRIDE; QStringList mimeTypes() const Q_DECL_OVERRIDE; Qt::DropActions supportedDropActions() const Q_DECL_OVERRIDE; QMimeData* mimeData(const QModelIndexList &indexes) const Q_DECL_OVERRIDE; bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) Q_DECL_OVERRIDE; + virtual QHash roleNames() const override; Q_INVOKABLE void setWhichJobs(WhichJobs whichjobs); KCupsRequest* modifyJob(int row, JobAction action, const QString &newDestName = QString(), const QModelIndex &parent = QModelIndex()); private slots: void getJobs(); void getJobFinished(KCupsRequest *request); void jobCompleted(const QString &text, const QString &printerUri, const QString &printerName, uint printerState, const QString &printerStateReasons, bool printerIsAcceptingJobs, uint jobId, uint jobState, const QString &jobStateReasons, const QString &jobName, uint jobImpressionsCompleted); void insertUpdateJob(const QString &text, const QString &printerUri, const QString &printerName, uint printerState, const QString &printerStateReasons, bool printerIsAcceptingJobs, uint jobId, uint jobState, const QString &jobStateReasons, const QString &jobName, uint jobImpressionsCompleted); private: int jobRow(int jobId); void insertJob(int pos, const KCupsJob &job); void updateJob(int pos, const KCupsJob &job); QString jobStatus(ipp_jstate_e job_state); void clear(); KCupsRequest *m_jobRequest; QString m_destName; QString m_processingJob; + QHash m_roles; int m_whichjobs; WId m_parentId; QStringList m_jobAttributes; }; #endif // JOB_MODEL_H diff --git a/libkcups/KIppRequest.cpp b/libkcups/KIppRequest.cpp index 9a7bd1f..0ece5ef 100644 --- a/libkcups/KIppRequest.cpp +++ b/libkcups/KIppRequest.cpp @@ -1,274 +1,276 @@ /*************************************************************************** * Copyright (C) 2010-2013 by Daniel Nicoletti * * dantti12@gmail.com * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; see the file COPYING. If not, write to * * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301, USA. * ***************************************************************************/ #include "KIppRequest.h" #include "KIppRequest_p.h" #include "Debug.h" #include KIppRequest::KIppRequest() : d_ptr(new KIppRequestPrivate) { } KIppRequest::KIppRequest(const KIppRequest &other) : d_ptr(new KIppRequestPrivate) { *this = other; } KIppRequest::KIppRequest(ipp_op_t operation, const char *resource, const QString &filename) : d_ptr(new KIppRequestPrivate) { Q_D(KIppRequest); d->operation = operation; d->resource = resource; d->filename = filename; // send our user name on the request too addString(IPP_TAG_OPERATION, IPP_TAG_NAME, KCUPS_REQUESTING_USER_NAME, cupsUser()); } KIppRequest::~KIppRequest() { Q_D(KIppRequest); delete d; } ipp_op_t KIppRequest::operation() const { Q_D(const KIppRequest); return d->operation; } QString KIppRequest::resource() const { Q_D(const KIppRequest); return d->resource; } QString KIppRequest::filename() const { Q_D(const KIppRequest); return d->filename; } ipp_t *KIppRequest::sendIppRequest() const { Q_D(const KIppRequest); ipp_t *request = ippNewRequest(d->operation); d->addRawRequestsToIpp(request); if (d->filename.isNull()) { return cupsDoRequest(CUPS_HTTP_DEFAULT, request, d->resource.toUtf8()); } else { return cupsDoFileRequest(CUPS_HTTP_DEFAULT, request, d->resource.toUtf8(), d->filename.toUtf8()); } } void KIppRequest::addString(ipp_tag_t group, ipp_tag_t valueTag, const QString &name, const QString &value) { Q_D(KIppRequest); d->addRequest(group, valueTag, name.toUtf8(), value); } void KIppRequest::addStringList(ipp_tag_t group, ipp_tag_t valueTag, const QString &name, const QStringList &value) { Q_D(KIppRequest); d->addRequest(group, valueTag, name.toUtf8(), value); } void KIppRequest::addInteger(ipp_tag_t group, ipp_tag_t valueTag, const QString &name, int value) { Q_D(KIppRequest); d->addRequest(group, valueTag, name.toUtf8(), value); } void KIppRequest::addBoolean(ipp_tag_t group, const QString &name, bool value) { Q_D(KIppRequest); d->addRequest(group, IPP_TAG_ZERO, name.toUtf8(), value); } void KIppRequest::addVariantValues(const QVariantHash &values) { QVariantHash::ConstIterator i = values.constBegin(); while (i != values.constEnd()) { QString key = i.key(); QVariant value = i.value(); switch (value.type()) { case QVariant::Bool: // Still in use at add-printer/PageAddPrinter.cpp if (key == QLatin1String(KCUPS_PRINTER_IS_ACCEPTING_JOBS)) { addBoolean(IPP_TAG_PRINTER, key, value.toBool()); } else { addBoolean(IPP_TAG_OPERATION, key, value.toBool()); } break; case QVariant::Int: // Still in use at add-printer/PageAddPrinter.cpp if (key == QLatin1String(KCUPS_PRINTER_STATE)) { addInteger(IPP_TAG_PRINTER, IPP_TAG_ENUM, key, value.toInt()); } else { addInteger(IPP_TAG_OPERATION, IPP_TAG_ENUM, key, value.toInt()); } break; case QVariant::String: // Still in use at add-printer/* if (key == QLatin1String(KCUPS_DEVICE_URI)) { // device uri has a different TAG addString(IPP_TAG_PRINTER, IPP_TAG_URI, key, value.toString()); } else if (key == QLatin1String(KCUPS_PRINTER_OP_POLICY) || key == QLatin1String(KCUPS_PRINTER_ERROR_POLICY) || key == QLatin1String("ppd-name")) { // printer-op-policy has a different TAG addString(IPP_TAG_PRINTER, IPP_TAG_NAME, key, value.toString()); } else if (key == QLatin1String(KCUPS_JOB_NAME)) { addString(IPP_TAG_OPERATION, IPP_TAG_NAME, key, value.toString()); } else if (key == QLatin1String(KCUPS_WHICH_JOBS)) { addString(IPP_TAG_OPERATION, IPP_TAG_KEYWORD, key, value.toString()); } else { addString(IPP_TAG_PRINTER, IPP_TAG_TEXT, key, value.toString()); } break; case QVariant::StringList: if (key == QLatin1String(KCUPS_MEMBER_URIS)) { addStringList(IPP_TAG_PRINTER, IPP_TAG_URI, key, value.toStringList()); } else { addStringList(IPP_TAG_PRINTER, IPP_TAG_NAME, key, value.toStringList()); } break; case QVariant::UInt: addInteger(IPP_TAG_OPERATION, IPP_TAG_ENUM, key, value.toInt()); break; default: qCWarning(LIBKCUPS) << "type NOT recognized! This will be ignored:" << key << "values" << i.value(); } ++i; } } void KIppRequest::addPrinterUri(const QString &printerName, bool isClass) { QString uri = assembleUrif(printerName, isClass); addString(IPP_TAG_OPERATION, IPP_TAG_URI, KCUPS_PRINTER_URI, uri); } QString KIppRequest::assembleUrif(const QString &name, bool isClass) { char uri[HTTP_MAX_URI]; // printer URI QString destination; if (isClass) { destination = QLatin1String("/classes/") % name; } else { destination = QLatin1String("/printers/") % name; } httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", cupsUser(), "localhost", ippPort(), destination.toUtf8()); return uri; } KIppRequest &KIppRequest::operator =(const KIppRequest &other) { Q_D(KIppRequest); if (this == &other) return *this; *d = *other.d_ptr; return *this; } void KIppRequestPrivate::addRequest(ipp_tag_t group, ipp_tag_t valueTag, const QString &name, const QVariant &value) { KCupsRawRequest request; request.group = group; request.valueTag = valueTag; request.name = name; request.value = value; rawRequests << request; } void KIppRequestPrivate::addRawRequestsToIpp(ipp_t *ipp) const { // sort the values as CUPS requires it - qSort(rawRequests.begin(), rawRequests.end(), rawRequestGroupLessThan); + std::sort(rawRequests.begin(), rawRequests.end(), [] (const KCupsRawRequest &a, const KCupsRawRequest &b) { + return a.group < b.group; + }); const QList &requests = rawRequests; for (const KCupsRawRequest &request :requests) { switch (request.value.type()) { case QVariant::Bool: ippAddBoolean(ipp, request.group, request.name.toUtf8(), request.value.toBool()); break; case QVariant::Int: case QVariant::UInt: ippAddInteger(ipp, request.group, request.valueTag, request.name.toUtf8(), request.value.toInt()); break; case QVariant::String: ippAddString(ipp, request.group, request.valueTag, request.name.toUtf8(), "utf-8", request.value.toString().toUtf8()); break; case QVariant::StringList: { QStringList list = request.value.toStringList(); QList valuesQByteArrayList; const char **values = qStringListToCharPtrPtr(list, valuesQByteArrayList); ippAddStrings(ipp, request.group, request.valueTag, request.name.toUtf8(), list.size(), "utf-8", values); // ippAddStrings deep copies everything so we can throw away the values. // the QBAList and content is auto discarded when going out of scope. delete [] values; break; } default: qCWarning(LIBKCUPS) << "type NOT recognized! This will be ignored:" << request.name << "values" << request.value; } } } diff --git a/libkcups/KIppRequest_p.h b/libkcups/KIppRequest_p.h index 0ba191c..2dc22bc 100644 --- a/libkcups/KIppRequest_p.h +++ b/libkcups/KIppRequest_p.h @@ -1,66 +1,61 @@ /*************************************************************************** * Copyright (C) 2010-2013 by Daniel Nicoletti * * dantti12@gmail.com * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; see the file COPYING. If not, write to * * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301, USA. * ***************************************************************************/ #ifndef KIPPREQUEST_P_H #define KIPPREQUEST_P_H #include class KCupsRawRequest { public: ipp_tag_t group; ipp_tag_t valueTag; QString name; QVariant value; }; class KIppRequestPrivate { public: void addRequest(ipp_tag_t group, ipp_tag_t valueTag, const QString &name, const QVariant &value); void addRawRequestsToIpp(ipp_t *ipp) const; ipp_op_t operation; QString resource; QString filename; mutable QList rawRequests; }; static const char **qStringListToCharPtrPtr(const QStringList &list, QList &qbaList) { const char **ptr = new const char *[list.size() + 1]; qbaList.reserve(qbaList.size() + list.size()); QByteArray qba; for (int i = 0; i < list.size(); ++i) { qba = list.at(i).toUtf8(); qbaList.append(qba); ptr[i] = qba.constData(); } ptr[list.size()] = 0; return ptr; } -bool rawRequestGroupLessThan(const KCupsRawRequest &a, const KCupsRawRequest &b) -{ - return a.group < b.group; -} - #endif // KIPPREQUEST_P_H diff --git a/libkcups/PrinterModel.cpp b/libkcups/PrinterModel.cpp index 35b6053..bb1b6d0 100644 --- a/libkcups/PrinterModel.cpp +++ b/libkcups/PrinterModel.cpp @@ -1,518 +1,522 @@ /*************************************************************************** * Copyright (C) 2010-2018 by Daniel Nicoletti * * dantti12@gmail.com * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; see the file COPYING. If not, write to * * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301, USA. * ***************************************************************************/ #include "PrinterModel.h" #include "Debug.h" #include #include #include #include #include #include #include #include #include #include PrinterModel::PrinterModel(QObject *parent) : QStandardItemModel(parent), m_unavailable(true) { m_attributes << KCUPS_PRINTER_NAME; m_attributes << KCUPS_PRINTER_STATE; m_attributes << KCUPS_PRINTER_STATE_MESSAGE; m_attributes << KCUPS_PRINTER_IS_SHARED; m_attributes << KCUPS_PRINTER_IS_ACCEPTING_JOBS; m_attributes << KCUPS_PRINTER_TYPE; m_attributes << KCUPS_PRINTER_LOCATION; m_attributes << KCUPS_PRINTER_INFO; m_attributes << KCUPS_PRINTER_MAKE_AND_MODEL; m_attributes << KCUPS_PRINTER_COMMANDS; m_attributes << KCUPS_MARKER_CHANGE_TIME; m_attributes << KCUPS_MARKER_COLORS; m_attributes << KCUPS_MARKER_LEVELS; m_attributes << KCUPS_MARKER_NAMES; m_attributes << KCUPS_MARKER_TYPES; - QHash roles = roleNames(); - roles[DestStatus] = "stateMessage"; - roles[DestName] = "printerName"; - roles[DestState] = "printerState"; - roles[DestIsDefault] = "isDefault"; - roles[DestIsShared] = "isShared"; - roles[DestIsAcceptingJobs] = "isAcceptingJobs"; - roles[DestIsPaused] = "isPaused"; - roles[DestIsClass] = "isClass"; - roles[DestLocation] = "location"; - roles[DestDescription] = "info"; - roles[DestKind] = "kind"; - roles[DestType] = "type"; - roles[DestCommands] = "commands"; - roles[DestMarkerChangeTime] = "markerChangeTime"; - roles[DestMarkers] = "markers"; - roles[DestIconName] = "iconName"; - roles[DestRemote] = "remote"; - setRoleNames(roles); + m_roles = QStandardItemModel::roleNames(); + m_roles[DestStatus] = "stateMessage"; + m_roles[DestName] = "printerName"; + m_roles[DestState] = "printerState"; + m_roles[DestIsDefault] = "isDefault"; + m_roles[DestIsShared] = "isShared"; + m_roles[DestIsAcceptingJobs] = "isAcceptingJobs"; + m_roles[DestIsPaused] = "isPaused"; + m_roles[DestIsClass] = "isClass"; + m_roles[DestLocation] = "location"; + m_roles[DestDescription] = "info"; + m_roles[DestKind] = "kind"; + m_roles[DestType] = "type"; + m_roles[DestCommands] = "commands"; + m_roles[DestMarkerChangeTime] = "markerChangeTime"; + m_roles[DestMarkers] = "markers"; + m_roles[DestIconName] = "iconName"; + m_roles[DestRemote] = "remote"; // This is emitted when a printer is added connect(KCupsConnection::global(), &KCupsConnection::printerAdded, this, &PrinterModel::insertUpdatePrinter); // This is emitted when a printer is modified connect(KCupsConnection::global(), &KCupsConnection::printerModified, this, &PrinterModel::insertUpdatePrinter); // This is emitted when a printer has it's state changed connect(KCupsConnection::global(), &KCupsConnection::printerStateChanged, this, &PrinterModel::insertUpdatePrinter); // This is emitted when a printer is stopped connect(KCupsConnection::global(), &KCupsConnection::printerStopped, this, &PrinterModel::insertUpdatePrinter); // This is emitted when a printer is restarted connect(KCupsConnection::global(), &KCupsConnection::printerRestarted, this, &PrinterModel::insertUpdatePrinter); // This is emitted when a printer is shutdown connect(KCupsConnection::global(), &KCupsConnection::printerShutdown, this, &PrinterModel::insertUpdatePrinter); // This is emitted when a printer is removed connect(KCupsConnection::global(), &KCupsConnection::printerDeleted, this, &PrinterModel::printerRemoved); connect(KCupsConnection::global(), &KCupsConnection::serverAudit, this, &PrinterModel::serverChanged); connect(KCupsConnection::global(), &KCupsConnection::serverStarted, this, &PrinterModel::serverChanged); connect(KCupsConnection::global(), &KCupsConnection::serverStopped, this, &PrinterModel::serverChanged); connect(KCupsConnection::global(), &KCupsConnection::serverRestarted, this, &PrinterModel::serverChanged); // Deprecated stuff that works better than the above connect(KCupsConnection::global(), &KCupsConnection::rhPrinterAdded, this, &PrinterModel::insertUpdatePrinterName); connect(KCupsConnection::global(), &KCupsConnection::rhPrinterRemoved, this, &PrinterModel::printerRemovedName); connect(KCupsConnection::global(), &KCupsConnection::rhQueueChanged, this, &PrinterModel::insertUpdatePrinterName); connect(this, &PrinterModel::rowsInserted, this, &PrinterModel::slotCountChanged); connect(this, &PrinterModel::rowsRemoved, this, &PrinterModel::slotCountChanged); connect(this, &PrinterModel::modelReset, this, &PrinterModel::slotCountChanged); update(); } void PrinterModel::getDestsFinished(KCupsRequest *request) { // When there is no printer IPP_NOT_FOUND is returned if (request->hasError() && request->error() != IPP_NOT_FOUND) { // clear the model after so that the proper widget can be shown clear(); emit error(request->error(), request->serverError(), request->errorMsg()); if (request->error() == IPP_SERVICE_UNAVAILABLE && !m_unavailable) { m_unavailable = true; emit serverUnavailableChanged(m_unavailable); } } else { if (m_unavailable) { m_unavailable = false; emit serverUnavailableChanged(m_unavailable); } KCupsPrinters printers = request->printers(); for (int i = 0; i < printers.size(); ++i) { // If there is a printer and it's not the current one add it // as a new destination int dest_row = destRow(printers.at(i).name()); if (dest_row == -1) { // not found, insert new one insertDest(i, printers.at(i)); } else if (dest_row == i) { // update the printer updateDest(item(i), printers.at(i)); } else { // found at wrong position // take it and insert on the right position QList row = takeRow(dest_row); insertRow(i, row); updateDest(item(i), printers.at(i)); } } // remove old printers // The above code starts from 0 and make sure // dest == modelIndex(x) and if it's not the // case it either inserts or moves it. // so any item > num_jobs can be safely deleted while (rowCount() > printers.size()) { removeRow(rowCount() - 1); } emit error(IPP_OK, QString(), QString()); } request->deleteLater(); } void PrinterModel::slotCountChanged() { emit countChanged(rowCount()); } QVariant PrinterModel::headerData(int section, Qt::Orientation orientation, int role) const { if (section == 0 && orientation == Qt::Horizontal && role == Qt::DisplayRole) { return i18n("Printers"); } return QVariant(); } int PrinterModel::count() const { return rowCount(); } bool PrinterModel::serverUnavailable() const { return m_unavailable; } +QHash PrinterModel::roleNames() const +{ + return m_roles; +} + void PrinterModel::pausePrinter(const QString &printerName) { QPointer request = new KCupsRequest; request->pausePrinter(printerName); request->waitTillFinished(); if (request) { request->deleteLater(); } } void PrinterModel::resumePrinter(const QString &printerName) { QPointer request = new KCupsRequest; request->resumePrinter(printerName); request->waitTillFinished(); if (request) { request->deleteLater(); } } void PrinterModel::rejectJobs(const QString &printerName) { QPointer request = new KCupsRequest; request->rejectJobs(printerName); request->waitTillFinished(); if (request) { request->deleteLater(); } } void PrinterModel::acceptJobs(const QString &printerName) { QPointer request = new KCupsRequest; request->acceptJobs(printerName); request->waitTillFinished(); if (request) { request->deleteLater(); } } void PrinterModel::update() { // kcmshell(6331) PrinterModel::update: (QHash(("printer-type", QVariant(int, 75534348) ) ( "marker-names" , QVariant(QStringList, ("Cyan", "Yellow", "Magenta", "Black") ) ) ( "printer-name" , QVariant(QString, "EPSON_Stylus_TX105") ) ( "marker-colors" , QVariant(QStringList, ("#00ffff", "#ffff00", "#ff00ff", "#000000") ) ) ( "printer-location" , QVariant(QString, "Luiz Vitor’s MacBook Pro") ) ( "marker-levels" , QVariant(QList, ) ) ( "marker-types" , QVariant(QStringList, ("inkCartridge", "inkCartridge", "inkCartridge", "inkCartridge") ) ) ( "printer-is-shared" , QVariant(bool, true) ) ( "printer-state-message" , QVariant(QString, "") ) ( "printer-commands" , QVariant(QStringList, ("Clean", "PrintSelfTestPage", "ReportLevels") ) ) ( "marker-change-time" , QVariant(int, 1267903160) ) ( "printer-state" , QVariant(int, 3) ) ( "printer-info" , QVariant(QString, "EPSON Stylus TX105") ) ( "printer-make-and-model" , QVariant(QString, "EPSON TX105 Series") ) ) ) // Get destinations with these attributes auto request = new KCupsRequest; connect(request, &KCupsRequest::finished, this, &PrinterModel::getDestsFinished); request->getPrinters(m_attributes); } void PrinterModel::insertDest(int pos, const KCupsPrinter &printer) { // Create the printer item auto stdItem = new QStandardItem(printer.name()); stdItem->setData(printer.name(), DestName); stdItem->setIcon(printer.icon()); // update the item updateDest(stdItem, printer); // insert the printer Item insertRow(pos, stdItem); } void PrinterModel::updateDest(QStandardItem *destItem, const KCupsPrinter &printer) { // store if the printer is the network default bool isDefault = printer.isDefault(); if (isDefault != destItem->data(DestIsDefault).toBool()) { destItem->setData(isDefault, DestIsDefault); } // store the printer state KCupsPrinter::Status state = printer.state(); if (state != destItem->data(DestState)) { destItem->setData(state, DestState); } qCDebug(LIBKCUPS) << state << printer.name(); // store if the printer is accepting jobs bool accepting = printer.isAcceptingJobs(); if (accepting != destItem->data(DestIsAcceptingJobs)) { destItem->setData(accepting, DestIsAcceptingJobs); } // store the printer status message QString status = destStatus(state, printer.stateMsg(), accepting); if (status != destItem->data(DestStatus)) { destItem->setData(status, DestStatus); } bool paused = (state == KCupsPrinter::Stopped || !accepting); if (paused != destItem->data(DestIsPaused)) { destItem->setData(paused, DestIsPaused); } // store if the printer is shared bool shared = printer.isShared(); if (shared != destItem->data(DestIsShared)) { destItem->setData(shared, DestIsShared); } // store if the printer is a class // the printer-type param is a flag bool isClass = printer.isClass(); if (isClass != destItem->data(DestIsClass)) { destItem->setData(isClass, DestIsClass); } // store if the printer type // the printer-type param is a flag uint printerType = printer.type(); if (printerType != destItem->data(DestType)) { destItem->setData(printerType, DestType); destItem->setData(printerType & CUPS_PRINTER_REMOTE, DestRemote); } // store the printer location QString location = printer.location(); if (location != destItem->data(DestLocation).toString()) { destItem->setData(location, DestLocation); } // store the printer icon name QString iconName = printer.iconName(); if (iconName != destItem->data(DestIconName).toString()) { destItem->setData(iconName, DestIconName); } if (destItem->data(DestName).toString() != destItem->text()){ if (destItem->text() != destItem->data(DestName).toString()){ destItem->setText(destItem->data(DestName).toString()); } } // store the printer description QString description = printer.info(); if (description != destItem->data(DestDescription).toString()){ destItem->setData(description, DestDescription); } // store the printer kind QString kind = printer.makeAndModel(); if (kind != destItem->data(DestKind)) { destItem->setData(kind, DestKind); } // store the printer commands QStringList commands = printer.commands(); if (commands != destItem->data(DestCommands)) { destItem->setData(commands, DestCommands); } int markerChangeTime = printer.markerChangeTime(); if (markerChangeTime != destItem->data(DestMarkerChangeTime)) { destItem->setData(printer.markerChangeTime(), DestMarkerChangeTime); QVariantHash markers; markers["marker-change-time"] = printer.markerChangeTime(); markers["marker-colors"] = printer.argument("marker-colors"); markers["marker-levels"] = printer.argument("marker-levels"); markers["marker-names"] = printer.argument("marker-names"); markers["marker-types"] = printer.argument("marker-types"); destItem->setData(markers, DestMarkers); } } int PrinterModel::destRow(const QString &destName) { // find the position of the jobId inside the model for (int i = 0; i < rowCount(); i++) { if (destName == item(i)->data(DestName).toString()) { return i; } } // -1 if not found return -1; } QString PrinterModel::destStatus(KCupsPrinter::Status state, const QString &message, bool isAcceptingJobs) const { switch (state) { case KCupsPrinter::Idle: if (message.isEmpty()){ return isAcceptingJobs ? i18n("Idle") : i18n("Idle, rejecting jobs"); } else { return isAcceptingJobs ? i18n("Idle - '%1'", message) : i18n("Idle, rejecting jobs - '%1'", message); } case KCupsPrinter::Printing: if (message.isEmpty()){ return i18n("In use"); } else { return i18n("In use - '%1'", message); } case KCupsPrinter::Stopped: if (message.isEmpty()){ return isAcceptingJobs ? i18n("Paused") : i18n("Paused, rejecting jobs"); } else { return isAcceptingJobs ? i18n("Paused - '%1'", message) : i18n("Paused, rejecting jobs - '%1'", message); } default : if (message.isEmpty()){ return i18n("Unknown"); } else { return i18n("Unknown - '%1'", message); } } } void PrinterModel::clear() { removeRows(0, rowCount()); } Qt::ItemFlags PrinterModel::flags(const QModelIndex &index) const { Q_UNUSED(index) return Qt::ItemIsSelectable | Qt::ItemIsEnabled; } void PrinterModel::insertUpdatePrinterName(const QString &printerName) { auto request = new KCupsRequest; connect(request, &KCupsRequest::finished, this, &PrinterModel::insertUpdatePrinterFinished); // TODO how do we know if it's a class if this DBus signal // does not tell us request->getPrinterAttributes(printerName, false, m_attributes); } void PrinterModel::insertUpdatePrinter(const QString &text, const QString &printerUri, const QString &printerName, uint printerState, const QString &printerStateReasons, bool printerIsAcceptingJobs) { Q_UNUSED(text) Q_UNUSED(printerUri) Q_UNUSED(printerState) Q_UNUSED(printerStateReasons) Q_UNUSED(printerIsAcceptingJobs) qCDebug(LIBKCUPS) << text << printerUri << printerName << printerState << printerStateReasons << printerIsAcceptingJobs; insertUpdatePrinterName(printerName); } void PrinterModel::insertUpdatePrinterFinished(KCupsRequest *request) { if (!request->hasError()) { const KCupsPrinters printers = request->printers(); for (const KCupsPrinter &printer : printers) { // If there is a printer and it's not the current one add it // as a new destination int dest_row = destRow(printer.name()); if (dest_row == -1) { // not found, insert new one insertDest(0, printer); } else { // update the printer updateDest(item(dest_row), printer); } } } request->deleteLater(); } void PrinterModel::printerRemovedName(const QString &printerName) { qCDebug(LIBKCUPS) << printerName; // Look for the removed printer int dest_row = destRow(printerName); if (dest_row != -1) { removeRows(dest_row, 1); } } void PrinterModel::printerRemoved(const QString &text, const QString &printerUri, const QString &printerName, uint printerState, const QString &printerStateReasons, bool printerIsAcceptingJobs) { // REALLY? all these parameters just to say foo was deleted?? Q_UNUSED(text) Q_UNUSED(printerUri) Q_UNUSED(printerState) Q_UNUSED(printerStateReasons) Q_UNUSED(printerIsAcceptingJobs) qCDebug(LIBKCUPS) << text << printerUri << printerName << printerState << printerStateReasons << printerIsAcceptingJobs; // Look for the removed printer int dest_row = destRow(printerName); if (dest_row != -1) { removeRows(dest_row, 1); } } void PrinterModel::printerStateChanged(const QString &text, const QString &printerUri, const QString &printerName, uint printerState, const QString &printerStateReasons, bool printerIsAcceptingJobs) { qCDebug(LIBKCUPS) << text << printerUri << printerName << printerState << printerStateReasons << printerIsAcceptingJobs; } void PrinterModel::printerStopped(const QString &text, const QString &printerUri, const QString &printerName, uint printerState, const QString &printerStateReasons, bool printerIsAcceptingJobs) { qCDebug(LIBKCUPS) << text << printerUri << printerName << printerState << printerStateReasons << printerIsAcceptingJobs; } void PrinterModel::printerRestarted(const QString &text, const QString &printerUri, const QString &printerName, uint printerState, const QString &printerStateReasons, bool printerIsAcceptingJobs) { qCDebug(LIBKCUPS) << text << printerUri << printerName << printerState << printerStateReasons << printerIsAcceptingJobs; } void PrinterModel::printerShutdown(const QString &text, const QString &printerUri, const QString &printerName, uint printerState, const QString &printerStateReasons, bool printerIsAcceptingJobs) { qCDebug(LIBKCUPS) << text << printerUri << printerName << printerState << printerStateReasons << printerIsAcceptingJobs; } void PrinterModel::printerModified(const QString &text, const QString &printerUri, const QString &printerName, uint printerState, const QString &printerStateReasons, bool printerIsAcceptingJobs) { qCDebug(LIBKCUPS) << text << printerUri << printerName << printerState << printerStateReasons << printerIsAcceptingJobs; } void PrinterModel::serverChanged(const QString &text) { qCDebug(LIBKCUPS) << text; update(); } diff --git a/libkcups/PrinterModel.h b/libkcups/PrinterModel.h index a9dab13..d222bb1 100644 --- a/libkcups/PrinterModel.h +++ b/libkcups/PrinterModel.h @@ -1,118 +1,121 @@ /*************************************************************************** * Copyright (C) 2010-2018 by Daniel Nicoletti * * dantti12@gmail.com * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; see the file COPYING. If not, write to * * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301, USA. * ***************************************************************************/ #ifndef PRINTER_MODEL_H #define PRINTER_MODEL_H #include #include #include class KCupsRequest; class Q_DECL_EXPORT PrinterModel : public QStandardItemModel { Q_OBJECT Q_ENUMS(JobAction) Q_ENUMS(Role) Q_PROPERTY(int count READ count NOTIFY countChanged) Q_PROPERTY(bool serverUnavailable READ serverUnavailable NOTIFY serverUnavailableChanged) public: enum Role { DestStatus = Qt::UserRole, DestState, DestName, DestIsDefault, DestIsShared, DestIsAcceptingJobs, DestIsPaused, DestIsClass, DestLocation, DestDescription, DestKind, DestType, DestCommands, DestMarkerChangeTime, DestMarkers, DestIconName, DestRemote }; enum JobAction { Cancel, Hold, Release, Move }; explicit PrinterModel(QObject *parent = 0); Qt::ItemFlags flags(const QModelIndex &index) const Q_DECL_OVERRIDE; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE; int count() const; bool serverUnavailable() const; + virtual QHash roleNames() const override; + Q_INVOKABLE void pausePrinter(const QString &printerName); Q_INVOKABLE void resumePrinter(const QString &printerName); Q_INVOKABLE void rejectJobs(const QString &printerName); Q_INVOKABLE void acceptJobs(const QString &printerName); public slots: void update(); void getDestsFinished(KCupsRequest *request); void slotCountChanged(); signals: void countChanged(int count); void serverUnavailableChanged(bool unavailable); void error(int lastError, const QString &errorTitle, const QString &errorMsg); private slots: void insertUpdatePrinterName(const QString &printerName); void insertUpdatePrinter(const QString &text, const QString &printerUri, const QString &printerName, uint printerState, const QString &printerStateReasons, bool printerIsAcceptingJobs); void insertUpdatePrinterFinished(KCupsRequest *request); void printerRemovedName(const QString &printerName); void printerRemoved(const QString &text, const QString &printerUri, const QString &printerName, uint printerState, const QString &printerStateReasons, bool printerIsAcceptingJobs); void printerStateChanged(const QString &text, const QString &printerUri, const QString &printerName, uint printerState, const QString &printerStateReasons, bool printerIsAcceptingJobs); void printerStopped(const QString &text, const QString &printerUri, const QString &printerName, uint printerState, const QString &printerStateReasons, bool printerIsAcceptingJobs); void printerRestarted(const QString &text, const QString &printerUri, const QString &printerName, uint printerState, const QString &printerStateReasons, bool printerIsAcceptingJobs); void printerShutdown(const QString &text, const QString &printerUri, const QString &printerName, uint printerState, const QString &printerStateReasons, bool printerIsAcceptingJobs); void printerModified(const QString &text, const QString &printerUri, const QString &printerName, uint printerState, const QString &printerStateReasons, bool printerIsAcceptingJobs); void serverChanged(const QString &text); private: WId m_parentId; QStringList m_attributes; + QHash m_roles; bool m_unavailable; int destRow(const QString &destName); void insertDest(int pos, const KCupsPrinter &printer); void updateDest(QStandardItem *item, const KCupsPrinter &printer); QString destStatus(KCupsPrinter::Status state, const QString &message, bool isAcceptingJobs) const; void clear(); }; #endif // PRINTER_MODEL_H diff --git a/printqueue/PrintQueueUi.cpp b/printqueue/PrintQueueUi.cpp index 60b4ed8..abbc6ce 100644 --- a/printqueue/PrintQueueUi.cpp +++ b/printqueue/PrintQueueUi.cpp @@ -1,613 +1,613 @@ /*************************************************************************** * Copyright (C) 2010-2018 by Daniel Nicoletti * * dantti12@gmail.com * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; see the file COPYING. If not, write to * * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301, USA. * ***************************************************************************/ #include "PrintQueueUi.h" #include "ui_PrintQueueUi.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define PRINTER_ICON_SIZE 92 PrintQueueUi::PrintQueueUi(const KCupsPrinter &printer, QWidget *parent) : QDialog(parent), ui(new Ui::PrintQueueUi), m_destName(printer.name()), m_preparingMenu(false), m_printerPaused(false), m_lastState(0) { ui->setupUi(this); // since setupUi needs to setup on the mainWidget() // we need to manually connect the buttons connect(ui->cancelJobPB, &QPushButton::clicked, this, &PrintQueueUi::cancelJob); connect(ui->holdJobPB, &QPushButton::clicked, this, &PrintQueueUi::holdJob); connect(ui->resumeJobPB, &QPushButton::clicked, this, &PrintQueueUi::resumeJob); connect(ui->reprintPB, &QPushButton::clicked, this, &PrintQueueUi::reprintJob); connect(ui->pausePrinterPB, &QPushButton::clicked, this, &PrintQueueUi::pausePrinter); connect(ui->configurePrinterPB, &QPushButton::clicked, this, &PrintQueueUi::configurePrinter); connect(ui->whichJobsCB, static_cast(&QComboBox::currentIndexChanged), this, &PrintQueueUi::whichJobsIndexChanged); // Needed so we have our dialog size saved setAttribute(Qt::WA_DeleteOnClose); setWindowIcon(printer.icon()); if (printer.info().isEmpty()) { m_title = printer.name(); } else { m_title = printer.name() % QLatin1String(" - ") % printer.info(); } setWindowTitle(m_title); setSizeGripEnabled(true); (void) minimumSizeHint(); //Force the dialog to be laid out now layout()->setContentsMargins(0,0,0,0); m_isClass = printer.isClass(); // setup default options ui->jobsView->setCornerWidget(new QWidget); setupButtons(); // loads the standard key icon m_printerIcon = printer.icon().pixmap(PRINTER_ICON_SIZE, PRINTER_ICON_SIZE); ui->iconL->setPixmap(m_printerIcon); m_pauseIcon = KIconLoader::global()->loadIcon("media-playback-pause", KIconLoader::NoGroup, KIconLoader::SizeMedium, KIconLoader::DefaultState, QStringList(), 0, true); ui->printerStatusMsgL->setText(QString()); // setup the jobs model m_model = new JobModel(this); m_model->setParentWId(winId()); m_model->init(printer.name()); connect(m_model, &JobModel::dataChanged, this, &PrintQueueUi::updateButtons); connect(m_model, &JobModel::dataChanged, this, &PrintQueueUi::update); m_proxyModel = new JobSortFilterModel(this); m_proxyModel->setSourceModel(m_model); m_proxyModel->setDynamicSortFilter(true); ui->jobsView->setModel(m_proxyModel); ui->jobsView->setItemDelegate(new NoSelectionRectDelegate(this)); // sort by status column means the jobs will be sorted by the queue order ui->jobsView->sortByColumn(JobModel::ColStatus, Qt::AscendingOrder); connect(ui->jobsView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &PrintQueueUi::updateButtons); connect(ui->jobsView, &QTreeView::customContextMenuRequested, this, &PrintQueueUi::showContextMenu); ui->jobsView->header()->setContextMenuPolicy(Qt::CustomContextMenu); connect(ui->jobsView->header(), &QHeaderView::customContextMenuRequested, this, &PrintQueueUi::showHeaderContextMenu); QHeaderView *header = ui->jobsView->header(); - header->setResizeMode(QHeaderView::Interactive); + header->setSectionResizeMode(QHeaderView::Interactive); header->setStretchLastSection(false); - header->setResizeMode(JobModel::ColStatus, QHeaderView::ResizeToContents); - header->setResizeMode(JobModel::ColName, QHeaderView::Stretch); - header->setResizeMode(JobModel::ColUser, QHeaderView::ResizeToContents); - header->setResizeMode(JobModel::ColCreated, QHeaderView::ResizeToContents); - header->setResizeMode(JobModel::ColCompleted, QHeaderView::ResizeToContents); - header->setResizeMode(JobModel::ColPages, QHeaderView::ResizeToContents); - header->setResizeMode(JobModel::ColProcessed, QHeaderView::ResizeToContents); - header->setResizeMode(JobModel::ColSize, QHeaderView::ResizeToContents); - header->setResizeMode(JobModel::ColStatusMessage, QHeaderView::ResizeToContents); - header->setResizeMode(JobModel::ColPrinter, QHeaderView::ResizeToContents); + header->setSectionResizeMode(JobModel::ColStatus, QHeaderView::ResizeToContents); + header->setSectionResizeMode(JobModel::ColName, QHeaderView::Stretch); + header->setSectionResizeMode(JobModel::ColUser, QHeaderView::ResizeToContents); + header->setSectionResizeMode(JobModel::ColCreated, QHeaderView::ResizeToContents); + header->setSectionResizeMode(JobModel::ColCompleted, QHeaderView::ResizeToContents); + header->setSectionResizeMode(JobModel::ColPages, QHeaderView::ResizeToContents); + header->setSectionResizeMode(JobModel::ColProcessed, QHeaderView::ResizeToContents); + header->setSectionResizeMode(JobModel::ColSize, QHeaderView::ResizeToContents); + header->setSectionResizeMode(JobModel::ColStatusMessage, QHeaderView::ResizeToContents); + header->setSectionResizeMode(JobModel::ColPrinter, QHeaderView::ResizeToContents); KConfigGroup printQueue(KSharedConfig::openConfig("print-manager"), "PrintQueue"); if (printQueue.hasKey("ColumnState")) { // restore the header state order header->restoreState(printQueue.readEntry("ColumnState", QByteArray())); } else { // Hide some columns ColPrinter header->hideSection(JobModel::ColPrinter); header->hideSection(JobModel::ColUser); header->hideSection(JobModel::ColCompleted); header->hideSection(JobModel::ColSize); header->hideSection(JobModel::ColFromHost); } // This is emitted when a printer is modified connect(KCupsConnection::global(), &KCupsConnection::printerModified, this, &PrintQueueUi::updatePrinter); // This is emitted when a printer has it's state changed connect(KCupsConnection::global(), &KCupsConnection::printerStateChanged, this, &PrintQueueUi::updatePrinter); // This is emitted when a printer is stopped connect(KCupsConnection::global(), &KCupsConnection::printerStopped, this, &PrintQueueUi::updatePrinter); // This is emitted when a printer is restarted connect(KCupsConnection::global(), &KCupsConnection::printerRestarted, this, &PrintQueueUi::updatePrinter); // This is emitted when a printer is shutdown connect(KCupsConnection::global(), &KCupsConnection::printerShutdown, this, &PrintQueueUi::updatePrinter); // This is emitted when a printer is removed connect(KCupsConnection::global(), &KCupsConnection::printerDeleted, this, &PrintQueueUi::updatePrinter); // This is emitted when a printer/queue is changed // Deprecated stuff that works better than the above connect(KCupsConnection::global(), &KCupsConnection::rhPrinterAdded, this, &PrintQueueUi::updatePrinterByName); connect(KCupsConnection::global(), &KCupsConnection::rhPrinterRemoved, this, &PrintQueueUi::updatePrinterByName); connect(KCupsConnection::global(), &KCupsConnection::rhQueueChanged, this, &PrintQueueUi::updatePrinterByName); updatePrinterByName(m_destName); // Restore the dialog size KConfigGroup configGroup(KSharedConfig::openConfig("print-manager"), "PrintQueue"); KWindowConfig::restoreWindowSize(windowHandle(), configGroup); } PrintQueueUi::~PrintQueueUi() { KConfigGroup configGroup(KSharedConfig::openConfig("print-manager"), "PrintQueue"); // save the header state order configGroup.writeEntry("ColumnState", ui->jobsView->header()->saveState()); // Save the dialog size KWindowConfig::saveWindowSize(windowHandle(), configGroup); delete ui; } int PrintQueueUi::columnCount(const QModelIndex &parent) const { if (!parent.isValid()) { return JobModel::LastColumn; } return 0; } void PrintQueueUi::setState(int state, const QString &message) { qDebug() << state << message; if (state != m_lastState || ui->printerStatusMsgL->text() != message) { // save the last state so the ui doesn't need to keep updating if (ui->printerStatusMsgL->text() != message) { ui->printerStatusMsgL->setText(message); } m_lastState = state; QPixmap icon(m_printerIcon); m_printerPaused = false; switch (state) { case KCupsPrinter::Idle: ui->statusL->setText(i18n("Printer ready")); ui->pausePrinterPB->setText(i18n("Pause Printer")); ui->pausePrinterPB->setIcon(QIcon::fromTheme("media-playback-pause")); break; case KCupsPrinter::Printing: if (!m_title.isNull()) { QString jobTitle = m_model->processingJob(); if (jobTitle.isEmpty()) { ui->statusL->setText(i18n("Printing...")); } else { ui->statusL->setText(i18n("Printing '%1'", jobTitle)); } ui->pausePrinterPB->setText(i18n("Pause Printer")); ui->pausePrinterPB->setIcon(QIcon::fromTheme("media-playback-pause")); } break; case KCupsPrinter::Stopped: m_printerPaused = true; ui->statusL->setText(i18n("Printer paused")); ui->pausePrinterPB->setText(i18n("Resume Printer")); ui->pausePrinterPB->setIcon(QIcon::fromTheme("media-playback-start")); // create a paiter to paint the action icon over the key icon { QPainter painter(&icon); // the emblem icon to size 32 int overlaySize = KIconLoader::SizeMedium; QPoint startPoint; // bottom right corner startPoint = QPoint(PRINTER_ICON_SIZE - overlaySize - 2, PRINTER_ICON_SIZE - overlaySize - 2); painter.drawPixmap(startPoint, m_pauseIcon); } break; default : ui->statusL->setText(i18n("Printer state unknown")); break; } // set the printer icon setWindowIcon(icon); } } void PrintQueueUi::showContextMenu(const QPoint &point) { // check if the click was actually over a job if (!ui->jobsView->indexAt(point).isValid() || m_preparingMenu) { return; } m_preparingMenu = true; bool moveTo = false; QItemSelection selection; // we need to map the selection to source to get the real indexes selection = m_proxyModel->mapSelectionToSource(ui->jobsView->selectionModel()->selection()); // if the selection is empty the user clicked on an empty space if (!selection.indexes().isEmpty()) { const QModelIndexList indexes = selection.indexes(); for (const QModelIndex &index : indexes) { if (index.column() == 0 && index.flags() & Qt::ItemIsDragEnabled) { // Found a move to item moveTo = true; break; } } // if we can move a job create the menu if (moveTo) { // context menu auto menu = new QMenu(this); // move to menu auto moveToMenu = new QMenu(i18n("Move to"), this); // get printers we can move to QPointer request = new KCupsRequest; QStringList attr; attr << KCUPS_PRINTER_NAME; attr << KCUPS_PRINTER_INFO; request->getPrinters(attr); request->waitTillFinished(); if (!request) { return; } const KCupsPrinters printers = request->printers(); request->deleteLater(); for (const KCupsPrinter &printer : printers) { // If there is a printer and it's not the current one add it // as a new destination if (printer.name() != m_destName) { QAction *action = moveToMenu->addAction(printer.info()); action->setData(printer.name()); } } if (!moveToMenu->isEmpty()) { menu->addMenu(moveToMenu); // show the menu on the right point QAction *action = menu->exec(ui->jobsView->mapToGlobal(point)); if (action) { // move the job modifyJob(JobModel::Move, action->data().toString()); } } } } m_preparingMenu = false; } void PrintQueueUi::showHeaderContextMenu(const QPoint &point) { // Displays a menu containing the header name, and // a check box to indicate if it's being shown auto menu = new QMenu(this); for (int i = 0; i < m_proxyModel->columnCount(); i++) { QAction *action; QString name; name = m_proxyModel->headerData(i, Qt::Horizontal, Qt::DisplayRole).toString(); action = menu->addAction(name); action->setCheckable(true); action->setChecked(!ui->jobsView->header()->isSectionHidden(i)); action->setData(i); } QAction *action = menu->exec(ui->jobsView->header()->mapToGlobal(point)); if (action) { int section = action->data().toInt(); if (action->isChecked()) { ui->jobsView->header()->showSection(section); } else { ui->jobsView->header()->hideSection(section); } } } void PrintQueueUi::updatePrinterByName(const QString &printer) { qDebug() << printer << m_destName; if (printer != m_destName) { // It was another printer that changed return; } QStringList attr; attr << KCUPS_PRINTER_INFO; attr << KCUPS_PRINTER_TYPE; attr << KCUPS_PRINTER_STATE; attr << KCUPS_PRINTER_STATE_MESSAGE; auto request = new KCupsRequest; connect(request, &KCupsRequest::finished, this, &PrintQueueUi::getAttributesFinished); request->getPrinterAttributes(printer, m_isClass, attr); } void PrintQueueUi::updatePrinter(const QString &text, const QString &printerUri, const QString &printerName, uint printerState, const QString &printerStateReasons, bool printerIsAcceptingJobs) { // REALLY? all these parameters just to say foo was added?? Q_UNUSED(text) Q_UNUSED(printerUri) Q_UNUSED(printerState) Q_UNUSED(printerStateReasons) Q_UNUSED(printerIsAcceptingJobs) qDebug() << printerName << printerStateReasons; updatePrinterByName(printerName); } void PrintQueueUi::getAttributesFinished(KCupsRequest *request) { qDebug() << request->hasError() << request->printers().isEmpty(); if (request->hasError() || request->printers().isEmpty()) { // if cups stops we disable our queue setEnabled(false); request->deleteLater(); // DO not delete before using as the request is in another thread return; } else if (isEnabled() == false) { // if cups starts again we enable our queue setEnabled(true); } KCupsPrinter printer = request->printers().first(); // get printer-info if (printer.info().isEmpty()) { m_title = printer.name(); } else { m_title = printer.name() % QLatin1String(" - ") % printer.info(); } // get printer-state setState(printer.state(), printer.stateMsg()); // store if the printer is a class m_isClass = printer.isClass(); request->deleteLater(); update(); } void PrintQueueUi::update() { // Set window title if (m_model->rowCount()) { if (m_destName.isNull()) { setWindowTitle(i18np("All Printers (%1 Job)", "All Printers (%1 Jobs)", m_model->rowCount())); } else { setWindowTitle(i18np("%2 (%1 Job)", "%2 (%1 Jobs)", m_model->rowCount(), m_title)); } } else { setWindowTitle(m_destName.isNull() ? i18n("All Printers") : m_title); } } void PrintQueueUi::updateButtons() { bool cancel, hold, release, reprint; // Set all options to false cancel = hold = release = reprint = false; QItemSelection selection; // we need to map the selection to source to get the real indexes selection = m_proxyModel->mapSelectionToSource(ui->jobsView->selectionModel()->selection()); // enable or disable the job action buttons if something is selected if (!selection.indexes().isEmpty()) { const QModelIndexList indexes = selection.indexes(); for (const QModelIndex &index : indexes) { if (index.column() == 0) { switch (static_cast(index.data(JobModel::RoleJobState).toInt())) { case IPP_JOB_CANCELED : case IPP_JOB_COMPLETED : case IPP_JOB_ABORTED : break; case IPP_JOB_HELD : case IPP_JOB_STOPPED : release = true; cancel = true; break; default: cancel = hold = true; break; } if (index.data(JobModel::RoleJobRestartEnabled).toBool()) { reprint = true; } } } } ui->cancelJobPB->setEnabled(cancel); ui->holdJobPB->setEnabled(hold); ui->resumeJobPB->setEnabled(release); ui->reprintPB->setEnabled(reprint); } void PrintQueueUi::modifyJob(int action, const QString &destName) { // get all selected indexes QItemSelection selection; // we need to map the selection to source to get the real indexes selection = m_proxyModel->mapSelectionToSource(ui->jobsView->selectionModel()->selection()); const QModelIndexList indexes = selection.indexes(); for (const QModelIndex &index : indexes) { if (index.column() == 0) { KCupsRequest *request; request = m_model->modifyJob(index.row(), static_cast(action), destName); if (!request) { // probably the job already has this state // or this is an unknown action continue; } request->waitTillFinished(); if (request->hasError()) { QString msg, jobName; jobName = m_model->item(index.row(), static_cast(JobModel::ColName))->text(); switch (action) { case JobModel::Cancel: msg = i18n("Failed to cancel '%1'", jobName); break; case JobModel::Hold: msg = i18n("Failed to hold '%1'", jobName); break; case JobModel::Release: msg = i18n("Failed to release '%1'", jobName); break; case JobModel::Reprint: msg = i18n("Failed to reprint '%1'", jobName); break; case JobModel::Move: msg = i18n("Failed to move '%1' to '%2'", jobName, destName); break; } KMessageBox::detailedSorry(this, msg, request->errorMsg(), i18n("Failed")); } request->deleteLater(); } } } void PrintQueueUi::pausePrinter() { // STOP and RESUME printer QPointer request = new KCupsRequest; if (m_printerPaused) { qDebug() << m_destName << "m_printerPaused"; request->resumePrinter(m_destName); } else { qDebug() << m_destName << "NOT m_printerPaused"; request->pausePrinter(m_destName); } request->waitTillFinished(); if (request) { request->deleteLater(); } } void PrintQueueUi::configurePrinter() { QProcess::startDetached("configure-printer", {m_destName}); } void PrintQueueUi::cancelJob() { // CANCEL a job modifyJob(JobModel::Cancel); } void PrintQueueUi::holdJob() { // HOLD a job modifyJob(JobModel::Hold); } void PrintQueueUi::resumeJob() { // RESUME a job modifyJob(JobModel::Release); } void PrintQueueUi::reprintJob() { modifyJob(JobModel::Reprint); } void PrintQueueUi::whichJobsIndexChanged(int index) { switch (index) { case 1: m_model->setWhichJobs(JobModel::WhichCompleted); break; case 2: m_model->setWhichJobs(JobModel::WhichAll); break; default: m_model->setWhichJobs(JobModel::WhichActive); break; } } void PrintQueueUi::setupButtons() { // setup jobs buttons // cancel action ui->cancelJobPB->setIcon(QIcon::fromTheme("dialog-cancel")); // hold job action ui->holdJobPB->setIcon(QIcon::fromTheme("document-open-recent")); // resume job action // TODO we need a new icon ui->resumeJobPB->setIcon(QIcon::fromTheme("media-playback-start")); ui->reprintPB->setIcon(QIcon::fromTheme("view-refresh")); ui->whichJobsCB->setItemIcon(0, QIcon::fromTheme("view-filter")); ui->whichJobsCB->setItemIcon(1, QIcon::fromTheme("view-filter")); ui->whichJobsCB->setItemIcon(2, QIcon::fromTheme("view-filter")); // stop start printer ui->pausePrinterPB->setIcon(QIcon::fromTheme("media-playback-pause")); // configure printer ui->configurePrinterPB->setIcon(QIcon::fromTheme("configure")); } void PrintQueueUi::closeEvent(QCloseEvent *event) { // emits finished signal to be removed the cache emit finished(); QWidget::closeEvent(event); }