File indexing completed on 2025-01-05 05:09:31
0001 /* 0002 SPDX-FileCopyrightText: 2010-2018 Daniel Nicoletti <dantti12@gmail.com> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "PrinterModel.h" 0008 0009 #include "kcupslib_log.h" 0010 0011 #include <QDBusConnection> 0012 #include <QDBusInterface> 0013 #include <QDateTime> 0014 #include <QMimeData> 0015 #include <QPointer> 0016 0017 #include <KLocalizedString> 0018 #include <KMessageBox> 0019 #include <KUser> 0020 0021 #include <KCupsRequest.h> 0022 0023 #include <cups/cups.h> 0024 0025 const static QStringList attrs(QStringList{KCUPS_PRINTER_NAME, 0026 KCUPS_PRINTER_STATE, 0027 KCUPS_PRINTER_STATE_MESSAGE, 0028 KCUPS_PRINTER_IS_SHARED, 0029 KCUPS_PRINTER_IS_ACCEPTING_JOBS, 0030 KCUPS_PRINTER_TYPE, 0031 KCUPS_PRINTER_LOCATION, 0032 KCUPS_PRINTER_INFO, 0033 KCUPS_PRINTER_MAKE_AND_MODEL, 0034 KCUPS_PRINTER_COMMANDS, 0035 KCUPS_MARKER_CHANGE_TIME, 0036 KCUPS_MARKER_COLORS, 0037 KCUPS_MARKER_LEVELS, 0038 KCUPS_MARKER_NAMES, 0039 KCUPS_MARKER_TYPES, 0040 KCUPS_DEVICE_URI, 0041 KCUPS_PRINTER_URI_SUPPORTED, 0042 KCUPS_MEMBER_NAMES}); 0043 0044 PrinterModel::PrinterModel(QObject *parent) 0045 : QStandardItemModel(parent) 0046 { 0047 m_roles = QStandardItemModel::roleNames(); 0048 m_roles[DestStatus] = "stateMessage"; 0049 m_roles[DestName] = "printerName"; 0050 m_roles[DestState] = "printerState"; 0051 m_roles[DestIsDefault] = "isDefault"; 0052 m_roles[DestIsShared] = "isShared"; 0053 m_roles[DestIsAcceptingJobs] = "isAcceptingJobs"; 0054 m_roles[DestIsPaused] = "isPaused"; 0055 m_roles[DestIsClass] = "isClass"; 0056 m_roles[DestLocation] = "location"; 0057 m_roles[DestDescription] = "info"; 0058 m_roles[DestKind] = "kind"; 0059 m_roles[DestType] = "type"; 0060 m_roles[DestCommands] = "commands"; 0061 m_roles[DestMarkerChangeTime] = "markerChangeTime"; 0062 m_roles[DestMarkers] = "markers"; 0063 m_roles[DestIconName] = "iconName"; 0064 m_roles[DestRemote] = "remote"; 0065 m_roles[DestUri] = "printerUri"; 0066 m_roles[DestUriSupported] = "uriSupported"; 0067 m_roles[DestMemberNames] = "memberNames"; 0068 0069 // This is emitted when a printer is added 0070 connect(KCupsConnection::global(), &KCupsConnection::printerAdded, this, &PrinterModel::insertUpdatePrinter); 0071 0072 // This is emitted when a printer is modified 0073 connect(KCupsConnection::global(), &KCupsConnection::printerModified, this, &PrinterModel::insertUpdatePrinter); 0074 0075 // This is emitted when a printer has it's state changed 0076 connect(KCupsConnection::global(), &KCupsConnection::printerStateChanged, this, &PrinterModel::insertUpdatePrinter); 0077 0078 // This is emitted when a printer is stopped 0079 connect(KCupsConnection::global(), &KCupsConnection::printerStopped, this, &PrinterModel::insertUpdatePrinter); 0080 0081 // This is emitted when a printer is restarted 0082 connect(KCupsConnection::global(), &KCupsConnection::printerRestarted, this, &PrinterModel::insertUpdatePrinter); 0083 0084 // This is emitted when a printer is shutdown 0085 connect(KCupsConnection::global(), &KCupsConnection::printerShutdown, this, &PrinterModel::insertUpdatePrinter); 0086 0087 // This is emitted when a printer is removed 0088 connect(KCupsConnection::global(), &KCupsConnection::printerDeleted, this, &PrinterModel::printerRemoved); 0089 0090 connect(KCupsConnection::global(), &KCupsConnection::serverAudit, this, &PrinterModel::serverChanged); 0091 connect(KCupsConnection::global(), &KCupsConnection::serverStarted, this, &PrinterModel::serverChanged); 0092 connect(KCupsConnection::global(), &KCupsConnection::serverStopped, this, &PrinterModel::serverChanged); 0093 connect(KCupsConnection::global(), &KCupsConnection::serverRestarted, this, &PrinterModel::serverChanged); 0094 0095 // Deprecated stuff that works better than the above 0096 connect(KCupsConnection::global(), &KCupsConnection::rhPrinterAdded, this, &PrinterModel::insertUpdatePrinterName); 0097 connect(KCupsConnection::global(), &KCupsConnection::rhPrinterRemoved, this, &PrinterModel::printerRemovedName); 0098 connect(KCupsConnection::global(), &KCupsConnection::rhQueueChanged, this, &PrinterModel::insertUpdatePrinterName); 0099 0100 connect(this, &PrinterModel::rowsInserted, this, &PrinterModel::slotCountChanged); 0101 connect(this, &PrinterModel::rowsRemoved, this, &PrinterModel::slotCountChanged); 0102 connect(this, &PrinterModel::modelReset, this, &PrinterModel::slotCountChanged); 0103 0104 update(); 0105 } 0106 0107 void PrinterModel::getDestsFinished(KCupsRequest *request) 0108 { 0109 // When there is no printer IPP_NOT_FOUND is returned 0110 if (request->hasError() && request->error() != IPP_NOT_FOUND) { 0111 // clear the model after so that the proper widget can be shown 0112 clear(); 0113 0114 Q_EMIT error(request->error(), request->serverError(), request->errorMsg()); 0115 if (request->error() == IPP_SERVICE_UNAVAILABLE && !m_unavailable) { 0116 m_unavailable = true; 0117 Q_EMIT serverUnavailableChanged(m_unavailable); 0118 } 0119 } else { 0120 if (m_unavailable) { 0121 m_unavailable = false; 0122 Q_EMIT serverUnavailableChanged(m_unavailable); 0123 } 0124 0125 const KCupsPrinters printers = request->printers(); 0126 for (int i = 0; i < printers.size(); ++i) { 0127 // If there is a printer and it's not the current one add it 0128 // as a new destination 0129 int dest_row = destRow(printers.at(i).name()); 0130 if (dest_row == -1) { 0131 // not found, insert new one 0132 insertDest(i, printers.at(i)); 0133 } else if (dest_row == i) { 0134 // update the printer 0135 updateDest(item(i), printers.at(i)); 0136 } else { 0137 // found at wrong position 0138 // take it and insert on the right position 0139 QList<QStandardItem *> row = takeRow(dest_row); 0140 insertRow(i, row); 0141 updateDest(item(i), printers.at(i)); 0142 } 0143 } 0144 0145 // remove old printers 0146 // The above code starts from 0 and make sure 0147 // dest == modelIndex(x) and if it's not the 0148 // case it either inserts or moves it. 0149 // so any item > num_jobs can be safely deleted 0150 while (rowCount() > printers.size()) { 0151 removeRow(rowCount() - 1); 0152 } 0153 0154 setDisplayLocationHint(); 0155 0156 Q_EMIT error(IPP_OK, QString(), QString()); 0157 } 0158 request->deleteLater(); 0159 } 0160 0161 void PrinterModel::setDisplayLocationHint() 0162 { 0163 QStringList locList; 0164 0165 // get location list 0166 for (int i = 0; i < rowCount(); i++) { 0167 const auto val = item(i)->data(DestLocation).toString(); 0168 if (!val.isEmpty()) { 0169 locList.append(val); 0170 } 0171 } 0172 // only show the location if there is more than one printer 0173 // and at least two distinct locations exist 0174 locList.removeDuplicates(); 0175 m_displayLocationHint = rowCount() > 1 && locList.count() > 1; 0176 Q_EMIT displayLocationHintChanged(); 0177 } 0178 0179 bool PrinterModel::printersOnly() const 0180 { 0181 for (int i = 0; i < rowCount(); i++) { 0182 if (item(i)->data(DestIsClass).toBool()) { 0183 return false; 0184 } 0185 } 0186 return true; 0187 } 0188 0189 bool PrinterModel::displayLocationHint() const 0190 { 0191 return m_displayLocationHint; 0192 } 0193 0194 void PrinterModel::slotCountChanged() 0195 { 0196 Q_EMIT countChanged(rowCount()); 0197 } 0198 0199 QVariant PrinterModel::headerData(int section, Qt::Orientation orientation, int role) const 0200 { 0201 if (section == 0 && orientation == Qt::Horizontal && role == Qt::DisplayRole) { 0202 return i18n("Printers"); 0203 } 0204 return QVariant(); 0205 } 0206 0207 int PrinterModel::count() const 0208 { 0209 return rowCount(); 0210 } 0211 0212 bool PrinterModel::serverUnavailable() const 0213 { 0214 return m_unavailable; 0215 } 0216 0217 QHash<int, QByteArray> PrinterModel::roleNames() const 0218 { 0219 return m_roles; 0220 } 0221 0222 void PrinterModel::pausePrinter(const QString &printerName) 0223 { 0224 QPointer<KCupsRequest> request = new KCupsRequest; 0225 request->pausePrinter(printerName); 0226 request->waitTillFinished(); 0227 if (request) { 0228 request->deleteLater(); 0229 } 0230 } 0231 0232 void PrinterModel::resumePrinter(const QString &printerName) 0233 { 0234 QPointer<KCupsRequest> request = new KCupsRequest; 0235 request->resumePrinter(printerName); 0236 request->waitTillFinished(); 0237 if (request) { 0238 request->deleteLater(); 0239 } 0240 } 0241 0242 void PrinterModel::rejectJobs(const QString &printerName) 0243 { 0244 QPointer<KCupsRequest> request = new KCupsRequest; 0245 request->rejectJobs(printerName); 0246 request->waitTillFinished(); 0247 if (request) { 0248 request->deleteLater(); 0249 } 0250 } 0251 0252 void PrinterModel::acceptJobs(const QString &printerName) 0253 { 0254 QPointer<KCupsRequest> request = new KCupsRequest; 0255 request->acceptJobs(printerName); 0256 request->waitTillFinished(); 0257 if (request) { 0258 request->deleteLater(); 0259 } 0260 } 0261 0262 void PrinterModel::update() 0263 { 0264 // kcmshell(6331) PrinterModel::update: (QHash(("printer-type", QVariant(int, 75534348) ) ( "marker-names" , QVariant(QStringList, ("Cyan", 0265 // "Yellow", "Magenta", "Black") ) ) ( "printer-name" , QVariant(QString, "EPSON_Stylus_TX105") ) ( "marker-colors" , QVariant(QStringList, 0266 // ("#00ffff", "#ffff00", "#ff00ff", "#000000") ) ) ( "printer-location" , QVariant(QString, "Luiz Vitor’s MacBook Pro") ) ( 0267 // "marker-levels" , QVariant(QList<int>, ) ) ( "marker-types" , QVariant(QStringList, ("inkCartridge", "inkCartridge", "inkCartridge", 0268 // "inkCartridge") ) ) ( "printer-is-shared" , QVariant(bool, true) ) ( "printer-state-message" , QVariant(QString, "") ) ( 0269 // "printer-commands" , QVariant(QStringList, ("Clean", "PrintSelfTestPage", "ReportLevels") ) ) ( "marker-change-time" , QVariant(int, 0270 // 1267903160) ) ( "printer-state" , QVariant(int, 3) ) ( "printer-info" , QVariant(QString, "EPSON Stylus TX105") ) ( 0271 // "printer-make-and-model" , QVariant(QString, "EPSON TX105 Series") ) ) ) 0272 // Get destinations with these attributes 0273 auto request = new KCupsRequest; 0274 connect(request, &KCupsRequest::finished, this, &PrinterModel::getDestsFinished); 0275 request->getPrinters(attrs); 0276 } 0277 0278 void PrinterModel::insertDest(int pos, const KCupsPrinter &printer) 0279 { 0280 // Create the printer item 0281 auto stdItem = new QStandardItem(printer.name()); 0282 stdItem->setData(printer.name(), DestName); 0283 stdItem->setIcon(printer.icon()); 0284 // update the item 0285 updateDest(stdItem, printer); 0286 0287 // insert the printer Item 0288 insertRow(pos, stdItem); 0289 } 0290 0291 void PrinterModel::updateDest(QStandardItem *destItem, const KCupsPrinter &printer) 0292 { 0293 // store if the printer is the network default 0294 bool isDefault = printer.isDefault(); 0295 if (destItem->data(DestIsDefault).isNull() || isDefault != destItem->data(DestIsDefault).toBool()) { 0296 destItem->setData(isDefault, DestIsDefault); 0297 } 0298 0299 // store the printer state 0300 KCupsPrinter::Status state = printer.state(); 0301 if (state != destItem->data(DestState)) { 0302 destItem->setData(state, DestState); 0303 } 0304 qCDebug(LIBKCUPS) << state << printer.name(); 0305 0306 // store if the printer is accepting jobs 0307 bool accepting = printer.isAcceptingJobs(); 0308 if (accepting != destItem->data(DestIsAcceptingJobs)) { 0309 destItem->setData(accepting, DestIsAcceptingJobs); 0310 } 0311 0312 // store the printer status message 0313 QString status = destStatus(state, printer.stateMsg(), accepting); 0314 if (status != destItem->data(DestStatus)) { 0315 destItem->setData(status, DestStatus); 0316 } 0317 0318 bool paused = (state == KCupsPrinter::Stopped || !accepting); 0319 if (paused != destItem->data(DestIsPaused)) { 0320 destItem->setData(paused, DestIsPaused); 0321 } 0322 0323 // store if the printer is shared 0324 bool shared = printer.isShared(); 0325 if (shared != destItem->data(DestIsShared)) { 0326 destItem->setData(shared, DestIsShared); 0327 } 0328 0329 // store if the printer is a class 0330 // the printer-type param is a flag 0331 bool isClass = printer.isClass(); 0332 if (isClass != destItem->data(DestIsClass)) { 0333 destItem->setData(isClass, DestIsClass); 0334 } 0335 0336 // store if the printer type 0337 // the printer-type param is a flag 0338 uint printerType = printer.type(); 0339 if (printerType != destItem->data(DestType)) { 0340 destItem->setData(printerType, DestType); 0341 destItem->setData(printerType & CUPS_PRINTER_REMOTE, DestRemote); 0342 } 0343 0344 // store the printer location 0345 QString location = printer.location(); 0346 if (location.isEmpty() || location != destItem->data(DestLocation).toString()) { 0347 destItem->setData(location, DestLocation); 0348 } 0349 0350 // store the printer icon name 0351 QString iconName = printer.iconName(); 0352 if (iconName != destItem->data(DestIconName).toString()) { 0353 destItem->setData(iconName, DestIconName); 0354 } 0355 0356 if (destItem->data(DestName).toString() != destItem->text()) { 0357 destItem->setText(destItem->data(DestName).toString()); 0358 } 0359 0360 // store the printer description 0361 QString description = printer.info(); 0362 if (description.isEmpty() || description != destItem->data(DestDescription).toString()) { 0363 destItem->setData(description, DestDescription); 0364 } 0365 0366 // store the printer kind 0367 QString kind = printer.makeAndModel(); 0368 if (kind != destItem->data(DestKind)) { 0369 destItem->setData(kind, DestKind); 0370 } 0371 0372 // store the printer commands 0373 QStringList commands = printer.commands(); 0374 if (commands != destItem->data(DestCommands)) { 0375 destItem->setData(commands, DestCommands); 0376 } 0377 0378 // store the printer URI 0379 QString uri = printer.deviceUri(); 0380 if (uri != destItem->data(DestUri).toString()) { 0381 destItem->setData(uri, DestUri); 0382 } 0383 0384 QString us = printer.uriSupported(); 0385 if (us != destItem->data(DestUriSupported).toString()) { 0386 destItem->setData(us, DestUriSupported); 0387 } 0388 0389 // printer member names for type=class 0390 const auto members = printer.memberNames(); 0391 if (members != destItem->data(DestMemberNames)) { 0392 destItem->setData(members, DestMemberNames); 0393 } 0394 0395 int markerChangeTime = printer.markerChangeTime(); 0396 if (markerChangeTime != destItem->data(DestMarkerChangeTime)) { 0397 destItem->setData(printer.markerChangeTime(), DestMarkerChangeTime); 0398 const QVariantMap markers{{KCUPS_MARKER_CHANGE_TIME, printer.markerChangeTime()}, 0399 {KCUPS_MARKER_COLORS, printer.argument(KCUPS_MARKER_COLORS)}, 0400 {KCUPS_MARKER_LEVELS, printer.argument(KCUPS_MARKER_LEVELS)}, 0401 {KCUPS_MARKER_NAMES, printer.argument(KCUPS_MARKER_NAMES)}, 0402 {KCUPS_MARKER_TYPES, printer.argument(KCUPS_MARKER_TYPES)}}; 0403 destItem->setData(markers, DestMarkers); 0404 } 0405 } 0406 0407 int PrinterModel::destRow(const QString &destName) 0408 { 0409 // find the position of the jobId inside the model 0410 for (int i = 0; i < rowCount(); i++) { 0411 if (destName == item(i)->data(DestName).toString()) { 0412 return i; 0413 } 0414 } 0415 // -1 if not found 0416 return -1; 0417 } 0418 0419 QString PrinterModel::destStatus(KCupsPrinter::Status state, const QString &message, bool isAcceptingJobs) const 0420 { 0421 switch (state) { 0422 case KCupsPrinter::Idle: 0423 if (message.isEmpty()) { 0424 return isAcceptingJobs ? i18n("Idle") : i18n("Idle, rejecting jobs"); 0425 } else { 0426 return isAcceptingJobs ? i18n("Idle - '%1'", message) : i18n("Idle, rejecting jobs - '%1'", message); 0427 } 0428 case KCupsPrinter::Printing: 0429 if (message.isEmpty()) { 0430 return i18n("In use"); 0431 } else { 0432 return i18n("In use - '%1'", message); 0433 } 0434 case KCupsPrinter::Stopped: 0435 if (message.isEmpty()) { 0436 return isAcceptingJobs ? i18n("Paused") : i18n("Paused, rejecting jobs"); 0437 } else { 0438 return isAcceptingJobs ? i18n("Paused - '%1'", message) : i18n("Paused, rejecting jobs - '%1'", message); 0439 } 0440 default: 0441 if (message.isEmpty()) { 0442 return i18n("Unknown"); 0443 } else { 0444 return i18n("Unknown - '%1'", message); 0445 } 0446 } 0447 } 0448 0449 Qt::ItemFlags PrinterModel::flags(const QModelIndex &index) const 0450 { 0451 Q_UNUSED(index) 0452 return Qt::ItemIsSelectable | Qt::ItemIsEnabled; 0453 } 0454 0455 void PrinterModel::insertUpdatePrinterName(const QString &printerName) 0456 { 0457 auto request = new KCupsRequest; 0458 connect(request, &KCupsRequest::finished, this, &PrinterModel::insertUpdatePrinterFinished); 0459 // TODO how do we know if it's a class if this DBus signal 0460 // does not tell us 0461 request->getPrinterAttributes(printerName, false, attrs); 0462 } 0463 0464 void PrinterModel::insertUpdatePrinter(const QString &text, 0465 const QString &printerUri, 0466 const QString &printerName, 0467 uint printerState, 0468 const QString &printerStateReasons, 0469 bool printerIsAcceptingJobs) 0470 { 0471 Q_UNUSED(text) 0472 Q_UNUSED(printerUri) 0473 Q_UNUSED(printerState) 0474 Q_UNUSED(printerStateReasons) 0475 Q_UNUSED(printerIsAcceptingJobs) 0476 0477 qCDebug(LIBKCUPS) << text << printerUri << printerName << printerState << printerStateReasons << printerIsAcceptingJobs; 0478 insertUpdatePrinterName(printerName); 0479 } 0480 0481 void PrinterModel::insertUpdatePrinterFinished(KCupsRequest *request) 0482 { 0483 if (!request->hasError()) { 0484 const KCupsPrinters printers = request->printers(); 0485 for (const KCupsPrinter &printer : printers) { 0486 // If there is a printer and it's not the current one add it 0487 // as a new destination 0488 int dest_row = destRow(printer.name()); 0489 if (dest_row == -1) { 0490 // not found, insert new one 0491 insertDest(0, printer); 0492 } else { 0493 // update the printer 0494 updateDest(item(dest_row), printer); 0495 } 0496 } 0497 } 0498 setDisplayLocationHint(); 0499 request->deleteLater(); 0500 } 0501 0502 void PrinterModel::printerRemovedName(const QString &printerName) 0503 { 0504 qCDebug(LIBKCUPS) << printerName; 0505 0506 // Look for the removed printer 0507 int dest_row = destRow(printerName); 0508 if (dest_row != -1) { 0509 removeRows(dest_row, 1); 0510 } 0511 setDisplayLocationHint(); 0512 } 0513 0514 void PrinterModel::printerRemoved(const QString &text, 0515 const QString &printerUri, 0516 const QString &printerName, 0517 uint printerState, 0518 const QString &printerStateReasons, 0519 bool printerIsAcceptingJobs) 0520 { 0521 // REALLY? all these parameters just to say foo was deleted?? 0522 Q_UNUSED(text) 0523 Q_UNUSED(printerUri) 0524 Q_UNUSED(printerState) 0525 Q_UNUSED(printerStateReasons) 0526 Q_UNUSED(printerIsAcceptingJobs) 0527 qCDebug(LIBKCUPS) << text << printerUri << printerName << printerState << printerStateReasons << printerIsAcceptingJobs; 0528 0529 // Look for the removed printer 0530 int dest_row = destRow(printerName); 0531 if (dest_row != -1) { 0532 removeRows(dest_row, 1); 0533 } 0534 setDisplayLocationHint(); 0535 } 0536 0537 void PrinterModel::printerStateChanged(const QString &text, 0538 const QString &printerUri, 0539 const QString &printerName, 0540 uint printerState, 0541 const QString &printerStateReasons, 0542 bool printerIsAcceptingJobs) 0543 { 0544 qCDebug(LIBKCUPS) << text << printerUri << printerName << printerState << printerStateReasons << printerIsAcceptingJobs; 0545 } 0546 void PrinterModel::printerStopped(const QString &text, 0547 const QString &printerUri, 0548 const QString &printerName, 0549 uint printerState, 0550 const QString &printerStateReasons, 0551 bool printerIsAcceptingJobs) 0552 { 0553 qCDebug(LIBKCUPS) << text << printerUri << printerName << printerState << printerStateReasons << printerIsAcceptingJobs; 0554 } 0555 0556 void PrinterModel::printerRestarted(const QString &text, 0557 const QString &printerUri, 0558 const QString &printerName, 0559 uint printerState, 0560 const QString &printerStateReasons, 0561 bool printerIsAcceptingJobs) 0562 { 0563 qCDebug(LIBKCUPS) << text << printerUri << printerName << printerState << printerStateReasons << printerIsAcceptingJobs; 0564 } 0565 0566 void PrinterModel::printerShutdown(const QString &text, 0567 const QString &printerUri, 0568 const QString &printerName, 0569 uint printerState, 0570 const QString &printerStateReasons, 0571 bool printerIsAcceptingJobs) 0572 { 0573 qCDebug(LIBKCUPS) << text << printerUri << printerName << printerState << printerStateReasons << printerIsAcceptingJobs; 0574 } 0575 0576 void PrinterModel::printerModified(const QString &text, 0577 const QString &printerUri, 0578 const QString &printerName, 0579 uint printerState, 0580 const QString &printerStateReasons, 0581 bool printerIsAcceptingJobs) 0582 { 0583 qCDebug(LIBKCUPS) << text << printerUri << printerName << printerState << printerStateReasons << printerIsAcceptingJobs; 0584 setDisplayLocationHint(); 0585 } 0586 0587 void PrinterModel::serverChanged(const QString &text) 0588 { 0589 qCDebug(LIBKCUPS) << text; 0590 update(); 0591 } 0592 0593 #include "moc_PrinterModel.cpp"