Merge lp:~ahayzen/ubuntu-ui-extras/job-impressions-update into lp:~phablet-team/ubuntu-ui-extras/printer-staging

Proposed by Andrew Hayzen
Status: Superseded
Proposed branch: lp:~ahayzen/ubuntu-ui-extras/job-impressions-update
Merge into: lp:~phablet-team/ubuntu-ui-extras/printer-staging
Diff against target: 1428 lines (+640/-196)
22 files modified
modules/Ubuntu/Components/Extras/Example/PrinterQueue.qml (+1/-4)
modules/Ubuntu/Components/Extras/Printers/CMakeLists.txt (+2/-1)
modules/Ubuntu/Components/Extras/Printers/backend/backend.cpp (+15/-0)
modules/Ubuntu/Components/Extras/Printers/backend/backend.h (+6/-0)
modules/Ubuntu/Components/Extras/Printers/backend/backend_cups.cpp (+113/-21)
modules/Ubuntu/Components/Extras/Printers/backend/backend_cups.h (+9/-0)
modules/Ubuntu/Components/Extras/Printers/cups/jobloader.cpp (+90/-0)
modules/Ubuntu/Components/Extras/Printers/cups/jobloader.h (+50/-0)
modules/Ubuntu/Components/Extras/Printers/models/jobmodel.cpp (+168/-97)
modules/Ubuntu/Components/Extras/Printers/models/jobmodel.h (+35/-9)
modules/Ubuntu/Components/Extras/Printers/models/printermodel.cpp (+2/-18)
modules/Ubuntu/Components/Extras/Printers/models/printermodel.h (+2/-3)
modules/Ubuntu/Components/Extras/Printers/plugin.cpp (+1/-0)
modules/Ubuntu/Components/Extras/Printers/printer/printer.cpp (+12/-2)
modules/Ubuntu/Components/Extras/Printers/printer/printerjob.cpp (+32/-9)
modules/Ubuntu/Components/Extras/Printers/printer/printerjob.h (+1/-2)
modules/Ubuntu/Components/Extras/Printers/printer/signalratelimiter.cpp (+28/-6)
modules/Ubuntu/Components/Extras/Printers/printer/signalratelimiter.h (+8/-6)
modules/Ubuntu/Components/Extras/Printers/printers/printers.cpp (+34/-7)
po/ubuntu-ui-extras.pot (+2/-2)
tests/unittests/Printers/CMakeLists.txt (+3/-3)
tests/unittests/Printers/tst_signalratelimiter.cpp (+26/-6)
To merge this branch: bzr merge lp:~ahayzen/ubuntu-ui-extras/job-impressions-update
Reviewer Review Type Date Requested Status
Jonas G. Drange (community) Needs Fixing
Andrew Hayzen (community) Needs Information
Review via email: mp+319450@code.launchpad.net

This proposal has been superseded by a proposal from 2017-03-13.

Commit message

* Connect job-impressions-completed from cups to PrinterJob::impressionsCompleted()
* In the job model listen to printerStateChanged as changes t job-impressions-completed causes that signal
* Improve the PrinterSignalHandler to limit the maximum wait time of unprocessed signals to four times the timeout
* Add unit test for SignalRateLimiter to check it does perform a flush
* Rename PrinterSignalHandler to SignalRateLimiter

Description of the change

* Connect job-impressions-completed from cups to PrinterJob::impressionsCompleted()
* In the job model listen to printerStateChanged as changes t job-impressions-completed causes that signal
* Improve the PrinterSignalHandler to limit the maximum wait time of unprocessed signals to four times the timeout
* Add unit test for SignalRateLimiter to check it does perform a flush
* Rename PrinterSignalHandler to SignalRateLimiter

To post a comment you must log in.
148. By Andrew Hayzen

* Show impressions in the PrinterQueue.qml

Revision history for this message
Andrew Hayzen (ahayzen) wrote :

Testing:
- Add a printer which has an address of lpd://127.0.0.1 so that it will spool on the cups server
- Download the serverguide.pdf (which has loads of pages) from [0]
- Open the PrinterQueue.qml example and open the printer you just added
- Add the serverguide.pdf to the printer queue for the printer you just added
- Watch the printing N pages count increment (also note the freezes that happen, eg if there is already a job being rendered when starting the example it can freeze)

0 - http://bazaar.launchpad.net/~ubuntu-docviewer-dev/ubuntu-docviewer-app/lo-viewer/download/head:/serverguide.pdf-20150423201617-20cgubqqf8rhokei-1/serverguide.pdf

Revision history for this message
Andrew Hayzen (ahayzen) wrote :

Should job-media-sheets-completed be used instead of job-impressesions-completed? Does this then take duplex into account?

review: Needs Information
Revision history for this message
Jonas G. Drange (jonas-drange) wrote :

ack on the rate limiter! good stuff

Revision history for this message
Jonas G. Drange (jonas-drange) wrote :

Re: job-media-*, I'd have to read the IPP spec to be sure. Maybe mimic SCP here?

149. By Andrew Hayzen

* Merge of upstream

Revision history for this message
Jonas G. Drange (jonas-drange) wrote :

Got this one:
void PrinterJob::loadDefaults() 12
QObject::connect: Cannot queue arguments of type 'QVector<int>'
(Make sure 'QVector<int>' is registered using qRegisterMetaType().)

Introduced by this branch?

review: Needs Information
Revision history for this message
Jonas G. Drange (jonas-drange) wrote :

The window only freezes, no queues are displayed. I think we need to fix that.

And the freezing is introduced by this branch, because using printer-staging trunk, going to Printers.qml -> <printer> -> Jobs enumerates the jobs without the lag.

review: Needs Fixing
150. By Andrew Hayzen

* Rebase ontop of lp:~ahayzen/ubuntu-ui-extras/job-model-split-update

151. By Andrew Hayzen

* Pull of upstream

152. By Andrew Hayzen

* Use job-state from extendedJobAttributes so that when there is a signal flood the state is correct

153. By Andrew Hayzen

* Pull of upstream

154. By Andrew Hayzen

* Try to use job-media-sheets-completed first

155. By Andrew Hayzen

* Pull of upstream
* Fix tests (emulation in MockBackend::printerGetJobAttributes)

156. By Andrew Hayzen

