kde-extraapps/print-manager/libkcups/PrinterModel.cpp

553 lines
21 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/***************************************************************************
* 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. *
***************************************************************************/
#include "PrinterModel.h"
#include <QDateTime>
#include <QMimeData>
#include <QDBusConnection>
#include <QtDBus/QDBusInterface>
#include <QPointer>
#include <KUser>
#include <KDebug>
#include <KLocale>
#include <KMessageBox>
#include <KCupsRequest.h>
#include <cups/cups.h>
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<int, QByteArray> 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);
// This is emitted when a printer is added
connect(KCupsConnection::global(),
SIGNAL(printerAdded(QString,QString,QString,uint,QString,bool)),
this,
SLOT(insertUpdatePrinter(QString,QString,QString,uint,QString,bool)));
// This is emitted when a printer is modified
connect(KCupsConnection::global(),
SIGNAL(printerModified(QString,QString,QString,uint,QString,bool)),
this,
SLOT(insertUpdatePrinter(QString,QString,QString,uint,QString,bool)));
// This is emitted when a printer has it's state changed
connect(KCupsConnection::global(),
SIGNAL(printerStateChanged(QString,QString,QString,uint,QString,bool)),
this,
SLOT(insertUpdatePrinter(QString,QString,QString,uint,QString,bool)));
// This is emitted when a printer is stopped
connect(KCupsConnection::global(),
SIGNAL(printerStopped(QString,QString,QString,uint,QString,bool)),
this,
SLOT(insertUpdatePrinter(QString,QString,QString,uint,QString,bool)));
// This is emitted when a printer is restarted
connect(KCupsConnection::global(),
SIGNAL(printerRestarted(QString,QString,QString,uint,QString,bool)),
this,
SLOT(insertUpdatePrinter(QString,QString,QString,uint,QString,bool)));
// This is emitted when a printer is shutdown
connect(KCupsConnection::global(),
SIGNAL(printerShutdown(QString,QString,QString,uint,QString,bool)),
this,
SLOT(insertUpdatePrinter(QString,QString,QString,uint,QString,bool)));
// This is emitted when a printer is removed
connect(KCupsConnection::global(),
SIGNAL(printerDeleted(QString,QString,QString,uint,QString,bool)),
this,
SLOT(printerRemoved(QString,QString,QString,uint,QString,bool)));
connect(KCupsConnection::global(), SIGNAL(serverAudit(QString)),
SLOT(serverChanged(QString)));
connect(KCupsConnection::global(), SIGNAL(serverStarted(QString)),
SLOT(serverChanged(QString)));
connect(KCupsConnection::global(), SIGNAL(serverStopped(QString)),
SLOT(serverChanged(QString)));
connect(KCupsConnection::global(), SIGNAL(serverRestarted(QString)),
SLOT(serverChanged(QString)));
// Deprecated stuff that works better than the above
connect(KCupsConnection::global(), SIGNAL(rhPrinterAdded(QString)),
this, SLOT(insertUpdatePrinter(QString)));
connect(KCupsConnection::global(), SIGNAL(rhPrinterRemoved(QString)),
this, SLOT(printerRemoved(QString)));
connect(KCupsConnection::global(), SIGNAL(rhQueueChanged(QString)),
this, SLOT(insertUpdatePrinter(QString)));
connect(this, SIGNAL(rowsInserted(QModelIndex,int,int)),
this, SLOT(slotCountChanged()));
connect(this, SIGNAL(rowsRemoved(QModelIndex,int,int)),
this, SLOT(slotCountChanged()));
connect(this, SIGNAL(modelReset()),
this, SLOT(slotCountChanged()));
update();
}
void PrinterModel::getDestsFinished()
{
KCupsRequest *request = qobject_cast<KCupsRequest *>(sender());
// 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<QStandardItem *> 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;
}
void PrinterModel::pausePrinter(const QString &printerName)
{
QPointer<KCupsRequest> request = new KCupsRequest;
request->pausePrinter(printerName);
request->waitTillFinished();
if (request) {
request->deleteLater();
}
}
void PrinterModel::resumePrinter(const QString &printerName)
{
QPointer<KCupsRequest> request = new KCupsRequest;
request->resumePrinter(printerName);
request->waitTillFinished();
if (request) {
request->deleteLater();
}
}
void PrinterModel::rejectJobs(const QString &printerName)
{
QPointer<KCupsRequest> request = new KCupsRequest;
request->rejectJobs(printerName);
request->waitTillFinished();
if (request) {
request->deleteLater();
}
}
void PrinterModel::acceptJobs(const QString &printerName)
{
QPointer<KCupsRequest> 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 Vitors MacBook Pro") ) ( "marker-levels" , QVariant(QList<int>, ) ) ( "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
KCupsRequest *request = new KCupsRequest;
connect(request, SIGNAL(finished()), this, SLOT(getDestsFinished()));
request->getPrinters(m_attributes);
}
void PrinterModel::insertDest(int pos, const KCupsPrinter &printer)
{
// Create the printer item
QStandardItem *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);
}
kDebug() << 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::insertUpdatePrinter(const QString &printerName)
{
KCupsRequest *request = new KCupsRequest;
connect(request, SIGNAL(finished()), this, SLOT(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)
kDebug() << text << printerUri << printerName << printerState << printerStateReasons << printerIsAcceptingJobs;
insertUpdatePrinter(printerName);
}
void PrinterModel::insertUpdatePrinterFinished()
{
KCupsRequest *request = qobject_cast<KCupsRequest *>(sender());
if (!request->hasError()) {
foreach (const KCupsPrinter &printer, request->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::printerRemoved(const QString &printerName)
{
kDebug() << 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)
kDebug() << 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)
{
kDebug() << 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)
{
kDebug() << 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)
{
kDebug() << 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)
{
kDebug() << 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)
{
kDebug() << text << printerUri << printerName << printerState << printerStateReasons << printerIsAcceptingJobs;
}
void PrinterModel::serverChanged(const QString &text)
{
kDebug() << text;
update();
}
#include "PrinterModel.moc"