* Pull of upstream

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'modules/Ubuntu/Components/Extras/Example/PrinterQueue.qml'
2--- modules/Ubuntu/Components/Extras/Example/PrinterQueue.qml 2017-03-01 13:45:14 +0000
3+++ modules/Ubuntu/Components/Extras/Example/PrinterQueue.qml 2017-03-13 12:51:30 +0000
4@@ -72,10 +72,7 @@
5 ListItemLayout {
6 id: modelLayout
7 title.text: displayName
8- subtitle.text: model.title + " (" + model.id + ") State: " + model.state
9- + " Color: " + model.colorModel + " CreationTime: "
10- + model.creationTime + " PageRange: "
11- + model.printRange + " Messages: " + model.messages;
12+ subtitle.text: model.title + " (" + model.id + ")\nPrinting " + model.impressionsCompleted + " pages"
13 subtitle.wrapMode: Text.WrapAtWordBoundaryOrAnywhere
14 subtitle.maximumLineCount: 3
15 }
16
17=== modified file 'modules/Ubuntu/Components/Extras/Printers/CMakeLists.txt'
18--- modules/Ubuntu/Components/Extras/Printers/CMakeLists.txt 2017-03-08 14:47:16 +0000
19+++ modules/Ubuntu/Components/Extras/Printers/CMakeLists.txt 2017-03-13 12:51:30 +0000
20@@ -31,6 +31,7 @@
21
22 cups/devicesearcher.cpp
23 cups/ippclient.cpp
24+ cups/jobloader.cpp
25 cups/printerdriverloader.cpp
26 cups/printerloader.cpp
27
28@@ -41,7 +42,7 @@
29
30 printer/printer.cpp
31 printer/printerjob.cpp
32- printer/printersignalhandler.cpp
33+ printer/signalratelimiter.cpp
34 printers/printers.cpp
35
36 enums.h
37
38=== modified file 'modules/Ubuntu/Components/Extras/Printers/backend/backend.cpp'
39--- modules/Ubuntu/Components/Extras/Printers/backend/backend.cpp 2017-03-06 15:29:04 +0000
40+++ modules/Ubuntu/Components/Extras/Printers/backend/backend.cpp 2017-03-13 12:51:30 +0000
41@@ -174,6 +174,14 @@
42 return QList<QSharedPointer<PrinterJob>>{};
43 }
44
45+QSharedPointer<PrinterJob> PrinterBackend::printerGetJob(
46+ const QString &printerName, const int jobId)
47+{
48+ Q_UNUSED(printerName);
49+ Q_UNUSED(jobId);
50+ return QSharedPointer<PrinterJob>(Q_NULLPTR);
51+}
52+
53 QMap<QString, QVariant> PrinterBackend::printerGetJobAttributes(
54 const QString &name, const int jobId)
55 {
56@@ -273,6 +281,13 @@
57 return QString();
58 }
59
60+void PrinterBackend::requestJobExtendedAttributes(
61+ QSharedPointer<Printer> printer, QSharedPointer<PrinterJob> job)
62+{
63+ Q_UNUSED(printer);
64+ Q_UNUSED(job);
65+}
66+
67 void PrinterBackend::requestPrinterDrivers()
68 {
69 }
70
71=== modified file 'modules/Ubuntu/Components/Extras/Printers/backend/backend.h'
72--- modules/Ubuntu/Components/Extras/Printers/backend/backend.h 2017-03-09 14:34:05 +0000
73+++ modules/Ubuntu/Components/Extras/Printers/backend/backend.h 2017-03-13 12:51:30 +0000
74@@ -85,6 +85,8 @@
75 const QString &title,
76 const cups_dest_t *dest);
77 virtual QList<QSharedPointer<PrinterJob>> printerGetJobs();
78+ virtual QSharedPointer<PrinterJob> printerGetJob(const QString &printerName,
79+ const int jobId);
80 virtual QMap<QString, QVariant> printerGetJobAttributes(
81 const QString &name, const int jobId);
82
83@@ -110,6 +112,8 @@
84 virtual QSharedPointer<Printer> getPrinter(const QString &printerName);
85 virtual QString defaultPrinterName();
86
87+ virtual void requestJobExtendedAttributes(QSharedPointer<Printer> printer,
88+ QSharedPointer<PrinterJob> job);
89 virtual void requestPrinterDrivers();
90 virtual void requestPrinter(const QString &printerName);
91
92@@ -124,6 +128,8 @@
93 void printerDriversLoaded(const QList<PrinterDriver> &drivers);
94 void printerDriversFailedToLoad(const QString &errorMessage);
95
96+ void jobLoaded(QSharedPointer<PrinterJob> oldJob,
97+ QSharedPointer<PrinterJob> newJob);
98 void printerLoaded(QSharedPointer<Printer> printers);
99 void deviceFound(const Device &device);
100 void deviceSearchFinished();
101
102=== modified file 'modules/Ubuntu/Components/Extras/Printers/backend/backend_cups.cpp'
103--- modules/Ubuntu/Components/Extras/Printers/backend/backend_cups.cpp 2017-03-09 14:34:05 +0000
104+++ modules/Ubuntu/Components/Extras/Printers/backend/backend_cups.cpp 2017-03-13 12:51:30 +0000
105@@ -16,6 +16,7 @@
106
107 #include "backend/backend_cups.h"
108 #include "cups/devicesearcher.h"
109+#include "cups/jobloader.h"
110 #include "cups/printerdriverloader.h"
111 #include "cups/printerloader.h"
112 #include "utils.h"
113@@ -455,12 +456,30 @@
114 map.insert("ColorModel", QVariant(""));
115 }
116
117+ if (__CUPS_ATTR_EXISTS(rawMap, "date-time-at-completed", QDateTime)) {
118+ map.insert("CompletedTime", rawMap.value("date-time-at-completed"));
119+ } else {
120+ map.insert("CompletedTime", QVariant(QDateTime()));
121+ }
122+
123+ if (__CUPS_ATTR_EXISTS(rawMap, "date-time-at-creation", QDateTime)) {
124+ map.insert("CreationTime", rawMap.value("date-time-at-creation"));
125+ } else {
126+ map.insert("CreationTime", QVariant(QDateTime()));
127+ }
128+
129 if (__CUPS_ATTR_EXISTS(rawMap, "Duplex", QString)) {
130 map.insert("Duplex", rawMap.value("Duplex"));
131 } else {
132 map.insert("Duplex", QVariant(""));
133 }
134
135+ if (__CUPS_ATTR_EXISTS(rawMap, "job-impressions-completed", int)) {
136+ map.insert("impressionsCompleted", rawMap.value("job-impressions-completed"));
137+ } else {
138+ map.insert("impressionsCompleted", QVariant(0));
139+ }
140+
141 if (__CUPS_ATTR_EXISTS(rawMap, "landscape", bool)) {
142 map.insert("landscape", rawMap.value("landscape"));
143 } else {
144@@ -480,6 +499,12 @@
145 map.insert("page-ranges", QVariant(QStringList()));
146 }
147
148+ if (__CUPS_ATTR_EXISTS(rawMap, "date-time-at-processing", QDateTime)) {
149+ map.insert("ProcessingTime", rawMap.value("date-time-at-processing"));
150+ } else {
151+ map.insert("ProcessingTime", QVariant(QDateTime()));
152+ }
153+
154 Q_FOREACH(QString qualityOption, m_knownQualityOptions) {
155 if (rawMap.contains(qualityOption)
156 && rawMap.value(qualityOption).canConvert<QString>()) {
157@@ -497,6 +522,18 @@
158 map.insert("OutputOrder", "Normal");
159 }
160
161+ if (__CUPS_ATTR_EXISTS(rawMap, "job-k-octets", int)) {
162+ map.insert("Size", rawMap.value("job-k-octets"));
163+ } else {
164+ map.insert("Size", QVariant(0));
165+ }
166+
167+ if (__CUPS_ATTR_EXISTS(rawMap, "job-originating-user-name", QString)) {
168+ map.insert("User", rawMap.value("job-originating-user-name"));
169+ } else {
170+ map.insert("User", QVariant(""));
171+ }
172+
173 // Generate a list of messages
174 // TODO: for now just using job-printer-state-message, are there others?
175 QStringList messages;
176@@ -516,32 +553,16 @@
177 auto jobs = getCupsJobs();
178 QList<QSharedPointer<PrinterJob>> list;
179
180+ // TODO: once printerGetJob() only gets a single job
181+ // use that to build PrinterJob
182 Q_FOREACH(auto job, jobs) {
183+ // Note: extended attributes are not loaded here
184+ // they are loaded in JobLoader
185 auto newJob = QSharedPointer<PrinterJob>(
186 new PrinterJob(QString::fromUtf8(job->dest), this, job->id)
187 );
188-
189- // Extract the times
190- QDateTime completedTime;
191- completedTime.setTimeZone(QTimeZone::systemTimeZone());
192- completedTime.setTime_t(job->completed_time);
193-
194- QDateTime creationTime;
195- creationTime.setTimeZone(QTimeZone::systemTimeZone());
196- creationTime.setTime_t(job->creation_time);
197-
198- QDateTime processingTime;
199- processingTime.setTimeZone(QTimeZone::systemTimeZone());
200- processingTime.setTime_t(job->processing_time);
201-
202- // Load the information from the cups struct
203- newJob->setCompletedTime(completedTime);
204- newJob->setCreationTime(creationTime);
205- newJob->setProcessingTime(processingTime);
206- newJob->setSize(job->size);
207 newJob->setState(static_cast<PrinterEnum::JobState>(job->state));
208 newJob->setTitle(QString::fromLocal8Bit(job->title));
209- newJob->setUser(QString::fromLocal8Bit(job->user));
210
211 list.append(newJob);
212 }
213@@ -551,6 +572,38 @@
214 return list;
215 }
216
217+QSharedPointer<PrinterJob> PrinterCupsBackend::printerGetJob(
218+ const QString &printerName, const int jobId)
219+{
220+ // FIXME: this gets all the jobs for the printer
221+ // can we get a single one?
222+ // instead can we ask via IPP?
223+ auto jobs = getCupsJobs(printerName);
224+ cups_job_t *cupsJob = Q_NULLPTR;
225+ QSharedPointer<PrinterJob> job(Q_NULLPTR);
226+
227+ for (int i=0; i < jobs.size(); i++) {
228+ if (jobs.at(i)->id == jobId) {
229+ cupsJob = jobs.at(i);
230+ break;
231+ }
232+ }
233+
234+ if (cupsJob) {
235+ job = QSharedPointer<PrinterJob>(
236+ new PrinterJob(QString::fromUtf8(cupsJob->dest), this, cupsJob->id)
237+ );
238+
239+ job->setState(static_cast<PrinterEnum::JobState>(cupsJob->state));
240+ job->setTitle(QString::fromLocal8Bit(cupsJob->title));
241+ }
242+
243+ if (!jobs.size())
244+ cupsFreeJobs(jobs.size(), jobs.first());
245+
246+ return job;
247+}
248+
249 QString PrinterCupsBackend::printerName() const
250 {
251 return m_printerName;
252@@ -662,6 +715,34 @@
253 return QPrinterInfo::defaultPrinterName();
254 }
255
256+void PrinterCupsBackend::requestJobExtendedAttributes(
257+ QSharedPointer<Printer> printer, QSharedPointer<PrinterJob> job)
258+{
259+ QPair<QString, int> pair(printer->name(), job->jobId());
260+
261+ if (m_activeJobRequests.contains(pair)) {
262+ return;
263+ }
264+
265+ auto thread = new QThread;
266+ auto loader = new JobLoader(printer, job, this);
267+ loader->moveToThread(thread);
268+ connect(thread, SIGNAL(started()), loader, SLOT(load()));
269+ connect(loader, SIGNAL(finished()), thread, SLOT(quit()));
270+ connect(loader, SIGNAL(finished()), loader, SLOT(deleteLater()));
271+ connect(loader, SIGNAL(loaded(QSharedPointer<PrinterJob>, QSharedPointer<PrinterJob>)),
272+ this, SIGNAL(jobLoaded(QSharedPointer<PrinterJob>, QSharedPointer<PrinterJob>)));
273+ connect(loader, SIGNAL(printerLoaded(QSharedPointer<Printer>)),
274+ this, SIGNAL(printerLoaded(QSharedPointer<Printer>)));
275+ connect(loader, SIGNAL(loaded(QSharedPointer<PrinterJob>, QSharedPointer<PrinterJob>)),
276+ this, SLOT(onJobLoaded(QSharedPointer<PrinterJob>, QSharedPointer<PrinterJob>)));
277+ connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
278+
279+ m_activeJobRequests << pair;
280+
281+ thread->start();
282+}
283+
284 void PrinterCupsBackend::requestPrinter(const QString &printerName)
285 {
286 if (m_activeRequests.contains(printerName)) {
287@@ -679,9 +760,10 @@
288 connect(loader, SIGNAL(loaded(QSharedPointer<Printer>)),
289 this, SLOT(onPrinterLoaded(QSharedPointer<Printer>)));
290 connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
291- thread->start();
292
293 m_activeRequests << printerName;
294+
295+ thread->start();
296 }
297
298 void PrinterCupsBackend::requestPrinterDrivers()
299@@ -698,6 +780,7 @@
300 connect(loader, SIGNAL(loaded(const QList<PrinterDriver>&)),
301 this, SIGNAL(printerDriversLoaded(const QList<PrinterDriver>&)));
302 connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
303+
304 thread->start();
305 }
306
307@@ -788,6 +871,15 @@
308 return m_extendedAttributeNames.contains(attributeName);
309 }
310
311+void PrinterCupsBackend::onJobLoaded(QSharedPointer<PrinterJob> oldJob,
312+ QSharedPointer<PrinterJob> newJob)
313+{
314+ Q_UNUSED(newJob);
315+
316+ QPair<QString, int> pair(oldJob->printerName(), oldJob->jobId());
317+ m_activeJobRequests.remove(pair);
318+}
319+
320 void PrinterCupsBackend::onPrinterLoaded(QSharedPointer<Printer> printer)
321 {
322 m_activeRequests.remove(printer->name());
323
324=== modified file 'modules/Ubuntu/Components/Extras/Printers/backend/backend_cups.h'
325--- modules/Ubuntu/Components/Extras/Printers/backend/backend_cups.h 2017-03-08 11:30:48 +0000
326+++ modules/Ubuntu/Components/Extras/Printers/backend/backend_cups.h 2017-03-13 12:51:30 +0000
327@@ -79,6 +79,8 @@
328 const QString &title,
329 const cups_dest_t *dest) override;
330 virtual QList<QSharedPointer<PrinterJob>> printerGetJobs() override;
331+ virtual QSharedPointer<PrinterJob> printerGetJob(
332+ const QString &printerName, const int jobId) override;
333
334 virtual QString printerName() const override;
335 virtual QString description() const override;
336@@ -101,6 +103,10 @@
337 virtual QStringList availablePrinterNames() override;
338 virtual QSharedPointer<Printer> getPrinter(const QString &printerName) override;
339 virtual QString defaultPrinterName() override;
340+
341+ virtual void requestJobExtendedAttributes(
342+ QSharedPointer<Printer> printer,
343+ QSharedPointer<PrinterJob> job) override;
344 virtual void requestPrinterDrivers() override;
345 virtual void requestPrinter(const QString &printerName) override;
346 virtual QMap<QString, QVariant> printerGetJobAttributes(
347@@ -141,8 +147,11 @@
348 mutable QMap<QString, cups_dest_t*> m_dests; // Printer name, dest.
349 mutable QMap<QString, ppd_file_t*> m_ppds; // Printer name, ppd.
350 QSet<QString> m_activeRequests;
351+ QSet<QPair<QString, int>> m_activeJobRequests;
352
353 private Q_SLOTS:
354+ void onJobLoaded(QSharedPointer<PrinterJob> oldJob,
355+ QSharedPointer<PrinterJob> newJob);
356 void onPrinterLoaded(QSharedPointer<Printer> printer);
357 };
358
359
360=== added file 'modules/Ubuntu/Components/Extras/Printers/cups/jobloader.cpp'
361--- modules/Ubuntu/Components/Extras/Printers/cups/jobloader.cpp 1970-01-01 00:00:00 +0000
362+++ modules/Ubuntu/Components/Extras/Printers/cups/jobloader.cpp 2017-03-13 12:51:30 +0000
363@@ -0,0 +1,90 @@
364+/*
365+ * Copyright (C) 2017 Canonical, Ltd.
366+ *
367+ * This program is free software; you can redistribute it and/or modify
368+ * it under the terms of the GNU Lesser General Public License as published by
369+ * the Free Software Foundation; version 3.
370+ *
371+ * This program is distributed in the hope that it will be useful,
372+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
373+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
374+ * GNU Lesser General Public License for more details.
375+ *
376+ * You should have received a copy of the GNU Lesser General Public License
377+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
378+ */
379+
380+#include "jobloader.h"
381+
382+#include "backend/backend_cups.h"
383+
384+#include "cups/ippclient.h"
385+#include "cupsdnotifier.h" // Note: this file was generated.
386+
387+#include "printers/printers.h"
388+
389+#include <QDBusConnection>
390+#include <QPrinterInfo>
391+
392+class PrinterCupsBackend;
393+JobLoader::JobLoader(QSharedPointer<Printer> printer,
394+ QSharedPointer<PrinterJob> printerJob,
395+ PrinterBackend *backend,
396+ QObject *parent)
397+ : QObject(parent)
398+ , m_backend(backend)
399+ , m_job(printerJob)
400+ , m_printer(printer)
401+{
402+}
403+
404+JobLoader::~JobLoader()
405+{
406+}
407+
408+void JobLoader::load()
409+{
410+ QSharedPointer<Printer> printer;
411+ PrinterBackend *backend;
412+
413+ // FIXME: always build a new Printer and IppClient as it isn't thread safe
414+ if (true || m_printer->type() == PrinterEnum::PrinterType::ProxyType) {
415+ IppClient *client = new IppClient();
416+ OrgCupsCupsdNotifierInterface* notifier = new OrgCupsCupsdNotifierInterface(
417+ "", CUPSD_NOTIFIER_DBUS_PATH, QDBusConnection::systemBus());
418+ QPrinterInfo info = QPrinterInfo::printerInfo(m_printer->name());
419+
420+ backend = new PrinterCupsBackend(client, info, notifier);
421+ printer = QSharedPointer<Printer>(new Printer(backend));
422+ } else {
423+ backend = m_backend;
424+ printer = m_printer;
425+ }
426+
427+ // Construct a job
428+ QSharedPointer<PrinterJob> job = QSharedPointer<PrinterJob>(
429+ new PrinterJob(m_printer->name(), backend, m_job->jobId())
430+ );
431+
432+ // Copy things that we don't set in extended attributes
433+ job->setImpressionsCompleted(m_job->impressionsCompleted());
434+ job->setState(m_job->state());
435+ job->setTitle(m_job->title());
436+
437+ // Set the printer for this thread
438+ job->setPrinter(printer);
439+
440+ // Load the extended attributes of the job
441+ job->loadDefaults();
442+
443+ Q_EMIT loaded(m_job, job);
444+
445+ // If the given Printer was not loaded expose our loaded one
446+ // FIXME: for now skip this, need to check if having different
447+ // notifier/ippClient/backend could cause issues?
448+ if (false && m_printer->type() == PrinterEnum::PrinterType::ProxyType) {
449+ Q_EMIT printerLoaded(printer);
450+ }
451+
452+ Q_EMIT finished();
453+}
454
455=== added file 'modules/Ubuntu/Components/Extras/Printers/cups/jobloader.h'
456--- modules/Ubuntu/Components/Extras/Printers/cups/jobloader.h 1970-01-01 00:00:00 +0000
457+++ modules/Ubuntu/Components/Extras/Printers/cups/jobloader.h 2017-03-13 12:51:30 +0000
458@@ -0,0 +1,50 @@
459+/*
460+ * Copyright (C) 2017 Canonical, Ltd.
461+ *
462+ * This program is free software; you can redistribute it and/or modify
463+ * it under the terms of the GNU Lesser General Public License as published by
464+ * the Free Software Foundation; version 3.
465+ *
466+ * This program is distributed in the hope that it will be useful,
467+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
468+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
469+ * GNU Lesser General Public License for more details.
470+ *
471+ * You should have received a copy of the GNU Lesser General Public License
472+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
473+ */
474+
475+#ifndef USC_PRINTERS_CUPS_JOBLOADER_H
476+#define USC_PRINTERS_CUPS_JOBLOADER_H
477+
478+#include "printer/printer.h"
479+#include "printer/printerjob.h"
480+
481+#include <QList>
482+#include <QObject>
483+#include <QSharedPointer>
484+
485+class JobLoader : public QObject
486+{
487+ Q_OBJECT
488+ PrinterBackend *m_backend;
489+ QSharedPointer<PrinterJob> m_job;
490+ QSharedPointer<Printer> m_printer;
491+public:
492+ explicit JobLoader(QSharedPointer<Printer> printer,
493+ QSharedPointer<PrinterJob> printerJob,
494+ PrinterBackend *backend,
495+ QObject *parent = Q_NULLPTR);
496+ ~JobLoader();
497+
498+public Q_SLOTS:
499+ void load();
500+
501+Q_SIGNALS:
502+ void finished();
503+ void loaded(QSharedPointer<PrinterJob> oldJob,
504+ QSharedPointer<PrinterJob> newJob);
505+ void printerLoaded(QSharedPointer<Printer> printer);
506+};
507+
508+#endif // USC_PRINTERS_CUPS_JOBLOADER_H
509
510=== modified file 'modules/Ubuntu/Components/Extras/Printers/models/jobmodel.cpp'
511--- modules/Ubuntu/Components/Extras/Printers/models/jobmodel.cpp 2017-03-06 13:19:08 +0000
512+++ modules/Ubuntu/Components/Extras/Printers/models/jobmodel.cpp 2017-03-13 12:51:30 +0000
513@@ -31,113 +31,184 @@
514 : QAbstractListModel(parent)
515 , m_backend(backend)
516 {
517- update();
518-
519 QObject::connect(m_backend, &PrinterBackend::jobCreated,
520- this, &JobModel::jobSignalCatchAll);
521+ this, &JobModel::jobCreated);
522 QObject::connect(m_backend, &PrinterBackend::jobState,
523- this, &JobModel::jobSignalCatchAll);
524+ this, &JobModel::jobState);
525 QObject::connect(m_backend, &PrinterBackend::jobCompleted,
526- this, &JobModel::jobSignalCatchAll);
527+ this, &JobModel::jobCompleted);
528+
529+ connect(m_backend, SIGNAL(jobLoaded(QSharedPointer<PrinterJob>, QSharedPointer<PrinterJob>)),
530+ this, SLOT(updateJob(QSharedPointer<PrinterJob>, QSharedPointer<PrinterJob>)));
531+
532+ // Impressions completed happens via printer state changed
533+ QObject::connect(m_backend, &PrinterBackend::printerStateChanged,
534+ &m_signalHandler, &SignalRateLimiter::onPrinterStateChanged);
535+
536+ QObject::connect(&m_signalHandler, SIGNAL(printerModified(const QString&)),
537+ this, SLOT(jobSignalPrinterModified(const QString&)));
538+
539+ // Add already existing jobs
540+ // FIXME: even this should probably be in a background thread?
541+ // so that it doesn't block startup?
542+ Q_FOREACH(auto job, m_backend->printerGetJobs()) {
543+ addJob(job);
544+ }
545 }
546
547 JobModel::~JobModel()
548 {
549 }
550
551-void JobModel::jobSignalCatchAll(
552- const QString &text, const QString &printer_uri,
553- const QString &printer_name, uint printer_state,
554- const QString &printer_state_reasons, bool printer_is_accepting_jobs,
555- uint job_id, uint job_state, const QString &job_state_reasons,
556- const QString &job_name, uint job_impressions_completed)
557-{
558- Q_UNUSED(text);
559- Q_UNUSED(printer_uri);
560- Q_UNUSED(printer_name);
561- Q_UNUSED(printer_state);
562- Q_UNUSED(printer_state_reasons);
563- Q_UNUSED(printer_is_accepting_jobs);
564- Q_UNUSED(job_id);
565+void JobModel::jobCreated(
566+ const QString &text, const QString &printer_uri,
567+ const QString &printer_name, uint printer_state,
568+ const QString &printer_state_reasons, bool printer_is_accepting_jobs,
569+ uint job_id, uint job_state, const QString &job_state_reasons,
570+ const QString &job_name, uint job_impressions_completed)
571+{
572+ Q_UNUSED(text); // "Job Created"
573+ Q_UNUSED(printer_uri);
574+ Q_UNUSED(printer_state);
575+ Q_UNUSED(printer_state_reasons);
576+ Q_UNUSED(printer_is_accepting_jobs);
577+ Q_UNUSED(job_state_reasons);
578+
579+ QSharedPointer<PrinterJob> job = QSharedPointer<PrinterJob>(
580+ new PrinterJob(printer_name, m_backend, job_id)
581+ );
582+ job->setImpressionsCompleted(job_impressions_completed);
583+ job->setState(static_cast<PrinterEnum::JobState>(job_state));
584+ job->setTitle(job_name);
585+
586+ // Printers listens to rowInserted and spawns a JobLoader to set the
587+ // printer of the job, which triggers the extended attributes to be loaded
588+ // once complete this triggers JobModel::updateJob
589+ addJob(job);
590+}
591+
592+void JobModel::jobState(
593+ const QString &text, const QString &printer_uri,
594+ const QString &printer_name, uint printer_state,
595+ const QString &printer_state_reasons, bool printer_is_accepting_jobs,
596+ uint job_id, uint job_state, const QString &job_state_reasons,
597+ const QString &job_name, uint job_impressions_completed)
598+{
599+ Q_UNUSED(text);
600+ Q_UNUSED(printer_uri);
601+ Q_UNUSED(printer_state);
602+ Q_UNUSED(printer_state_reasons);
603+ Q_UNUSED(printer_is_accepting_jobs);
604+ Q_UNUSED(job_state_reasons);
605+ Q_UNUSED(job_name);
606+
607+ QSharedPointer<PrinterJob> job = getJob(printer_name, job_id);
608+
609+ if (job) {
610+ job->setImpressionsCompleted(job_impressions_completed);
611+ job->setState(static_cast<PrinterEnum::JobState>(job_state));
612+
613+ updateJob(job);
614+ } else {
615+ qWarning() << "JobModel::jobState for unknown job: " << job_name << " ("
616+ << job_id << ") for " << printer_name;
617+ }
618+}
619+
620+void JobModel::jobCompleted(
621+ const QString &text, const QString &printer_uri,
622+ const QString &printer_name, uint printer_state,
623+ const QString &printer_state_reasons, bool printer_is_accepting_jobs,
624+ uint job_id, uint job_state, const QString &job_state_reasons,
625+ const QString &job_name, uint job_impressions_completed)
626+{
627+ Q_UNUSED(text);
628+ Q_UNUSED(printer_uri);
629+ Q_UNUSED(printer_state);
630+ Q_UNUSED(printer_state_reasons);
631+ Q_UNUSED(printer_is_accepting_jobs);
632 Q_UNUSED(job_state);
633 Q_UNUSED(job_state_reasons);
634 Q_UNUSED(job_name);
635-
636- auto job = getJobById(job_id);
637- if (job)
638- job->setImpressionsCompleted(job_impressions_completed);
639-
640- update();
641-}
642-
643-void JobModel::update()
644-{
645- // Store the old count and get the new printers
646- int oldCount = m_jobs.size();
647- QList<QSharedPointer<PrinterJob>> newJobs = m_backend->printerGetJobs();
648-
649- // Go through the old model
650- for (int i=0; i < m_jobs.count(); i++) {
651- // Determine if the old printer exists in the new model
652- bool exists = false;
653-
654- Q_FOREACH(QSharedPointer<PrinterJob> p, newJobs) {
655- if (p->jobId() == m_jobs.at(i)->jobId()) {
656- exists = true;
657-
658- // Ensure the other properties of the job are up to date
659- if (!m_jobs.at(i)->deepCompare(p)) {
660- m_jobs.at(i)->updateFrom(p);
661-
662- Q_EMIT dataChanged(index(i), index(i));
663- }
664-
665- break;
666- }
667- }
668-
669- // If it doesn't exist then remove it from the old model
670- if (!exists) {
671- beginRemoveRows(QModelIndex(), i, i);
672- QSharedPointer<PrinterJob> p = m_jobs.takeAt(i);
673- endRemoveRows();
674-
675- i--; // as we have removed an item decrement
676- }
677- }
678-
679- // Go through the new model
680- for (int i=0; i < newJobs.count(); i++) {
681- // Determine if the new printer exists in the old model
682- bool exists = false;
683- int j;
684-
685- for (j=0; j < m_jobs.count(); j++) {
686- if (m_jobs.at(j)->jobId() == newJobs.at(i)->jobId()) {
687- exists = true;
688- break;
689- }
690- }
691-
692- if (exists) {
693- if (j == i) { // New printer exists and in correct position
694- continue;
695- } else {
696- // New printer does exist but needs to be moved in old model
697- beginMoveRows(QModelIndex(), j, 1, QModelIndex(), i);
698- m_jobs.move(j, i);
699- endMoveRows();
700- }
701- } else {
702- // New printer does not exist insert into model
703- beginInsertRows(QModelIndex(), i, i);
704- m_jobs.insert(i, newJobs.at(i));
705- endInsertRows();
706- }
707- }
708-
709- if (oldCount != m_jobs.size()) {
710- Q_EMIT countChanged();
711+ Q_UNUSED(job_impressions_completed);
712+
713+ auto job = getJob(printer_name, job_id);
714+ if (job) {
715+ removeJob(job);
716+ } else {
717+ qWarning() << "JobModel::jobCompleted for unknown job: " << job_name << " ("
718+ << job_id << ") for " << printer_name;
719+ }
720+}
721+
722+void JobModel::jobSignalPrinterModified(const QString &printerName)
723+{
724+ qDebug() << Q_FUNC_INFO << printerName;
725+
726+ // Find the active job and force a refresh
727+ Q_FOREACH(auto job, m_jobs) {
728+ if (job->printerName() == printerName
729+ && job->state() == PrinterEnum::JobState::Processing) {
730+ qDebug() << Q_FUNC_INFO << "Forcing refresh" << job->jobId();
731+ Q_EMIT forceJobRefresh(printerName, job->jobId());
732+ }
733+ }
734+}
735+
736+void JobModel::addJob(QSharedPointer<PrinterJob> job)
737+{
738+ int i = m_jobs.size();
739+ qDebug() << Q_FUNC_INFO << job->jobId() << i;
740+
741+ beginInsertRows(QModelIndex(), i, i);
742+ m_jobs.append(job);
743+ endInsertRows();
744+
745+ Q_EMIT countChanged();
746+}
747+
748+void JobModel::removeJob(QSharedPointer<PrinterJob> job)
749+{
750+ qDebug() << Q_FUNC_INFO << job->jobId();
751+ int i = m_jobs.indexOf(job);
752+ beginRemoveRows(QModelIndex(), i, i);
753+ m_jobs.removeAt(i);
754+ endRemoveRows();
755+
756+ Q_EMIT countChanged();
757+}
758+
759+// This is used by JobModel::jobState as it has modified an existing job
760+void JobModel::updateJob(QSharedPointer<PrinterJob> job)
761+{
762+ qDebug() << Q_FUNC_INFO << job->jobId();
763+
764+ int i = m_jobs.indexOf(job);
765+ QModelIndex idx = index(i);
766+ Q_EMIT dataChanged(idx, idx);
767+}
768+
769+// This is used by JobLoader as it creates a new job to prevent threading issues
770+void JobModel::updateJob(QSharedPointer<PrinterJob> oldJob,
771+ QSharedPointer<PrinterJob> newJob)
772+{
773+ qDebug() << Q_FUNC_INFO << oldJob->jobId() << newJob->jobId();
774+
775+ int i = m_jobs.indexOf(oldJob);
776+ QModelIndex idx = index(i);
777+
778+ if (i > -1) {
779+ // Copy the preloaded Printer (?) so that JobModel always shows correct
780+ // attributes, eg colorModel needs Printer::supportedColorModels
781+ //
782+ // FIXME: does it break anything as the Printer is not from PrinterModel
783+ // Maybe all comparisions should just use printerName() ?
784+ oldJob->setPrinter(newJob->printer());
785+
786+ oldJob->updateFrom(newJob);
787+ Q_EMIT dataChanged(idx, idx);
788+ } else {
789+ qWarning() << "Tried to updateJob which doesn't exist:" << newJob->printerName() << newJob->jobId();
790 }
791 }
792
793@@ -307,10 +378,10 @@
794 return result;
795 }
796
797-QSharedPointer<PrinterJob> JobModel::getJobById(const int &id)
798+QSharedPointer<PrinterJob> JobModel::getJob(const QString &printerName, const int &id)
799 {
800 Q_FOREACH(auto job, m_jobs) {
801- if (job->jobId() == id) {
802+ if (job->printerName() == printerName && job->jobId() == id) {
803 return job;
804 }
805 }
806
807=== modified file 'modules/Ubuntu/Components/Extras/Printers/models/jobmodel.h'
808--- modules/Ubuntu/Components/Extras/Printers/models/jobmodel.h 2017-03-06 13:19:08 +0000
809+++ modules/Ubuntu/Components/Extras/Printers/models/jobmodel.h 2017-03-13 12:51:30 +0000
810@@ -20,6 +20,7 @@
811 #include "printers_global.h"
812 #include "backend/backend.h"
813 #include "printer/printerjob.h"
814+#include "printer/signalratelimiter.h"
815
816 #include <QAbstractListModel>
817 #include <QByteArray>
818@@ -30,6 +31,8 @@
819 #include <QTimer>
820 #include <QVariant>
821
822+class PrinterBackend;
823+class PrinterJob;
824 class PRINTERS_DECL_EXPORT JobModel : public QAbstractListModel
825 {
826 Q_OBJECT
827@@ -76,23 +79,46 @@
828 int count() const;
829
830 Q_INVOKABLE QVariantMap get(const int row) const;
831- QSharedPointer<PrinterJob> getJobById(const int &id);
832+ QSharedPointer<PrinterJob> getJob(const QString &printerName, const int &id);
833+public Q_SLOTS:
834+ void updateJob(QSharedPointer<PrinterJob> oldJob,
835+ QSharedPointer<PrinterJob> newJob);
836 private:
837+ void addJob(QSharedPointer<PrinterJob> job);
838+ void removeJob(QSharedPointer<PrinterJob> job);
839+ void updateJob(QSharedPointer<PrinterJob> Job);
840+
841 PrinterBackend *m_backend;
842
843 QList<QSharedPointer<PrinterJob>> m_jobs;
844+ SignalRateLimiter m_signalHandler;
845 private Q_SLOTS:
846- void update();
847- void jobSignalCatchAll(const QString &text, const QString &printer_uri,
848- const QString &printer_name, uint printer_state,
849- const QString &printer_state_reasons,
850- bool printer_is_accepting_jobs, uint job_id,
851- uint job_state, const QString &job_state_reasons,
852- const QString &job_name,
853- uint job_impressions_completed);
854+ void jobCreated(const QString &text, const QString &printer_uri,
855+ const QString &printer_name, uint printer_state,
856+ const QString &printer_state_reasons,
857+ bool printer_is_accepting_jobs, uint job_id,
858+ uint job_state, const QString &job_state_reasons,
859+ const QString &job_name,
860+ uint job_impressions_completed);
861+ void jobState(const QString &text, const QString &printer_uri,
862+ const QString &printer_name, uint printer_state,
863+ const QString &printer_state_reasons,
864+ bool printer_is_accepting_jobs, uint job_id,
865+ uint job_state, const QString &job_state_reasons,
866+ const QString &job_name,
867+ uint job_impressions_completed);
868+ void jobCompleted(const QString &text, const QString &printer_uri,
869+ const QString &printer_name, uint printer_state,
870+ const QString &printer_state_reasons,
871+ bool printer_is_accepting_jobs, uint job_id,
872+ uint job_state, const QString &job_state_reasons,
873+ const QString &job_name,
874+ uint job_impressions_completed);
875+ void jobSignalPrinterModified(const QString &printerName);
876
877 Q_SIGNALS:
878 void countChanged();
879+ void forceJobRefresh(const QString &printerName, const int jobId);
880 };
881
882 class PRINTERS_DECL_EXPORT JobFilter : public QSortFilterProxyModel
883
884=== modified file 'modules/Ubuntu/Components/Extras/Printers/models/printermodel.cpp'
885--- modules/Ubuntu/Components/Extras/Printers/models/printermodel.cpp 2017-03-06 15:29:04 +0000
886+++ modules/Ubuntu/Components/Extras/Printers/models/printermodel.cpp 2017-03-13 12:51:30 +0000
887@@ -32,9 +32,9 @@
888 QObject::connect(m_backend, &PrinterBackend::printerAdded,
889 this, &PrinterModel::printerAdded);
890 QObject::connect(m_backend, &PrinterBackend::printerModified,
891- &m_signalHandler, &PrinterSignalHandler::onPrinterModified);
892+ &m_signalHandler, &SignalRateLimiter::onPrinterModified);
893 QObject::connect(m_backend, &PrinterBackend::printerStateChanged,
894- &m_signalHandler, &PrinterSignalHandler::onPrinterModified);
895+ &m_signalHandler, &SignalRateLimiter::onPrinterModified);
896 QObject::connect(m_backend, &PrinterBackend::printerDeleted,
897 this, &PrinterModel::printerDeleted);
898
899@@ -127,22 +127,6 @@
900 return QSharedPointer<Printer>(Q_NULLPTR);
901 }
902
903-void PrinterModel::movePrinter(const int &from, const int &to)
904-{
905- int size = m_printers.size();
906- if (from < 0 || to < 0 || from >= size || to >= size) {
907- qWarning() << Q_FUNC_INFO << "Illegal move operation from"
908- << from << "to" << to << ". Size was" << size;
909- return;
910- }
911- if (!beginMoveRows(QModelIndex(), from, from, QModelIndex(), to)) {
912- qWarning() << Q_FUNC_INFO << "failed to move rows.";
913- return;
914- }
915- m_printers.move(from, to);
916- endMoveRows();
917-}
918-
919 void PrinterModel::removePrinter(QSharedPointer<Printer> printer, const CountChangeSignal &notify)
920 {
921 int idx = m_printers.indexOf(printer);
922
923=== modified file 'modules/Ubuntu/Components/Extras/Printers/models/printermodel.h'
924--- modules/Ubuntu/Components/Extras/Printers/models/printermodel.h 2017-03-06 15:29:04 +0000
925+++ modules/Ubuntu/Components/Extras/Printers/models/printermodel.h 2017-03-13 12:51:30 +0000
926@@ -21,7 +21,7 @@
927
928 #include "models/jobmodel.h"
929 #include "printer/printer.h"
930-#include "printer/printersignalhandler.h"
931+#include "printer/signalratelimiter.h"
932
933 #include <QAbstractListModel>
934 #include <QByteArray>
935@@ -98,13 +98,12 @@
936 const CountChangeSignal &notify = CountChangeSignal::Defer);
937 void removePrinter(QSharedPointer<Printer> printer,
938 const CountChangeSignal &notify = CountChangeSignal::Defer);
939- void movePrinter(const int &from, const int &to);
940 void updatePrinter(QSharedPointer<Printer> old,
941 QSharedPointer<Printer> newPrinter);
942 PrinterBackend *m_backend;
943
944 QList<QSharedPointer<Printer>> m_printers;
945- PrinterSignalHandler m_signalHandler;
946+ SignalRateLimiter m_signalHandler;
947
948 private Q_SLOTS:
949 void printerLoaded(QSharedPointer<Printer> printer);
950
951=== modified file 'modules/Ubuntu/Components/Extras/Printers/plugin.cpp'
952--- modules/Ubuntu/Components/Extras/Printers/plugin.cpp 2017-03-08 11:29:01 +0000
953+++ modules/Ubuntu/Components/Extras/Printers/plugin.cpp 2017-03-13 12:51:30 +0000
954@@ -54,6 +54,7 @@
955
956 qmlRegisterUncreatableType<PrinterEnum>(uri, 0, 1, "PrinterEnum", "Is an enum");
957 qRegisterMetaType<QList<PrinterDriver>>("QList<PrinterDriver>");
958+ qRegisterMetaType<QSharedPointer<PrinterJob>>("QSharedPointer<PrinterJob>");
959 qRegisterMetaType<QList<QSharedPointer<Printer>>>("QList<QSharedPointer<Printer>>");
960 qRegisterMetaType<Device>("Device");
961 }
962
963=== modified file 'modules/Ubuntu/Components/Extras/Printers/printer/printer.cpp'
964--- modules/Ubuntu/Components/Extras/Printers/printer/printer.cpp 2017-03-09 15:50:41 +0000
965+++ modules/Ubuntu/Components/Extras/Printers/printer/printer.cpp 2017-03-13 12:51:30 +0000
966@@ -379,10 +379,20 @@
967 void Printer::updateFrom(QSharedPointer<Printer> other)
968 {
969 PrinterBackend *tmp = m_backend;
970+
971+ // Copy values from other printer which has been loaded in another thread
972+ // Note: do not use loadAttributes otherwise can cause UI block
973+ m_acceptJobs = other->m_acceptJobs;
974 m_backend = other->m_backend;
975+ m_defaultColorModel = other->m_defaultColorModel;
976+ m_defaultPrintQuality = other->m_defaultPrintQuality;
977+ m_deviceUri = other->m_deviceUri;
978+ m_shared = other->m_shared;
979+ m_stateMessage = other->m_stateMessage;
980+ m_supportedColorModels = other->m_supportedColorModels;
981+ m_supportedPrintQualities = other->m_supportedPrintQualities;
982+
983 other->m_backend = tmp;
984-
985- loadAttributes();
986 }
987
988 void Printer::onPrinterStateChanged(
989
990=== modified file 'modules/Ubuntu/Components/Extras/Printers/printer/printerjob.cpp'
991--- modules/Ubuntu/Components/Extras/Printers/printer/printerjob.cpp 2017-02-24 17:47:22 +0000
992+++ modules/Ubuntu/Components/Extras/Printers/printer/printerjob.cpp 2017-03-13 12:51:30 +0000
993@@ -150,8 +150,6 @@
994 return;
995 }
996
997- qWarning() << Q_FUNC_INFO << jobId();
998-
999 if (jobId() > 0) {
1000 // Load the extra attributes for the job
1001 // NOTE: we don't need to type check them as they have been filtered for us
1002@@ -165,11 +163,15 @@
1003 // No colorModel will result in PrinterJob using defaultColorModel
1004 QString colorModel = attributes.value("ColorModel").toString();
1005 for (int i=0; i < m_printer->supportedColorModels().length(); i++) {
1006- if (m_printer->supportedColorModels().at(i).originalOption == colorModel) {
1007+ if (m_printer->supportedColorModels().at(i).name == colorModel) {
1008 setColorModel(i);
1009 }
1010 }
1011
1012+ // TODO: do we need to set timezone?
1013+ setCompletedTime(attributes.value("CompletedTime").toDateTime());
1014+ setCreationTime(attributes.value("CreationTime").toDateTime());
1015+
1016 // No duplexMode will result in PrinterJob using defaultDuplexMode
1017 QString duplex = attributes.value("Duplex").toString();
1018 PrinterEnum::DuplexMode duplexMode = Utils::ppdChoiceToDuplexMode(duplex);
1019@@ -179,6 +181,7 @@
1020 }
1021 }
1022
1023+ setImpressionsCompleted(attributes.value("impressionsCompleted").toInt());
1024 setLandscape(attributes.value("landscape").toBool());
1025 setMessages(attributes.value("messages").toStringList());
1026
1027@@ -192,6 +195,11 @@
1028 setPrintRange(pageRanges.join(QLocale::system().groupSeparator()));
1029 }
1030
1031+ // TODO: do we need timezone?
1032+// processingTime.setTimeZone(QTimeZone::systemTimeZone());
1033+// processingTime.setTime_t(cupsJob->processing_time);
1034+ setProcessingTime(attributes.value("ProcessingTime").toDateTime());
1035+
1036 // No quality will result in PrinterJob using defaultPrintQuality
1037 QString quality = attributes.value("quality").toString();
1038 for (int i=0; i < m_printer->supportedPrintQualities().length(); i++) {
1039@@ -201,11 +209,13 @@
1040 }
1041
1042 setReverse(attributes.value("OutputOrder").toString() == "Reverse");
1043+ setSize(attributes.value("Size").toInt());
1044+ setUser(attributes.value("User").toString());
1045+ } else {
1046+ setColorModel(m_printer->supportedColorModels().indexOf(m_printer->defaultColorModel()));
1047+ setDuplexMode(m_printer->supportedDuplexModes().indexOf(m_printer->defaultDuplexMode()));
1048+ setQuality(m_printer->supportedPrintQualities().indexOf(m_printer->defaultPrintQuality()));
1049 }
1050-
1051- setColorModel(m_printer->supportedColorModels().indexOf(m_printer->defaultColorModel()));
1052- setDuplexMode(m_printer->supportedDuplexModes().indexOf(m_printer->defaultDuplexMode()));
1053- setQuality(m_printer->supportedPrintQualities().indexOf(m_printer->defaultPrintQuality()));
1054 }
1055
1056 QStringList PrinterJob::messages() const
1057@@ -382,7 +392,6 @@
1058
1059 Q_EMIT printerChanged();
1060 }
1061- loadDefaults();
1062 }
1063
1064 void PrinterJob::setPrintRange(const QString &printRange)
1065@@ -478,30 +487,44 @@
1066 // Return true if they are the same
1067 return collate() == other->collate()
1068 && colorModel() == other->colorModel()
1069+ && completedTime() == other->completedTime()
1070 && copies() == other->copies()
1071+ && creationTime() == other->creationTime()
1072 && duplexMode() == other->duplexMode()
1073+ && impressionsCompleted() == other->impressionsCompleted()
1074 && landscape() == other->landscape()
1075+ && messages() == other->messages()
1076 && printRange() == other->printRange()
1077 && printRangeMode() == other->printRangeMode()
1078+ && processingTime() == other->processingTime()
1079 && quality() == other->quality()
1080 && reverse() == other->reverse()
1081+ && size() == other->size()
1082 && state() == other->state()
1083- && title() == other->title();
1084+ && title() == other->title()
1085+ && user() == other->user();
1086 }
1087
1088 void PrinterJob::updateFrom(QSharedPointer<PrinterJob> other)
1089 {
1090 setCollate(other->collate());
1091 setColorModel(other->colorModel());
1092+ setCompletedTime(other->completedTime());
1093 setCopies(other->copies());
1094+ setCreationTime(other->creationTime());
1095 setDuplexMode(other->duplexMode());
1096+ setImpressionsCompleted(other->impressionsCompleted());
1097 setLandscape(other->landscape());
1098+ setMessages(other->messages());
1099 setPrintRange(other->printRange());
1100 setPrintRangeMode(other->printRangeMode());
1101+ setProcessingTime(other->processingTime());
1102 setQuality(other->quality());
1103 setReverse(other->reverse());
1104+ setSize(other->size());
1105 setState(other->state());
1106 setTitle(other->title());
1107+ setUser(other->user());
1108 }
1109
1110 QString PrinterJob::user() const
1111
1112=== modified file 'modules/Ubuntu/Components/Extras/Printers/printer/printerjob.h'
1113--- modules/Ubuntu/Components/Extras/Printers/printer/printerjob.h 2017-03-01 14:17:52 +0000
1114+++ modules/Ubuntu/Components/Extras/Printers/printer/printerjob.h 2017-03-13 12:51:30 +0000
1115@@ -95,6 +95,7 @@
1116 PrinterEnum::DuplexMode getDuplexMode() const;
1117 ColorModel getColorModel() const;
1118 PrintQuality getPrintQuality() const;
1119+ void loadDefaults();
1120 Q_INVOKABLE void printFile(const QUrl &url);
1121 void setCollate(const bool collate);
1122 void setColorModel(const int colorModel);
1123@@ -118,8 +119,6 @@
1124 void setUser(const QString &user);
1125
1126 void updateFrom(QSharedPointer<PrinterJob> other);
1127-private Q_SLOTS:
1128- void loadDefaults();
1129 Q_SIGNALS:
1130 void collateChanged();
1131 void colorModelChanged();
1132
1133=== renamed file 'modules/Ubuntu/Components/Extras/Printers/printer/printersignalhandler.cpp' => 'modules/Ubuntu/Components/Extras/Printers/printer/signalratelimiter.cpp'
1134--- modules/Ubuntu/Components/Extras/Printers/printer/printersignalhandler.cpp 2017-02-21 10:46:29 +0000
1135+++ modules/Ubuntu/Components/Extras/Printers/printer/signalratelimiter.cpp 2017-03-13 12:51:30 +0000
1136@@ -14,9 +14,9 @@
1137 * along with this program. If not, see <http://www.gnu.org/licenses/>.
1138 */
1139
1140-#include "printersignalhandler.h"
1141+#include "signalratelimiter.h"
1142
1143-PrinterSignalHandler::PrinterSignalHandler(int triggerEventDelay,
1144+SignalRateLimiter::SignalRateLimiter(int triggerEventDelay,
1145 QObject *parent)
1146 : QObject(parent)
1147 {
1148@@ -24,11 +24,11 @@
1149 connect(&m_timer, SIGNAL(timeout()), this, SLOT(process()));
1150 }
1151
1152-PrinterSignalHandler::~PrinterSignalHandler()
1153+SignalRateLimiter::~SignalRateLimiter()
1154 {
1155 }
1156
1157-void PrinterSignalHandler::process()
1158+void SignalRateLimiter::process()
1159 {
1160 Q_FOREACH(auto printer, m_unprocessed) {
1161 Q_EMIT printerModified(printer);
1162@@ -37,7 +37,7 @@
1163 m_timer.stop();
1164 }
1165
1166-void PrinterSignalHandler::onPrinterModified(
1167+void SignalRateLimiter::onPrinterModified(
1168 const QString &text, const QString &printerUri,
1169 const QString &printerName, uint printerState,
1170 const QString &printerStateReason, bool acceptingJobs)
1171@@ -49,11 +49,22 @@
1172 Q_UNUSED(printerStateReason);
1173 Q_UNUSED(acceptingJobs);
1174
1175+ // Track when the first item was added to the unprocessed queue
1176+ if (m_unprocessed.count() == 0) {
1177+ m_unprocessed_time = QDateTime::currentDateTime();
1178+ }
1179+
1180 m_unprocessed << printerName;
1181 m_timer.start();
1182+
1183+ // Ensure that process is fired if we have reached four times
1184+ // longer than the timer, this is due to many signals coming in rapidly
1185+ if (m_unprocessed_time.msecsTo(QDateTime::currentDateTime()) > m_timer.interval() * 4) {
1186+ process();
1187+ }
1188 }
1189
1190-void PrinterSignalHandler::onPrinterStateChanged(
1191+void SignalRateLimiter::onPrinterStateChanged(
1192 const QString &text, const QString &printerUri,
1193 const QString &printerName, uint printerState,
1194 const QString &printerStateReason, bool acceptingJobs)
1195@@ -64,6 +75,17 @@
1196 Q_UNUSED(printerStateReason);
1197 Q_UNUSED(acceptingJobs);
1198
1199+ // Track when the first item was added to the unprocessed queue
1200+ if (m_unprocessed.count() == 0) {
1201+ m_unprocessed_time = QDateTime::currentDateTime();
1202+ }
1203+
1204 m_unprocessed << printerName;
1205 m_timer.start();
1206+
1207+ // Ensure that process is fired if we have reached four times
1208+ // longer than the timer, this is due to many signals coming in rapidly
1209+ if (m_unprocessed_time.msecsTo(QDateTime::currentDateTime()) > m_timer.interval() * 4) {
1210+ process();
1211+ }
1212 }
1213
1214=== renamed file 'modules/Ubuntu/Components/Extras/Printers/printer/printersignalhandler.h' => 'modules/Ubuntu/Components/Extras/Printers/printer/signalratelimiter.h'
1215--- modules/Ubuntu/Components/Extras/Printers/printer/printersignalhandler.h 2017-02-21 10:46:29 +0000
1216+++ modules/Ubuntu/Components/Extras/Printers/printer/signalratelimiter.h 2017-03-13 12:51:30 +0000
1217@@ -14,24 +14,26 @@
1218 * along with this program. If not, see <http://www.gnu.org/licenses/>.
1219 */
1220
1221-#ifndef USC_PRINTER_SIGNAL_HANDLER_H
1222-#define USC_PRINTER_SIGNAL_HANDLER_H
1223+#ifndef USC_SIGNAL_RATE_LIMITER_H
1224+#define USC_SIGNAL_RATE_LIMITER_H
1225
1226 #include "printers_global.h"
1227
1228+#include <QDateTime>
1229 #include <QObject>
1230 #include <QSet>
1231 #include <QTimer>
1232
1233-class PRINTERS_DECL_EXPORT PrinterSignalHandler : public QObject
1234+class PRINTERS_DECL_EXPORT SignalRateLimiter : public QObject
1235 {
1236 Q_OBJECT
1237 QTimer m_timer;
1238 QSet<QString> m_unprocessed;
1239+ QDateTime m_unprocessed_time;
1240 public:
1241- explicit PrinterSignalHandler(int triggerEventDelay = 500,
1242+ explicit SignalRateLimiter(int triggerEventDelay = 500,
1243 QObject *parent = Q_NULLPTR);
1244- ~PrinterSignalHandler();
1245+ ~SignalRateLimiter();
1246
1247 public Q_SLOTS:
1248 void onPrinterModified(
1249@@ -52,4 +54,4 @@
1250 void printerModified(const QString &printerName);
1251 };
1252
1253-#endif // USC_PRINTER_SIGNAL_HANDLER_H
1254+#endif // USC_SIGNAL_RATE_LIMITERR_H
1255
1256=== modified file 'modules/Ubuntu/Components/Extras/Printers/printers/printers.cpp'
1257--- modules/Ubuntu/Components/Extras/Printers/printers/printers.cpp 2017-03-09 14:34:05 +0000
1258+++ modules/Ubuntu/Components/Extras/Printers/printers/printers.cpp 2017-03-13 12:51:30 +0000
1259@@ -58,8 +58,20 @@
1260 const QModelIndex &parent, int first, int) {
1261 int jobId = m_jobs.data(m_jobs.index(first, 0, parent),
1262 JobModel::Roles::IdRole).toInt();
1263- jobAdded(m_jobs.getJobById(jobId));
1264- });
1265+ QString printerName = m_jobs.data(
1266+ m_jobs.index(first, 0, parent),
1267+ JobModel::Roles::PrinterNameRole
1268+ ).toString();
1269+
1270+ jobAdded(m_jobs.getJob(printerName, jobId));
1271+ });
1272+
1273+ // If the jobModel forces a refresh, load extended attributes for the job
1274+ connect(&m_jobs, &JobModel::forceJobRefresh, [this](
1275+ const QString &printerName, const int jobId) {
1276+ jobAdded(m_jobs.getJob(printerName, jobId));
1277+ });
1278+
1279 connect(&m_model, &QAbstractItemModel::rowsInserted, [this](
1280 const QModelIndex &parent, int first, int) {
1281 auto printer = m_model.data(
1282@@ -78,6 +90,17 @@
1283 );
1284 }
1285
1286+ // Ensure existing jobs have been added, incase some were added before
1287+ // the connect to rowsInserted was done
1288+ for (int i = 0; i < m_jobs.rowCount(); i++) {
1289+ jobAdded(
1290+ m_jobs.getJob(
1291+ m_jobs.data(m_jobs.index(i), JobModel::Roles::PrinterNameRole).toString(),
1292+ m_jobs.data(m_jobs.index(i), JobModel::IdRole).toInt()
1293+ )
1294+ );
1295+ }
1296+
1297 if (m_backend->type() == PrinterEnum::PrinterType::CupsType) {
1298 ((PrinterCupsBackend*) m_backend)->createSubscription();
1299 }
1300@@ -271,8 +294,13 @@
1301 void Printers::jobAdded(QSharedPointer<PrinterJob> job)
1302 {
1303 auto printer = m_model.getPrinterByName(job->printerName());
1304- if (printer && job)
1305- job->setPrinter(printer);
1306+
1307+ // Check if we have a valid printer, does not need to be loaded as JobLoader
1308+ // creates it's own Backend.
1309+ if (printer && job) {
1310+ // Trigger JobLoader to load extended attributes in the background
1311+ m_backend->requestJobExtendedAttributes(printer, job);
1312+ }
1313 }
1314
1315 void Printers::printerAdded(QSharedPointer<Printer> printer)
1316@@ -288,10 +316,9 @@
1317 ).toString();
1318
1319 int jobId = m_jobs.data(idx, JobModel::Roles::IdRole).toInt();
1320- auto job = m_jobs.getJobById(jobId);
1321+ auto job = m_jobs.getJob(printerName, jobId);
1322 if (printerName == printer->name() && !job->printer()) {
1323- job->setPrinter(printer);
1324- return;
1325+ jobAdded(job);
1326 }
1327 }
1328 }
1329
1330=== modified file 'po/ubuntu-ui-extras.pot'
1331--- po/ubuntu-ui-extras.pot 2017-03-08 16:55:21 +0000
1332+++ po/ubuntu-ui-extras.pot 2017-03-13 12:51:30 +0000
1333@@ -8,7 +8,7 @@
1334 msgstr ""
1335 "Project-Id-Version: ubuntu-ui-extras\n"
1336 "Report-Msgid-Bugs-To: \n"
1337-"POT-Creation-Date: 2017-03-08 17:54+0100\n"
1338+"POT-Creation-Date: 2017-03-13 12:44+0000\n"
1339 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
1340 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
1341 "Language-Team: LANGUAGE <LL@li.org>\n"
1342@@ -99,7 +99,7 @@
1343 msgid "Stopped"
1344 msgstr ""
1345
1346-#: modules/Ubuntu/Components/Extras/Printers/printers/printers.cpp:331
1347+#: modules/Ubuntu/Components/Extras/Printers/printers/printers.cpp:357
1348 msgid "Test page"
1349 msgstr ""
1350
1351
1352=== modified file 'tests/unittests/Printers/CMakeLists.txt'
1353--- tests/unittests/Printers/CMakeLists.txt 2017-03-08 16:13:02 +0000
1354+++ tests/unittests/Printers/CMakeLists.txt 2017-03-13 12:51:30 +0000
1355@@ -45,9 +45,9 @@
1356 target_link_libraries(testPrintersJobFilter UbuntuComponentsExtrasPrintersQml Qt5::Test Qt5::Gui)
1357 add_test(tst_jobfilter testPrintersJobFilter)
1358
1359-add_executable(testPrintersSignalHandler tst_signalhandler.cpp ${MOCK_SOURCES})
1360-target_link_libraries(testPrintersSignalHandler UbuntuComponentsExtrasPrintersQml Qt5::Test Qt5::Gui)
1361-add_test(tst_signalhandler testPrintersSignalHandler)
1362+add_executable(testPrintersSignalRateLimiter tst_signalratelimiter.cpp ${MOCK_SOURCES})
1363+target_link_libraries(testPrintersSignalRateLimiter UbuntuComponentsExtrasPrintersQml Qt5::Test Qt5::Gui)
1364+add_test(tst_signalratelimiter testPrintersSignalRateLimiter)
1365
1366 add_executable(testPrintersDevice tst_printerdevice.cpp ${MOCK_SOURCES})
1367 target_link_libraries(testPrintersDevice UbuntuComponentsExtrasPrintersQml Qt5::Test Qt5::Gui)
1368
1369=== renamed file 'tests/unittests/Printers/tst_signalhandler.cpp' => 'tests/unittests/Printers/tst_signalratelimiter.cpp'
1370--- tests/unittests/Printers/tst_signalhandler.cpp 2017-02-21 10:46:29 +0000
1371+++ tests/unittests/Printers/tst_signalratelimiter.cpp 2017-03-13 12:51:30 +0000
1372@@ -14,31 +14,51 @@
1373 * along with this program. If not, see <http://www.gnu.org/licenses/>.
1374 */
1375
1376-#include "printer/printersignalhandler.h"
1377+#include "printer/signalratelimiter.h"
1378
1379+#include <QDateTime>
1380 #include <QDebug>
1381 #include <QObject>
1382 #include <QSignalSpy>
1383 #include <QTest>
1384
1385-class TestSignalHandler : public QObject
1386+class TestSignalRateLimiter : public QObject
1387 {
1388 Q_OBJECT
1389 private Q_SLOTS:
1390 void testEmptyCount()
1391 {
1392- PrinterSignalHandler handler(500);
1393+ SignalRateLimiter handler(500);
1394 QSignalSpy modifiedSpy(&handler, SIGNAL(printerModified(const QString&)));
1395
1396- for (int i = 0; i < 500; i++) {
1397+ for (int i = 0; i < 20; i++) {
1398 handler.onPrinterStateChanged("spam!", "ipp://bar/baz", "printer-a", 0, "none", true);
1399 }
1400
1401 modifiedSpy.wait(1000);
1402 QCOMPARE(modifiedSpy.count(), 1);
1403 }
1404+ void testUnprocessedTime()
1405+ {
1406+ // Keep sending jobs with no gap for longer than four times the
1407+ // event delay. Check that two signals are emitted.
1408+ // One from the forcing of the signal and one as the timer finishes
1409+
1410+ int eventDelay = 200;
1411+ SignalRateLimiter handler(eventDelay);
1412+ QSignalSpy modifiedSpy(&handler, SIGNAL(printerModified(const QString&)));
1413+
1414+ QDateTime start = QDateTime::currentDateTime();
1415+
1416+ while (start.msecsTo(QDateTime::currentDateTime()) < eventDelay * 5) {
1417+ handler.onPrinterStateChanged("spam!", "ipp://foo/bar", "printer-a", 0, "none", true);
1418+ }
1419+
1420+ modifiedSpy.wait(eventDelay * 2);
1421+ QCOMPARE(modifiedSpy.count(), 2);
1422+ }
1423 };
1424
1425-QTEST_GUILESS_MAIN(TestSignalHandler)
1426-#include "tst_signalhandler.moc"
1427+QTEST_GUILESS_MAIN(TestSignalRateLimiter)
1428+#include "tst_signalratelimiter.moc"
1429

Subscribers

People subscribed via source and target branches