Merge lp:ubuntu-ui-extras/staging into lp:ubuntu-ui-extras
- staging
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Andrew Hayzen |
Approved revision: | no longer in the source branch. |
Merged at revision: | 131 |
Proposed branch: | lp:ubuntu-ui-extras/staging |
Merge into: | lp:ubuntu-ui-extras |
Diff against target: |
6506 lines (+3970/-590) 46 files modified
debian/changelog (+67/-0) modules/Ubuntu/Components/Extras/Example/PrinterQueue.qml (+0/-117) modules/Ubuntu/Components/Extras/Example/Printers.qml (+232/-75) modules/Ubuntu/Components/Extras/Printers/CMakeLists.txt (+4/-1) modules/Ubuntu/Components/Extras/Printers/backend/backend.cpp (+48/-0) modules/Ubuntu/Components/Extras/Printers/backend/backend.h (+13/-0) modules/Ubuntu/Components/Extras/Printers/backend/backend_cups.cpp (+225/-44) modules/Ubuntu/Components/Extras/Printers/backend/backend_cups.h (+25/-1) modules/Ubuntu/Components/Extras/Printers/backend/backend_pdf.cpp (+10/-0) modules/Ubuntu/Components/Extras/Printers/cups/devicesearcher.cpp (+70/-0) modules/Ubuntu/Components/Extras/Printers/cups/devicesearcher.h (+57/-0) modules/Ubuntu/Components/Extras/Printers/cups/ippclient.cpp (+163/-9) modules/Ubuntu/Components/Extras/Printers/cups/ippclient.h (+13/-1) modules/Ubuntu/Components/Extras/Printers/cups/jobloader.cpp (+50/-0) modules/Ubuntu/Components/Extras/Printers/cups/jobloader.h (+47/-0) modules/Ubuntu/Components/Extras/Printers/cups/printerloader.cpp (+3/-0) modules/Ubuntu/Components/Extras/Printers/enums.h (+23/-0) modules/Ubuntu/Components/Extras/Printers/models/devicemodel.cpp (+149/-0) modules/Ubuntu/Components/Extras/Printers/models/devicemodel.h (+78/-0) modules/Ubuntu/Components/Extras/Printers/models/jobmodel.cpp (+267/-126) modules/Ubuntu/Components/Extras/Printers/models/jobmodel.h (+53/-11) modules/Ubuntu/Components/Extras/Printers/models/printermodel.cpp (+70/-30) modules/Ubuntu/Components/Extras/Printers/models/printermodel.h (+13/-4) modules/Ubuntu/Components/Extras/Printers/plugin.cpp (+2/-0) modules/Ubuntu/Components/Extras/Printers/printer/printer.cpp (+150/-24) modules/Ubuntu/Components/Extras/Printers/printer/printer.h (+29/-3) modules/Ubuntu/Components/Extras/Printers/printer/printerjob.cpp (+108/-60) modules/Ubuntu/Components/Extras/Printers/printer/printerjob.h (+14/-11) 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 (+152/-7) modules/Ubuntu/Components/Extras/Printers/printers/printers.h (+20/-0) modules/Ubuntu/Components/Extras/Printers/structs.h (+84/-0) po/ubuntu-ui-extras.pot (+26/-2) tests/unittests/Printers/CMakeLists.txt (+13/-3) tests/unittests/Printers/mockbackend.h (+127/-4) tests/unittests/Printers/tst_jobfilter.cpp (+4/-2) tests/unittests/Printers/tst_jobmodel.cpp (+446/-29) tests/unittests/Printers/tst_printer.cpp (+96/-0) tests/unittests/Printers/tst_printerdevice.cpp (+131/-0) tests/unittests/Printers/tst_printerdevicemodel.cpp (+162/-0) tests/unittests/Printers/tst_printerfilter.cpp (+30/-0) tests/unittests/Printers/tst_printerjob.cpp (+53/-0) tests/unittests/Printers/tst_printermodel.cpp (+489/-1) tests/unittests/Printers/tst_printers.cpp (+92/-7) tests/unittests/Printers/tst_signalratelimiter.cpp (+26/-6) |
To merge this branch: | bzr merge lp:ubuntu-ui-extras/staging |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Andrew Hayzen (community) | Approve | ||
Jonas G. Drange (community) | Approve | ||
Review via email: mp+320797@code.launchpad.net |
Commit message
Description of the change
Please see the changelog entry for details.
Testing:
1. Build it
2. qmlscene -I <path/to/
3. Add a printer
4. Explore configured printers
5. Change configured printers
Andrew Hayzen (ahayzen) wrote : | # |
I approve all the changes made by Jonas, which is this whole branch minus my own changes.
- 131. By CI Train Bot Account
-
* Adds devices as an model on Printers, and re-instates remote printer model on Printers.
* makes sure the backend is refreshed on enable/disable
* If printerAdded is called ensure there is always a proxy printer
corrects thread affinity for printerloaded printers, as well as any qobject children it might have
* Remove PrinterQueue.qml example and add missing job methods to Printers.qml example, so we have only one example
* improves the deviceName by dropping the CMD (note that the full string remains accessible from device.id)
* Connect job-impressions-completed from cups to PrinterJob: :impressionsCom pleted( )
* 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
* Add a JobLoader for loading a specific jobId for a printer and loading the extended attributes
* Move the loading of creationTime, completedTime, processingTime, size and user to extended attributes as signals don't give us those
* Add method for PrinterBackend for getting a specific job
* Split up JobModel::update so there is jobCreated, jobState and jobCompleted which then call addJob, removeJob and updateJob
* Improve Printer::updateFrom to not call loadAttributes as this results in a possible UI block
* Add missing comparisions for PrinterJob deepCompare and updateFrom
* Change PrinterJob::setPrinter to not call loadDefaults and instead explicitly call it
* Change Printers to trigger requestJobExtendedAttributes which triggers a background thread rather than doing in foreground
* exposes copies attribute on the printer
* includes cups/adminutils.h into which the cups device callback was moved in >= libcups2-dev 2.2.2
* allows browsing on local and remote printers
* Add filters for active, paused and queued
* Add sorting by creationTime and then falling back to id
* Change time related roles in JobModel to return QDateTime, not QString, so sorting doesn't break
* Update tests
* Change printerGetJobAttributes to use printer-uri and job-id (the same as holdJob and releaseJob
* Give printerGetJobAttributes printerName so we can ensure we get the correct job
* adds read/write of the shared property
* adds extended attribute fetching (IppClient::printerGetAttr ibutes)
* refactors attribute retrieval from cups
* implements deviceUri and lastMessage on Printer
* adds Printers.printTestPage
* Add tests for JobModel roles
* Add tests for make and location in Printer
* Fix JobModel color and quality roles to use fallback if no text
* Add tests for all roles in PrinterModel
* Remove PdfRole it is not used (IsPdfRole instead)
* Add holdJob and releaseJob methods to Printers
* Add HeldRole to JobModel
* Add tests for hold and releasing a job
* Update example to show how hold and release work
* Update StateRole to return just the state, not text
* Remove PrinterJob friends and make setters public as they aren't exposed to QML anyway
* adds make and location to printermodel and printer
* Pull of trunk
* Remove use of bitewise or as it fails in CI
* guards against excessive loading of printers and drivers
* adds loadPrinter method on Printers
* drops a lot of code that was not used, and some TODOS/FIXMES that aren't necessary
* Fix/add commented or empty tests
* Implement tests that had QSKIP
* removes empty brackets
* adds cmake extras, required by printer stuff
* Remove remaining fixme's
* Rename UbuntuSettingsComponentsPrinte rsPlugin to UbuntuComponent sExtrasPrinters Plugin
* Remove import Ubuntu.Settings. Components 0.1 from example qml imports
* Rename definition UBUNTUSETTINGSPRINTERS_ LIBRARY to UBUNTUCOMPONENT SEXTRASPRINTERS _LIBRARY
* Rename UbuntuSettingsPrintersQml to UbuntuComponent sExtrasPrinters Qml
* Rename translation domain to ubuntu-ui-extras
* Fix for printsupport being build-depends rather than depends
Preview Diff
1 | === modified file 'debian/changelog' |
2 | --- debian/changelog 2017-03-01 22:24:08 +0000 |
3 | +++ debian/changelog 2017-03-23 12:51:36 +0000 |
4 | @@ -1,3 +1,70 @@ |
5 | +ubuntu-ui-extras (0.3+17.04.20170301.2-0ubuntu2) UNRELEASED; urgency=medium |
6 | + |
7 | + [ Andrew Hayzen, Jonas G. Drange ] |
8 | + * Adds devices as an model on Printers, and re-instates remote printer model on Printers. |
9 | + * makes sure the backend is refreshed on enable/disable |
10 | + * If printerAdded is called ensure there is always a proxy printer |
11 | + corrects thread affinity for printerloaded printers, as well as any qobject children it might have |
12 | + * Remove PrinterQueue.qml example and add missing job methods to Printers.qml example, so we have only one example |
13 | + * improves the deviceName by dropping the CMD (note that the full string remains accessible from device.id) |
14 | + * Connect job-impressions-completed from cups to PrinterJob::impressionsCompleted() |
15 | + * In the job model listen to printerStateChanged as changes t job-impressions-completed causes that signal |
16 | + * Improve the PrinterSignalHandler to limit the maximum wait time of unprocessed signals to four times the timeout |
17 | + * Add unit test for SignalRateLimiter to check it does perform a flush |
18 | + * Rename PrinterSignalHandler to SignalRateLimiter |
19 | + * Add a JobLoader for loading a specific jobId for a printer and loading the extended attributes |
20 | + * Move the loading of creationTime, completedTime, processingTime, size and user to extended attributes as signals don't give us those |
21 | + * Add method for PrinterBackend for getting a specific job |
22 | + * Split up JobModel::update so there is jobCreated, jobState and jobCompleted which then call addJob, removeJob and updateJob |
23 | + * Improve Printer::updateFrom to not call loadAttributes as this results in a possible UI block |
24 | + * Add missing comparisions for PrinterJob deepCompare and updateFrom |
25 | + * Change PrinterJob::setPrinter to not call loadDefaults and instead explicitly call it |
26 | + * Change Printers to trigger requestJobExtendedAttributes which triggers a background thread rather than doing in foreground |
27 | + * exposes copies attribute on the printer |
28 | + * includes cups/adminutils.h into which the cups device callback was moved in >= libcups2-dev 2.2.2 |
29 | + * allows browsing on local and remote printers |
30 | + * Add filters for active, paused and queued |
31 | + * Add sorting by creationTime and then falling back to id |
32 | + * Change time related roles in JobModel to return QDateTime, not QString, so sorting doesn't break |
33 | + * Update tests |
34 | + * Change printerGetJobAttributes to use printer-uri and job-id (the same as holdJob and releaseJob |
35 | + * Give printerGetJobAttributes printerName so we can ensure we get the correct job |
36 | + * adds read/write of the shared property |
37 | + * adds extended attribute fetching (IppClient::printerGetAttributes) |
38 | + * refactors attribute retrieval from cups |
39 | + * implements deviceUri and lastMessage on Printer |
40 | + * adds Printers.printTestPage |
41 | + * Add tests for JobModel roles |
42 | + * Add tests for make and location in Printer |
43 | + * Fix JobModel color and quality roles to use fallback if no text |
44 | + * Add tests for all roles in PrinterModel |
45 | + * Remove PdfRole it is not used (IsPdfRole instead) |
46 | + * Add holdJob and releaseJob methods to Printers |
47 | + * Add HeldRole to JobModel |
48 | + * Add tests for hold and releasing a job |
49 | + * Update example to show how hold and release work |
50 | + * Update StateRole to return just the state, not text |
51 | + * Remove PrinterJob friends and make setters public as they aren't exposed to QML anyway |
52 | + * adds make and location to printermodel and printer |
53 | + * Pull of trunk |
54 | + * Remove use of bitewise or as it fails in CI |
55 | + * guards against excessive loading of printers and drivers |
56 | + * adds loadPrinter method on Printers |
57 | + * drops a lot of code that was not used, and some TODOS/FIXMES that aren't necessary |
58 | + * Fix/add commented or empty tests |
59 | + * Implement tests that had QSKIP |
60 | + * removes empty brackets |
61 | + * adds cmake extras, required by printer stuff |
62 | + * Remove remaining fixme's |
63 | + * Rename UbuntuSettingsComponentsPrintersPlugin to UbuntuComponentsExtrasPrintersPlugin |
64 | + * Remove import Ubuntu.Settings.Components 0.1 from example qml imports |
65 | + * Rename definition UBUNTUSETTINGSPRINTERS_LIBRARY to UBUNTUCOMPONENTSEXTRASPRINTERS_LIBRARY |
66 | + * Rename UbuntuSettingsPrintersQml to UbuntuComponentsExtrasPrintersQml |
67 | + * Rename translation domain to ubuntu-ui-extras |
68 | + * Fix for printsupport being build-depends rather than depends |
69 | + |
70 | + -- Jonas G. Drange <jonas.drange@canonical.com> Mon, 20 Mar 2017 21:04:05 +0100 |
71 | + |
72 | ubuntu-ui-extras (0.2+17.04.20170301.2-0ubuntu1) zesty; urgency=medium |
73 | |
74 | [ Andrew Hayzen, Jonas G. Drange ] |
75 | |
76 | === removed file 'modules/Ubuntu/Components/Extras/Example/PrinterQueue.qml' |
77 | --- modules/Ubuntu/Components/Extras/Example/PrinterQueue.qml 2017-02-21 11:31:44 +0000 |
78 | +++ modules/Ubuntu/Components/Extras/Example/PrinterQueue.qml 1970-01-01 00:00:00 +0000 |
79 | @@ -1,117 +0,0 @@ |
80 | -/* |
81 | - * Copyright 2017 Canonical Ltd. |
82 | - * |
83 | - * This program is free software; you can redistribute it and/or modify |
84 | - * it under the terms of the GNU Lesser General Public License as published by |
85 | - * the Free Software Foundation; version 3. |
86 | - * |
87 | - * This program is distributed in the hope that it will be useful, |
88 | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
89 | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
90 | - * GNU Lesser General Public License for more details. |
91 | - * |
92 | - * You should have received a copy of the GNU Lesser General Public License |
93 | - * along with this program. If not, see <http://www.gnu.org/licenses/>. |
94 | - * |
95 | - * Authored by Jonas G. Drange <jonas.drange@canonical.com> |
96 | - * Andrew Hayzen <andrew.hayzen@canonical.com> |
97 | - */ |
98 | - |
99 | -import QtQuick 2.4 |
100 | -import QtQuick.Layouts 1.1 |
101 | -import Ubuntu.Components 1.3 |
102 | -import Ubuntu.Components.ListItems 1.3 as ListItems |
103 | -import Ubuntu.Components.Extras.Printers 0.1 |
104 | - |
105 | -MainView { |
106 | - width: units.gu(50) |
107 | - height: units.gu(90) |
108 | - |
109 | - Component { |
110 | - id: queuePage |
111 | - |
112 | - Page { |
113 | - header: PageHeader { |
114 | - title: "Queue: " + printer.name |
115 | - flickable: queueView |
116 | - } |
117 | - visible: false |
118 | - |
119 | - property var printer |
120 | - |
121 | - ListView { |
122 | - id: queueView |
123 | - anchors { |
124 | - fill: parent |
125 | - } |
126 | - delegate: ListItem { |
127 | - height: modelLayout.height + (divider.visible ? divider.height : 0) |
128 | - ListItemLayout { |
129 | - id: modelLayout |
130 | - title.text: displayName |
131 | - subtitle.text: "Job: " + model.id + " State: " + model.state |
132 | - + " Color: " + model.colorModel + " CreationTime: " |
133 | - + model.creationTime + " PageRange: " |
134 | - + model.printRange + " Messages: " + model.messages; |
135 | - subtitle.wrapMode: Text.WrapAtWordBoundaryOrAnywhere |
136 | - subtitle.maximumLineCount: 3 |
137 | - } |
138 | - onClicked: { |
139 | - console.debug("Cancel:", printer.name, model.id); |
140 | - Printers.cancelJob(printer.name, model.id); |
141 | - } |
142 | - } |
143 | - model: printer.jobs |
144 | - |
145 | - Label { |
146 | - anchors { |
147 | - centerIn: parent |
148 | - } |
149 | - text: "Empty queue" |
150 | - visible: queueView.count === 0 |
151 | - } |
152 | - } |
153 | - } |
154 | - } |
155 | - |
156 | - PageStack { |
157 | - id: pageStack |
158 | - |
159 | - Page { |
160 | - id: printersPage |
161 | - header: PageHeader { |
162 | - title: "Printers" |
163 | - flickable: printerList |
164 | - } |
165 | - visible: false |
166 | - |
167 | - ListView { |
168 | - id: printerList |
169 | - anchors { fill: parent } |
170 | - model: Printers.allPrintersWithPdf |
171 | - delegate: ListItem { |
172 | - height: modelLayout.height + (divider.visible ? divider.height : 0) |
173 | - ListItemLayout { |
174 | - id: modelLayout |
175 | - title.text: displayName |
176 | - title.font.bold: model.default |
177 | - subtitle.text: description |
178 | - |
179 | - Icon { |
180 | - id: icon |
181 | - width: height |
182 | - height: units.gu(2.5) |
183 | - name: "printer-symbolic" |
184 | - SlotsLayout.position: SlotsLayout.First |
185 | - } |
186 | - |
187 | - ProgressionSlot {} |
188 | - } |
189 | - onClicked: pageStack.push(queuePage, { printer: model }) |
190 | - } |
191 | - } |
192 | - } |
193 | - |
194 | - Component.onCompleted: push(printersPage) |
195 | - } |
196 | -} |
197 | |
198 | === modified file 'modules/Ubuntu/Components/Extras/Example/Printers.qml' |
199 | --- modules/Ubuntu/Components/Extras/Example/Printers.qml 2017-02-21 11:31:44 +0000 |
200 | +++ modules/Ubuntu/Components/Extras/Example/Printers.qml 2017-03-23 12:51:36 +0000 |
201 | @@ -51,17 +51,17 @@ |
202 | } |
203 | } |
204 | |
205 | - Component.onCompleted: { |
206 | - printer.description; |
207 | - } |
208 | - |
209 | Flickable { |
210 | id: printerFlickable |
211 | anchors.fill: parent |
212 | - |
213 | + contentHeight: contentItem.childrenRect.height |
214 | Loader { |
215 | id: printerPageBitsLoader |
216 | - anchors.fill: parent |
217 | + anchors { |
218 | + left: parent.left |
219 | + right: parent.right |
220 | + } |
221 | + |
222 | sourceComponent: printer.isLoaded ? printerPageLoaded : printerPageNotYetLoaded |
223 | } |
224 | } |
225 | @@ -70,7 +70,7 @@ |
226 | id: printerPageLoaded |
227 | |
228 | Column { |
229 | - spacing: units.gu(2) |
230 | + height: childrenRect.height + anchors.topMargin |
231 | anchors { |
232 | top: parent.top |
233 | topMargin: units.gu(2) |
234 | @@ -91,6 +91,31 @@ |
235 | } |
236 | } |
237 | |
238 | + ListItems.SingleValue { |
239 | + anchors { |
240 | + left: parent.left |
241 | + right: parent.right |
242 | + } |
243 | + text: "Status" |
244 | + |
245 | + value: { |
246 | + var state; |
247 | + if (printer.state == PrinterEnum.IdleState) { |
248 | + state = i18n.tr("Idle"); |
249 | + } else if (printer.state == PrinterEnum.AbortedState) { |
250 | + state = i18n.tr("Aborted"); |
251 | + } else if (printer.state == PrinterEnum.ActiveState) { |
252 | + state = i18n.tr("Active"); |
253 | + } else if (printer.state == PrinterEnum.ErrorState) { |
254 | + state = i18n.tr("Stopped"); |
255 | + } |
256 | + return "%1 — %2" |
257 | + .arg(state) |
258 | + .arg(printer.lastMessage ? |
259 | + printer.lastMessage : i18n.tr("No messages")); |
260 | + } |
261 | + } |
262 | + |
263 | ListItems.Standard { |
264 | anchors { |
265 | left: parent.left |
266 | @@ -109,25 +134,30 @@ |
267 | left: parent.left |
268 | right: parent.right |
269 | } |
270 | + text: "Shared" |
271 | + |
272 | + control: Switch { |
273 | + checked: printer.shared |
274 | + onCheckedChanged: printer.shared = checked |
275 | + } |
276 | + } |
277 | + |
278 | + ListItems.Standard { |
279 | + anchors { |
280 | + left: parent.left |
281 | + right: parent.right |
282 | + } |
283 | text: "Jobs" |
284 | progression: true |
285 | onClicked: pageStack.push(jobPage, { printer: printer }) |
286 | } |
287 | |
288 | - Label { |
289 | + ListItems.Standard { |
290 | anchors { |
291 | left: parent.left |
292 | right: parent.right |
293 | - margins: units.gu(2) |
294 | } |
295 | text: "Description" |
296 | - } |
297 | - |
298 | - ListItems.SingleControl { |
299 | - anchors { |
300 | - left: parent.left |
301 | - right: parent.right |
302 | - } |
303 | |
304 | control: TextField { |
305 | anchors { |
306 | @@ -141,6 +171,27 @@ |
307 | } |
308 | } |
309 | |
310 | + ListItems.Standard { |
311 | + anchors { |
312 | + left: parent.left |
313 | + right: parent.right |
314 | + } |
315 | + text: "Copies" |
316 | + |
317 | + control: TextField { |
318 | + id: copiesField |
319 | + inputMethodHints: Qt.ImhDigitsOnly |
320 | + text: printer.copies |
321 | + validator: IntValidator { |
322 | + bottom: 1 |
323 | + top: 999 |
324 | + } |
325 | + width: units.gu(10) |
326 | + onTextChanged: printer.copies = text |
327 | + } |
328 | + |
329 | + } |
330 | + |
331 | |
332 | ListItems.ValueSelector { |
333 | anchors { |
334 | @@ -200,12 +251,70 @@ |
335 | selectedIndex = printer.printQuality |
336 | } |
337 | } |
338 | + |
339 | + ListItems.SingleControl { |
340 | + anchors { |
341 | + left: parent.left |
342 | + right: parent.right |
343 | + } |
344 | + control: Button { |
345 | + text: "Print test page" |
346 | + onClicked: Printers.printTestPage(printer.name) |
347 | + } |
348 | + } |
349 | + |
350 | + ListItems.SingleValue { |
351 | + anchors { |
352 | + left: parent.left |
353 | + right: parent.right |
354 | + } |
355 | + text: "Device URI" |
356 | + value: printer.deviceUri |
357 | + } |
358 | } |
359 | } |
360 | } |
361 | } |
362 | |
363 | Component { |
364 | + id: jobDelegate |
365 | + |
366 | + ListItem { |
367 | + height: modelLayout.height + (divider.visible ? divider.height : 0) |
368 | + trailingActions: ListItemActions { |
369 | + actions: [ |
370 | + Action { |
371 | + iconName: model.held ? "media-playback-start" : "media-playback-pause" |
372 | + text: model.held ? "Release" : "Hold" |
373 | + |
374 | + onTriggered: { |
375 | + if (model.held) { |
376 | + Printers.releaseJob(model.printerName, model.id); |
377 | + } else { |
378 | + Printers.holdJob(model.printerName, model.id); |
379 | + } |
380 | + } |
381 | + }, |
382 | + Action { |
383 | + iconName: "cancel" |
384 | + text: "Cancel" |
385 | + |
386 | + onTriggered: Printers.cancelJob(model.printerName, model.id); |
387 | + } |
388 | + ] |
389 | + } |
390 | + |
391 | + ListItemLayout { |
392 | + id: modelLayout |
393 | + title.text: displayName |
394 | + subtitle.text: "Printing " + model.impressionsCompleted + " pages" + "\n" + model.printerName |
395 | + subtitle.wrapMode: Text.WrapAtWordBoundaryOrAnywhere |
396 | + subtitle.maximumLineCount: 3 |
397 | + } |
398 | + } |
399 | + } |
400 | + |
401 | + Component { |
402 | id: jobPage |
403 | Page { |
404 | property var printer |
405 | @@ -219,26 +328,11 @@ |
406 | id: jobList |
407 | anchors.fill: parent |
408 | model: printer.jobs |
409 | - delegate: ListItem { |
410 | - height: jobLayout.height + (divider.visible ? divider.height : 0) |
411 | - ListItemLayout { |
412 | - id: jobLayout |
413 | - title.text: displayName |
414 | - |
415 | - Icon { |
416 | - id: icon |
417 | - width: height |
418 | - height: units.gu(2.5) |
419 | - name: "stock_document" |
420 | - SlotsLayout.position: SlotsLayout.First |
421 | - } |
422 | - } |
423 | - } |
424 | + delegate: jobDelegate |
425 | } |
426 | } |
427 | } |
428 | |
429 | - |
430 | Component { |
431 | id: allJobsPage |
432 | Page { |
433 | @@ -252,21 +346,7 @@ |
434 | id: jobsList |
435 | anchors.fill: parent |
436 | model: Printers.printJobs |
437 | - delegate: ListItem { |
438 | - height: jobsLayout.height + (divider.visible ? divider.height : 0) |
439 | - ListItemLayout { |
440 | - id: jobsLayout |
441 | - title.text: displayName |
442 | - |
443 | - Icon { |
444 | - id: icon |
445 | - width: height |
446 | - height: units.gu(2.5) |
447 | - name: "stock_document" |
448 | - SlotsLayout.position: SlotsLayout.First |
449 | - } |
450 | - } |
451 | - } |
452 | + delegate: jobDelegate |
453 | } |
454 | } |
455 | } |
456 | @@ -338,9 +418,18 @@ |
457 | |
458 | ProgressionSlot {} |
459 | } |
460 | - onClicked: pageStack.push(printerPage, { printer: model }) |
461 | + onClicked: { |
462 | + Printers.loadPrinter(model.name); |
463 | + pageStack.push(printerPage, { printer: model }); |
464 | + } |
465 | } |
466 | } |
467 | + |
468 | + Label { |
469 | + anchors.centerIn: parent |
470 | + visible: printerList.count === 0 |
471 | + text: "No printers found" |
472 | + } |
473 | } |
474 | } |
475 | |
476 | @@ -423,9 +512,7 @@ |
477 | } |
478 | } |
479 | |
480 | - Component.onCompleted: { |
481 | - Printers.prepareToAddPrinter(); |
482 | - } |
483 | + Component.onCompleted: Printers.prepareToAddPrinter() |
484 | |
485 | Timer { |
486 | id: okTimer |
487 | @@ -436,14 +523,12 @@ |
488 | Flickable { |
489 | id: addPrinterFlickable |
490 | anchors.fill: parent |
491 | + contentHeight: contentItem.childrenRect.height |
492 | |
493 | Column { |
494 | id: addPrinterCol |
495 | property bool enabled: true |
496 | - anchors { |
497 | - left: parent.left |
498 | - right: parent.right |
499 | - } |
500 | + anchors { left: parent.left; right: parent.right } |
501 | |
502 | Item { |
503 | id: errorMessageContainer |
504 | @@ -476,10 +561,7 @@ |
505 | |
506 | ListItems.ValueSelector { |
507 | id: driverSelector |
508 | - anchors { |
509 | - left: parent.left |
510 | - right: parent.right |
511 | - } |
512 | + anchors { left: parent.left; right: parent.right } |
513 | text: "Choose driver" |
514 | values: [ |
515 | "Select printer from database", |
516 | @@ -489,10 +571,7 @@ |
517 | } |
518 | |
519 | ListItems.Standard { |
520 | - anchors { |
521 | - left: parent.left |
522 | - right: parent.right |
523 | - } |
524 | + anchors { left: parent.left; right: parent.right } |
525 | text: "Filter drivers" |
526 | control: TextField { |
527 | id: driverFilter |
528 | @@ -559,10 +638,7 @@ |
529 | } |
530 | |
531 | ListItems.Standard { |
532 | - anchors { |
533 | - left: parent.left |
534 | - right: parent.right |
535 | - } |
536 | + anchors { left: parent.left; right: parent.right } |
537 | text: "Printer name" |
538 | control: TextField { |
539 | id: printerName |
540 | @@ -572,10 +648,7 @@ |
541 | } |
542 | |
543 | ListItems.Standard { |
544 | - anchors { |
545 | - left: parent.left |
546 | - right: parent.right |
547 | - } |
548 | + anchors { left: parent.left; right: parent.right } |
549 | text: "Description (optional)" |
550 | control: TextField { |
551 | id: printerDescription |
552 | @@ -585,10 +658,7 @@ |
553 | } |
554 | |
555 | ListItems.Standard { |
556 | - anchors { |
557 | - left: parent.left |
558 | - right: parent.right |
559 | - } |
560 | + anchors { left: parent.left; right: parent.right } |
561 | text: "Location (optional)" |
562 | control: TextField { |
563 | id: printerLocation |
564 | @@ -597,6 +667,93 @@ |
565 | enabled: parent.enabled |
566 | } |
567 | } |
568 | + |
569 | + Label { |
570 | + id: remotePrintersLabel |
571 | + anchors { |
572 | + left: parent.left |
573 | + right: parent.right |
574 | + margins: units.gu(2) |
575 | + top: addPrinterCol.bottom |
576 | + } |
577 | + text: "Other printers" |
578 | + |
579 | + ActivityIndicator { |
580 | + id: remotePrintersSearchIndicator |
581 | + anchors { |
582 | + right: parent.right |
583 | + verticalCenter: parent.verticalCenter |
584 | + } |
585 | + property var target |
586 | + Component.onCompleted: target = Printers.devices |
587 | + running: target.searching |
588 | + } |
589 | + } |
590 | + |
591 | + ListView { |
592 | + id: remotePrintersList |
593 | + anchors { |
594 | + left: parent.left |
595 | + right: parent.right |
596 | + top: remotePrintersLabel.bottom |
597 | + topMargin: units.gu(2) |
598 | + } |
599 | + height: contentItem.childrenRect.height |
600 | + model: Printers.devices |
601 | + delegate: ListItem { |
602 | + height: modelLayout.height + (divider.visible ? divider.height : 0) |
603 | + ListItemLayout { |
604 | + id: modelLayout |
605 | + title.text: displayName |
606 | + subtitle.text: { |
607 | + if (type == PrinterEnum.LPDType) return "LPD"; |
608 | + if (type == PrinterEnum.IppSType) return "IppS"; |
609 | + if (type == PrinterEnum.Ipp14Type) return "Ipp14"; |
610 | + if (type == PrinterEnum.HttpType) return "Http"; |
611 | + if (type == PrinterEnum.BehType) return "Beh"; |
612 | + if (type == PrinterEnum.SocketType) return "Socket"; |
613 | + if (type == PrinterEnum.HttpsType) return "Https"; |
614 | + if (type == PrinterEnum.IppType) return "Ipp"; |
615 | + if (type == PrinterEnum.HPType) return "HP"; |
616 | + if (type == PrinterEnum.USBType) return "USB"; |
617 | + if (type == PrinterEnum.HPFaxType) return "HPFax"; |
618 | + if (type == PrinterEnum.DNSSDType) return "DNSSD"; |
619 | + else return "Unknown protocol"; |
620 | + } |
621 | + |
622 | + Icon { |
623 | + id: icon |
624 | + width: height |
625 | + height: units.gu(2.5) |
626 | + name: "network-printer-symbolic" |
627 | + SlotsLayout.position: SlotsLayout.First |
628 | + } |
629 | + |
630 | + Button { |
631 | + text: "Select printer" |
632 | + onClicked: { |
633 | + var suggestedPrinterName = (" " + displayName).slice(1); |
634 | + suggestedPrinterName = suggestedPrinterName.replace(/\ /g, "\-"); |
635 | + printerUri.text = uri; |
636 | + printerName.text = suggestedPrinterName; |
637 | + printerDescription.text = info; |
638 | + printerLocation.text = location; |
639 | + } |
640 | + } |
641 | + } |
642 | + } |
643 | + } |
644 | + |
645 | + Label { |
646 | + anchors { |
647 | + left: parent.left |
648 | + right: parent.right |
649 | + top: remotePrintersLabel.bottom |
650 | + topMargin: units.gu(2) |
651 | + } |
652 | + text: "No other printers found" |
653 | + visible: !remotePrintersSearchIndicator.running && remotePrintersList.count == 0 |
654 | + } |
655 | } |
656 | } |
657 | } |
658 | |
659 | === modified file 'modules/Ubuntu/Components/Extras/Printers/CMakeLists.txt' |
660 | --- modules/Ubuntu/Components/Extras/Printers/CMakeLists.txt 2017-02-21 16:55:36 +0000 |
661 | +++ modules/Ubuntu/Components/Extras/Printers/CMakeLists.txt 2017-03-23 12:51:36 +0000 |
662 | @@ -29,17 +29,20 @@ |
663 | backend/backend_cups.cpp |
664 | backend/backend_pdf.cpp |
665 | |
666 | + cups/devicesearcher.cpp |
667 | cups/ippclient.cpp |
668 | + cups/jobloader.cpp |
669 | cups/printerdriverloader.cpp |
670 | cups/printerloader.cpp |
671 | |
672 | + models/devicemodel.cpp |
673 | models/drivermodel.cpp |
674 | models/jobmodel.cpp |
675 | models/printermodel.cpp |
676 | |
677 | printer/printer.cpp |
678 | printer/printerjob.cpp |
679 | - printer/printersignalhandler.cpp |
680 | + printer/signalratelimiter.cpp |
681 | printers/printers.cpp |
682 | |
683 | enums.h |
684 | |
685 | === modified file 'modules/Ubuntu/Components/Extras/Printers/backend/backend.cpp' |
686 | --- modules/Ubuntu/Components/Extras/Printers/backend/backend.cpp 2017-02-23 14:04:26 +0000 |
687 | +++ modules/Ubuntu/Components/Extras/Printers/backend/backend.cpp 2017-03-23 12:51:36 +0000 |
688 | @@ -91,6 +91,22 @@ |
689 | return QString(); |
690 | } |
691 | |
692 | +QString PrinterBackend::printerSetCopies(const QString &name, |
693 | + const int &copies) |
694 | +{ |
695 | + Q_UNUSED(name); |
696 | + Q_UNUSED(copies); |
697 | + return QString(); |
698 | +} |
699 | + |
700 | +QString PrinterBackend::printerSetShared(const QString &name, |
701 | + const bool shared) |
702 | +{ |
703 | + Q_UNUSED(name); |
704 | + Q_UNUSED(shared); |
705 | + return QString(); |
706 | +} |
707 | + |
708 | QString PrinterBackend::printerSetInfo(const QString &name, |
709 | const QString &info) |
710 | { |
711 | @@ -139,6 +155,18 @@ |
712 | Q_UNUSED(name); |
713 | } |
714 | |
715 | +void PrinterBackend::holdJob(const QString &name, const int jobId) |
716 | +{ |
717 | + Q_UNUSED(jobId); |
718 | + Q_UNUSED(name); |
719 | +} |
720 | + |
721 | +void PrinterBackend::releaseJob(const QString &name, const int jobId) |
722 | +{ |
723 | + Q_UNUSED(jobId); |
724 | + Q_UNUSED(name); |
725 | +} |
726 | + |
727 | int PrinterBackend::printFileToDest(const QString &filepath, |
728 | const QString &title, |
729 | const cups_dest_t *dest) |
730 | @@ -154,6 +182,14 @@ |
731 | return QList<QSharedPointer<PrinterJob>>{}; |
732 | } |
733 | |
734 | +QSharedPointer<PrinterJob> PrinterBackend::printerGetJob( |
735 | + const QString &printerName, const int jobId) |
736 | +{ |
737 | + Q_UNUSED(printerName); |
738 | + Q_UNUSED(jobId); |
739 | + return QSharedPointer<PrinterJob>(Q_NULLPTR); |
740 | +} |
741 | + |
742 | QMap<QString, QVariant> PrinterBackend::printerGetJobAttributes( |
743 | const QString &name, const int jobId) |
744 | { |
745 | @@ -182,6 +218,11 @@ |
746 | return QString(); |
747 | } |
748 | |
749 | +bool PrinterBackend::isRemote() const |
750 | +{ |
751 | + return false; |
752 | +} |
753 | + |
754 | PrinterEnum::State PrinterBackend::state() const |
755 | { |
756 | return PrinterEnum::State::IdleState; |
757 | @@ -248,6 +289,13 @@ |
758 | return QString(); |
759 | } |
760 | |
761 | +void PrinterBackend::requestJobExtendedAttributes( |
762 | + QSharedPointer<Printer> printer, QSharedPointer<PrinterJob> job) |
763 | +{ |
764 | + Q_UNUSED(printer); |
765 | + Q_UNUSED(job); |
766 | +} |
767 | + |
768 | void PrinterBackend::requestPrinterDrivers() |
769 | { |
770 | } |
771 | |
772 | === modified file 'modules/Ubuntu/Components/Extras/Printers/backend/backend.h' |
773 | --- modules/Ubuntu/Components/Extras/Printers/backend/backend.h 2017-02-23 14:04:26 +0000 |
774 | +++ modules/Ubuntu/Components/Extras/Printers/backend/backend.h 2017-03-23 12:51:36 +0000 |
775 | @@ -63,6 +63,9 @@ |
776 | const QString &name, |
777 | const bool accept, |
778 | const QString &reason = QString::null); |
779 | + virtual QString printerSetCopies(const QString &name, const int &copies); |
780 | + virtual QString printerSetShared(const QString &name, |
781 | + const bool shared); |
782 | virtual QString printerSetInfo(const QString &name, |
783 | const QString &info); |
784 | virtual QString printerAddOption(const QString &name, |
785 | @@ -77,10 +80,14 @@ |
786 | const PrinterJob *options); |
787 | |
788 | virtual void cancelJob(const QString &name, const int jobId); |
789 | + virtual void holdJob(const QString &name, const int jobId); |
790 | + virtual void releaseJob(const QString &name, const int jobId); |
791 | virtual int printFileToDest(const QString &filepath, |
792 | const QString &title, |
793 | const cups_dest_t *dest); |
794 | virtual QList<QSharedPointer<PrinterJob>> printerGetJobs(); |
795 | + virtual QSharedPointer<PrinterJob> printerGetJob(const QString &printerName, |
796 | + const int jobId); |
797 | virtual QMap<QString, QVariant> printerGetJobAttributes( |
798 | const QString &name, const int jobId); |
799 | |
800 | @@ -88,6 +95,7 @@ |
801 | virtual QString description() const; |
802 | virtual QString location() const; |
803 | virtual QString makeAndModel() const; |
804 | + virtual bool isRemote() const; |
805 | |
806 | virtual PrinterEnum::State state() const; |
807 | virtual QList<QPageSize> supportedPageSizes() const; |
808 | @@ -105,6 +113,8 @@ |
809 | virtual QSharedPointer<Printer> getPrinter(const QString &printerName); |
810 | virtual QString defaultPrinterName(); |
811 | |
812 | + virtual void requestJobExtendedAttributes(QSharedPointer<Printer> printer, |
813 | + QSharedPointer<PrinterJob> job); |
814 | virtual void requestPrinterDrivers(); |
815 | virtual void requestPrinter(const QString &printerName); |
816 | |
817 | @@ -119,7 +129,10 @@ |
818 | void printerDriversLoaded(const QList<PrinterDriver> &drivers); |
819 | void printerDriversFailedToLoad(const QString &errorMessage); |
820 | |
821 | + void jobLoaded(QString, int, QMap<QString, QVariant>); |
822 | void printerLoaded(QSharedPointer<Printer> printers); |
823 | + void deviceFound(const Device &device); |
824 | + void deviceSearchFinished(); |
825 | |
826 | void jobCompleted( |
827 | const QString &text, |
828 | |
829 | === modified file 'modules/Ubuntu/Components/Extras/Printers/backend/backend_cups.cpp' |
830 | --- modules/Ubuntu/Components/Extras/Printers/backend/backend_cups.cpp 2017-02-24 12:53:34 +0000 |
831 | +++ modules/Ubuntu/Components/Extras/Printers/backend/backend_cups.cpp 2017-03-23 12:51:36 +0000 |
832 | @@ -15,6 +15,8 @@ |
833 | */ |
834 | |
835 | #include "backend/backend_cups.h" |
836 | +#include "cups/devicesearcher.h" |
837 | +#include "cups/jobloader.h" |
838 | #include "cups/printerdriverloader.h" |
839 | #include "cups/printerloader.h" |
840 | #include "utils.h" |
841 | @@ -40,6 +42,10 @@ |
842 | , m_knownQualityOptions({ |
843 | "Quality", "PrintQuality", "HPPrintQuality", "StpQuality", |
844 | "OutputMode",}) |
845 | + , m_extendedAttributeNames({ |
846 | + QStringLiteral("StateMessage"), QStringLiteral("DeviceUri"), |
847 | + QStringLiteral("IsShared"), QStringLiteral("Copies"), |
848 | + }) |
849 | , m_client(client) |
850 | , m_info(info) |
851 | , m_notifier(notifier) |
852 | @@ -178,6 +184,24 @@ |
853 | return QString(); |
854 | } |
855 | |
856 | +QString PrinterCupsBackend::printerSetCopies(const QString &name, |
857 | + const int &copies) |
858 | +{ |
859 | + if (!m_client->printerSetCopies(name, copies)) { |
860 | + return m_client->getLastError(); |
861 | + } |
862 | + return QString(); |
863 | +} |
864 | + |
865 | +QString PrinterCupsBackend::printerSetShared(const QString &name, |
866 | + const bool shared) |
867 | +{ |
868 | + if (!m_client->printerSetShared(name, shared)) { |
869 | + return m_client->getLastError(); |
870 | + } |
871 | + return QString(); |
872 | +} |
873 | + |
874 | QString PrinterCupsBackend::printerSetInfo(const QString &name, |
875 | const QString &info) |
876 | { |
877 | @@ -213,12 +237,27 @@ |
878 | cups_dest_t *dest = getDest(name); |
879 | ppd_file_t* ppd = getPpd(name); |
880 | |
881 | - if (!dest || !ppd) { |
882 | - return ret; |
883 | + // Used to store extended attributes, which we should request maximum once. |
884 | + QMap<QString, QVariant> extendedAttributesResults; |
885 | + |
886 | + /* Goes through known extended attributes. If one is being asked for, |
887 | + ask for all of them right away. */ |
888 | + Q_FOREACH(const QString &extendedOption, m_extendedAttributeNames) { |
889 | + if (options.contains(extendedOption)) { |
890 | + extendedAttributesResults = m_client->printerGetAttributes( |
891 | + name, QStringList({ |
892 | + QStringLiteral("device-uri"), |
893 | + QStringLiteral("printer-uri-supported"), |
894 | + QStringLiteral("printer-state-message"), |
895 | + QStringLiteral("copies-default"), |
896 | + }) |
897 | + ); |
898 | + break; |
899 | + } |
900 | } |
901 | |
902 | Q_FOREACH(const QString &option, options) { |
903 | - if (option == QStringLiteral("DefaultColorModel")) { |
904 | + if (option == QStringLiteral("DefaultColorModel") && ppd) { |
905 | ColorModel model; |
906 | ppd_option_t *ppdColorModel = ppdFindOption(ppd, "ColorModel"); |
907 | if (ppdColorModel) { |
908 | @@ -231,7 +270,7 @@ |
909 | } |
910 | } |
911 | ret[option] = QVariant::fromValue(model); |
912 | - } else if (option == QStringLiteral("DefaultPrintQuality")) { |
913 | + } else if (option == QStringLiteral("DefaultPrintQuality") && ppd) { |
914 | PrintQuality quality; |
915 | Q_FOREACH(const QString opt, m_knownQualityOptions) { |
916 | ppd_option_t *ppdQuality = ppdFindOption(ppd, opt.toUtf8()); |
917 | @@ -245,7 +284,7 @@ |
918 | } |
919 | } |
920 | ret[option] = QVariant::fromValue(quality); |
921 | - } else if (option == QStringLiteral("SupportedPrintQualities")) { |
922 | + } else if (option == QStringLiteral("SupportedPrintQualities") && ppd) { |
923 | QList<PrintQuality> qualities; |
924 | Q_FOREACH(const QString &opt, m_knownQualityOptions) { |
925 | ppd_option_t *qualityOpt = ppdFindOption(ppd, opt.toUtf8()); |
926 | @@ -262,7 +301,7 @@ |
927 | } |
928 | } |
929 | ret[option] = QVariant::fromValue(qualities); |
930 | - } else if (option == QStringLiteral("SupportedColorModels")) { |
931 | + } else if (option == QStringLiteral("SupportedColorModels") && ppd) { |
932 | QList<ColorModel> models; |
933 | ppd_option_t *colorModels = ppdFindOption(ppd, "ColorModel"); |
934 | if (colorModels) { |
935 | @@ -277,19 +316,29 @@ |
936 | } |
937 | } |
938 | ret[option] = QVariant::fromValue(models); |
939 | - } else if (option == QStringLiteral("AcceptJobs")) { |
940 | + } else if (option == QStringLiteral("AcceptJobs") && dest) { |
941 | // "true" if the destination is accepting new jobs, "false" if not. |
942 | QString res = cupsGetOption("printer-is-accepting-jobs", |
943 | dest->num_options, dest->options); |
944 | ret[option] = res.contains("true"); |
945 | - } else { |
946 | - ppd_option_t *val = ppdFindOption(ppd, option.toUtf8()); |
947 | - |
948 | - if (val) { |
949 | - qWarning() << "asking for" << option << "returns" << val->text; |
950 | - } else { |
951 | - qWarning() << "option" << option << "yielded no option"; |
952 | - } |
953 | + } else if (option == QStringLiteral("StateReasons") && dest) { |
954 | + ret[option] = cupsGetOption("printer-state-reasons", |
955 | + dest->num_options, dest->options); |
956 | + } else if (option == QStringLiteral("StateMessage")) { |
957 | + ret[option] = extendedAttributesResults["printer-state-message"]; |
958 | + } else if (option == QStringLiteral("DeviceUri")) { |
959 | + auto res = extendedAttributesResults; |
960 | + if (!res["printer-uri-supported"].toString().isEmpty()) { |
961 | + ret[option] = res["printer-uri-supported"]; |
962 | + } |
963 | + if (!res["device-uri"].toString().isEmpty()) { |
964 | + ret[option] = res["device-uri"]; |
965 | + } |
966 | + } else if (option == QStringLiteral("Copies")) { |
967 | + ret[option] = extendedAttributesResults[QStringLiteral("copies-default")]; |
968 | + } else if (option == QStringLiteral("Shared") && dest) { |
969 | + ret[option] = cupsGetOption("printer-is-shared", |
970 | + dest->num_options, dest->options); |
971 | } |
972 | } |
973 | return ret; |
974 | @@ -306,10 +355,7 @@ |
975 | __CUPS_ADD_OPTION(dest, "Collate", "False"); |
976 | } |
977 | |
978 | - if (options->copies() > 1) { |
979 | - __CUPS_ADD_OPTION(dest, "copies", QString::number(options->copies()).toLocal8Bit()); |
980 | - } |
981 | - |
982 | + __CUPS_ADD_OPTION(dest, "copies", QString::number(options->copies()).toLocal8Bit()); |
983 | __CUPS_ADD_OPTION(dest, "ColorModel", options->getColorModel().name.toLocal8Bit()); |
984 | __CUPS_ADD_OPTION(dest, "Duplex", Utils::duplexModeToPpdChoice(options->getDuplexMode()).toLocal8Bit()); |
985 | |
986 | @@ -347,6 +393,20 @@ |
987 | } |
988 | } |
989 | |
990 | +void PrinterCupsBackend::holdJob(const QString &name, const int jobId) |
991 | +{ |
992 | + if (!m_client->printerHoldJob(name, jobId)) { |
993 | + qWarning() << "Failed to hold job:" << jobId << "for" << name; |
994 | + } |
995 | +} |
996 | + |
997 | +void PrinterCupsBackend::releaseJob(const QString &name, const int jobId) |
998 | +{ |
999 | + if (!m_client->printerReleaseJob(name, jobId)) { |
1000 | + qWarning() << "Failed to release job:" << jobId << "for" << name; |
1001 | + } |
1002 | +} |
1003 | + |
1004 | int PrinterCupsBackend::printFileToDest(const QString &filepath, |
1005 | const QString &title, |
1006 | const cups_dest_t *dest) |
1007 | @@ -385,7 +445,7 @@ |
1008 | const QString &name, const int jobId) |
1009 | { |
1010 | Q_UNUSED(name); |
1011 | - QMap<QString, QVariant> rawMap = m_client->printerGetJobAttributes(jobId); |
1012 | + QMap<QString, QVariant> rawMap = m_client->printerGetJobAttributes(name, jobId); |
1013 | QMap<QString, QVariant> map; |
1014 | |
1015 | // Filter attributes to know values |
1016 | @@ -409,12 +469,34 @@ |
1017 | map.insert("ColorModel", QVariant("")); |
1018 | } |
1019 | |
1020 | + if (__CUPS_ATTR_EXISTS(rawMap, "date-time-at-completed", QDateTime)) { |
1021 | + map.insert("CompletedTime", rawMap.value("date-time-at-completed")); |
1022 | + } else { |
1023 | + map.insert("CompletedTime", QVariant(QDateTime())); |
1024 | + } |
1025 | + |
1026 | + if (__CUPS_ATTR_EXISTS(rawMap, "date-time-at-creation", QDateTime)) { |
1027 | + map.insert("CreationTime", rawMap.value("date-time-at-creation")); |
1028 | + } else { |
1029 | + map.insert("CreationTime", QVariant(QDateTime())); |
1030 | + } |
1031 | + |
1032 | if (__CUPS_ATTR_EXISTS(rawMap, "Duplex", QString)) { |
1033 | map.insert("Duplex", rawMap.value("Duplex")); |
1034 | } else { |
1035 | map.insert("Duplex", QVariant("")); |
1036 | } |
1037 | |
1038 | + // Try job-media-sheets-completed first as it should include duplex |
1039 | + // if it doesn't exist fallback to job-impressions-completed |
1040 | + if (__CUPS_ATTR_EXISTS(rawMap, "job-media-sheets-completed", int)) { |
1041 | + map.insert("impressionsCompleted", rawMap.value("job-media-sheets-completed")); |
1042 | + } else if (__CUPS_ATTR_EXISTS(rawMap, "job-impressions-completed", int)) { |
1043 | + map.insert("impressionsCompleted", rawMap.value("job-impressions-completed")); |
1044 | + } else { |
1045 | + map.insert("impressionsCompleted", QVariant(0)); |
1046 | + } |
1047 | + |
1048 | if (__CUPS_ATTR_EXISTS(rawMap, "landscape", bool)) { |
1049 | map.insert("landscape", rawMap.value("landscape")); |
1050 | } else { |
1051 | @@ -434,6 +516,12 @@ |
1052 | map.insert("page-ranges", QVariant(QStringList())); |
1053 | } |
1054 | |
1055 | + if (__CUPS_ATTR_EXISTS(rawMap, "date-time-at-processing", QDateTime)) { |
1056 | + map.insert("ProcessingTime", rawMap.value("date-time-at-processing")); |
1057 | + } else { |
1058 | + map.insert("ProcessingTime", QVariant(QDateTime())); |
1059 | + } |
1060 | + |
1061 | Q_FOREACH(QString qualityOption, m_knownQualityOptions) { |
1062 | if (rawMap.contains(qualityOption) |
1063 | && rawMap.value(qualityOption).canConvert<QString>()) { |
1064 | @@ -451,6 +539,24 @@ |
1065 | map.insert("OutputOrder", "Normal"); |
1066 | } |
1067 | |
1068 | + if (__CUPS_ATTR_EXISTS(rawMap, "job-k-octets", int)) { |
1069 | + map.insert("Size", rawMap.value("job-k-octets")); |
1070 | + } else { |
1071 | + map.insert("Size", QVariant(0)); |
1072 | + } |
1073 | + |
1074 | + // If there is a state then get it, as there could have been a signal |
1075 | + // flood. Which then means a forceJobRefresh is able to update the state |
1076 | + if (__CUPS_ATTR_EXISTS(rawMap, "job-state", int)) { |
1077 | + map.insert("State", rawMap.value("job-state").toInt()); |
1078 | + } |
1079 | + |
1080 | + if (__CUPS_ATTR_EXISTS(rawMap, "job-originating-user-name", QString)) { |
1081 | + map.insert("User", rawMap.value("job-originating-user-name")); |
1082 | + } else { |
1083 | + map.insert("User", QVariant("")); |
1084 | + } |
1085 | + |
1086 | // Generate a list of messages |
1087 | // TODO: for now just using job-printer-state-message, are there others? |
1088 | QStringList messages; |
1089 | @@ -471,31 +577,13 @@ |
1090 | QList<QSharedPointer<PrinterJob>> list; |
1091 | |
1092 | Q_FOREACH(auto job, jobs) { |
1093 | + // Note: extended attributes are not loaded here |
1094 | + // they are loaded in JobLoader |
1095 | auto newJob = QSharedPointer<PrinterJob>( |
1096 | new PrinterJob(QString::fromUtf8(job->dest), this, job->id) |
1097 | ); |
1098 | - |
1099 | - // Extract the times |
1100 | - QDateTime completedTime; |
1101 | - completedTime.setTimeZone(QTimeZone::systemTimeZone()); |
1102 | - completedTime.setTime_t(job->completed_time); |
1103 | - |
1104 | - QDateTime creationTime; |
1105 | - creationTime.setTimeZone(QTimeZone::systemTimeZone()); |
1106 | - creationTime.setTime_t(job->creation_time); |
1107 | - |
1108 | - QDateTime processingTime; |
1109 | - processingTime.setTimeZone(QTimeZone::systemTimeZone()); |
1110 | - processingTime.setTime_t(job->processing_time); |
1111 | - |
1112 | - // Load the information from the cups struct |
1113 | - newJob->setCompletedTime(completedTime); |
1114 | - newJob->setCreationTime(creationTime); |
1115 | - newJob->setProcessingTime(processingTime); |
1116 | - newJob->setSize(job->size); |
1117 | newJob->setState(static_cast<PrinterEnum::JobState>(job->state)); |
1118 | newJob->setTitle(QString::fromLocal8Bit(job->title)); |
1119 | - newJob->setUser(QString::fromLocal8Bit(job->user)); |
1120 | |
1121 | list.append(newJob); |
1122 | } |
1123 | @@ -505,6 +593,35 @@ |
1124 | return list; |
1125 | } |
1126 | |
1127 | +QSharedPointer<PrinterJob> PrinterCupsBackend::printerGetJob( |
1128 | + const QString &printerName, const int jobId) |
1129 | +{ |
1130 | + auto jobs = getCupsJobs(printerName); |
1131 | + cups_job_t *cupsJob = Q_NULLPTR; |
1132 | + QSharedPointer<PrinterJob> job(Q_NULLPTR); |
1133 | + |
1134 | + for (int i=0; i < jobs.size(); i++) { |
1135 | + if (jobs.at(i)->id == jobId) { |
1136 | + cupsJob = jobs.at(i); |
1137 | + break; |
1138 | + } |
1139 | + } |
1140 | + |
1141 | + if (cupsJob) { |
1142 | + job = QSharedPointer<PrinterJob>( |
1143 | + new PrinterJob(QString::fromUtf8(cupsJob->dest), this, cupsJob->id) |
1144 | + ); |
1145 | + |
1146 | + job->setState(static_cast<PrinterEnum::JobState>(cupsJob->state)); |
1147 | + job->setTitle(QString::fromLocal8Bit(cupsJob->title)); |
1148 | + } |
1149 | + |
1150 | + if (!jobs.size()) |
1151 | + cupsFreeJobs(jobs.size(), jobs.first()); |
1152 | + |
1153 | + return job; |
1154 | +} |
1155 | + |
1156 | QString PrinterCupsBackend::printerName() const |
1157 | { |
1158 | return m_printerName; |
1159 | @@ -525,6 +642,11 @@ |
1160 | return m_info.makeAndModel(); |
1161 | } |
1162 | |
1163 | +bool PrinterCupsBackend::isRemote() const |
1164 | +{ |
1165 | + return m_info.isRemote(); |
1166 | +} |
1167 | + |
1168 | PrinterEnum::State PrinterCupsBackend::state() const |
1169 | { |
1170 | switch (m_info.state()) { |
1171 | @@ -611,9 +733,35 @@ |
1172 | return QPrinterInfo::defaultPrinterName(); |
1173 | } |
1174 | |
1175 | +void PrinterCupsBackend::requestJobExtendedAttributes( |
1176 | + QSharedPointer<Printer> printer, QSharedPointer<PrinterJob> job) |
1177 | +{ |
1178 | + QPair<QString, int> pair(printer->name(), job->jobId()); |
1179 | + |
1180 | + if (m_activeJobRequests.contains(pair)) { |
1181 | + return; |
1182 | + } |
1183 | + |
1184 | + auto thread = new QThread; |
1185 | + auto loader = new JobLoader(this, printer->name(), job->jobId()); |
1186 | + loader->moveToThread(thread); |
1187 | + connect(thread, SIGNAL(started()), loader, SLOT(load())); |
1188 | + connect(loader, SIGNAL(finished()), thread, SLOT(quit())); |
1189 | + connect(loader, SIGNAL(finished()), loader, SLOT(deleteLater())); |
1190 | + connect(loader, SIGNAL(loaded(QString, int, QMap<QString, QVariant>)), |
1191 | + this, SIGNAL(jobLoaded(QString, int, QMap<QString, QVariant>))); |
1192 | + connect(loader, SIGNAL(loaded(QString, int, QMap<QString, QVariant>)), |
1193 | + this, SLOT(onJobLoaded(QString, int, QMap<QString, QVariant>))); |
1194 | + connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); |
1195 | + |
1196 | + m_activeJobRequests << pair; |
1197 | + |
1198 | + thread->start(); |
1199 | +} |
1200 | + |
1201 | void PrinterCupsBackend::requestPrinter(const QString &printerName) |
1202 | { |
1203 | - if (m_activeRequests.contains(printerName)) { |
1204 | + if (m_activePrinterRequests.contains(printerName)) { |
1205 | return; |
1206 | } |
1207 | |
1208 | @@ -628,9 +776,10 @@ |
1209 | connect(loader, SIGNAL(loaded(QSharedPointer<Printer>)), |
1210 | this, SLOT(onPrinterLoaded(QSharedPointer<Printer>))); |
1211 | connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); |
1212 | + |
1213 | + m_activePrinterRequests << printerName; |
1214 | + |
1215 | thread->start(); |
1216 | - |
1217 | - m_activeRequests << printerName; |
1218 | } |
1219 | |
1220 | void PrinterCupsBackend::requestPrinterDrivers() |
1221 | @@ -647,6 +796,7 @@ |
1222 | connect(loader, SIGNAL(loaded(const QList<PrinterDriver>&)), |
1223 | this, SIGNAL(printerDriversLoaded(const QList<PrinterDriver>&))); |
1224 | connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); |
1225 | + |
1226 | thread->start(); |
1227 | } |
1228 | |
1229 | @@ -655,6 +805,22 @@ |
1230 | Q_EMIT requestPrinterDriverCancel(); |
1231 | } |
1232 | |
1233 | +void PrinterCupsBackend::searchForDevices() |
1234 | +{ |
1235 | + |
1236 | + auto thread = new QThread; |
1237 | + auto searcher = new DeviceSearcher(); |
1238 | + searcher->moveToThread(thread); |
1239 | + connect(thread, SIGNAL(started()), searcher, SLOT(load())); |
1240 | + connect(searcher, SIGNAL(finished()), thread, SLOT(quit())); |
1241 | + connect(searcher, SIGNAL(finished()), searcher, SLOT(deleteLater())); |
1242 | + connect(searcher, SIGNAL(finished()), this, SIGNAL(deviceSearchFinished())); |
1243 | + connect(searcher, SIGNAL(loaded(const Device&)), |
1244 | + this, SIGNAL(deviceFound(const Device&))); |
1245 | + connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); |
1246 | + thread->start(); |
1247 | +} |
1248 | + |
1249 | void PrinterCupsBackend::refresh() |
1250 | { |
1251 | if (m_printerName.isEmpty()) { |
1252 | @@ -716,7 +882,22 @@ |
1253 | } |
1254 | } |
1255 | |
1256 | +bool PrinterCupsBackend::isExtendedAttribute(const QString &attributeName) const |
1257 | +{ |
1258 | + return m_extendedAttributeNames.contains(attributeName); |
1259 | +} |
1260 | + |
1261 | +void PrinterCupsBackend::onJobLoaded(QString printerName, int jobId, |
1262 | + QMap<QString, QVariant> attributes) |
1263 | +{ |
1264 | + Q_UNUSED(attributes); |
1265 | + |
1266 | + QPair<QString, int> pair(printerName, jobId); |
1267 | + m_activeJobRequests.remove(pair); |
1268 | +} |
1269 | + |
1270 | void PrinterCupsBackend::onPrinterLoaded(QSharedPointer<Printer> printer) |
1271 | { |
1272 | - m_activeRequests.remove(printer->name()); |
1273 | + m_activePrinterRequests.remove(printer->name()); |
1274 | } |
1275 | + |
1276 | |
1277 | === modified file 'modules/Ubuntu/Components/Extras/Printers/backend/backend_cups.h' |
1278 | --- modules/Ubuntu/Components/Extras/Printers/backend/backend_cups.h 2017-02-24 12:53:34 +0000 |
1279 | +++ modules/Ubuntu/Components/Extras/Printers/backend/backend_cups.h 2017-03-23 12:51:36 +0000 |
1280 | @@ -55,6 +55,10 @@ |
1281 | const QString &name, |
1282 | const bool accept, |
1283 | const QString &reason = QString::null) override; |
1284 | + virtual QString printerSetCopies( |
1285 | + const QString &name, const int &copies) override; |
1286 | + virtual QString printerSetShared(const QString &name, |
1287 | + const bool shared) override; |
1288 | virtual QString printerSetInfo(const QString &name, |
1289 | const QString &info) override; |
1290 | virtual QString printerAddOption(const QString &name, |
1291 | @@ -71,15 +75,20 @@ |
1292 | const PrinterJob *options) override; |
1293 | |
1294 | virtual void cancelJob(const QString &name, const int jobId) override; |
1295 | + virtual void holdJob(const QString &name, const int jobId) override; |
1296 | + virtual void releaseJob(const QString &name, const int jobId) override; |
1297 | virtual int printFileToDest(const QString &filepath, |
1298 | const QString &title, |
1299 | const cups_dest_t *dest) override; |
1300 | virtual QList<QSharedPointer<PrinterJob>> printerGetJobs() override; |
1301 | + virtual QSharedPointer<PrinterJob> printerGetJob( |
1302 | + const QString &printerName, const int jobId) override; |
1303 | |
1304 | virtual QString printerName() const override; |
1305 | virtual QString description() const override; |
1306 | virtual QString location() const override; |
1307 | virtual QString makeAndModel() const override; |
1308 | + virtual bool isRemote() const override; |
1309 | |
1310 | virtual PrinterEnum::State state() const override; |
1311 | virtual QList<QPageSize> supportedPageSizes() const override; |
1312 | @@ -96,6 +105,10 @@ |
1313 | virtual QStringList availablePrinterNames() override; |
1314 | virtual QSharedPointer<Printer> getPrinter(const QString &printerName) override; |
1315 | virtual QString defaultPrinterName() override; |
1316 | + |
1317 | + virtual void requestJobExtendedAttributes( |
1318 | + QSharedPointer<Printer> printer, |
1319 | + QSharedPointer<PrinterJob> job) override; |
1320 | virtual void requestPrinterDrivers() override; |
1321 | virtual void requestPrinter(const QString &printerName) override; |
1322 | virtual QMap<QString, QVariant> printerGetJobAttributes( |
1323 | @@ -105,6 +118,11 @@ |
1324 | virtual void refresh() override; |
1325 | void createSubscription(); |
1326 | |
1327 | + /* Starts a search for devices. Devices are synonymous with network devices |
1328 | + that may potentially be queues. This search will take at minimum 5 seconds, |
1329 | + normally longer. */ |
1330 | + void searchForDevices(); |
1331 | + |
1332 | Q_SIGNALS: |
1333 | void cancelWorkers(); |
1334 | void printerDriversLoaded(const QList<PrinterDriver> &drivers); |
1335 | @@ -120,16 +138,22 @@ |
1336 | QString getPrinterInstance(const QString &name) const; |
1337 | cups_dest_t* getDest(const QString &name) const; |
1338 | ppd_file_t* getPpd(const QString &name) const; |
1339 | + bool isExtendedAttribute(const QString &attributeName) const; |
1340 | + |
1341 | const QStringList m_knownQualityOptions; |
1342 | + const QStringList m_extendedAttributeNames; |
1343 | IppClient *m_client; |
1344 | QPrinterInfo m_info; |
1345 | OrgCupsCupsdNotifierInterface *m_notifier; |
1346 | int m_cupsSubscriptionId; |
1347 | mutable QMap<QString, cups_dest_t*> m_dests; // Printer name, dest. |
1348 | mutable QMap<QString, ppd_file_t*> m_ppds; // Printer name, ppd. |
1349 | - QSet<QString> m_activeRequests; |
1350 | + QSet<QString> m_activePrinterRequests; |
1351 | + QSet<QPair<QString, int>> m_activeJobRequests; |
1352 | |
1353 | private Q_SLOTS: |
1354 | + void onJobLoaded(QString printerName, int jobId, |
1355 | + QMap<QString, QVariant> attributes); |
1356 | void onPrinterLoaded(QSharedPointer<Printer> printer); |
1357 | }; |
1358 | |
1359 | |
1360 | === modified file 'modules/Ubuntu/Components/Extras/Printers/backend/backend_pdf.cpp' |
1361 | --- modules/Ubuntu/Components/Extras/Printers/backend/backend_pdf.cpp 2017-02-21 10:46:29 +0000 |
1362 | +++ modules/Ubuntu/Components/Extras/Printers/backend/backend_pdf.cpp 2017-03-23 12:51:36 +0000 |
1363 | @@ -59,6 +59,16 @@ |
1364 | ret[option] = QVariant::fromValue(models); |
1365 | } else if (option == QLatin1String("AcceptJobs")) { |
1366 | ret[option] = true; |
1367 | + } else if (option == QLatin1String("StateReasons")) { |
1368 | + ret[option] = QLatin1String(""); |
1369 | + } else if (option == QLatin1String("StateMessage")) { |
1370 | + ret[option] = QLatin1String(""); |
1371 | + } else if (option == QLatin1String("DeviceUri")) { |
1372 | + ret[option] = QLatin1String(""); |
1373 | + } else if (option == QLatin1String("Copies")) { |
1374 | + ret[option] = 0; |
1375 | + } else if (option == QLatin1String("Shared")) { |
1376 | + ret[option] = false; |
1377 | } else { |
1378 | throw std::invalid_argument("Invalid value for PDF printer: " + option.toStdString()); |
1379 | } |
1380 | |
1381 | === added file 'modules/Ubuntu/Components/Extras/Printers/cups/devicesearcher.cpp' |
1382 | --- modules/Ubuntu/Components/Extras/Printers/cups/devicesearcher.cpp 1970-01-01 00:00:00 +0000 |
1383 | +++ modules/Ubuntu/Components/Extras/Printers/cups/devicesearcher.cpp 2017-03-23 12:51:36 +0000 |
1384 | @@ -0,0 +1,70 @@ |
1385 | +/* |
1386 | + * Copyright (C) 2017 Canonical, Ltd. |
1387 | + * |
1388 | + * This program is free software; you can redistribute it and/or modify |
1389 | + * it under the terms of the GNU Lesser General Public License as published by |
1390 | + * the Free Software Foundation; version 3. |
1391 | + * |
1392 | + * This program is distributed in the hope that it will be useful, |
1393 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1394 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1395 | + * GNU Lesser General Public License for more details. |
1396 | + * |
1397 | + * You should have received a copy of the GNU Lesser General Public License |
1398 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1399 | + */ |
1400 | + |
1401 | +#include "cups/ippclient.h" |
1402 | +#include "devicesearcher.h" |
1403 | + |
1404 | +#include <QUrl> |
1405 | + |
1406 | +DeviceSearcher::DeviceSearcher(IppClient *client, QObject *parent) |
1407 | + : QObject(parent) |
1408 | + , m_client(client) |
1409 | +{ |
1410 | +} |
1411 | + |
1412 | +DeviceSearcher::~DeviceSearcher() |
1413 | +{ |
1414 | + delete m_client; |
1415 | +} |
1416 | + |
1417 | +void DeviceSearcher::load() |
1418 | +{ |
1419 | + if (!m_client->getDevices(&DeviceSearcher::deviceCallBack, this)) { |
1420 | + Q_EMIT failed(cupsLastErrorString()); |
1421 | + } |
1422 | + Q_EMIT finished(); |
1423 | +} |
1424 | + |
1425 | +void DeviceSearcher::deviceCallBack( |
1426 | + const char *deviceClass, |
1427 | + const char *deviceId, |
1428 | + const char *deviceInfo, |
1429 | + const char *deviceMakeAndModel, |
1430 | + const char *deviceUri, |
1431 | + const char *deviceLocation, |
1432 | + void *context) |
1433 | +{ |
1434 | + auto searcher = qobject_cast<DeviceSearcher*>(static_cast<QObject*>(context)); |
1435 | + if (!searcher) { |
1436 | + qWarning() << Q_FUNC_INFO << "context was not a DeviceSearcher."; |
1437 | + return; |
1438 | + } |
1439 | + |
1440 | + Device d; |
1441 | + d.cls = deviceClass; |
1442 | + d.id = deviceId; |
1443 | + d.info = deviceInfo; |
1444 | + d.makeModel = deviceMakeAndModel; |
1445 | + d.uri = deviceUri; |
1446 | + d.location = deviceLocation; |
1447 | + |
1448 | + searcher->deviceFound(d); |
1449 | +} |
1450 | + |
1451 | +void DeviceSearcher::deviceFound(const Device &device) |
1452 | +{ |
1453 | + Q_EMIT loaded(device); |
1454 | +} |
1455 | |
1456 | === added file 'modules/Ubuntu/Components/Extras/Printers/cups/devicesearcher.h' |
1457 | --- modules/Ubuntu/Components/Extras/Printers/cups/devicesearcher.h 1970-01-01 00:00:00 +0000 |
1458 | +++ modules/Ubuntu/Components/Extras/Printers/cups/devicesearcher.h 2017-03-23 12:51:36 +0000 |
1459 | @@ -0,0 +1,57 @@ |
1460 | +/* |
1461 | + * Copyright (C) 2017 Canonical, Ltd. |
1462 | + * |
1463 | + * This program is free software; you can redistribute it and/or modify |
1464 | + * it under the terms of the GNU Lesser General Public License as published by |
1465 | + * the Free Software Foundation; version 3. |
1466 | + * |
1467 | + * This program is distributed in the hope that it will be useful, |
1468 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1469 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1470 | + * GNU Lesser General Public License for more details. |
1471 | + * |
1472 | + * You should have received a copy of the GNU Lesser General Public License |
1473 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1474 | + */ |
1475 | + |
1476 | +#ifndef USC_PRINTERS_CUPS_DEVICESEARCHER_H |
1477 | +#define USC_PRINTERS_CUPS_DEVICESEARCHER_H |
1478 | + |
1479 | +#include "cups/ippclient.h" |
1480 | +#include "structs.h" |
1481 | + |
1482 | +#include <QObject> |
1483 | +#include <QString> |
1484 | + |
1485 | +/* Class representing a device search. It is a worker object due to the time |
1486 | +such a search takes (minimum 10 seconds). */ |
1487 | +class DeviceSearcher : public QObject |
1488 | +{ |
1489 | + Q_OBJECT |
1490 | + IppClient *m_client; |
1491 | +public: |
1492 | + explicit DeviceSearcher(IppClient *client = new IppClient, |
1493 | + QObject *parent = Q_NULLPTR); |
1494 | + ~DeviceSearcher(); |
1495 | + |
1496 | +public Q_SLOTS: |
1497 | + void load(); |
1498 | + |
1499 | +private: |
1500 | + static void deviceCallBack( |
1501 | + const char *cls, |
1502 | + const char *id, |
1503 | + const char *info, |
1504 | + const char *makeAndModel, |
1505 | + const char *uri, |
1506 | + const char *location, |
1507 | + void *context); |
1508 | + void deviceFound(const Device &device); |
1509 | + |
1510 | +Q_SIGNALS: |
1511 | + void loaded(const Device &device); |
1512 | + void failed(const QString &errorMessage); |
1513 | + void finished(); |
1514 | +}; |
1515 | + |
1516 | +#endif // USC_PRINTERS_CUPS_DEVICESEARCHER_H |
1517 | |
1518 | === modified file 'modules/Ubuntu/Components/Extras/Printers/cups/ippclient.cpp' |
1519 | --- modules/Ubuntu/Components/Extras/Printers/cups/ippclient.cpp 2017-02-21 10:46:29 +0000 |
1520 | +++ modules/Ubuntu/Components/Extras/Printers/cups/ippclient.cpp 2017-03-23 12:51:36 +0000 |
1521 | @@ -25,6 +25,7 @@ |
1522 | |
1523 | #include <QDebug> |
1524 | #include <QDateTime> |
1525 | +#include <QtAlgorithms> |
1526 | #include <QTimeZone> |
1527 | #include <QUrl> |
1528 | |
1529 | @@ -173,6 +174,30 @@ |
1530 | return postRequest(request, ppdFileName.toUtf8(), CupsResourceAdmin); |
1531 | } |
1532 | |
1533 | +bool IppClient::printerHoldJob(const QString &printerName, const int jobId) |
1534 | +{ |
1535 | + ipp_t *request = ippNewRequest(IPP_HOLD_JOB); |
1536 | + addPrinterUri(request, printerName); |
1537 | + addRequestingUsername(request, NULL); |
1538 | + |
1539 | + ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, |
1540 | + "job-id", jobId); |
1541 | + |
1542 | + return sendRequest(request, CupsResourceJobs); |
1543 | +} |
1544 | + |
1545 | +bool IppClient::printerReleaseJob(const QString &printerName, const int jobId) |
1546 | +{ |
1547 | + ipp_t *request = ippNewRequest(IPP_RELEASE_JOB); |
1548 | + addPrinterUri(request, printerName); |
1549 | + addRequestingUsername(request, NULL); |
1550 | + |
1551 | + ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, |
1552 | + "job-id", jobId); |
1553 | + |
1554 | + return sendRequest(request, CupsResourceJobs); |
1555 | +} |
1556 | + |
1557 | bool IppClient::printerSetDefault(const QString &printerName) |
1558 | { |
1559 | return sendNewSimpleRequest(CUPS_SET_DEFAULT, printerName.toUtf8(), |
1560 | @@ -225,9 +250,46 @@ |
1561 | } |
1562 | } |
1563 | |
1564 | +bool IppClient::printerSetCopies(const QString &printerName, const int &copies) |
1565 | +{ |
1566 | + ipp_t *request; |
1567 | + |
1568 | + if (!isPrinterNameValid(printerName)) { |
1569 | + setInternalStatus(QString("%1 is not a valid printer name.").arg(printerName)); |
1570 | + return false; |
1571 | + } |
1572 | + request = ippNewRequest(CUPS_ADD_MODIFY_PRINTER); |
1573 | + addPrinterUri(request, printerName); |
1574 | + addRequestingUsername(request, NULL); |
1575 | + ippAddInteger(request, IPP_TAG_PRINTER, IPP_TAG_INTEGER, |
1576 | + "copies-default", copies); |
1577 | + /* TODO: The request will fail if this was a printer class, and it should |
1578 | + be retried. */ |
1579 | + return sendRequest(request, CupsResourceAdmin); |
1580 | +} |
1581 | + |
1582 | +bool IppClient::printerSetShared(const QString &printerName, const bool shared) |
1583 | +{ |
1584 | + ipp_t *request; |
1585 | + |
1586 | + if (!isPrinterNameValid(printerName)) { |
1587 | + setInternalStatus(QString("%1 is not a valid printer name.").arg(printerName)); |
1588 | + return false; |
1589 | + } |
1590 | + |
1591 | + request = ippNewRequest(CUPS_ADD_MODIFY_PRINTER); |
1592 | + addPrinterUri(request, printerName); |
1593 | + addRequestingUsername(request, NULL); |
1594 | + ippAddBoolean(request, IPP_TAG_OPERATION, |
1595 | + "printer-is-shared", shared ? 1 : 0); |
1596 | + /* TODO: The request will fail if this was a printer class, and it should |
1597 | + be retried. */ |
1598 | + |
1599 | + return sendRequest(request, CupsResourceAdmin); |
1600 | +} |
1601 | |
1602 | bool IppClient::printerClassSetInfo(const QString &name, |
1603 | - const QString &info) |
1604 | + const QString &info) |
1605 | { |
1606 | if (!isPrinterNameValid(name)) { |
1607 | setInternalStatus(QString("%1 is not a valid printer name.").arg(name)); |
1608 | @@ -336,18 +398,99 @@ |
1609 | return retval; |
1610 | } |
1611 | |
1612 | -QMap<QString, QVariant> IppClient::printerGetJobAttributes(const int jobId) |
1613 | +QMap<QString, QVariant> IppClient::printerGetAttributes( |
1614 | + const QString &printerName, const QStringList &attributes) |
1615 | +{ |
1616 | + QMap<QString, QVariant> result; |
1617 | + QList<QByteArray*> attrByteArrays; |
1618 | + ipp_t *request; |
1619 | + ipp_attribute_t *attr; |
1620 | + |
1621 | + if (attributes.isEmpty()) { |
1622 | + return result; |
1623 | + } |
1624 | + |
1625 | + int i; |
1626 | + size_t n = attributes.size(); |
1627 | + char **attrs; |
1628 | + attrs = (char**) malloc ((n + 1) * sizeof (char *)); |
1629 | + |
1630 | + /* This is some trickery to compensate for a lack of C array/pointer |
1631 | + skills. The VLA attrs gets the underlying data structure of a QByterArray |
1632 | + on the heap. When cups is done with it, and in the end of this method, |
1633 | + the QByteArrays are deleted, and attrs is freed. */ |
1634 | + for (i = 0; i < ((int) n); i++) { |
1635 | + QByteArray *array = new QByteArray(attributes.value(i).toLocal8Bit()); |
1636 | + attrByteArrays << array; |
1637 | + attrs[i] = array->data(); |
1638 | + } |
1639 | + attrs[n] = NULL; |
1640 | + |
1641 | + request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES); |
1642 | + addPrinterUri(request, printerName); |
1643 | + addRequestingUsername(request, QString()); |
1644 | + ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, |
1645 | + "requested-attributes", attributes.size(), NULL, attrs); |
1646 | + auto resource = getResource(CupsResource::CupsResourceRoot); |
1647 | + |
1648 | + ipp_t *reply; |
1649 | + reply = cupsDoRequest(m_connection, request, resource.toUtf8()); |
1650 | + |
1651 | + if (!isReplyOk(reply, false)) { |
1652 | + qWarning() << Q_FUNC_INFO << "failed to get attributes" |
1653 | + << attributes << "for printer" << printerName; |
1654 | + } else { |
1655 | + for (attr = ippFirstAttribute(reply); attr; attr = ippNextAttribute(reply)) { |
1656 | + while (attr && ippGetGroupTag(attr) != IPP_TAG_PRINTER) |
1657 | + attr = ippNextAttribute(reply); |
1658 | + |
1659 | + if (!attr) |
1660 | + break; |
1661 | + |
1662 | + for (; attr && ippGetGroupTag(attr) == IPP_TAG_PRINTER; |
1663 | + attr = ippNextAttribute(reply)) { |
1664 | + |
1665 | + /* TODO: Here we need to take into account that the returned |
1666 | + attributes could be lists and other things. */ |
1667 | + result[ippGetName(attr)] = getAttributeValue(attr); |
1668 | + |
1669 | + if (!attr) |
1670 | + break; |
1671 | + } |
1672 | + } |
1673 | + } |
1674 | + |
1675 | + if (reply) |
1676 | + ippDelete(reply); |
1677 | + |
1678 | + qDeleteAll(attrByteArrays); |
1679 | + free(attrs); |
1680 | + |
1681 | + return result; |
1682 | +} |
1683 | + |
1684 | +QMap<QString, QVariant> IppClient::printerGetJobAttributes(const QString &printerName, |
1685 | + const int jobId) |
1686 | { |
1687 | ipp_t *request; |
1688 | QMap<QString, QVariant> map; |
1689 | |
1690 | + // Try to get the lock, if we can't after 5 seconds then fail and return |
1691 | + if (!m_thread_lock.tryLock(5000)) { |
1692 | + qWarning() << "Unable to get lock for IppClient::printerGetJobAttributes." |
1693 | + << "Unable to load attributes for job:" << jobId << " for " |
1694 | + << printerName; |
1695 | + return map; |
1696 | + } |
1697 | + |
1698 | // Construct request |
1699 | request = ippNewRequest(IPP_GET_JOB_ATTRIBUTES); |
1700 | - QString uri = QStringLiteral("ipp://localhost/jobs/") + QString::number(jobId); |
1701 | - qDebug() << "URI:" << uri; |
1702 | - |
1703 | - ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", NULL, uri.toStdString().data()); |
1704 | - |
1705 | + |
1706 | + addPrinterUri(request, printerName); |
1707 | + addRequestingUsername(request, NULL); |
1708 | + |
1709 | + ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, |
1710 | + "job-id", jobId); |
1711 | |
1712 | // Send request and construct reply |
1713 | ipp_t *reply; |
1714 | @@ -365,7 +508,8 @@ |
1715 | map.insert(ippGetName(attr), value); |
1716 | } |
1717 | } else { |
1718 | - qWarning() << "Not able to get attributes of job:" << jobId; |
1719 | + qWarning() << "Not able to get attributes of job:" << jobId << " for " |
1720 | + << printerName; |
1721 | } |
1722 | |
1723 | // Destruct the reply if valid |
1724 | @@ -373,6 +517,8 @@ |
1725 | ippDelete(reply); |
1726 | } |
1727 | |
1728 | + m_thread_lock.unlock(); |
1729 | + |
1730 | return map; |
1731 | } |
1732 | |
1733 | @@ -972,8 +1118,8 @@ |
1734 | case IPP_TAG_DATE: { |
1735 | time_t time = ippDateToTime(ippGetDate(attr, index)); |
1736 | QDateTime datetime; |
1737 | - datetime.setTimeZone(QTimeZone::systemTimeZone()); |
1738 | datetime.setTime_t(time); |
1739 | + datetime.setTimeZone(QTimeZone::utc()); |
1740 | |
1741 | var = QVariant::fromValue<QDateTime>(datetime); |
1742 | break; |
1743 | @@ -986,3 +1132,11 @@ |
1744 | |
1745 | return var; |
1746 | } |
1747 | + |
1748 | +bool IppClient::getDevices(cups_device_cb_t callback, void *context) const |
1749 | +{ |
1750 | + auto reply = cupsGetDevices(m_connection, CUPS_TIMEOUT_DEFAULT, |
1751 | + CUPS_INCLUDE_ALL, CUPS_EXCLUDE_NONE, callback, |
1752 | + context); |
1753 | + return reply == IPP_OK; |
1754 | +} |
1755 | |
1756 | === modified file 'modules/Ubuntu/Components/Extras/Printers/cups/ippclient.h' |
1757 | --- modules/Ubuntu/Components/Extras/Printers/cups/ippclient.h 2017-02-21 10:46:29 +0000 |
1758 | +++ modules/Ubuntu/Components/Extras/Printers/cups/ippclient.h 2017-03-23 12:51:36 +0000 |
1759 | @@ -19,11 +19,13 @@ |
1760 | |
1761 | #include "structs.h" |
1762 | |
1763 | +#include <cups/adminutil.h> |
1764 | #include <cups/cups.h> |
1765 | #include <cups/http.h> |
1766 | #include <cups/ipp.h> |
1767 | #include <cups/ppd.h> |
1768 | |
1769 | +#include <QMutex> |
1770 | #include <QString> |
1771 | #include <QStringList> |
1772 | |
1773 | @@ -51,17 +53,25 @@ |
1774 | const QString &ppdFileName, |
1775 | const QString &info, |
1776 | const QString &location); |
1777 | + bool printerHoldJob(const QString &printerName, const int jobId); |
1778 | + bool printerReleaseJob(const QString &printerName, const int jobId); |
1779 | bool printerSetDefault(const QString &printerName); |
1780 | bool printerSetEnabled(const QString &printerName, const bool enabled); |
1781 | bool printerSetAcceptJobs(const QString &printerName, const bool accept, |
1782 | const QString &reason); |
1783 | + bool printerSetCopies(const QString &printerName, const int &copies); |
1784 | + bool printerSetShared(const QString &printerName, const bool shared); |
1785 | bool printerClassSetInfo(const QString &name, const QString &info); |
1786 | bool printerClassSetOption(const QString &name, const QString &option, |
1787 | const QStringList &values); |
1788 | ppd_file_t* getPpdFile(const QString &name, const QString &instance) const; |
1789 | cups_dest_t* getDest(const QString &name, const QString &instance) const; |
1790 | |
1791 | - QMap<QString, QVariant> printerGetJobAttributes(const int jobId); |
1792 | + // Fetch more attributes, as normally just a small subset is fetched. |
1793 | + QMap<QString, QVariant> printerGetAttributes(const QString &printerName, |
1794 | + const QStringList &attributes); |
1795 | + QMap<QString, QVariant> printerGetJobAttributes(const QString &printerName, |
1796 | + const int jobId); |
1797 | |
1798 | QString getLastError() const; |
1799 | |
1800 | @@ -73,6 +83,7 @@ |
1801 | ); |
1802 | int createSubscription(); |
1803 | void cancelSubscription(const int &subscriptionId); |
1804 | + bool getDevices(cups_device_cb_t callback, void *context) const; |
1805 | |
1806 | private: |
1807 | enum CupsResource |
1808 | @@ -115,6 +126,7 @@ |
1809 | http_t *m_connection; |
1810 | ipp_status_t m_lastStatus = IPP_OK; |
1811 | mutable QString m_internalStatus = QString::null; |
1812 | + QMutex m_thread_lock; |
1813 | }; |
1814 | |
1815 | |
1816 | |
1817 | === added file 'modules/Ubuntu/Components/Extras/Printers/cups/jobloader.cpp' |
1818 | --- modules/Ubuntu/Components/Extras/Printers/cups/jobloader.cpp 1970-01-01 00:00:00 +0000 |
1819 | +++ modules/Ubuntu/Components/Extras/Printers/cups/jobloader.cpp 2017-03-23 12:51:36 +0000 |
1820 | @@ -0,0 +1,50 @@ |
1821 | +/* |
1822 | + * Copyright (C) 2017 Canonical, Ltd. |
1823 | + * |
1824 | + * This program is free software; you can redistribute it and/or modify |
1825 | + * it under the terms of the GNU Lesser General Public License as published by |
1826 | + * the Free Software Foundation; version 3. |
1827 | + * |
1828 | + * This program is distributed in the hope that it will be useful, |
1829 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1830 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1831 | + * GNU Lesser General Public License for more details. |
1832 | + * |
1833 | + * You should have received a copy of the GNU Lesser General Public License |
1834 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1835 | + */ |
1836 | + |
1837 | +#include "jobloader.h" |
1838 | + |
1839 | +#include "backend/backend.h" |
1840 | + |
1841 | +#include "printers/printers.h" |
1842 | + |
1843 | +#include <QDBusConnection> |
1844 | +#include <QPrinterInfo> |
1845 | + |
1846 | +class PrinterCupsBackend; |
1847 | +JobLoader::JobLoader(PrinterBackend *backend, |
1848 | + QString printerName, |
1849 | + int jobId, |
1850 | + QObject *parent) |
1851 | + : QObject(parent) |
1852 | + , m_backend(backend) |
1853 | + , m_job_id(jobId) |
1854 | + , m_printer_name(printerName) |
1855 | +{ |
1856 | +} |
1857 | + |
1858 | +JobLoader::~JobLoader() |
1859 | +{ |
1860 | +} |
1861 | + |
1862 | +void JobLoader::load() |
1863 | +{ |
1864 | + QMap<QString, QVariant> map = m_backend->printerGetJobAttributes( |
1865 | + m_printer_name, m_job_id |
1866 | + ); |
1867 | + |
1868 | + Q_EMIT loaded(m_printer_name, m_job_id, map); |
1869 | + Q_EMIT finished(); |
1870 | +} |
1871 | |
1872 | === added file 'modules/Ubuntu/Components/Extras/Printers/cups/jobloader.h' |
1873 | --- modules/Ubuntu/Components/Extras/Printers/cups/jobloader.h 1970-01-01 00:00:00 +0000 |
1874 | +++ modules/Ubuntu/Components/Extras/Printers/cups/jobloader.h 2017-03-23 12:51:36 +0000 |
1875 | @@ -0,0 +1,47 @@ |
1876 | +/* |
1877 | + * Copyright (C) 2017 Canonical, Ltd. |
1878 | + * |
1879 | + * This program is free software; you can redistribute it and/or modify |
1880 | + * it under the terms of the GNU Lesser General Public License as published by |
1881 | + * the Free Software Foundation; version 3. |
1882 | + * |
1883 | + * This program is distributed in the hope that it will be useful, |
1884 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1885 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1886 | + * GNU Lesser General Public License for more details. |
1887 | + * |
1888 | + * You should have received a copy of the GNU Lesser General Public License |
1889 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1890 | + */ |
1891 | + |
1892 | +#ifndef USC_PRINTERS_CUPS_JOBLOADER_H |
1893 | +#define USC_PRINTERS_CUPS_JOBLOADER_H |
1894 | + |
1895 | +#include "printer/printer.h" |
1896 | +#include "printer/printerjob.h" |
1897 | + |
1898 | +#include <QList> |
1899 | +#include <QObject> |
1900 | +#include <QSharedPointer> |
1901 | + |
1902 | +class JobLoader : public QObject |
1903 | +{ |
1904 | + Q_OBJECT |
1905 | + PrinterBackend *m_backend; |
1906 | + int m_job_id; |
1907 | + QString m_printer_name; |
1908 | +public: |
1909 | + explicit JobLoader(PrinterBackend *backend, |
1910 | + QString printerName, int jobId, |
1911 | + QObject *parent = Q_NULLPTR); |
1912 | + ~JobLoader(); |
1913 | + |
1914 | +public Q_SLOTS: |
1915 | + void load(); |
1916 | + |
1917 | +Q_SIGNALS: |
1918 | + void finished(); |
1919 | + void loaded(QString, int, QMap<QString, QVariant>); |
1920 | +}; |
1921 | + |
1922 | +#endif // USC_PRINTERS_CUPS_JOBLOADER_H |
1923 | |
1924 | === modified file 'modules/Ubuntu/Components/Extras/Printers/cups/printerloader.cpp' |
1925 | --- modules/Ubuntu/Components/Extras/Printers/cups/printerloader.cpp 2017-02-21 10:46:29 +0000 |
1926 | +++ modules/Ubuntu/Components/Extras/Printers/cups/printerloader.cpp 2017-03-23 12:51:36 +0000 |
1927 | @@ -18,6 +18,7 @@ |
1928 | #include "backend/backend_cups.h" |
1929 | #include "printerloader.h" |
1930 | |
1931 | +#include <QApplication> |
1932 | #include <QPrinterInfo> |
1933 | |
1934 | class PrinterCupsBackend; |
1935 | @@ -48,6 +49,8 @@ |
1936 | |
1937 | auto p = QSharedPointer<Printer>(new Printer(backend)); |
1938 | |
1939 | + p->moveToThread(QApplication::instance()->thread()); |
1940 | + |
1941 | Q_EMIT loaded(p); |
1942 | Q_EMIT finished(); |
1943 | } |
1944 | |
1945 | === modified file 'modules/Ubuntu/Components/Extras/Printers/enums.h' |
1946 | --- modules/Ubuntu/Components/Extras/Printers/enums.h 2017-02-23 14:04:26 +0000 |
1947 | +++ modules/Ubuntu/Components/Extras/Printers/enums.h 2017-03-23 12:51:36 +0000 |
1948 | @@ -101,6 +101,29 @@ |
1949 | PdfType, |
1950 | }; |
1951 | Q_ENUM(PrinterType) |
1952 | + |
1953 | + enum class DeviceType |
1954 | + { |
1955 | + UnknownType = 0, |
1956 | + LPDType, |
1957 | + IppSType, |
1958 | + Ipp14Type, |
1959 | + HttpType, |
1960 | + BehType, |
1961 | + SocketType, |
1962 | + HttpsType, |
1963 | + IppType, |
1964 | + HPType, |
1965 | + USBType, |
1966 | + HPFaxType, |
1967 | + DNSSDType, |
1968 | + }; |
1969 | + Q_ENUM(DeviceType) |
1970 | }; |
1971 | |
1972 | +inline uint qHash(const PrinterEnum::JobState &state, uint seed) |
1973 | +{ |
1974 | + return qHash((int) state, seed); |
1975 | +} |
1976 | + |
1977 | #endif // USC_PRINTERS_ENUMS_H |
1978 | |
1979 | === added file 'modules/Ubuntu/Components/Extras/Printers/models/devicemodel.cpp' |
1980 | --- modules/Ubuntu/Components/Extras/Printers/models/devicemodel.cpp 1970-01-01 00:00:00 +0000 |
1981 | +++ modules/Ubuntu/Components/Extras/Printers/models/devicemodel.cpp 2017-03-23 12:51:36 +0000 |
1982 | @@ -0,0 +1,149 @@ |
1983 | +/* |
1984 | + * Copyright (C) 2017 Canonical, Ltd. |
1985 | + * |
1986 | + * This program is free software; you can redistribute it and/or modify |
1987 | + * it under the terms of the GNU Lesser General Public License as published by |
1988 | + * the Free Software Foundation; version 3. |
1989 | + * |
1990 | + * This program is distributed in the hope that it will be useful, |
1991 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1992 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1993 | + * GNU Lesser General Public License for more details. |
1994 | + * |
1995 | + * You should have received a copy of the GNU Lesser General Public License |
1996 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1997 | + */ |
1998 | + |
1999 | +#include "backend/backend_cups.h" |
2000 | +#include "models/devicemodel.h" |
2001 | + |
2002 | +#include <QDebug> |
2003 | + |
2004 | +DeviceModel::DeviceModel(PrinterBackend *backend, QObject *parent) |
2005 | + : QAbstractListModel(parent) |
2006 | + , m_backend(backend) |
2007 | + , m_isSearching(false) |
2008 | +{ |
2009 | + connect(m_backend, SIGNAL(deviceFound(const Device&)), |
2010 | + this, SLOT(deviceLoaded(const Device&))); |
2011 | + connect(m_backend, SIGNAL(deviceSearchFinished()), |
2012 | + this, SLOT(deviceSearchFinished())); |
2013 | +} |
2014 | + |
2015 | +DeviceModel::~DeviceModel() |
2016 | +{ |
2017 | +} |
2018 | + |
2019 | +int DeviceModel::rowCount(const QModelIndex &parent) const |
2020 | +{ |
2021 | + Q_UNUSED(parent); |
2022 | + return m_devices.size(); |
2023 | +} |
2024 | + |
2025 | +int DeviceModel::count() const |
2026 | +{ |
2027 | + return rowCount(); |
2028 | +} |
2029 | + |
2030 | +QVariant DeviceModel::data(const QModelIndex &index, int role) const |
2031 | +{ |
2032 | + QVariant ret; |
2033 | + |
2034 | + if ((0 <= index.row()) && (index.row() < m_devices.size())) { |
2035 | + |
2036 | + auto device = m_devices[index.row()]; |
2037 | + |
2038 | + switch (role) { |
2039 | + case Qt::DisplayRole: |
2040 | + ret = device.toString(); |
2041 | + break; |
2042 | + case IdRole: |
2043 | + ret = device.id; |
2044 | + break; |
2045 | + case InfoRole: |
2046 | + ret = device.info; |
2047 | + break; |
2048 | + case UriRole: |
2049 | + ret = device.uri; |
2050 | + break; |
2051 | + case LocationRole: |
2052 | + ret = device.location; |
2053 | + break; |
2054 | + case TypeRole: |
2055 | + ret = QVariant::fromValue(device.type()); |
2056 | + break; |
2057 | + case MakeModelRole: |
2058 | + ret = device.makeModel; |
2059 | + break; |
2060 | + } |
2061 | + } |
2062 | + |
2063 | + return ret; |
2064 | +} |
2065 | + |
2066 | +QHash<int, QByteArray> DeviceModel::roleNames() const |
2067 | +{ |
2068 | + static QHash<int,QByteArray> names; |
2069 | + |
2070 | + if (Q_UNLIKELY(names.empty())) { |
2071 | + names[Qt::DisplayRole] = "displayName"; |
2072 | + names[IdRole] = "id"; |
2073 | + names[InfoRole] = "info"; |
2074 | + names[UriRole] = "uri"; |
2075 | + names[LocationRole] = "location"; |
2076 | + names[TypeRole] = "type"; |
2077 | + names[MakeModelRole] = "makeModel"; |
2078 | + } |
2079 | + |
2080 | + return names; |
2081 | +} |
2082 | + |
2083 | +void DeviceModel::deviceLoaded(const Device &device) |
2084 | +{ |
2085 | + if (!deviceWanted(device)) { |
2086 | + return; |
2087 | + } |
2088 | + |
2089 | + if (!m_devices.contains(device)) { |
2090 | + int i = m_devices.size(); |
2091 | + beginInsertRows(QModelIndex(), i, i); |
2092 | + m_devices.append(device); |
2093 | + endInsertRows(); |
2094 | + |
2095 | + Q_EMIT countChanged(); |
2096 | + } |
2097 | +} |
2098 | + |
2099 | +bool DeviceModel::deviceWanted(const Device &device) |
2100 | +{ |
2101 | + auto parts = device.uri.split(":", QString::SkipEmptyParts); |
2102 | + return parts.size() > 1; |
2103 | +} |
2104 | + |
2105 | +void DeviceModel::clear() |
2106 | +{ |
2107 | + beginResetModel(); |
2108 | + m_devices.clear(); |
2109 | + endResetModel(); |
2110 | +} |
2111 | + |
2112 | +void DeviceModel::load() |
2113 | +{ |
2114 | + if (m_isSearching) { |
2115 | + qWarning() << Q_FUNC_INFO |
2116 | + << "Ignoring load request as search is ongoing."; |
2117 | + } else { |
2118 | + clear(); |
2119 | + if (m_backend->type() == PrinterEnum::PrinterType::CupsType) { |
2120 | + ((PrinterCupsBackend*) m_backend)->searchForDevices(); |
2121 | + m_isSearching = true; |
2122 | + Q_EMIT searchingChanged(); |
2123 | + } |
2124 | + } |
2125 | +} |
2126 | + |
2127 | +void DeviceModel::deviceSearchFinished() |
2128 | +{ |
2129 | + m_isSearching = false; |
2130 | + Q_EMIT searchingChanged(); |
2131 | +} |
2132 | |
2133 | === added file 'modules/Ubuntu/Components/Extras/Printers/models/devicemodel.h' |
2134 | --- modules/Ubuntu/Components/Extras/Printers/models/devicemodel.h 1970-01-01 00:00:00 +0000 |
2135 | +++ modules/Ubuntu/Components/Extras/Printers/models/devicemodel.h 2017-03-23 12:51:36 +0000 |
2136 | @@ -0,0 +1,78 @@ |
2137 | +/* |
2138 | + * Copyright (C) 2017 Canonical, Ltd. |
2139 | + * |
2140 | + * This program is free software; you can redistribute it and/or modify |
2141 | + * it under the terms of the GNU Lesser General Public License as published by |
2142 | + * the Free Software Foundation; version 3. |
2143 | + * |
2144 | + * This program is distributed in the hope that it will be useful, |
2145 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
2146 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2147 | + * GNU Lesser General Public License for more details. |
2148 | + * |
2149 | + * You should have received a copy of the GNU Lesser General Public License |
2150 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
2151 | + */ |
2152 | + |
2153 | +#ifndef USC_PRINTER_DEVICEMODEL_H |
2154 | +#define USC_PRINTER_DEVICEMODEL_H |
2155 | + |
2156 | +#include "printers_global.h" |
2157 | + |
2158 | +#include "structs.h" |
2159 | + |
2160 | +#include <QAbstractListModel> |
2161 | +#include <QList> |
2162 | +#include <QModelIndex> |
2163 | +#include <QObject> |
2164 | +#include <QVariant> |
2165 | + |
2166 | +class PRINTERS_DECL_EXPORT DeviceModel : public QAbstractListModel |
2167 | +{ |
2168 | + Q_OBJECT |
2169 | + Q_PROPERTY(int count READ count NOTIFY countChanged) |
2170 | + Q_PROPERTY(bool searching MEMBER m_isSearching NOTIFY searchingChanged) |
2171 | +public: |
2172 | + explicit DeviceModel(PrinterBackend *backend, QObject *parent = Q_NULLPTR); |
2173 | + ~DeviceModel(); |
2174 | + |
2175 | + enum Roles |
2176 | + { |
2177 | + // Qt::DisplayRole holds device name |
2178 | + ClassRole = Qt::UserRole, |
2179 | + IdRole, |
2180 | + InfoRole, |
2181 | + UriRole, |
2182 | + LocationRole, |
2183 | + TypeRole, |
2184 | + MakeModelRole, |
2185 | + LastRole = MakeModelRole, |
2186 | + }; |
2187 | + |
2188 | + virtual int rowCount(const QModelIndex &parent = QModelIndex()) const override; |
2189 | + virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; |
2190 | + virtual QHash<int, QByteArray> roleNames() const override; |
2191 | + |
2192 | + int count() const; |
2193 | + void load(); |
2194 | + void clear(); |
2195 | + |
2196 | +private Q_SLOTS: |
2197 | + void deviceLoaded(const Device &device); |
2198 | + void deviceSearchFinished(); |
2199 | + |
2200 | +Q_SIGNALS: |
2201 | + void countChanged(); |
2202 | + void searchingChanged(); |
2203 | + |
2204 | +private: |
2205 | + /* Checks if we want it in the list or not. Wanted is intentionally |
2206 | + ambiguous. */ |
2207 | + bool deviceWanted(const Device &device); |
2208 | + |
2209 | + PrinterBackend *m_backend; |
2210 | + QList<Device> m_devices; |
2211 | + bool m_isSearching; |
2212 | +}; |
2213 | + |
2214 | +#endif // USC_PRINTER_DEVICEMODEL_H |
2215 | |
2216 | === modified file 'modules/Ubuntu/Components/Extras/Printers/models/jobmodel.cpp' |
2217 | --- modules/Ubuntu/Components/Extras/Printers/models/jobmodel.cpp 2017-02-23 14:04:26 +0000 |
2218 | +++ modules/Ubuntu/Components/Extras/Printers/models/jobmodel.cpp 2017-03-23 12:51:36 +0000 |
2219 | @@ -31,113 +31,190 @@ |
2220 | : QAbstractListModel(parent) |
2221 | , m_backend(backend) |
2222 | { |
2223 | - update(); |
2224 | - |
2225 | QObject::connect(m_backend, &PrinterBackend::jobCreated, |
2226 | - this, &JobModel::jobSignalCatchAll); |
2227 | + this, &JobModel::jobCreated); |
2228 | QObject::connect(m_backend, &PrinterBackend::jobState, |
2229 | - this, &JobModel::jobSignalCatchAll); |
2230 | + this, &JobModel::jobState); |
2231 | QObject::connect(m_backend, &PrinterBackend::jobCompleted, |
2232 | - this, &JobModel::jobSignalCatchAll); |
2233 | + this, &JobModel::jobCompleted); |
2234 | + |
2235 | + connect(m_backend, SIGNAL(jobLoaded(QString, int, QMap<QString, QVariant>)), |
2236 | + this, SLOT(updateJob(QString, int, QMap<QString, QVariant>))); |
2237 | + |
2238 | + // Impressions completed happens via printer state changed |
2239 | + QObject::connect(m_backend, &PrinterBackend::printerStateChanged, |
2240 | + &m_signalHandler, &SignalRateLimiter::onPrinterStateChanged); |
2241 | + |
2242 | + QObject::connect(&m_signalHandler, SIGNAL(printerModified(const QString&)), |
2243 | + this, SLOT(jobSignalPrinterModified(const QString&))); |
2244 | + |
2245 | + // Add already existing jobs |
2246 | + // TODO: even this should probably be in a background thread? |
2247 | + // so that it doesn't block startup? |
2248 | + Q_FOREACH(auto job, m_backend->printerGetJobs()) { |
2249 | + addJob(job); |
2250 | + } |
2251 | } |
2252 | |
2253 | JobModel::~JobModel() |
2254 | { |
2255 | } |
2256 | |
2257 | -void JobModel::jobSignalCatchAll( |
2258 | - const QString &text, const QString &printer_uri, |
2259 | - const QString &printer_name, uint printer_state, |
2260 | - const QString &printer_state_reasons, bool printer_is_accepting_jobs, |
2261 | - uint job_id, uint job_state, const QString &job_state_reasons, |
2262 | - const QString &job_name, uint job_impressions_completed) |
2263 | -{ |
2264 | - Q_UNUSED(text); |
2265 | - Q_UNUSED(printer_uri); |
2266 | - Q_UNUSED(printer_name); |
2267 | - Q_UNUSED(printer_state); |
2268 | - Q_UNUSED(printer_state_reasons); |
2269 | - Q_UNUSED(printer_is_accepting_jobs); |
2270 | - Q_UNUSED(job_id); |
2271 | +void JobModel::jobCreated( |
2272 | + const QString &text, const QString &printer_uri, |
2273 | + const QString &printer_name, uint printer_state, |
2274 | + const QString &printer_state_reasons, bool printer_is_accepting_jobs, |
2275 | + uint job_id, uint job_state, const QString &job_state_reasons, |
2276 | + const QString &job_name, uint job_impressions_completed) |
2277 | +{ |
2278 | + Q_UNUSED(text); // "Job Created" |
2279 | + Q_UNUSED(printer_uri); |
2280 | + Q_UNUSED(printer_state); |
2281 | + Q_UNUSED(printer_state_reasons); |
2282 | + Q_UNUSED(printer_is_accepting_jobs); |
2283 | + Q_UNUSED(job_state_reasons); |
2284 | + |
2285 | + QSharedPointer<PrinterJob> job = QSharedPointer<PrinterJob>( |
2286 | + new PrinterJob(printer_name, m_backend, job_id) |
2287 | + ); |
2288 | + job->setImpressionsCompleted(job_impressions_completed); |
2289 | + job->setState(static_cast<PrinterEnum::JobState>(job_state)); |
2290 | + job->setTitle(job_name); |
2291 | + |
2292 | + // Printers listens to rowInserted and spawns a JobLoader to set the |
2293 | + // printer of the job, which triggers the extended attributes to be loaded |
2294 | + // once complete this triggers JobModel::updateJob |
2295 | + addJob(job); |
2296 | +} |
2297 | + |
2298 | +void JobModel::jobState( |
2299 | + const QString &text, const QString &printer_uri, |
2300 | + const QString &printer_name, uint printer_state, |
2301 | + const QString &printer_state_reasons, bool printer_is_accepting_jobs, |
2302 | + uint job_id, uint job_state, const QString &job_state_reasons, |
2303 | + const QString &job_name, uint job_impressions_completed) |
2304 | +{ |
2305 | + Q_UNUSED(text); |
2306 | + Q_UNUSED(printer_uri); |
2307 | + Q_UNUSED(printer_state); |
2308 | + Q_UNUSED(printer_state_reasons); |
2309 | + Q_UNUSED(printer_is_accepting_jobs); |
2310 | + Q_UNUSED(job_state_reasons); |
2311 | + Q_UNUSED(job_name); |
2312 | + |
2313 | + QSharedPointer<PrinterJob> job = getJob(printer_name, job_id); |
2314 | + |
2315 | + if (job) { |
2316 | + job->setImpressionsCompleted(job_impressions_completed); |
2317 | + job->setState(static_cast<PrinterEnum::JobState>(job_state)); |
2318 | + |
2319 | + updateJob(job); |
2320 | + } else { |
2321 | + qWarning() << "JobModel::jobState for unknown job: " << job_name << " (" |
2322 | + << job_id << ") for " << printer_name; |
2323 | + } |
2324 | +} |
2325 | + |
2326 | +void JobModel::jobCompleted( |
2327 | + const QString &text, const QString &printer_uri, |
2328 | + const QString &printer_name, uint printer_state, |
2329 | + const QString &printer_state_reasons, bool printer_is_accepting_jobs, |
2330 | + uint job_id, uint job_state, const QString &job_state_reasons, |
2331 | + const QString &job_name, uint job_impressions_completed) |
2332 | +{ |
2333 | + Q_UNUSED(text); |
2334 | + Q_UNUSED(printer_uri); |
2335 | + Q_UNUSED(printer_state); |
2336 | + Q_UNUSED(printer_state_reasons); |
2337 | + Q_UNUSED(printer_is_accepting_jobs); |
2338 | Q_UNUSED(job_state); |
2339 | Q_UNUSED(job_state_reasons); |
2340 | Q_UNUSED(job_name); |
2341 | - |
2342 | - auto job = getJobById(job_id); |
2343 | - if (job) |
2344 | - job->setImpressionsCompleted(job_impressions_completed); |
2345 | - |
2346 | - update(); |
2347 | -} |
2348 | - |
2349 | -void JobModel::update() |
2350 | -{ |
2351 | - // Store the old count and get the new printers |
2352 | - int oldCount = m_jobs.size(); |
2353 | - QList<QSharedPointer<PrinterJob>> newJobs = m_backend->printerGetJobs(); |
2354 | - |
2355 | - // Go through the old model |
2356 | - for (int i=0; i < m_jobs.count(); i++) { |
2357 | - // Determine if the old printer exists in the new model |
2358 | - bool exists = false; |
2359 | - |
2360 | - Q_FOREACH(QSharedPointer<PrinterJob> p, newJobs) { |
2361 | - if (p->jobId() == m_jobs.at(i)->jobId()) { |
2362 | - exists = true; |
2363 | - |
2364 | - // Ensure the other properties of the job are up to date |
2365 | - if (!m_jobs.at(i)->deepCompare(p)) { |
2366 | - m_jobs.at(i)->updateFrom(p); |
2367 | - |
2368 | - Q_EMIT dataChanged(index(i), index(i)); |
2369 | - } |
2370 | - |
2371 | - break; |
2372 | - } |
2373 | - } |
2374 | - |
2375 | - // If it doesn't exist then remove it from the old model |
2376 | - if (!exists) { |
2377 | - beginRemoveRows(QModelIndex(), i, i); |
2378 | - QSharedPointer<PrinterJob> p = m_jobs.takeAt(i); |
2379 | - endRemoveRows(); |
2380 | - |
2381 | - i--; // as we have removed an item decrement |
2382 | - } |
2383 | - } |
2384 | - |
2385 | - // Go through the new model |
2386 | - for (int i=0; i < newJobs.count(); i++) { |
2387 | - // Determine if the new printer exists in the old model |
2388 | - bool exists = false; |
2389 | - int j; |
2390 | - |
2391 | - for (j=0; j < m_jobs.count(); j++) { |
2392 | - if (m_jobs.at(j)->jobId() == newJobs.at(i)->jobId()) { |
2393 | - exists = true; |
2394 | - break; |
2395 | - } |
2396 | - } |
2397 | - |
2398 | - if (exists) { |
2399 | - if (j == i) { // New printer exists and in correct position |
2400 | - continue; |
2401 | - } else { |
2402 | - // New printer does exist but needs to be moved in old model |
2403 | - beginMoveRows(QModelIndex(), j, 1, QModelIndex(), i); |
2404 | - m_jobs.move(j, i); |
2405 | - endMoveRows(); |
2406 | - } |
2407 | - } else { |
2408 | - // New printer does not exist insert into model |
2409 | - beginInsertRows(QModelIndex(), i, i); |
2410 | - m_jobs.insert(i, newJobs.at(i)); |
2411 | - endInsertRows(); |
2412 | - } |
2413 | - } |
2414 | - |
2415 | - if (oldCount != m_jobs.size()) { |
2416 | - Q_EMIT countChanged(); |
2417 | + Q_UNUSED(job_impressions_completed); |
2418 | + |
2419 | + auto job = getJob(printer_name, job_id); |
2420 | + if (job) { |
2421 | + removeJob(job); |
2422 | + } else { |
2423 | + qWarning() << "JobModel::jobCompleted for unknown job: " << job_name << " (" |
2424 | + << job_id << ") for " << printer_name; |
2425 | + } |
2426 | +} |
2427 | + |
2428 | +void JobModel::jobSignalPrinterModified(const QString &printerName) |
2429 | +{ |
2430 | + // Find the active or pending job and force a refresh |
2431 | + // We force refresh pending jobs incase there is a flood of signals |
2432 | + // meaning that the jobStateChanged signal might not have happened yet |
2433 | + Q_FOREACH(auto job, m_jobs) { |
2434 | + if (job->printerName() == printerName |
2435 | + && (job->state() == PrinterEnum::JobState::Processing |
2436 | + || job->state() == PrinterEnum::JobState::Pending)) { |
2437 | + Q_EMIT forceJobRefresh(printerName, job->jobId()); |
2438 | + } |
2439 | + } |
2440 | +} |
2441 | + |
2442 | +void JobModel::addJob(QSharedPointer<PrinterJob> job) |
2443 | +{ |
2444 | + int i = m_jobs.size(); |
2445 | + |
2446 | + beginInsertRows(QModelIndex(), i, i); |
2447 | + m_jobs.append(job); |
2448 | + endInsertRows(); |
2449 | + |
2450 | + Q_EMIT countChanged(); |
2451 | +} |
2452 | + |
2453 | +void JobModel::removeJob(QSharedPointer<PrinterJob> job) |
2454 | +{ |
2455 | + int i = m_jobs.indexOf(job); |
2456 | + |
2457 | + beginRemoveRows(QModelIndex(), i, i); |
2458 | + m_jobs.removeAt(i); |
2459 | + endRemoveRows(); |
2460 | + |
2461 | + Q_EMIT countChanged(); |
2462 | +} |
2463 | + |
2464 | +// This is used by JobModel::jobState as it has modified an existing job |
2465 | +void JobModel::updateJob(QSharedPointer<PrinterJob> job) |
2466 | +{ |
2467 | + int i = m_jobs.indexOf(job); |
2468 | + QModelIndex idx = index(i); |
2469 | + Q_EMIT dataChanged(idx, idx); |
2470 | +} |
2471 | + |
2472 | +// This is used by JobLoader as it gives us the oldJob and a newJob which has |
2473 | +// the extended attributes loaded. We then load the data from this newJob. |
2474 | +void JobModel::updateJob(QString printerName, int jobId, |
2475 | + QMap<QString, QVariant> attributes) |
2476 | +{ |
2477 | + QSharedPointer<PrinterJob> job = getJob(printerName, jobId); |
2478 | + |
2479 | + int i = m_jobs.indexOf(job); |
2480 | + QModelIndex idx = index(i); |
2481 | + |
2482 | + if (i > -1) { |
2483 | + job->loadAttributes(attributes); |
2484 | + |
2485 | + Q_EMIT dataChanged(idx, idx); |
2486 | + } else { |
2487 | + qWarning() << "Tried to updateJob which doesn't exist:" << printerName << jobId; |
2488 | + } |
2489 | +} |
2490 | + |
2491 | +void JobModel::updateJobPrinter(QSharedPointer<PrinterJob> job, QSharedPointer<Printer> printer) |
2492 | +{ |
2493 | + int i = m_jobs.indexOf(job); |
2494 | + QModelIndex idx = index(i); |
2495 | + |
2496 | + if (i > -1) { |
2497 | + job->setPrinter(printer); |
2498 | + |
2499 | + Q_EMIT dataChanged(idx, idx); |
2500 | + } else { |
2501 | + qWarning() << "Tried to updateJobPrinter which doesn't exist:" << printer->name() << job->jobId(); |
2502 | } |
2503 | } |
2504 | |
2505 | @@ -166,7 +243,8 @@ |
2506 | break; |
2507 | case ColorModelRole: { |
2508 | if (job->printer()) { |
2509 | - ret = job->printer()->supportedColorModels().at(job->colorModel()).text; |
2510 | + ColorModel m = job->printer()->supportedColorModels().at(job->colorModel()); |
2511 | + ret = m.text.isEmpty() ? m.name : m.text; |
2512 | } else { |
2513 | qWarning() << "Printer is undefined, no colorModel"; |
2514 | ret = ""; |
2515 | @@ -174,13 +252,13 @@ |
2516 | break; |
2517 | } |
2518 | case CompletedTimeRole: |
2519 | - ret = job->completedTime().toString(QLocale::system().dateTimeFormat()); |
2520 | + ret = job->completedTime().toLocalTime(); |
2521 | break; |
2522 | case CopiesRole: |
2523 | ret = job->copies(); |
2524 | break; |
2525 | case CreationTimeRole: |
2526 | - ret = job->creationTime().toString(QLocale::system().dateTimeFormat()); |
2527 | + ret = job->creationTime().toLocalTime(); |
2528 | break; |
2529 | case DuplexRole: { |
2530 | if (job->printer()) { |
2531 | @@ -194,6 +272,9 @@ |
2532 | case IdRole: |
2533 | ret = job->jobId(); |
2534 | break; |
2535 | + case HeldRole: |
2536 | + ret = job->state() == PrinterEnum::JobState::Held; |
2537 | + break; |
2538 | case ImpressionsCompletedRole: |
2539 | ret = job->impressionsCompleted(); |
2540 | break; |
2541 | @@ -213,11 +294,12 @@ |
2542 | ret = QVariant::fromValue<PrinterEnum::PrintRange>(job->printRangeMode()); |
2543 | break; |
2544 | case ProcessingTimeRole: |
2545 | - ret = job->processingTime().toString(QLocale::system().dateTimeFormat()); |
2546 | + ret = job->processingTime().toLocalTime(); |
2547 | break; |
2548 | case QualityRole: { |
2549 | if (job->printer()) { |
2550 | - ret = job->printer()->supportedPrintQualities().at(job->quality()).text; |
2551 | + PrintQuality q = job->printer()->supportedPrintQualities().at(job->quality()); |
2552 | + ret = q.text.isEmpty() ? q.name : q.text; |
2553 | } else { |
2554 | qWarning() << "Printer is undefined, no quality"; |
2555 | ret = ""; |
2556 | @@ -231,30 +313,7 @@ |
2557 | ret = job->size(); |
2558 | break; |
2559 | case StateRole: |
2560 | - // TODO: improve, for now have a switch |
2561 | - switch (job->state()) { |
2562 | - case PrinterEnum::JobState::Aborted: |
2563 | - ret = "Aborted"; |
2564 | - break; |
2565 | - case PrinterEnum::JobState::Canceled: |
2566 | - ret = "Canceled"; |
2567 | - break; |
2568 | - case PrinterEnum::JobState::Complete: |
2569 | - ret = "Compelete"; |
2570 | - break; |
2571 | - case PrinterEnum::JobState::Held: |
2572 | - ret = "Held"; |
2573 | - break; |
2574 | - case PrinterEnum::JobState::Pending: |
2575 | - ret = "Pending"; |
2576 | - break; |
2577 | - case PrinterEnum::JobState::Processing: |
2578 | - ret = "Processing"; |
2579 | - break; |
2580 | - case PrinterEnum::JobState::Stopped: |
2581 | - ret = "Stopped"; |
2582 | - break; |
2583 | - } |
2584 | + ret = QVariant::fromValue<PrinterEnum::JobState>(job->state()); |
2585 | break; |
2586 | case Qt::DisplayRole: |
2587 | case TitleRole: |
2588 | @@ -263,6 +322,15 @@ |
2589 | case UserRole: |
2590 | ret = job->user(); |
2591 | break; |
2592 | + case LastStateMessageRole: { |
2593 | + if (job->printer()) { |
2594 | + ret = job->printer()->lastMessage(); |
2595 | + } else { |
2596 | + ret = ""; |
2597 | + } |
2598 | + break; |
2599 | + } |
2600 | + |
2601 | } |
2602 | } |
2603 | |
2604 | @@ -283,6 +351,7 @@ |
2605 | names[CreationTimeRole] = "creationTime"; |
2606 | names[DuplexRole] = "duplexMode"; |
2607 | names[ImpressionsCompletedRole] = "impressionsCompleted"; |
2608 | + names[HeldRole] = "held"; |
2609 | names[LandscapeRole] = "landscape"; |
2610 | names[MessagesRole] = "messages"; |
2611 | names[PrinterNameRole] = "printerName"; |
2612 | @@ -315,10 +384,10 @@ |
2613 | return result; |
2614 | } |
2615 | |
2616 | -QSharedPointer<PrinterJob> JobModel::getJobById(const int &id) |
2617 | +QSharedPointer<PrinterJob> JobModel::getJob(const QString &printerName, const int &id) |
2618 | { |
2619 | Q_FOREACH(auto job, m_jobs) { |
2620 | - if (job->jobId() == id) { |
2621 | + if (job->printerName() == printerName && job->jobId() == id) { |
2622 | return job; |
2623 | } |
2624 | } |
2625 | @@ -367,6 +436,33 @@ |
2626 | return rowCount(); |
2627 | } |
2628 | |
2629 | +void JobFilter::filterOnActive() |
2630 | +{ |
2631 | + m_activeStates = QSet<PrinterEnum::JobState>{ |
2632 | + PrinterEnum::JobState::Processing, |
2633 | + }; |
2634 | + m_activeFilterEnabled = true; |
2635 | + invalidate(); |
2636 | +} |
2637 | + |
2638 | +void JobFilter::filterOnPaused() |
2639 | +{ |
2640 | + m_pausedStates = QSet<PrinterEnum::JobState>{ |
2641 | + PrinterEnum::JobState::Held, |
2642 | + }; |
2643 | + m_pausedFilterEnabled = true; |
2644 | + invalidate(); |
2645 | +} |
2646 | + |
2647 | +void JobFilter::filterOnQueued() |
2648 | +{ |
2649 | + m_queuedStates = QSet<PrinterEnum::JobState>{ |
2650 | + PrinterEnum::JobState::Pending, |
2651 | + }; |
2652 | + m_queuedFilterEnabled = true; |
2653 | + invalidate(); |
2654 | +} |
2655 | + |
2656 | void JobFilter::filterOnPrinterName(const QString &name) |
2657 | { |
2658 | m_printerName = name; |
2659 | @@ -386,5 +482,50 @@ |
2660 | accepts = m_printerName == printerName; |
2661 | } |
2662 | |
2663 | + if (accepts && m_activeFilterEnabled) { |
2664 | + PrinterEnum::JobState state = childIndex.model()->data( |
2665 | + childIndex, JobModel::StateRole |
2666 | + ).value<PrinterEnum::JobState>(); |
2667 | + |
2668 | + accepts = m_activeStates.contains(state); |
2669 | + } |
2670 | + |
2671 | + if (accepts && m_pausedFilterEnabled) { |
2672 | + PrinterEnum::JobState state = childIndex.model()->data( |
2673 | + childIndex, JobModel::StateRole |
2674 | + ).value<PrinterEnum::JobState>(); |
2675 | + |
2676 | + accepts = m_pausedStates.contains(state); |
2677 | + } |
2678 | + |
2679 | + if (accepts && m_queuedFilterEnabled) { |
2680 | + PrinterEnum::JobState state = childIndex.model()->data( |
2681 | + childIndex, JobModel::StateRole |
2682 | + ).value<PrinterEnum::JobState>(); |
2683 | + |
2684 | + accepts = m_queuedStates.contains(state); |
2685 | + } |
2686 | + |
2687 | return accepts; |
2688 | } |
2689 | + |
2690 | +bool JobFilter::lessThan(const QModelIndex &source_left, |
2691 | + const QModelIndex &source_right) const |
2692 | +{ |
2693 | + QVariant leftData = sourceModel()->data(source_left, sortRole()); |
2694 | + QVariant rightData = sourceModel()->data(source_right, sortRole()); |
2695 | + |
2696 | + if (sortRole() == (int) JobModel::CreationTimeRole) { |
2697 | + // If creationDateTime is the same, fallback to Id |
2698 | + if (leftData.toDateTime() == rightData.toDateTime()) { |
2699 | + int leftId = sourceModel()->data(source_left, JobModel::IdRole).toInt(); |
2700 | + int rightId = sourceModel()->data(source_right, JobModel::IdRole).toInt(); |
2701 | + |
2702 | + return leftId < rightId; |
2703 | + } else { |
2704 | + return leftData.toDateTime() < rightData.toDateTime(); |
2705 | + } |
2706 | + } else { |
2707 | + return leftData < rightData; |
2708 | + } |
2709 | +} |
2710 | |
2711 | === modified file 'modules/Ubuntu/Components/Extras/Printers/models/jobmodel.h' |
2712 | --- modules/Ubuntu/Components/Extras/Printers/models/jobmodel.h 2017-02-21 10:46:29 +0000 |
2713 | +++ modules/Ubuntu/Components/Extras/Printers/models/jobmodel.h 2017-03-23 12:51:36 +0000 |
2714 | @@ -20,6 +20,7 @@ |
2715 | #include "printers_global.h" |
2716 | #include "backend/backend.h" |
2717 | #include "printer/printerjob.h" |
2718 | +#include "printer/signalratelimiter.h" |
2719 | |
2720 | #include <QAbstractListModel> |
2721 | #include <QByteArray> |
2722 | @@ -30,6 +31,8 @@ |
2723 | #include <QTimer> |
2724 | #include <QVariant> |
2725 | |
2726 | +class PrinterBackend; |
2727 | +class PrinterJob; |
2728 | class PRINTERS_DECL_EXPORT JobModel : public QAbstractListModel |
2729 | { |
2730 | Q_OBJECT |
2731 | @@ -51,6 +54,7 @@ |
2732 | CopiesRole, |
2733 | CreationTimeRole, |
2734 | DuplexRole, |
2735 | + HeldRole, |
2736 | ImpressionsCompletedRole, |
2737 | LandscapeRole, |
2738 | MessagesRole, |
2739 | @@ -75,23 +79,46 @@ |
2740 | int count() const; |
2741 | |
2742 | Q_INVOKABLE QVariantMap get(const int row) const; |
2743 | - QSharedPointer<PrinterJob> getJobById(const int &id); |
2744 | + QSharedPointer<PrinterJob> getJob(const QString &printerName, const int &id); |
2745 | + |
2746 | + void updateJobPrinter(QSharedPointer<PrinterJob> job, QSharedPointer<Printer> printer); |
2747 | private: |
2748 | + void addJob(QSharedPointer<PrinterJob> job); |
2749 | + void removeJob(QSharedPointer<PrinterJob> job); |
2750 | + void updateJob(QSharedPointer<PrinterJob> Job); |
2751 | + |
2752 | PrinterBackend *m_backend; |
2753 | |
2754 | QList<QSharedPointer<PrinterJob>> m_jobs; |
2755 | + SignalRateLimiter m_signalHandler; |
2756 | private Q_SLOTS: |
2757 | - void update(); |
2758 | - void jobSignalCatchAll(const QString &text, const QString &printer_uri, |
2759 | - const QString &printer_name, uint printer_state, |
2760 | - const QString &printer_state_reasons, |
2761 | - bool printer_is_accepting_jobs, uint job_id, |
2762 | - uint job_state, const QString &job_state_reasons, |
2763 | - const QString &job_name, |
2764 | - uint job_impressions_completed); |
2765 | + void jobCreated(const QString &text, const QString &printer_uri, |
2766 | + const QString &printer_name, uint printer_state, |
2767 | + const QString &printer_state_reasons, |
2768 | + bool printer_is_accepting_jobs, uint job_id, |
2769 | + uint job_state, const QString &job_state_reasons, |
2770 | + const QString &job_name, |
2771 | + uint job_impressions_completed); |
2772 | + void jobState(const QString &text, const QString &printer_uri, |
2773 | + const QString &printer_name, uint printer_state, |
2774 | + const QString &printer_state_reasons, |
2775 | + bool printer_is_accepting_jobs, uint job_id, |
2776 | + uint job_state, const QString &job_state_reasons, |
2777 | + const QString &job_name, |
2778 | + uint job_impressions_completed); |
2779 | + void jobCompleted(const QString &text, const QString &printer_uri, |
2780 | + const QString &printer_name, uint printer_state, |
2781 | + const QString &printer_state_reasons, |
2782 | + bool printer_is_accepting_jobs, uint job_id, |
2783 | + uint job_state, const QString &job_state_reasons, |
2784 | + const QString &job_name, |
2785 | + uint job_impressions_completed); |
2786 | + void jobSignalPrinterModified(const QString &printerName); |
2787 | + void updateJob(QString printerName, int jobId, QMap<QString, QVariant> attributes); |
2788 | |
2789 | Q_SIGNALS: |
2790 | void countChanged(); |
2791 | + void forceJobRefresh(const QString &printerName, const int jobId); |
2792 | }; |
2793 | |
2794 | class PRINTERS_DECL_EXPORT JobFilter : public QSortFilterProxyModel |
2795 | @@ -102,13 +129,19 @@ |
2796 | explicit JobFilter(QObject *parent = Q_NULLPTR); |
2797 | ~JobFilter(); |
2798 | |
2799 | - Q_INVOKABLE QVariantMap get(const int row) const; |
2800 | - |
2801 | void filterOnPrinterName(const QString &name); |
2802 | int count() const; |
2803 | +public Q_SLOTS: |
2804 | + QVariantMap get(const int row) const; |
2805 | + |
2806 | + void filterOnActive(); |
2807 | + void filterOnQueued(); |
2808 | + void filterOnPaused(); |
2809 | protected: |
2810 | virtual bool filterAcceptsRow( |
2811 | int sourceRow, const QModelIndex &sourceParent) const override; |
2812 | + virtual bool lessThan(const QModelIndex &source_left, |
2813 | + const QModelIndex &source_right) const; |
2814 | |
2815 | Q_SIGNALS: |
2816 | void countChanged(); |
2817 | @@ -120,6 +153,15 @@ |
2818 | private: |
2819 | QString m_printerName = QString::null; |
2820 | bool m_printerNameFilterEnabled = false; |
2821 | + |
2822 | + bool m_activeFilterEnabled = false; |
2823 | + QSet<PrinterEnum::JobState> m_activeStates; |
2824 | + |
2825 | + bool m_queuedFilterEnabled = false; |
2826 | + QSet<PrinterEnum::JobState> m_queuedStates; |
2827 | + |
2828 | + bool m_pausedFilterEnabled = false; |
2829 | + QSet<PrinterEnum::JobState> m_pausedStates; |
2830 | }; |
2831 | |
2832 | #endif // USC_JOB_MODEL_H |
2833 | |
2834 | === modified file 'modules/Ubuntu/Components/Extras/Printers/models/printermodel.cpp' |
2835 | --- modules/Ubuntu/Components/Extras/Printers/models/printermodel.cpp 2017-02-23 14:04:26 +0000 |
2836 | +++ modules/Ubuntu/Components/Extras/Printers/models/printermodel.cpp 2017-03-23 12:51:36 +0000 |
2837 | @@ -22,6 +22,7 @@ |
2838 | #include "utils.h" |
2839 | |
2840 | #include <QDebug> |
2841 | +#include <QUrl> |
2842 | |
2843 | PrinterModel::PrinterModel(PrinterBackend *backend, QObject *parent) |
2844 | : QAbstractListModel(parent) |
2845 | @@ -31,9 +32,9 @@ |
2846 | QObject::connect(m_backend, &PrinterBackend::printerAdded, |
2847 | this, &PrinterModel::printerAdded); |
2848 | QObject::connect(m_backend, &PrinterBackend::printerModified, |
2849 | - &m_signalHandler, &PrinterSignalHandler::onPrinterModified); |
2850 | + &m_signalHandler, &SignalRateLimiter::onPrinterModified); |
2851 | QObject::connect(m_backend, &PrinterBackend::printerStateChanged, |
2852 | - &m_signalHandler, &PrinterSignalHandler::onPrinterModified); |
2853 | + &m_signalHandler, &SignalRateLimiter::onPrinterModified); |
2854 | QObject::connect(m_backend, &PrinterBackend::printerDeleted, |
2855 | this, &PrinterModel::printerDeleted); |
2856 | |
2857 | @@ -63,20 +64,18 @@ |
2858 | |
2859 | void PrinterModel::printerLoaded(QSharedPointer<Printer> printer) |
2860 | { |
2861 | - // Find and possibly replace an old printer. |
2862 | - for (int i=0; i < m_printers.count(); i++) { |
2863 | - auto oldPrinter = m_printers.at(i); |
2864 | - if (printer->name() == oldPrinter->name()) { |
2865 | - if (!oldPrinter->deepCompare(printer)) { |
2866 | - updatePrinter(oldPrinter, printer); |
2867 | - } |
2868 | + // If there is an existing printer then get it |
2869 | + QSharedPointer<Printer> oldPrinter = getPrinterByName(printer->name()); |
2870 | |
2871 | - // We're done. |
2872 | - return; |
2873 | + if (oldPrinter) { |
2874 | + // Check if the existing printer needs updating |
2875 | + if (!oldPrinter->deepCompare(printer)) { |
2876 | + updatePrinter(oldPrinter, printer); |
2877 | } |
2878 | + } else { |
2879 | + // There isn't an existing printer so add it |
2880 | + addPrinter(printer, CountChangeSignal::Emit); |
2881 | } |
2882 | - |
2883 | - addPrinter(printer, CountChangeSignal::Emit); |
2884 | } |
2885 | |
2886 | void PrinterModel::printerModified(const QString &printerName) |
2887 | @@ -97,6 +96,12 @@ |
2888 | Q_UNUSED(printerStateReason); |
2889 | Q_UNUSED(acceptingJobs); |
2890 | |
2891 | + // If there isn't an existing printer then add a proxy printer |
2892 | + if (!getPrinterByName(printerName)) { |
2893 | + auto p = QSharedPointer<Printer>(new Printer(new PrinterBackend(printerName))); |
2894 | + addPrinter(p); |
2895 | + } |
2896 | + |
2897 | m_backend->requestPrinter(printerName); |
2898 | } |
2899 | |
2900 | @@ -126,22 +131,6 @@ |
2901 | return QSharedPointer<Printer>(Q_NULLPTR); |
2902 | } |
2903 | |
2904 | -void PrinterModel::movePrinter(const int &from, const int &to) |
2905 | -{ |
2906 | - int size = m_printers.size(); |
2907 | - if (from < 0 || to < 0 || from >= size || to >= size) { |
2908 | - qWarning() << Q_FUNC_INFO << "Illegal move operation from" |
2909 | - << from << "to" << to << ". Size was" << size; |
2910 | - return; |
2911 | - } |
2912 | - if (!beginMoveRows(QModelIndex(), from, from, QModelIndex(), to)) { |
2913 | - qWarning() << Q_FUNC_INFO << "failed to move rows."; |
2914 | - return; |
2915 | - } |
2916 | - m_printers.move(from, to); |
2917 | - endMoveRows(); |
2918 | -} |
2919 | - |
2920 | void PrinterModel::removePrinter(QSharedPointer<Printer> printer, const CountChangeSignal ¬ify) |
2921 | { |
2922 | int idx = m_printers.indexOf(printer); |
2923 | @@ -214,6 +203,15 @@ |
2924 | case Qt::DisplayRole: |
2925 | ret = printer->name(); |
2926 | break; |
2927 | + case DeviceUriRole: |
2928 | + ret = printer->deviceUri(); |
2929 | + break; |
2930 | + case HostNameRole: |
2931 | + ret = QUrl(printer->deviceUri()).host(); |
2932 | + break; |
2933 | + case MakeRole: |
2934 | + ret = printer->make(); |
2935 | + break; |
2936 | case ColorModelRole: |
2937 | ret = printer->supportedColorModels().indexOf(printer->defaultColorModel()); |
2938 | break; |
2939 | @@ -248,6 +246,9 @@ |
2940 | case DescriptionRole: |
2941 | ret = printer->description(); |
2942 | break; |
2943 | + case LocationRole: |
2944 | + ret = printer->location(); |
2945 | + break; |
2946 | case PageSizeRole: |
2947 | ret = printer->defaultPageSize().name(); |
2948 | break; |
2949 | @@ -274,15 +275,27 @@ |
2950 | case IsRawRole: |
2951 | ret = !printer->holdsDefinition(); |
2952 | break; |
2953 | + case IsRemoteRole: |
2954 | + ret = printer->isRemote(); |
2955 | + break; |
2956 | + case LastMessageRole: |
2957 | + ret = printer->lastMessage(); |
2958 | + break; |
2959 | case JobRole: |
2960 | ret = QVariant::fromValue(printer->jobs()); |
2961 | break; |
2962 | + case CopiesRole: |
2963 | + ret = printer->copies(); |
2964 | + break; |
2965 | case EnabledRole: |
2966 | ret = printer->enabled(); |
2967 | break; |
2968 | case AcceptJobsRole: |
2969 | ret = printer->acceptJobs(); |
2970 | break; |
2971 | + case SharedRole: |
2972 | + ret = printer->shared(); |
2973 | + break; |
2974 | } |
2975 | } |
2976 | |
2977 | @@ -338,6 +351,11 @@ |
2978 | case AcceptJobsRole: |
2979 | printer->setAcceptJobs(value.toBool()); |
2980 | break; |
2981 | + case CopiesRole: |
2982 | + printer->setCopies(value.toInt()); |
2983 | + case SharedRole: |
2984 | + printer->setShared(value.toBool()); |
2985 | + break; |
2986 | } |
2987 | } |
2988 | |
2989 | @@ -356,12 +374,16 @@ |
2990 | names[DuplexRole] = "duplexMode"; |
2991 | names[SupportedDuplexModesRole] = "supportedDuplexModes"; |
2992 | names[NameRole] = "name"; |
2993 | + names[DeviceUriRole] = "deviceUri"; |
2994 | + names[HostNameRole] = "hostname"; |
2995 | + names[MakeRole] = "make"; |
2996 | names[EnabledRole] = "printerEnabled"; |
2997 | names[AcceptJobsRole] = "acceptJobs"; |
2998 | - names[PdfModeRole] = "pdfMode"; |
2999 | + names[SharedRole] = "shared"; |
3000 | names[PrintQualityRole] = "printQuality"; |
3001 | names[SupportedPrintQualitiesRole] = "supportedPrintQualities"; |
3002 | names[DescriptionRole] = "description"; |
3003 | + names[LocationRole] = "location"; |
3004 | names[PageSizeRole] = "pageSize"; |
3005 | names[SupportedPageSizesRole] = "supportedPageSizes"; |
3006 | names[StateRole] = "state"; |
3007 | @@ -369,6 +391,9 @@ |
3008 | names[IsPdfRole] = "isPdf"; |
3009 | names[IsLoadedRole] = "isLoaded"; |
3010 | names[IsRawRole] = "isRaw"; |
3011 | + names[IsRemoteRole] = "isRemote"; |
3012 | + names[LastMessageRole] = "lastMessage"; |
3013 | + names[CopiesRole] = "copies"; |
3014 | names[JobRole] = "jobs"; |
3015 | } |
3016 | |
3017 | @@ -447,6 +472,13 @@ |
3018 | m_pdf = pdf; |
3019 | } |
3020 | |
3021 | +void PrinterFilter::filterOnRemote(const bool remote) |
3022 | +{ |
3023 | + m_remoteEnabled = true; |
3024 | + m_remote = remote; |
3025 | + invalidate(); |
3026 | +} |
3027 | + |
3028 | bool PrinterFilter::filterAcceptsRow(int sourceRow, |
3029 | const QModelIndex &sourceParent) const |
3030 | { |
3031 | @@ -472,6 +504,14 @@ |
3032 | accepts = m_state == state; |
3033 | } |
3034 | |
3035 | + // If m_remote is true, we only show remote printers. |
3036 | + if (accepts && m_remoteEnabled) { |
3037 | + const bool isRemote = childIndex.model()->data( |
3038 | + childIndex, PrinterModel::IsRemoteRole |
3039 | + ).toBool(); |
3040 | + accepts = m_remote == isRemote; |
3041 | + } |
3042 | + |
3043 | return accepts; |
3044 | } |
3045 | bool PrinterFilter::lessThan(const QModelIndex &left, |
3046 | |
3047 | === modified file 'modules/Ubuntu/Components/Extras/Printers/models/printermodel.h' |
3048 | --- modules/Ubuntu/Components/Extras/Printers/models/printermodel.h 2017-02-23 14:04:26 +0000 |
3049 | +++ modules/Ubuntu/Components/Extras/Printers/models/printermodel.h 2017-03-23 12:51:36 +0000 |
3050 | @@ -21,7 +21,7 @@ |
3051 | |
3052 | #include "models/jobmodel.h" |
3053 | #include "printer/printer.h" |
3054 | -#include "printer/printersignalhandler.h" |
3055 | +#include "printer/signalratelimiter.h" |
3056 | |
3057 | #include <QAbstractListModel> |
3058 | #include <QByteArray> |
3059 | @@ -47,9 +47,13 @@ |
3060 | DuplexRole, |
3061 | SupportedDuplexModesRole, |
3062 | NameRole, |
3063 | + DeviceUriRole, |
3064 | + HostNameRole, |
3065 | + MakeRole, |
3066 | + LocationRole, |
3067 | EnabledRole, |
3068 | AcceptJobsRole, |
3069 | - PdfModeRole, |
3070 | + SharedRole, |
3071 | PrintQualityRole, |
3072 | SupportedPrintQualitiesRole, |
3073 | DescriptionRole, |
3074 | @@ -68,6 +72,9 @@ |
3075 | |
3076 | /* Indicates that this printers has no associated PPD. */ |
3077 | IsRawRole, |
3078 | + IsRemoteRole, |
3079 | + LastMessageRole, |
3080 | + CopiesRole, |
3081 | JobRole, |
3082 | LastRole = JobRole, |
3083 | }; |
3084 | @@ -92,13 +99,12 @@ |
3085 | const CountChangeSignal ¬ify = CountChangeSignal::Defer); |
3086 | void removePrinter(QSharedPointer<Printer> printer, |
3087 | const CountChangeSignal ¬ify = CountChangeSignal::Defer); |
3088 | - void movePrinter(const int &from, const int &to); |
3089 | void updatePrinter(QSharedPointer<Printer> old, |
3090 | QSharedPointer<Printer> newPrinter); |
3091 | PrinterBackend *m_backend; |
3092 | |
3093 | QList<QSharedPointer<Printer>> m_printers; |
3094 | - PrinterSignalHandler m_signalHandler; |
3095 | + SignalRateLimiter m_signalHandler; |
3096 | |
3097 | private Q_SLOTS: |
3098 | void printerLoaded(QSharedPointer<Printer> printer); |
3099 | @@ -127,6 +133,7 @@ |
3100 | void filterOnState(const PrinterEnum::State &state); |
3101 | void filterOnRecent(const bool recent); |
3102 | void filterOnPdf(const bool pdf); |
3103 | + void filterOnRemote(const bool remote); |
3104 | |
3105 | int count() const; |
3106 | protected: |
3107 | @@ -149,6 +156,8 @@ |
3108 | bool m_recentEnabled = false; |
3109 | bool m_pdfEnabled = false; |
3110 | bool m_pdf = false; |
3111 | + bool m_remoteEnabled = false; |
3112 | + bool m_remote = false; |
3113 | }; |
3114 | |
3115 | #endif // USC_PRINTER_MODEL_H |
3116 | |
3117 | === modified file 'modules/Ubuntu/Components/Extras/Printers/plugin.cpp' |
3118 | --- modules/Ubuntu/Components/Extras/Printers/plugin.cpp 2017-02-21 11:31:44 +0000 |
3119 | +++ modules/Ubuntu/Components/Extras/Printers/plugin.cpp 2017-03-23 12:51:36 +0000 |
3120 | @@ -54,5 +54,7 @@ |
3121 | |
3122 | qmlRegisterUncreatableType<PrinterEnum>(uri, 0, 1, "PrinterEnum", "Is an enum"); |
3123 | qRegisterMetaType<QList<PrinterDriver>>("QList<PrinterDriver>"); |
3124 | + qRegisterMetaType<QSharedPointer<PrinterJob>>("QSharedPointer<PrinterJob>"); |
3125 | qRegisterMetaType<QList<QSharedPointer<Printer>>>("QList<QSharedPointer<Printer>>"); |
3126 | + qRegisterMetaType<Device>("Device"); |
3127 | } |
3128 | |
3129 | === modified file 'modules/Ubuntu/Components/Extras/Printers/printer/printer.cpp' |
3130 | --- modules/Ubuntu/Components/Extras/Printers/printer/printer.cpp 2017-02-24 17:47:22 +0000 |
3131 | +++ modules/Ubuntu/Components/Extras/Printers/printer/printer.cpp 2017-03-23 12:51:36 +0000 |
3132 | @@ -14,22 +14,26 @@ |
3133 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
3134 | */ |
3135 | |
3136 | +#include "i18n.h" |
3137 | #include "utils.h" |
3138 | |
3139 | #include "printer.h" |
3140 | |
3141 | #include <QDebug> |
3142 | +#include <QLocale> |
3143 | #include <QQmlEngine> |
3144 | |
3145 | Printer::Printer(PrinterBackend *backend, QObject *parent) |
3146 | : QObject(parent) |
3147 | , m_backend(backend) |
3148 | { |
3149 | - loadColorModel(); |
3150 | - loadPrintQualities(); |
3151 | - loadAcceptJobs(); |
3152 | + loadAttributes(); |
3153 | |
3154 | + m_jobs.setParent(this); |
3155 | m_jobs.filterOnPrinterName(name()); |
3156 | + |
3157 | + QObject::connect(m_backend, &PrinterBackend::printerStateChanged, |
3158 | + this, &Printer::onPrinterStateChanged); |
3159 | } |
3160 | |
3161 | Printer::~Printer() |
3162 | @@ -41,48 +45,87 @@ |
3163 | { |
3164 | if (!m_jobs.sourceModel()) { |
3165 | m_jobs.setSourceModel(jobModel); |
3166 | - m_jobs.sort(JobModel::Roles::IdRole); |
3167 | } |
3168 | } |
3169 | |
3170 | -void Printer::loadAcceptJobs() |
3171 | +void Printer::updateAcceptJobs(const QMap<QString, QVariant> &serverAttrs) |
3172 | { |
3173 | - auto opt = QStringLiteral("AcceptJobs"); |
3174 | - m_acceptJobs = m_backend->printerGetOption(name(), opt).toBool(); |
3175 | + m_acceptJobs = serverAttrs.value(QStringLiteral("AcceptJobs")).toBool(); |
3176 | } |
3177 | |
3178 | -void Printer::loadColorModel() |
3179 | +void Printer::updateColorModel(const QMap<QString, QVariant> &serverAttrs) |
3180 | { |
3181 | auto defModel = QStringLiteral("DefaultColorModel"); |
3182 | auto models = QStringLiteral("SupportedColorModels"); |
3183 | - auto result = m_backend->printerGetOptions( |
3184 | - name(), QStringList({defModel, models}) |
3185 | - ); |
3186 | |
3187 | - m_defaultColorModel = result.value(defModel).value<ColorModel>(); |
3188 | - m_supportedColorModels = result.value(models).value<QList<ColorModel>>(); |
3189 | + m_defaultColorModel = serverAttrs.value(defModel).value<ColorModel>(); |
3190 | + m_supportedColorModels = serverAttrs.value(models).value<QList<ColorModel>>(); |
3191 | |
3192 | if (m_supportedColorModels.size() == 0) { |
3193 | m_supportedColorModels.append(m_defaultColorModel); |
3194 | } |
3195 | } |
3196 | |
3197 | -void Printer::loadPrintQualities() |
3198 | +void Printer::updatePrintQualities(const QMap<QString, QVariant> &serverAttrs) |
3199 | { |
3200 | auto defQuality = QStringLiteral("DefaultPrintQuality"); |
3201 | auto qualities = QStringLiteral("SupportedPrintQualities"); |
3202 | - auto result = m_backend->printerGetOptions( |
3203 | - name(), QStringList({defQuality, qualities}) |
3204 | - ); |
3205 | |
3206 | - m_supportedPrintQualities = result.value(qualities).value<QList<PrintQuality>>(); |
3207 | - m_defaultPrintQuality = result.value(defQuality).value<PrintQuality>(); |
3208 | + m_supportedPrintQualities = serverAttrs.value(qualities).value<QList<PrintQuality>>(); |
3209 | + m_defaultPrintQuality = serverAttrs.value(defQuality).value<PrintQuality>(); |
3210 | |
3211 | if (m_supportedPrintQualities.size() == 0) { |
3212 | m_supportedPrintQualities.append(m_defaultPrintQuality); |
3213 | } |
3214 | } |
3215 | |
3216 | +void Printer::updateLastMessage(const QMap<QString, QVariant> &serverAttrs) |
3217 | +{ |
3218 | + m_stateMessage = serverAttrs.value(QStringLiteral("StateMessage")).toString(); |
3219 | +} |
3220 | + |
3221 | + |
3222 | +void Printer::updateDeviceUri(const QMap<QString, QVariant> &serverAttrs) |
3223 | +{ |
3224 | + m_deviceUri = serverAttrs.value(QStringLiteral("DeviceUri")).toString(); |
3225 | +} |
3226 | + |
3227 | +void Printer::updateCopies(const QMap<QString, QVariant> &serverAttrs) |
3228 | +{ |
3229 | + m_copies = serverAttrs.value(QStringLiteral("Copies")).toInt(); |
3230 | + if (m_copies <= 0) |
3231 | + m_copies = 1; |
3232 | +} |
3233 | + |
3234 | +void Printer::updateShared(const QMap<QString, QVariant> &serverAttrs) |
3235 | +{ |
3236 | + m_shared = serverAttrs.value(QStringLiteral("Shared")).toBool(); |
3237 | +} |
3238 | + |
3239 | +void Printer::loadAttributes() |
3240 | +{ |
3241 | + auto opts = QStringList({ |
3242 | + QStringLiteral("AcceptJobs"), |
3243 | + QStringLiteral("DefaultColorModel"), |
3244 | + QStringLiteral("SupportedColorModels"), |
3245 | + QStringLiteral("DefaultPrintQuality"), |
3246 | + QStringLiteral("SupportedPrintQualities"), |
3247 | + QStringLiteral("StateMessage"), |
3248 | + QStringLiteral("DeviceUri"), |
3249 | + QStringLiteral("Copies"), |
3250 | + QStringLiteral("Shared"), |
3251 | + }); |
3252 | + auto result = m_backend->printerGetOptions(name(), opts); |
3253 | + |
3254 | + updateAcceptJobs(result); |
3255 | + updateColorModel(result); |
3256 | + updatePrintQualities(result); |
3257 | + updateLastMessage(result); |
3258 | + updateDeviceUri(result); |
3259 | + updateCopies(result); |
3260 | + updateShared(result); |
3261 | +} |
3262 | + |
3263 | ColorModel Printer::defaultColorModel() const |
3264 | { |
3265 | return m_defaultColorModel; |
3266 | @@ -135,11 +178,26 @@ |
3267 | return m_backend->printerName(); |
3268 | } |
3269 | |
3270 | +QString Printer::deviceUri() const |
3271 | +{ |
3272 | + return m_deviceUri; |
3273 | +} |
3274 | + |
3275 | +QString Printer::make() const |
3276 | +{ |
3277 | + return m_backend->makeAndModel(); |
3278 | +} |
3279 | + |
3280 | QString Printer::description() const |
3281 | { |
3282 | return m_backend->description(); |
3283 | } |
3284 | |
3285 | +QString Printer::location() const |
3286 | +{ |
3287 | + return m_backend->location(); |
3288 | +} |
3289 | + |
3290 | QPageSize Printer::defaultPageSize() const |
3291 | { |
3292 | return m_backend->defaultPageSize(); |
3293 | @@ -170,6 +228,11 @@ |
3294 | return m_backend->state(); |
3295 | } |
3296 | |
3297 | +bool Printer::shared() const |
3298 | +{ |
3299 | + return m_shared; |
3300 | +} |
3301 | + |
3302 | bool Printer::acceptJobs() const |
3303 | { |
3304 | return m_acceptJobs; |
3305 | @@ -180,11 +243,21 @@ |
3306 | return m_backend->holdsDefinition(); |
3307 | } |
3308 | |
3309 | +bool Printer::isRemote() const |
3310 | +{ |
3311 | + return m_backend->isRemote(); |
3312 | +} |
3313 | + |
3314 | PrinterEnum::PrinterType Printer::type() const |
3315 | { |
3316 | return m_backend->type(); |
3317 | } |
3318 | |
3319 | +int Printer::copies() const |
3320 | +{ |
3321 | + return m_copies; |
3322 | +} |
3323 | + |
3324 | void Printer::setDefaultColorModel(const ColorModel &colorModel) |
3325 | { |
3326 | if (defaultColorModel() == colorModel) { |
3327 | @@ -229,6 +302,7 @@ |
3328 | if (!reply.isEmpty()) { |
3329 | qWarning() << Q_FUNC_INFO << "failed to set enabled:" << reply; |
3330 | } |
3331 | + m_backend->refresh(); |
3332 | } |
3333 | } |
3334 | |
3335 | @@ -242,6 +316,16 @@ |
3336 | } |
3337 | } |
3338 | |
3339 | +void Printer::setShared(const bool shared) |
3340 | +{ |
3341 | + if (this->shared() != shared) { |
3342 | + QString reply = m_backend->printerSetShared(name(), shared); |
3343 | + if (!reply.isEmpty()) { |
3344 | + qWarning() << Q_FUNC_INFO << "failed to set shared:" << reply; |
3345 | + } |
3346 | + } |
3347 | +} |
3348 | + |
3349 | void Printer::setDefaultPrintQuality(const PrintQuality &quality) |
3350 | { |
3351 | if (defaultPrintQuality() == quality) { |
3352 | @@ -278,6 +362,20 @@ |
3353 | m_backend->refresh(); |
3354 | } |
3355 | |
3356 | +void Printer::setCopies(const int &copies) |
3357 | +{ |
3358 | + if (this->copies() == copies) { |
3359 | + return; |
3360 | + } |
3361 | + |
3362 | + m_backend->printerSetCopies(name(), copies); |
3363 | +} |
3364 | + |
3365 | +QString Printer::lastMessage() const |
3366 | +{ |
3367 | + return m_stateMessage; |
3368 | +} |
3369 | + |
3370 | QAbstractItemModel* Printer::jobs() |
3371 | { |
3372 | auto ret = &m_jobs; |
3373 | @@ -296,16 +394,44 @@ |
3374 | && type() == other->type() |
3375 | && acceptJobs() == other->acceptJobs() |
3376 | && enabled() == other->enabled() |
3377 | - && state() == other->state(); |
3378 | + && state() == other->state() |
3379 | + && lastMessage() == other->lastMessage() |
3380 | + && deviceUri() == other->deviceUri() |
3381 | + && shared() == other->shared() |
3382 | + && copies() == other->copies() |
3383 | + && isRemote() == other->isRemote(); |
3384 | } |
3385 | |
3386 | void Printer::updateFrom(QSharedPointer<Printer> other) |
3387 | { |
3388 | PrinterBackend *tmp = m_backend; |
3389 | + |
3390 | + // Copy values from other printer which has been loaded in another thread |
3391 | + // Note: do not use loadAttributes otherwise can cause UI block |
3392 | + m_acceptJobs = other->m_acceptJobs; |
3393 | m_backend = other->m_backend; |
3394 | + m_defaultColorModel = other->m_defaultColorModel; |
3395 | + m_defaultPrintQuality = other->m_defaultPrintQuality; |
3396 | + m_deviceUri = other->m_deviceUri; |
3397 | + m_shared = other->m_shared; |
3398 | + m_stateMessage = other->m_stateMessage; |
3399 | + m_supportedColorModels = other->m_supportedColorModels; |
3400 | + m_supportedPrintQualities = other->m_supportedPrintQualities; |
3401 | + |
3402 | other->m_backend = tmp; |
3403 | - |
3404 | - loadColorModel(); |
3405 | - loadPrintQualities(); |
3406 | - loadAcceptJobs(); |
3407 | +} |
3408 | + |
3409 | +void Printer::onPrinterStateChanged( |
3410 | + const QString &text, const QString &printerUri, |
3411 | + const QString &printerName, uint printerState, |
3412 | + const QString &printerStateReason, bool acceptingJobs) |
3413 | +{ |
3414 | + Q_UNUSED(printerUri); |
3415 | + Q_UNUSED(printerState); |
3416 | + Q_UNUSED(printerStateReason); |
3417 | + Q_UNUSED(acceptingJobs); |
3418 | + |
3419 | + if (name() == printerName) { |
3420 | + m_stateMessage = text; |
3421 | + } |
3422 | } |
3423 | |
3424 | === modified file 'modules/Ubuntu/Components/Extras/Printers/printer/printer.h' |
3425 | --- modules/Ubuntu/Components/Extras/Printers/printer/printer.h 2017-02-23 14:04:26 +0000 |
3426 | +++ modules/Ubuntu/Components/Extras/Printers/printer/printer.h 2017-03-23 12:51:36 +0000 |
3427 | @@ -49,17 +49,24 @@ |
3428 | QStringList supportedDuplexStrings() const; |
3429 | PrinterEnum::DuplexMode defaultDuplexMode() const; |
3430 | QString name() const; |
3431 | + QString deviceUri() const; |
3432 | + QString make() const; |
3433 | PrintQuality defaultPrintQuality() const; |
3434 | QList<PrintQuality> supportedPrintQualities() const; |
3435 | QString description() const; |
3436 | + QString location() const; |
3437 | QPageSize defaultPageSize() const; |
3438 | QList<QPageSize> supportedPageSizes() const; |
3439 | PrinterEnum::AccessControl accessControl() const; |
3440 | PrinterEnum::ErrorPolicy errorPolicy() const; |
3441 | PrinterEnum::State state() const; |
3442 | + bool shared() const; |
3443 | bool acceptJobs() const; |
3444 | bool holdsDefinition() const; |
3445 | + bool isRemote() const; |
3446 | + QString lastMessage() const; |
3447 | QAbstractItemModel* jobs(); |
3448 | + int copies() const; |
3449 | |
3450 | PrinterEnum::PrinterType type() const; |
3451 | |
3452 | @@ -68,9 +75,11 @@ |
3453 | void setDefaultDuplexMode(const PrinterEnum::DuplexMode &duplexMode); |
3454 | void setEnabled(const bool enabled); |
3455 | void setAcceptJobs(const bool accepting); |
3456 | + void setShared(const bool shared); |
3457 | void setDefaultPrintQuality(const PrintQuality &quality); |
3458 | void setDefaultPageSize(const QPageSize &pageSize); |
3459 | void setJobModel(JobModel* jobModel); |
3460 | + void setCopies(const int &copies); |
3461 | |
3462 | bool deepCompare(QSharedPointer<Printer> other) const; |
3463 | void updateFrom(QSharedPointer<Printer> other); |
3464 | @@ -79,10 +88,22 @@ |
3465 | public Q_SLOTS: |
3466 | int printFile(const QString &filepath, const PrinterJob *options); |
3467 | |
3468 | +private Q_SLOTS: |
3469 | + void onPrinterStateChanged( |
3470 | + const QString &text, const QString &printerUri, |
3471 | + const QString &printerName, uint printerState, |
3472 | + const QString &printerStateReason, bool acceptingJobs |
3473 | + ); |
3474 | + |
3475 | private: |
3476 | - void loadAcceptJobs(); |
3477 | - void loadColorModel(); |
3478 | - void loadPrintQualities(); |
3479 | + void updateAcceptJobs(const QMap<QString, QVariant> &serverAttrs); |
3480 | + void updateColorModel(const QMap<QString, QVariant> &serverAttrs); |
3481 | + void updatePrintQualities(const QMap<QString, QVariant> &serverAttrs); |
3482 | + void updateLastMessage(const QMap<QString, QVariant> &serverAttrs); |
3483 | + void updateDeviceUri(const QMap<QString, QVariant> &serverAttrs); |
3484 | + void updateCopies(const QMap<QString, QVariant> &serverAttrs); |
3485 | + void updateShared(const QMap<QString, QVariant> &serverAttrs); |
3486 | + void loadAttributes(); |
3487 | |
3488 | JobFilter m_jobs; |
3489 | PrinterBackend *m_backend; |
3490 | @@ -91,6 +112,11 @@ |
3491 | PrintQuality m_defaultPrintQuality; |
3492 | QList<PrintQuality> m_supportedPrintQualities; |
3493 | bool m_acceptJobs; |
3494 | + bool m_shared; |
3495 | + QString m_deviceUri; |
3496 | + int m_copies; |
3497 | + |
3498 | + QString m_stateMessage; |
3499 | }; |
3500 | |
3501 | #endif // USC_PRINTERS_PRINTER_H |
3502 | |
3503 | === modified file 'modules/Ubuntu/Components/Extras/Printers/printer/printerjob.cpp' |
3504 | --- modules/Ubuntu/Components/Extras/Printers/printer/printerjob.cpp 2017-02-24 17:47:22 +0000 |
3505 | +++ modules/Ubuntu/Components/Extras/Printers/printer/printerjob.cpp 2017-03-23 12:51:36 +0000 |
3506 | @@ -55,6 +55,8 @@ |
3507 | , m_title(QStringLiteral("")) |
3508 | , m_user("") |
3509 | { |
3510 | + connect(this, SIGNAL(printerAboutToChange(QSharedPointer<Printer>, QSharedPointer<Printer>)), |
3511 | + this, SLOT(onPrinterAboutToChange(QSharedPointer<Printer>, QSharedPointer<Printer>))); |
3512 | } |
3513 | |
3514 | PrinterJob::~PrinterJob() |
3515 | @@ -143,6 +145,69 @@ |
3516 | return m_landscape; |
3517 | } |
3518 | |
3519 | +void PrinterJob::loadAttributes(const QMap<QString, QVariant> &attributes) |
3520 | +{ |
3521 | + // Load the extra attributes for the job |
3522 | + // NOTE: we don't need to type check them as they have been filtered for us |
3523 | + |
3524 | + setCollate(attributes.value("Collate").toBool()); |
3525 | + setCopies(attributes.value("copies").toInt()); |
3526 | + |
3527 | + // No colorModel will result in PrinterJob using defaultColorModel |
3528 | + QString colorModel = attributes.value("ColorModel").toString(); |
3529 | + for (int i=0; i < m_printer->supportedColorModels().length(); i++) { |
3530 | + if (m_printer->supportedColorModels().at(i).name == colorModel) { |
3531 | + setColorModel(i); |
3532 | + } |
3533 | + } |
3534 | + |
3535 | + setCompletedTime(attributes.value("CompletedTime").toDateTime()); |
3536 | + setCreationTime(attributes.value("CreationTime").toDateTime()); |
3537 | + |
3538 | + // No duplexMode will result in PrinterJob using defaultDuplexMode |
3539 | + QString duplex = attributes.value("Duplex").toString(); |
3540 | + PrinterEnum::DuplexMode duplexMode = Utils::ppdChoiceToDuplexMode(duplex); |
3541 | + for (int i=0; i < m_printer->supportedDuplexModes().length(); i++) { |
3542 | + if (m_printer->supportedDuplexModes().at(i) == duplexMode) { |
3543 | + setDuplexMode(i); |
3544 | + } |
3545 | + } |
3546 | + |
3547 | + setImpressionsCompleted(attributes.value("impressionsCompleted").toInt()); |
3548 | + setLandscape(attributes.value("landscape").toBool()); |
3549 | + setMessages(attributes.value("messages").toStringList()); |
3550 | + |
3551 | + QStringList pageRanges = attributes.value("page-ranges").toStringList(); |
3552 | + if (pageRanges.isEmpty()) { |
3553 | + setPrintRangeMode(PrinterEnum::PrintRange::AllPages); |
3554 | + setPrintRange(QStringLiteral("")); |
3555 | + } else { |
3556 | + setPrintRangeMode(PrinterEnum::PrintRange::PageRange); |
3557 | + // Use groupSeparator as createSeparatedList adds "and" into the string |
3558 | + setPrintRange(pageRanges.join(QLocale::system().groupSeparator())); |
3559 | + } |
3560 | + |
3561 | + setProcessingTime(attributes.value("ProcessingTime").toDateTime()); |
3562 | + |
3563 | + // No quality will result in PrinterJob using defaultPrintQuality |
3564 | + QString quality = attributes.value("quality").toString(); |
3565 | + for (int i=0; i < m_printer->supportedPrintQualities().length(); i++) { |
3566 | + if (m_printer->supportedPrintQualities().at(i).name == quality) { |
3567 | + setQuality(i); |
3568 | + } |
3569 | + } |
3570 | + |
3571 | + setReverse(attributes.value("OutputOrder").toString() == "Reverse"); |
3572 | + |
3573 | + // If there was a state then set it |
3574 | + if (attributes.contains("State")) { |
3575 | + setState(static_cast<PrinterEnum::JobState>(attributes.value("State").toInt())); |
3576 | + } |
3577 | + |
3578 | + setSize(attributes.value("Size").toInt()); |
3579 | + setUser(attributes.value("User").toString()); |
3580 | +} |
3581 | + |
3582 | void PrinterJob::loadDefaults() |
3583 | { |
3584 | if (!m_printer) { |
3585 | @@ -150,62 +215,17 @@ |
3586 | return; |
3587 | } |
3588 | |
3589 | - qWarning() << Q_FUNC_INFO << jobId(); |
3590 | - |
3591 | if (jobId() > 0) { |
3592 | - // Load the extra attributes for the job |
3593 | - // NOTE: we don't need to type check them as they have been filtered for us |
3594 | - |
3595 | - QMap<QString, QVariant> attributes = m_backend->printerGetJobAttributes( |
3596 | - m_printer->name(), jobId()); |
3597 | - |
3598 | - setCollate(attributes.value("Collate").toBool()); |
3599 | - setCopies(attributes.value("copies").toInt()); |
3600 | - |
3601 | - // No colorModel will result in PrinterJob using defaultColorModel |
3602 | - QString colorModel = attributes.value("ColorModel").toString(); |
3603 | - for (int i=0; i < m_printer->supportedColorModels().length(); i++) { |
3604 | - if (m_printer->supportedColorModels().at(i).originalOption == colorModel) { |
3605 | - setColorModel(i); |
3606 | - } |
3607 | - } |
3608 | - |
3609 | - // No duplexMode will result in PrinterJob using defaultDuplexMode |
3610 | - QString duplex = attributes.value("Duplex").toString(); |
3611 | - PrinterEnum::DuplexMode duplexMode = Utils::ppdChoiceToDuplexMode(duplex); |
3612 | - for (int i=0; i < m_printer->supportedDuplexModes().length(); i++) { |
3613 | - if (m_printer->supportedDuplexModes().at(i) == duplexMode) { |
3614 | - setDuplexMode(i); |
3615 | - } |
3616 | - } |
3617 | - |
3618 | - setLandscape(attributes.value("landscape").toBool()); |
3619 | - setMessages(attributes.value("messages").toStringList()); |
3620 | - |
3621 | - QStringList pageRanges = attributes.value("page-ranges").toStringList(); |
3622 | - if (pageRanges.isEmpty()) { |
3623 | - setPrintRangeMode(PrinterEnum::PrintRange::AllPages); |
3624 | - setPrintRange(QStringLiteral("")); |
3625 | - } else { |
3626 | - setPrintRangeMode(PrinterEnum::PrintRange::PageRange); |
3627 | - // Use groupSeparator as createSeparatedList adds "and" into the string |
3628 | - setPrintRange(pageRanges.join(QLocale::system().groupSeparator())); |
3629 | - } |
3630 | - |
3631 | - // No quality will result in PrinterJob using defaultPrintQuality |
3632 | - QString quality = attributes.value("quality").toString(); |
3633 | - for (int i=0; i < m_printer->supportedPrintQualities().length(); i++) { |
3634 | - if (m_printer->supportedPrintQualities().at(i).name == quality) { |
3635 | - setQuality(i); |
3636 | - } |
3637 | - } |
3638 | - |
3639 | - setReverse(attributes.value("OutputOrder").toString() == "Reverse"); |
3640 | + loadAttributes( |
3641 | + m_backend->printerGetJobAttributes( |
3642 | + printerName(), jobId() |
3643 | + ) |
3644 | + ); |
3645 | + } else { |
3646 | + setColorModel(m_printer->supportedColorModels().indexOf(m_printer->defaultColorModel())); |
3647 | + setDuplexMode(m_printer->supportedDuplexModes().indexOf(m_printer->defaultDuplexMode())); |
3648 | + setQuality(m_printer->supportedPrintQualities().indexOf(m_printer->defaultPrintQuality())); |
3649 | } |
3650 | - |
3651 | - setColorModel(m_printer->supportedColorModels().indexOf(m_printer->defaultColorModel())); |
3652 | - setDuplexMode(m_printer->supportedDuplexModes().indexOf(m_printer->defaultDuplexMode())); |
3653 | - setQuality(m_printer->supportedPrintQualities().indexOf(m_printer->defaultPrintQuality())); |
3654 | } |
3655 | |
3656 | QStringList PrinterJob::messages() const |
3657 | @@ -373,16 +393,16 @@ |
3658 | void PrinterJob::setPrinter(QSharedPointer<Printer> printer) |
3659 | { |
3660 | if (m_printer != printer) { |
3661 | - m_printer = printer; |
3662 | + Q_EMIT printerAboutToChange(m_printer, printer); |
3663 | + m_printer = printer; |
3664 | |
3665 | - if (printer->name() != m_printerName) { |
3666 | + if (printer->name() != m_printerName) { |
3667 | m_printerName = printer->name(); |
3668 | Q_EMIT printerNameChanged(); |
3669 | - } |
3670 | + } |
3671 | |
3672 | - Q_EMIT printerChanged(); |
3673 | + Q_EMIT printerChanged(); |
3674 | } |
3675 | - loadDefaults(); |
3676 | } |
3677 | |
3678 | void PrinterJob::setPrintRange(const QString &printRange) |
3679 | @@ -478,33 +498,61 @@ |
3680 | // Return true if they are the same |
3681 | return collate() == other->collate() |
3682 | && colorModel() == other->colorModel() |
3683 | + && completedTime() == other->completedTime() |
3684 | && copies() == other->copies() |
3685 | + && creationTime() == other->creationTime() |
3686 | && duplexMode() == other->duplexMode() |
3687 | + && impressionsCompleted() == other->impressionsCompleted() |
3688 | && landscape() == other->landscape() |
3689 | + && messages() == other->messages() |
3690 | && printRange() == other->printRange() |
3691 | && printRangeMode() == other->printRangeMode() |
3692 | + && processingTime() == other->processingTime() |
3693 | && quality() == other->quality() |
3694 | && reverse() == other->reverse() |
3695 | + && size() == other->size() |
3696 | && state() == other->state() |
3697 | - && title() == other->title(); |
3698 | + && title() == other->title() |
3699 | + && user() == other->user(); |
3700 | } |
3701 | |
3702 | void PrinterJob::updateFrom(QSharedPointer<PrinterJob> other) |
3703 | { |
3704 | setCollate(other->collate()); |
3705 | setColorModel(other->colorModel()); |
3706 | + setCompletedTime(other->completedTime()); |
3707 | setCopies(other->copies()); |
3708 | + setCreationTime(other->creationTime()); |
3709 | setDuplexMode(other->duplexMode()); |
3710 | + setImpressionsCompleted(other->impressionsCompleted()); |
3711 | setLandscape(other->landscape()); |
3712 | + setMessages(other->messages()); |
3713 | setPrintRange(other->printRange()); |
3714 | setPrintRangeMode(other->printRangeMode()); |
3715 | + setProcessingTime(other->processingTime()); |
3716 | setQuality(other->quality()); |
3717 | setReverse(other->reverse()); |
3718 | + setSize(other->size()); |
3719 | setState(other->state()); |
3720 | setTitle(other->title()); |
3721 | + setUser(other->user()); |
3722 | } |
3723 | |
3724 | QString PrinterJob::user() const |
3725 | { |
3726 | return m_user; |
3727 | } |
3728 | + |
3729 | +void PrinterJob::onPrinterAboutToChange(QSharedPointer<Printer> old, |
3730 | + QSharedPointer<Printer> replacement) |
3731 | +{ |
3732 | + /* If we have an old printer, and the current copies value matches that |
3733 | + of the old printer's, we'll use the new printer's copy value. |
3734 | + |
3735 | + The second case is if there was no old printer. */ |
3736 | + bool haveOld = old && replacement && (copies() == old->copies()); |
3737 | + bool noOld = !old && replacement; |
3738 | + if (haveOld || noOld) { |
3739 | + setCopies(replacement->copies()); |
3740 | + } |
3741 | +} |
3742 | |
3743 | === modified file 'modules/Ubuntu/Components/Extras/Printers/printer/printerjob.h' |
3744 | --- modules/Ubuntu/Components/Extras/Printers/printer/printerjob.h 2017-02-23 14:04:26 +0000 |
3745 | +++ modules/Ubuntu/Components/Extras/Printers/printer/printerjob.h 2017-03-23 12:51:36 +0000 |
3746 | @@ -58,8 +58,6 @@ |
3747 | Q_PROPERTY(PrinterEnum::JobState state READ state NOTIFY stateChanged) |
3748 | Q_PROPERTY(QString title READ title WRITE setTitle NOTIFY titleChanged) |
3749 | Q_PROPERTY(QString user READ user NOTIFY userChanged) |
3750 | - |
3751 | - friend class PrinterCupsBackend; |
3752 | public: |
3753 | explicit PrinterJob(QString dest, |
3754 | PrinterBackend *backend, |
3755 | @@ -97,31 +95,34 @@ |
3756 | PrinterEnum::DuplexMode getDuplexMode() const; |
3757 | ColorModel getColorModel() const; |
3758 | PrintQuality getPrintQuality() const; |
3759 | + void loadAttributes(const QMap<QString, QVariant>& attributes); |
3760 | + void loadDefaults(); |
3761 | Q_INVOKABLE void printFile(const QUrl &url); |
3762 | void setCollate(const bool collate); |
3763 | void setColorModel(const int colorModel); |
3764 | + void setCompletedTime(const QDateTime &completedTime); |
3765 | void setCopies(const int copies); |
3766 | + void setCreationTime(const QDateTime &creationTime); |
3767 | void setDuplexMode(const int duplexMode); |
3768 | void setImpressionsCompleted(const int &impressionsCompleted); |
3769 | + void setIsTwoSided(const bool isTwoSided); |
3770 | void setLandscape(const bool landscape); |
3771 | + void setMessages(const QStringList &messages); |
3772 | void setPrinter(QSharedPointer<Printer> printer); |
3773 | void setPrintRange(const QString &printRange); |
3774 | void setPrintRangeMode(const PrinterEnum::PrintRange printRangeMode); |
3775 | + void setProcessingTime(const QDateTime &processingTime); |
3776 | void setQuality(const int quality); |
3777 | void setReverse(const bool reverse); |
3778 | + void setSize(const int size); |
3779 | + void setState(const PrinterEnum::JobState &state); |
3780 | void setTitle(const QString &title); |
3781 | + void setUser(const QString &user); |
3782 | |
3783 | void updateFrom(QSharedPointer<PrinterJob> other); |
3784 | private Q_SLOTS: |
3785 | - void loadDefaults(); |
3786 | - void setCompletedTime(const QDateTime &completedTime); |
3787 | - void setCreationTime(const QDateTime &creationTime); |
3788 | - void setIsTwoSided(const bool isTwoSided); |
3789 | - void setMessages(const QStringList &messages); |
3790 | - void setProcessingTime(const QDateTime &processingTime); |
3791 | - void setSize(const int size); |
3792 | - void setState(const PrinterEnum::JobState &state); |
3793 | - void setUser(const QString &user); |
3794 | + void onPrinterAboutToChange(QSharedPointer<Printer> old, |
3795 | + QSharedPointer<Printer> replacement); |
3796 | Q_SIGNALS: |
3797 | void collateChanged(); |
3798 | void colorModelChanged(); |
3799 | @@ -134,6 +135,8 @@ |
3800 | void isTwoSidedChanged(); |
3801 | void landscapeChanged(); |
3802 | void messagesChanged(); |
3803 | + void printerAboutToChange(QSharedPointer<Printer> old, |
3804 | + QSharedPointer<Printer> replacement); |
3805 | void printerChanged(); |
3806 | void printerNameChanged(); |
3807 | void printRangeChanged(); |
3808 | |
3809 | === renamed file 'modules/Ubuntu/Components/Extras/Printers/printer/printersignalhandler.cpp' => 'modules/Ubuntu/Components/Extras/Printers/printer/signalratelimiter.cpp' |
3810 | --- modules/Ubuntu/Components/Extras/Printers/printer/printersignalhandler.cpp 2017-02-21 10:46:29 +0000 |
3811 | +++ modules/Ubuntu/Components/Extras/Printers/printer/signalratelimiter.cpp 2017-03-23 12:51:36 +0000 |
3812 | @@ -14,9 +14,9 @@ |
3813 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
3814 | */ |
3815 | |
3816 | -#include "printersignalhandler.h" |
3817 | +#include "signalratelimiter.h" |
3818 | |
3819 | -PrinterSignalHandler::PrinterSignalHandler(int triggerEventDelay, |
3820 | +SignalRateLimiter::SignalRateLimiter(int triggerEventDelay, |
3821 | QObject *parent) |
3822 | : QObject(parent) |
3823 | { |
3824 | @@ -24,11 +24,11 @@ |
3825 | connect(&m_timer, SIGNAL(timeout()), this, SLOT(process())); |
3826 | } |
3827 | |
3828 | -PrinterSignalHandler::~PrinterSignalHandler() |
3829 | +SignalRateLimiter::~SignalRateLimiter() |
3830 | { |
3831 | } |
3832 | |
3833 | -void PrinterSignalHandler::process() |
3834 | +void SignalRateLimiter::process() |
3835 | { |
3836 | Q_FOREACH(auto printer, m_unprocessed) { |
3837 | Q_EMIT printerModified(printer); |
3838 | @@ -37,7 +37,7 @@ |
3839 | m_timer.stop(); |
3840 | } |
3841 | |
3842 | -void PrinterSignalHandler::onPrinterModified( |
3843 | +void SignalRateLimiter::onPrinterModified( |
3844 | const QString &text, const QString &printerUri, |
3845 | const QString &printerName, uint printerState, |
3846 | const QString &printerStateReason, bool acceptingJobs) |
3847 | @@ -49,11 +49,22 @@ |
3848 | Q_UNUSED(printerStateReason); |
3849 | Q_UNUSED(acceptingJobs); |
3850 | |
3851 | + // Track when the first item was added to the unprocessed queue |
3852 | + if (m_unprocessed.count() == 0) { |
3853 | + m_unprocessed_time = QDateTime::currentDateTime(); |
3854 | + } |
3855 | + |
3856 | m_unprocessed << printerName; |
3857 | m_timer.start(); |
3858 | + |
3859 | + // Ensure that process is fired if we have reached four times |
3860 | + // longer than the timer, this is due to many signals coming in rapidly |
3861 | + if (m_unprocessed_time.msecsTo(QDateTime::currentDateTime()) > m_timer.interval() * 4) { |
3862 | + process(); |
3863 | + } |
3864 | } |
3865 | |
3866 | -void PrinterSignalHandler::onPrinterStateChanged( |
3867 | +void SignalRateLimiter::onPrinterStateChanged( |
3868 | const QString &text, const QString &printerUri, |
3869 | const QString &printerName, uint printerState, |
3870 | const QString &printerStateReason, bool acceptingJobs) |
3871 | @@ -64,6 +75,17 @@ |
3872 | Q_UNUSED(printerStateReason); |
3873 | Q_UNUSED(acceptingJobs); |
3874 | |
3875 | + // Track when the first item was added to the unprocessed queue |
3876 | + if (m_unprocessed.count() == 0) { |
3877 | + m_unprocessed_time = QDateTime::currentDateTime(); |
3878 | + } |
3879 | + |
3880 | m_unprocessed << printerName; |
3881 | m_timer.start(); |
3882 | + |
3883 | + // Ensure that process is fired if we have reached four times |
3884 | + // longer than the timer, this is due to many signals coming in rapidly |
3885 | + if (m_unprocessed_time.msecsTo(QDateTime::currentDateTime()) > m_timer.interval() * 4) { |
3886 | + process(); |
3887 | + } |
3888 | } |
3889 | |
3890 | === renamed file 'modules/Ubuntu/Components/Extras/Printers/printer/printersignalhandler.h' => 'modules/Ubuntu/Components/Extras/Printers/printer/signalratelimiter.h' |
3891 | --- modules/Ubuntu/Components/Extras/Printers/printer/printersignalhandler.h 2017-02-21 10:46:29 +0000 |
3892 | +++ modules/Ubuntu/Components/Extras/Printers/printer/signalratelimiter.h 2017-03-23 12:51:36 +0000 |
3893 | @@ -14,24 +14,26 @@ |
3894 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
3895 | */ |
3896 | |
3897 | -#ifndef USC_PRINTER_SIGNAL_HANDLER_H |
3898 | -#define USC_PRINTER_SIGNAL_HANDLER_H |
3899 | +#ifndef USC_SIGNAL_RATE_LIMITER_H |
3900 | +#define USC_SIGNAL_RATE_LIMITER_H |
3901 | |
3902 | #include "printers_global.h" |
3903 | |
3904 | +#include <QDateTime> |
3905 | #include <QObject> |
3906 | #include <QSet> |
3907 | #include <QTimer> |
3908 | |
3909 | -class PRINTERS_DECL_EXPORT PrinterSignalHandler : public QObject |
3910 | +class PRINTERS_DECL_EXPORT SignalRateLimiter : public QObject |
3911 | { |
3912 | Q_OBJECT |
3913 | QTimer m_timer; |
3914 | QSet<QString> m_unprocessed; |
3915 | + QDateTime m_unprocessed_time; |
3916 | public: |
3917 | - explicit PrinterSignalHandler(int triggerEventDelay = 500, |
3918 | + explicit SignalRateLimiter(int triggerEventDelay = 500, |
3919 | QObject *parent = Q_NULLPTR); |
3920 | - ~PrinterSignalHandler(); |
3921 | + ~SignalRateLimiter(); |
3922 | |
3923 | public Q_SLOTS: |
3924 | void onPrinterModified( |
3925 | @@ -52,4 +54,4 @@ |
3926 | void printerModified(const QString &printerName); |
3927 | }; |
3928 | |
3929 | -#endif // USC_PRINTER_SIGNAL_HANDLER_H |
3930 | +#endif // USC_SIGNAL_RATE_LIMITERR_H |
3931 | |
3932 | === modified file 'modules/Ubuntu/Components/Extras/Printers/printers/printers.cpp' |
3933 | --- modules/Ubuntu/Components/Extras/Printers/printers/printers.cpp 2017-02-24 13:14:13 +0000 |
3934 | +++ modules/Ubuntu/Components/Extras/Printers/printers/printers.cpp 2017-03-23 12:51:36 +0000 |
3935 | @@ -15,6 +15,7 @@ |
3936 | */ |
3937 | |
3938 | #include "backend/backend_cups.h" |
3939 | +#include "i18n.h" |
3940 | #include "printers/printers.h" |
3941 | #include "cupsdnotifier.h" // Note: this file was generated. |
3942 | |
3943 | @@ -33,6 +34,7 @@ |
3944 | Printers::Printers(PrinterBackend *backend, QObject *parent) |
3945 | : QObject(parent) |
3946 | , m_backend(backend) |
3947 | + , m_devices(backend) |
3948 | , m_drivers(backend) |
3949 | , m_model(backend) |
3950 | , m_jobs(backend) |
3951 | @@ -56,8 +58,20 @@ |
3952 | const QModelIndex &parent, int first, int) { |
3953 | int jobId = m_jobs.data(m_jobs.index(first, 0, parent), |
3954 | JobModel::Roles::IdRole).toInt(); |
3955 | - jobAdded(m_jobs.getJobById(jobId)); |
3956 | - }); |
3957 | + QString printerName = m_jobs.data( |
3958 | + m_jobs.index(first, 0, parent), |
3959 | + JobModel::Roles::PrinterNameRole |
3960 | + ).toString(); |
3961 | + |
3962 | + jobAdded(m_jobs.getJob(printerName, jobId)); |
3963 | + }); |
3964 | + |
3965 | + // If the jobModel forces a refresh, load extended attributes for the job |
3966 | + connect(&m_jobs, &JobModel::forceJobRefresh, [this]( |
3967 | + const QString &printerName, const int jobId) { |
3968 | + jobAdded(m_jobs.getJob(printerName, jobId)); |
3969 | + }); |
3970 | + |
3971 | connect(&m_model, &QAbstractItemModel::rowsInserted, [this]( |
3972 | const QModelIndex &parent, int first, int) { |
3973 | auto printer = m_model.data( |
3974 | @@ -76,6 +90,17 @@ |
3975 | ); |
3976 | } |
3977 | |
3978 | + // Ensure existing jobs have been added, incase some were added before |
3979 | + // the connect to rowsInserted was done |
3980 | + for (int i = 0; i < m_jobs.rowCount(); i++) { |
3981 | + jobAdded( |
3982 | + m_jobs.getJob( |
3983 | + m_jobs.data(m_jobs.index(i), JobModel::Roles::PrinterNameRole).toString(), |
3984 | + m_jobs.data(m_jobs.index(i), JobModel::IdRole).toInt() |
3985 | + ) |
3986 | + ); |
3987 | + } |
3988 | + |
3989 | if (m_backend->type() == PrinterEnum::PrinterType::CupsType) { |
3990 | ((PrinterCupsBackend*) m_backend)->createSubscription(); |
3991 | } |
3992 | @@ -103,6 +128,41 @@ |
3993 | return ret; |
3994 | } |
3995 | |
3996 | +QAbstractItemModel* Printers::remotePrinters() |
3997 | +{ |
3998 | + /* Lazily initialize this model. Local printers are discerned from remotes |
3999 | + by checking if they are remote. */ |
4000 | + if (!m_remotePrinters.sourceModel()) { |
4001 | + m_remotePrinters.setSourceModel(&m_model); |
4002 | + m_remotePrinters.filterOnRemote(true); |
4003 | + m_remotePrinters.filterOnPdf(false); |
4004 | + m_remotePrinters.invalidate(); |
4005 | + m_remotePrinters.sort(0, Qt::DescendingOrder); |
4006 | + } |
4007 | + |
4008 | + auto ret = &m_remotePrinters; |
4009 | + QQmlEngine::setObjectOwnership(ret, QQmlEngine::CppOwnership); |
4010 | + return ret; |
4011 | +} |
4012 | + |
4013 | +QAbstractItemModel* Printers::localPrinters() |
4014 | +{ |
4015 | + /* Lazily initialize this model. Local printers are discerned from remotes |
4016 | + by checking if they are remote. */ |
4017 | + if (!m_localPrinters.sourceModel()) { |
4018 | + m_localPrinters.setSourceModel(&m_model); |
4019 | + m_localPrinters.filterOnRemote(false); |
4020 | + m_localPrinters.filterOnPdf(false); |
4021 | + m_localPrinters.setSortRole(PrinterModel::Roles::DefaultPrinterRole); |
4022 | + m_localPrinters.invalidate(); |
4023 | + m_localPrinters.sort(0, Qt::DescendingOrder); |
4024 | + } |
4025 | + |
4026 | + auto ret = &m_localPrinters; |
4027 | + QQmlEngine::setObjectOwnership(ret, QQmlEngine::CppOwnership); |
4028 | + return ret; |
4029 | +} |
4030 | + |
4031 | QAbstractItemModel* Printers::printJobs() |
4032 | { |
4033 | auto ret = &m_jobs; |
4034 | @@ -117,6 +177,14 @@ |
4035 | return ret; |
4036 | } |
4037 | |
4038 | +QAbstractItemModel* Printers::devices() |
4039 | +{ |
4040 | + m_devices.load(); |
4041 | + auto ret = &m_devices; |
4042 | + QQmlEngine::setObjectOwnership(ret, QQmlEngine::CppOwnership); |
4043 | + return ret; |
4044 | +} |
4045 | + |
4046 | QString Printers::driverFilter() const |
4047 | { |
4048 | return m_drivers.filter(); |
4049 | @@ -143,11 +211,33 @@ |
4050 | return new PrinterJob(printerName, m_backend); |
4051 | } |
4052 | |
4053 | +QAbstractItemModel* Printers::createJobFilter() |
4054 | +{ |
4055 | + // Note: If called by QML, it gains ownership of the job filter. |
4056 | + JobFilter *filter = new JobFilter(); |
4057 | + filter->setSourceModel(&m_jobs); |
4058 | + |
4059 | + filter->setSortRole(JobModel::Roles::CreationTimeRole); |
4060 | + filter->sort(0, Qt::AscendingOrder); |
4061 | + |
4062 | + return filter; |
4063 | +} |
4064 | + |
4065 | void Printers::cancelJob(const QString &printerName, const int jobId) |
4066 | { |
4067 | m_backend->cancelJob(printerName, jobId); |
4068 | } |
4069 | |
4070 | +void Printers::holdJob(const QString &printerName, const int jobId) |
4071 | +{ |
4072 | + m_backend->holdJob(printerName, jobId); |
4073 | +} |
4074 | + |
4075 | +void Printers::releaseJob(const QString &printerName, const int jobId) |
4076 | +{ |
4077 | + m_backend->releaseJob(printerName, jobId); |
4078 | +} |
4079 | + |
4080 | void Printers::setDefaultPrinterName(const QString &name) |
4081 | { |
4082 | QString reply = m_backend->printerSetDefault(name); |
4083 | @@ -164,6 +254,11 @@ |
4084 | } |
4085 | } |
4086 | |
4087 | +void Printers::searchForDevices() |
4088 | +{ |
4089 | + m_devices.load(); |
4090 | +} |
4091 | + |
4092 | bool Printers::addPrinter(const QString &name, const QString &ppd, |
4093 | const QString &device, const QString &description, |
4094 | const QString &location) |
4095 | @@ -174,6 +269,9 @@ |
4096 | m_lastMessage = reply; |
4097 | return false; |
4098 | } |
4099 | + |
4100 | + provisionPrinter(name); |
4101 | + |
4102 | return true; |
4103 | } |
4104 | |
4105 | @@ -189,9 +287,19 @@ |
4106 | m_lastMessage = reply; |
4107 | return false; |
4108 | } |
4109 | + |
4110 | + provisionPrinter(name); |
4111 | + |
4112 | return true; |
4113 | } |
4114 | |
4115 | +void Printers::provisionPrinter(const QString &name) |
4116 | +{ |
4117 | + // We mimic what System Config Printer does here. |
4118 | + m_backend->printerSetEnabled(name, true); |
4119 | + m_backend->printerSetAcceptJobs(name, true); |
4120 | +} |
4121 | + |
4122 | bool Printers::removePrinter(const QString &name) |
4123 | { |
4124 | QString reply = m_backend->printerDelete(name); |
4125 | @@ -206,8 +314,21 @@ |
4126 | void Printers::jobAdded(QSharedPointer<PrinterJob> job) |
4127 | { |
4128 | auto printer = m_model.getPrinterByName(job->printerName()); |
4129 | - if (printer && job) |
4130 | - job->setPrinter(printer); |
4131 | + |
4132 | + // Check if we have a valid printer, does not need to be loaded as JobLoader |
4133 | + // creates it's own Backend. |
4134 | + if (printer && job) { |
4135 | + // TODO: this printer may not be fully loaded |
4136 | + // Which has the side affect of colorModel, duplex, quality not working |
4137 | + // in PrinterJob as Printer::supportedColorModels etc fail |
4138 | + // Potentially trigger loadPrinter and listen for the new printer? |
4139 | + |
4140 | + // Set the printer to the job |
4141 | + m_jobs.updateJobPrinter(job, printer); |
4142 | + |
4143 | + // Trigger JobLoader to load extended attributes in the background |
4144 | + m_backend->requestJobExtendedAttributes(printer, job); |
4145 | + } |
4146 | } |
4147 | |
4148 | void Printers::printerAdded(QSharedPointer<Printer> printer) |
4149 | @@ -223,10 +344,9 @@ |
4150 | ).toString(); |
4151 | |
4152 | int jobId = m_jobs.data(idx, JobModel::Roles::IdRole).toInt(); |
4153 | - auto job = m_jobs.getJobById(jobId); |
4154 | + auto job = m_jobs.getJob(printerName, jobId); |
4155 | if (printerName == printer->name() && !job->printer()) { |
4156 | - job->setPrinter(printer); |
4157 | - return; |
4158 | + jobAdded(job); |
4159 | } |
4160 | } |
4161 | } |
4162 | @@ -243,3 +363,28 @@ |
4163 | m_backend->requestPrinter(name); |
4164 | } |
4165 | } |
4166 | + |
4167 | +void Printers::printTestPage(const QString &name) |
4168 | +{ |
4169 | + |
4170 | + auto printer = m_model.getPrinterByName(name); |
4171 | + if (!printer) { |
4172 | + qWarning() << Q_FUNC_INFO << "no known printer named" << name; |
4173 | + return; |
4174 | + } |
4175 | + |
4176 | + auto page = QStandardPaths::locate(QStandardPaths::GenericDataLocation, |
4177 | + "cups/data/default-testpage.pdf", |
4178 | + QStandardPaths::LocateFile); |
4179 | + |
4180 | + if (page.isEmpty()) { |
4181 | + qCritical() << Q_FUNC_INFO << "Could not find test page."; |
4182 | + return; |
4183 | + } |
4184 | + |
4185 | + auto job = new PrinterJob(name, m_backend); |
4186 | + job->setPrinter(printer); |
4187 | + job->setTitle(__("Test page")); |
4188 | + job->printFile(QUrl::fromLocalFile(page)); |
4189 | + job->deleteLater(); |
4190 | +} |
4191 | |
4192 | === modified file 'modules/Ubuntu/Components/Extras/Printers/printers/printers.h' |
4193 | --- modules/Ubuntu/Components/Extras/Printers/printers/printers.h 2017-02-24 12:20:07 +0000 |
4194 | +++ modules/Ubuntu/Components/Extras/Printers/printers/printers.h 2017-03-23 12:51:36 +0000 |
4195 | @@ -19,6 +19,7 @@ |
4196 | |
4197 | #include "printers_global.h" |
4198 | |
4199 | +#include "models/devicemodel.h" |
4200 | #include "models/drivermodel.h" |
4201 | #include "models/printermodel.h" |
4202 | #include "printer/printer.h" |
4203 | @@ -36,8 +37,11 @@ |
4204 | Q_OBJECT |
4205 | Q_PROPERTY(QAbstractItemModel* allPrinters READ allPrinters CONSTANT) |
4206 | Q_PROPERTY(QAbstractItemModel* allPrintersWithPdf READ allPrintersWithPdf CONSTANT) |
4207 | + Q_PROPERTY(QAbstractItemModel* remotePrinters READ remotePrinters CONSTANT) |
4208 | + Q_PROPERTY(QAbstractItemModel* localPrinters READ localPrinters CONSTANT) |
4209 | Q_PROPERTY(QAbstractItemModel* printJobs READ printJobs CONSTANT) |
4210 | Q_PROPERTY(QAbstractItemModel* drivers READ drivers CONSTANT) |
4211 | + Q_PROPERTY(QAbstractItemModel* devices READ devices CONSTANT) |
4212 | Q_PROPERTY(QString driverFilter READ driverFilter WRITE setDriverFilter NOTIFY driverFilterChanged) |
4213 | Q_PROPERTY(QString defaultPrinterName READ defaultPrinterName WRITE setDefaultPrinterName NOTIFY defaultPrinterNameChanged) |
4214 | Q_PROPERTY(QString lastMessage READ lastMessage CONSTANT) |
4215 | @@ -51,8 +55,11 @@ |
4216 | |
4217 | QAbstractItemModel* allPrinters(); |
4218 | QAbstractItemModel* allPrintersWithPdf(); |
4219 | + QAbstractItemModel* remotePrinters(); |
4220 | + QAbstractItemModel* localPrinters(); |
4221 | QAbstractItemModel* printJobs(); |
4222 | QAbstractItemModel* drivers(); |
4223 | + QAbstractItemModel* devices(); |
4224 | QString driverFilter() const; |
4225 | QString defaultPrinterName() const; |
4226 | QString lastMessage() const; |
4227 | @@ -62,7 +69,11 @@ |
4228 | |
4229 | public Q_SLOTS: |
4230 | PrinterJob* createJob(const QString &printerName); |
4231 | + QAbstractItemModel* createJobFilter(); |
4232 | + |
4233 | void cancelJob(const QString &printerName, const int jobId); |
4234 | + void holdJob(const QString &printerName, const int jobId); |
4235 | + void releaseJob(const QString &printerName, const int jobId); |
4236 | |
4237 | /* Instructs us to start loading drivers and what have you. In most cases, |
4238 | the user is likely to merely configure existing printers/jobs. Loading |
4239 | @@ -70,6 +81,9 @@ |
4240 | add printers, this method should be called first. */ |
4241 | void prepareToAddPrinter(); |
4242 | |
4243 | + // Starts a search for devices on local and remote resources. |
4244 | + void searchForDevices(); |
4245 | + |
4246 | bool addPrinter(const QString &name, const QString &ppd, |
4247 | const QString &device, const QString &description, |
4248 | const QString &location); |
4249 | @@ -83,6 +97,8 @@ |
4250 | // Instruct us to start (eagerly) loading a printer. |
4251 | void loadPrinter(const QString &name); |
4252 | |
4253 | + void printTestPage(const QString &name); |
4254 | + |
4255 | private Q_SLOTS: |
4256 | void jobAdded(QSharedPointer<PrinterJob> job); |
4257 | void printerAdded(QSharedPointer<Printer> printer); |
4258 | @@ -92,12 +108,16 @@ |
4259 | void driverFilterChanged(); |
4260 | |
4261 | private: |
4262 | + void provisionPrinter(const QString &name); |
4263 | PrinterBackend *m_backend; |
4264 | + DeviceModel m_devices; |
4265 | DriverModel m_drivers; |
4266 | PrinterModel m_model; |
4267 | JobModel m_jobs; |
4268 | PrinterFilter m_allPrinters; |
4269 | PrinterFilter m_allPrintersWithPdf; |
4270 | + PrinterFilter m_remotePrinters; |
4271 | + PrinterFilter m_localPrinters; |
4272 | PrinterFilter m_recentPrinters; |
4273 | QString m_lastMessage; |
4274 | }; |
4275 | |
4276 | === modified file 'modules/Ubuntu/Components/Extras/Printers/structs.h' |
4277 | --- modules/Ubuntu/Components/Extras/Printers/structs.h 2017-02-23 14:04:26 +0000 |
4278 | +++ modules/Ubuntu/Components/Extras/Printers/structs.h 2017-03-23 12:51:36 +0000 |
4279 | @@ -84,6 +84,87 @@ |
4280 | } |
4281 | }; |
4282 | |
4283 | +struct Device |
4284 | +{ |
4285 | +public: |
4286 | + QString cls; |
4287 | + QString id; |
4288 | + QString info; |
4289 | + QString makeModel; |
4290 | + QString uri; |
4291 | + QString location; |
4292 | + PrinterEnum::DeviceType type() |
4293 | + { |
4294 | + auto parts = uri.split(":", QString::SkipEmptyParts); |
4295 | + QString scheme = parts.size() > 0 ? parts[0] : QStringLiteral(""); |
4296 | + if (scheme == QStringLiteral("dnssd")) |
4297 | + return PrinterEnum::DeviceType::DNSSDType; |
4298 | + else if (scheme == QStringLiteral("lpd")) |
4299 | + return PrinterEnum::DeviceType::LPDType; |
4300 | + else if (scheme == QStringLiteral("ipps")) |
4301 | + return PrinterEnum::DeviceType::IppSType; |
4302 | + else if (scheme == QStringLiteral("ipp14")) |
4303 | + return PrinterEnum::DeviceType::Ipp14Type; |
4304 | + else if (scheme == QStringLiteral("http")) |
4305 | + return PrinterEnum::DeviceType::HttpType; |
4306 | + else if (scheme == QStringLiteral("beh")) |
4307 | + return PrinterEnum::DeviceType::BehType; |
4308 | + else if (scheme == QStringLiteral("socket")) |
4309 | + return PrinterEnum::DeviceType::SocketType; |
4310 | + else if (scheme == QStringLiteral("https")) |
4311 | + return PrinterEnum::DeviceType::HttpsType; |
4312 | + else if (scheme == QStringLiteral("ipp")) |
4313 | + return PrinterEnum::DeviceType::IppType; |
4314 | + else if (scheme == QStringLiteral("hp")) |
4315 | + return PrinterEnum::DeviceType::HPType; |
4316 | + else if (scheme == QStringLiteral("usb")) |
4317 | + return PrinterEnum::DeviceType::USBType; |
4318 | + else if (scheme == QStringLiteral("hpfax")) |
4319 | + return PrinterEnum::DeviceType::HPFaxType; |
4320 | + else |
4321 | + return PrinterEnum::DeviceType::UnknownType; |
4322 | + } |
4323 | + |
4324 | + QString toString() const { |
4325 | + /* 1. Split the id, which is of format "KEY:VAL; … KEYN:VALN;" into |
4326 | + ["KEY:VAL", …, "KEYN:VALN"] |
4327 | + 2. Split each pair into |
4328 | + ["KEY", "VAL"] … ["KEYN", "VALN"]*/ |
4329 | + QMap<QString, QString> idMap; |
4330 | + auto pairs = id.split(";"); |
4331 | + Q_FOREACH(const QString &pair, pairs) { |
4332 | + auto keyValue = pair.split(":"); |
4333 | + |
4334 | + /* Sometimes key,val pairs are not terminated by ";". We just |
4335 | + use the first value in that case. E.g.: |
4336 | + "MFG:HP MDL:Laserfjert;" |
4337 | + Will give "HP" as MFG, and "" as MDL. */ |
4338 | + if (keyValue.size() >= 2) { |
4339 | + idMap[keyValue[0]] = keyValue[1]; |
4340 | + } |
4341 | + } |
4342 | + auto mfg = idMap.value("MFG", ""); |
4343 | + auto mdl = idMap.value("MDL", ""); |
4344 | + |
4345 | + /* If the MDL field contains CMD, somebody forgot to terminate, and we |
4346 | + remove it. */ |
4347 | + if (mdl.contains("CMD")) { |
4348 | + mdl = mdl.split("CMD")[0]; |
4349 | + } |
4350 | + |
4351 | + return QString("%1 %2").arg(mfg).arg(mdl); |
4352 | + } |
4353 | + |
4354 | + bool operator==(const Device &other) |
4355 | + { |
4356 | + return ((cls == other.cls) && (id == other.id) && (info == other.info) && |
4357 | + (makeModel == other.makeModel) && (uri == other.uri) && |
4358 | + (location == other.location)); |
4359 | + } |
4360 | +}; |
4361 | + |
4362 | + |
4363 | + |
4364 | Q_DECLARE_TYPEINFO(ColorModel, Q_PRIMITIVE_TYPE); |
4365 | Q_DECLARE_METATYPE(ColorModel) |
4366 | |
4367 | @@ -94,4 +175,7 @@ |
4368 | Q_DECLARE_METATYPE(PrinterDriver) |
4369 | Q_DECLARE_METATYPE(QList<PrinterDriver>) |
4370 | |
4371 | +Q_DECLARE_TYPEINFO(Device, Q_MOVABLE_TYPE); |
4372 | +Q_DECLARE_METATYPE(Device) |
4373 | + |
4374 | #endif // USC_PRINTERS_STRUCTS_H |
4375 | |
4376 | === modified file 'po/ubuntu-ui-extras.pot' |
4377 | --- po/ubuntu-ui-extras.pot 2017-02-21 10:46:29 +0000 |
4378 | +++ po/ubuntu-ui-extras.pot 2017-03-23 12:51:36 +0000 |
4379 | @@ -8,7 +8,7 @@ |
4380 | msgstr "" |
4381 | "Project-Id-Version: ubuntu-ui-extras\n" |
4382 | "Report-Msgid-Bugs-To: \n" |
4383 | -"POT-Creation-Date: 2017-02-21 10:42+0000\n" |
4384 | +"POT-Creation-Date: 2017-03-21 15:33+0100\n" |
4385 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" |
4386 | "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" |
4387 | "Language-Team: LANGUAGE <LL@li.org>\n" |
4388 | @@ -17,6 +17,14 @@ |
4389 | "Content-Type: text/plain; charset=CHARSET\n" |
4390 | "Content-Transfer-Encoding: 8bit\n" |
4391 | |
4392 | +#: modules/Ubuntu/Components/Extras/Example/Printers.qml:106 |
4393 | +msgid "Aborted" |
4394 | +msgstr "" |
4395 | + |
4396 | +#: modules/Ubuntu/Components/Extras/Example/Printers.qml:108 |
4397 | +msgid "Active" |
4398 | +msgstr "" |
4399 | + |
4400 | #: modules/Ubuntu/Components/Extras/PhotoEditor.qml:206 |
4401 | #: modules/Ubuntu/Components/Extras/PhotoEditor/ExposureAdjuster.qml:81 |
4402 | msgid "Cancel" |
4403 | @@ -26,7 +34,7 @@ |
4404 | msgid "Color" |
4405 | msgstr "" |
4406 | |
4407 | -#: modules/Ubuntu/Components/Extras/Printers/models/printermodel.cpp:53 |
4408 | +#: modules/Ubuntu/Components/Extras/Printers/models/printermodel.cpp:54 |
4409 | msgid "Create PDF" |
4410 | msgstr "" |
4411 | |
4412 | @@ -43,10 +51,18 @@ |
4413 | msgid "Enhancing photo..." |
4414 | msgstr "" |
4415 | |
4416 | +#: modules/Ubuntu/Components/Extras/Example/Printers.qml:104 |
4417 | +msgid "Idle" |
4418 | +msgstr "" |
4419 | + |
4420 | #: modules/Ubuntu/Components/Extras/Printers/utils.h:61 |
4421 | msgid "Long Edge (Standard)" |
4422 | msgstr "" |
4423 | |
4424 | +#: modules/Ubuntu/Components/Extras/Example/Printers.qml:115 |
4425 | +msgid "No messages" |
4426 | +msgstr "" |
4427 | + |
4428 | #: modules/Ubuntu/Components/Extras/Printers/backend/backend_pdf.cpp:47 |
4429 | msgid "Normal" |
4430 | msgstr "" |
4431 | @@ -79,6 +95,14 @@ |
4432 | msgid "Short Edge (Flip)" |
4433 | msgstr "" |
4434 | |
4435 | +#: modules/Ubuntu/Components/Extras/Example/Printers.qml:110 |
4436 | +msgid "Stopped" |
4437 | +msgstr "" |
4438 | + |
4439 | +#: modules/Ubuntu/Components/Extras/Printers/printers/printers.cpp:387 |
4440 | +msgid "Test page" |
4441 | +msgstr "" |
4442 | + |
4443 | #: modules/Ubuntu/Components/Extras/PhotoEditor.qml:197 |
4444 | msgid "This will undo all edits, including those from previous sessions." |
4445 | msgstr "" |
4446 | |
4447 | === modified file 'tests/unittests/Printers/CMakeLists.txt' |
4448 | --- tests/unittests/Printers/CMakeLists.txt 2017-02-21 16:55:36 +0000 |
4449 | +++ tests/unittests/Printers/CMakeLists.txt 2017-03-23 12:51:36 +0000 |
4450 | @@ -22,6 +22,8 @@ |
4451 | add_executable(testPrintersPrinters tst_printers.cpp ${MOCK_SOURCES}) |
4452 | target_link_libraries(testPrintersPrinters UbuntuComponentsExtrasPrintersQml Qt5::Test Qt5::Gui) |
4453 | add_test(tst_printers testPrintersPrinters) |
4454 | +set_tests_properties(tst_printers PROPERTIES |
4455 | + ENVIRONMENT "XDG_DATA_DIRS=${CMAKE_CURRENT_SOURCE_DIR}/testdata") |
4456 | |
4457 | add_executable(testPrintersPrinterModel tst_printermodel.cpp ${MOCK_SOURCES}) |
4458 | target_link_libraries(testPrintersPrinterModel UbuntuComponentsExtrasPrintersQml Qt5::Test Qt5::Gui) |
4459 | @@ -43,6 +45,14 @@ |
4460 | target_link_libraries(testPrintersJobFilter UbuntuComponentsExtrasPrintersQml Qt5::Test Qt5::Gui) |
4461 | add_test(tst_jobfilter testPrintersJobFilter) |
4462 | |
4463 | -add_executable(testPrintersSignalHandler tst_signalhandler.cpp ${MOCK_SOURCES}) |
4464 | -target_link_libraries(testPrintersSignalHandler UbuntuComponentsExtrasPrintersQml Qt5::Test Qt5::Gui) |
4465 | -add_test(tst_signalhandler testPrintersSignalHandler) |
4466 | +add_executable(testPrintersSignalRateLimiter tst_signalratelimiter.cpp ${MOCK_SOURCES}) |
4467 | +target_link_libraries(testPrintersSignalRateLimiter UbuntuComponentsExtrasPrintersQml Qt5::Test Qt5::Gui) |
4468 | +add_test(tst_signalratelimiter testPrintersSignalRateLimiter) |
4469 | + |
4470 | +add_executable(testPrintersDevice tst_printerdevice.cpp ${MOCK_SOURCES}) |
4471 | +target_link_libraries(testPrintersDevice UbuntuComponentsExtrasPrintersQml Qt5::Test Qt5::Gui) |
4472 | +add_test(tst_printerdevice testPrintersDevice) |
4473 | + |
4474 | +add_executable(testPrintersDeviceModel tst_printerdevicemodel.cpp ${MOCK_SOURCES}) |
4475 | +target_link_libraries(testPrintersDeviceModel UbuntuComponentsExtrasPrintersQml Qt5::Test Qt5::Gui) |
4476 | +add_test(tst_printerdevicemodel testPrintersDeviceModel) |
4477 | |
4478 | === modified file 'tests/unittests/Printers/mockbackend.h' |
4479 | --- tests/unittests/Printers/mockbackend.h 2017-02-24 12:20:07 +0000 |
4480 | +++ tests/unittests/Printers/mockbackend.h 2017-03-23 12:51:36 +0000 |
4481 | @@ -40,7 +40,7 @@ |
4482 | |
4483 | virtual bool holdsDefinition() const override |
4484 | { |
4485 | - return true; |
4486 | + return m_holdsDefinition; |
4487 | } |
4488 | |
4489 | virtual QString printerAdd(const QString &name, |
4490 | @@ -105,6 +105,19 @@ |
4491 | return returnValue; |
4492 | } |
4493 | |
4494 | + virtual QString printerSetCopies(const QString &name, const int &copies) |
4495 | + { |
4496 | + printerOptions[name].insert("Copies", copies); |
4497 | + return returnValue; |
4498 | + } |
4499 | + |
4500 | + virtual QString printerSetShared(const QString &name, |
4501 | + const bool shared) override |
4502 | + { |
4503 | + printerOptions[name].insert("Shared", shared); |
4504 | + return returnValue; |
4505 | + } |
4506 | + |
4507 | virtual QString printerSetInfo(const QString &name, |
4508 | const QString &info) override |
4509 | { |
4510 | @@ -163,6 +176,32 @@ |
4511 | } |
4512 | } |
4513 | |
4514 | + virtual void holdJob(const QString &printerName, const int jobId) override |
4515 | + { |
4516 | + // Change the faked job state |
4517 | + Q_FOREACH(auto job, m_jobs) { |
4518 | + if (job->printerName() == printerName |
4519 | + && job->jobId() == jobId) { |
4520 | + job->setState(PrinterEnum::JobState::Held); |
4521 | + } |
4522 | + } |
4523 | + |
4524 | + Q_EMIT jobState("", "", printerName, 1, "", true, jobId, static_cast<uint>(PrinterEnum::JobState::Held), "", "", 1); |
4525 | + } |
4526 | + |
4527 | + virtual void releaseJob(const QString &printerName, const int jobId) override |
4528 | + { |
4529 | + // Change the faked job state |
4530 | + Q_FOREACH(auto job, m_jobs) { |
4531 | + if (job->printerName() == printerName |
4532 | + && job->jobId() == jobId) { |
4533 | + job->setState(PrinterEnum::JobState::Pending); |
4534 | + } |
4535 | + } |
4536 | + |
4537 | + Q_EMIT jobState("", "", printerName, 1, "", true, jobId, static_cast<uint>(PrinterEnum::JobState::Pending), "", "", 1); |
4538 | + } |
4539 | + |
4540 | virtual int printFileToDest(const QString &filepath, |
4541 | const QString &title, |
4542 | const cups_dest_t *dest) override |
4543 | @@ -184,9 +223,39 @@ |
4544 | virtual QMap<QString, QVariant> printerGetJobAttributes( |
4545 | const QString &name, const int jobId) override |
4546 | { |
4547 | - Q_UNUSED(name); |
4548 | - Q_UNUSED(jobId); |
4549 | - return QMap<QString, QVariant>(); |
4550 | + QMap<QString, QVariant> attributes; |
4551 | + |
4552 | + Q_FOREACH(auto job, m_jobs) { |
4553 | + if (job->printerName() == name |
4554 | + && job->jobId() == jobId) { |
4555 | + // Emulate reverse of PrinterJob::loadAttributes |
4556 | + // using local jobs defined in tests |
4557 | + attributes.insert("Collate", job->collate()); |
4558 | + attributes.insert("copies", job->copies()); |
4559 | + attributes.insert("ColorModel", job->getColorModel().name); |
4560 | + attributes.insert("CompletedTime", job->completedTime()); |
4561 | + attributes.insert("CreationTime", job->creationTime()); |
4562 | + attributes.insert("Duplex", Utils::duplexModeToPpdChoice(job->getDuplexMode())); |
4563 | + attributes.insert("impressionsCompleted", job->impressionsCompleted()); |
4564 | + attributes.insert("landscape", job->landscape()); |
4565 | + attributes.insert("messages", job->messages()); |
4566 | + if (job->printRangeMode() == PrinterEnum::PrintRange::AllPages) { |
4567 | + attributes.insert("page-ranges", QStringList()); |
4568 | + } else { |
4569 | + attributes.insert("page-ranges", job->printRange().split(QLocale::system().groupSeparator())); |
4570 | + } |
4571 | + attributes.insert("ProcessingTime", job->processingTime()); |
4572 | + attributes.insert("Quality", job->getPrintQuality().name); |
4573 | + attributes.insert("OutputOrder", job->reverse() ? "Reverse" : "Normal"); |
4574 | + attributes.insert("State", static_cast<int>(job->state())); |
4575 | + attributes.insert("Size", job->size()); |
4576 | + attributes.insert("User", job->user()); |
4577 | + |
4578 | + break; |
4579 | + } |
4580 | + } |
4581 | + |
4582 | + return attributes; |
4583 | } |
4584 | |
4585 | virtual QString printerName() const override |
4586 | @@ -209,6 +278,11 @@ |
4587 | return m_makeAndModel; |
4588 | } |
4589 | |
4590 | + virtual bool isRemote() const override |
4591 | + { |
4592 | + return m_remote; |
4593 | + } |
4594 | + |
4595 | virtual PrinterEnum::State state() const override |
4596 | { |
4597 | return m_state; |
4598 | @@ -287,6 +361,17 @@ |
4599 | m_requestedPrinters << printerName; |
4600 | } |
4601 | |
4602 | + virtual void requestJobExtendedAttributes(QSharedPointer<Printer> printer, QSharedPointer<PrinterJob> job) override |
4603 | + { |
4604 | + QMap<QString, QVariant> attributes = printerGetJobAttributes(printer->name(), job->jobId()); |
4605 | + |
4606 | + Q_EMIT jobLoaded(printer->name(), job->jobId(), attributes); |
4607 | + } |
4608 | + |
4609 | + virtual PrinterEnum::PrinterType type() const override |
4610 | + { |
4611 | + return m_type; |
4612 | + } |
4613 | |
4614 | void mockPrinterAdded( |
4615 | const QString &text, |
4616 | @@ -339,6 +424,34 @@ |
4617 | ); |
4618 | } |
4619 | |
4620 | + void mockJobCompleted( |
4621 | + const QString &text, const QString &printer_uri, |
4622 | + const QString &printer_name, uint printer_state, |
4623 | + const QString &printer_state_reasons, bool printer_is_accepting_jobs, |
4624 | + uint job_id, uint job_state, const QString &job_state_reasons, |
4625 | + const QString &job_name, uint job_impressions_completed) |
4626 | + { |
4627 | + Q_EMIT jobCompleted( |
4628 | + text, printer_uri, printer_name, printer_state, |
4629 | + printer_state_reasons, printer_is_accepting_jobs, job_id, |
4630 | + job_state, job_state_reasons, job_name, job_impressions_completed |
4631 | + ); |
4632 | + } |
4633 | + |
4634 | + void mockJobState( |
4635 | + const QString &text, const QString &printer_uri, |
4636 | + const QString &printer_name, uint printer_state, |
4637 | + const QString &printer_state_reasons, bool printer_is_accepting_jobs, |
4638 | + uint job_id, uint job_state, const QString &job_state_reasons, |
4639 | + const QString &job_name, uint job_impressions_completed) |
4640 | + { |
4641 | + Q_EMIT jobState( |
4642 | + text, printer_uri, printer_name, printer_state, |
4643 | + printer_state_reasons, printer_is_accepting_jobs, job_id, |
4644 | + job_state, job_state_reasons, job_name, job_impressions_completed |
4645 | + ); |
4646 | + } |
4647 | + |
4648 | void mockDriversLoaded(const QList<PrinterDriver> &drivers) |
4649 | { |
4650 | Q_EMIT printerDriversLoaded(drivers); |
4651 | @@ -354,6 +467,11 @@ |
4652 | Q_EMIT printerLoaded(printer); |
4653 | } |
4654 | |
4655 | + void mockDeviceFound(const Device &device) |
4656 | + { |
4657 | + Q_EMIT deviceFound(device); |
4658 | + } |
4659 | + |
4660 | QString returnValue = QString::null; |
4661 | |
4662 | // Map from printer to key/val. |
4663 | @@ -366,6 +484,9 @@ |
4664 | QMap<QString, PrinterEnum::ErrorPolicy> errorPolicies; |
4665 | QMap<QString, PrinterEnum::OperationPolicy> operationPolicies; |
4666 | |
4667 | + bool m_holdsDefinition = true; |
4668 | + bool m_remote = false; |
4669 | + |
4670 | QString m_description = QString::null; |
4671 | QString m_location = QString::null; |
4672 | QString m_makeAndModel = QString::null; |
4673 | @@ -383,6 +504,8 @@ |
4674 | QList<QSharedPointer<PrinterJob>> m_jobs; |
4675 | QStringList m_requestedPrinters; |
4676 | |
4677 | + PrinterEnum::PrinterType m_type = PrinterEnum::PrinterType::ProxyType; |
4678 | + |
4679 | Q_SIGNALS: |
4680 | void printToFile(const QString &filepath, const QString &title); |
4681 | }; |
4682 | |
4683 | === added directory 'tests/unittests/Printers/testdata' |
4684 | === added directory 'tests/unittests/Printers/testdata/cups' |
4685 | === added directory 'tests/unittests/Printers/testdata/cups/data' |
4686 | === added file 'tests/unittests/Printers/testdata/cups/data/default-testpage.pdf' |
4687 | === modified file 'tests/unittests/Printers/tst_jobfilter.cpp' |
4688 | --- tests/unittests/Printers/tst_jobfilter.cpp 2017-02-21 10:46:29 +0000 |
4689 | +++ tests/unittests/Printers/tst_jobfilter.cpp 2017-03-23 12:51:36 +0000 |
4690 | @@ -32,10 +32,12 @@ |
4691 | QScopedPointer<MockPrinterBackend> backend(new MockPrinterBackend); |
4692 | JobModel model(backend.data()); |
4693 | |
4694 | - auto job = QSharedPointer<PrinterJob>(new PrinterJob("test-printer", backend.data())); |
4695 | + auto job = QSharedPointer<PrinterJob>( |
4696 | + new PrinterJob("test-printer", backend.data(), 100) |
4697 | + ); |
4698 | backend->m_jobs << job; |
4699 | // Trigger update. |
4700 | - backend->mockJobCreated("", "", "", 1, "", true, 100, 1, "", "", 1); |
4701 | + backend->mockJobCreated("", "", "test-printer", 1, "", true, 100, 3, "", "", 1); |
4702 | |
4703 | JobFilter filter; |
4704 | filter.setSourceModel(&model); |
4705 | |
4706 | === modified file 'tests/unittests/Printers/tst_jobmodel.cpp' |
4707 | --- tests/unittests/Printers/tst_jobmodel.cpp 2017-02-21 10:46:29 +0000 |
4708 | +++ tests/unittests/Printers/tst_jobmodel.cpp 2017-03-23 12:51:36 +0000 |
4709 | @@ -18,12 +18,14 @@ |
4710 | |
4711 | #include "backend/backend.h" |
4712 | #include "models/jobmodel.h" |
4713 | +#include "printers/printers.h" |
4714 | |
4715 | #include <QDebug> |
4716 | #include <QObject> |
4717 | #include <QSignalSpy> |
4718 | #include <QTest> |
4719 | |
4720 | + |
4721 | class TestJobModel : public QObject |
4722 | { |
4723 | Q_OBJECT |
4724 | @@ -31,15 +33,23 @@ |
4725 | void init() |
4726 | { |
4727 | m_backend = new MockPrinterBackend; |
4728 | - m_model = new JobModel(m_backend); |
4729 | + m_printers = new Printers(m_backend); |
4730 | + |
4731 | + m_model = static_cast<JobModel *>(m_printers->printJobs()); |
4732 | + |
4733 | + PrinterBackend* backend = new MockPrinterBackend("test-printer"); |
4734 | + auto printer = QSharedPointer<Printer>(new Printer(backend)); |
4735 | + m_backend->mockPrinterLoaded(printer); |
4736 | } |
4737 | void cleanup() |
4738 | { |
4739 | - QSignalSpy destroyedSpy(m_model, SIGNAL(destroyed(QObject*))); |
4740 | - m_model->deleteLater(); |
4741 | + QSignalSpy destroyedSpy(m_printers, SIGNAL(destroyed(QObject*))); |
4742 | + m_printers->deleteLater(); |
4743 | QTRY_COMPARE(destroyedSpy.count(), 1); |
4744 | - delete m_backend; |
4745 | } |
4746 | + |
4747 | + // Tests for adding/removing/changing things in the model |
4748 | + |
4749 | void testInsert() |
4750 | { |
4751 | QSignalSpy countSpy(m_model, SIGNAL(countChanged())); |
4752 | @@ -68,7 +78,7 @@ |
4753 | catchall handler in the model. */ |
4754 | QSignalSpy removeSpy(m_model, SIGNAL(rowsRemoved(const QModelIndex&, int, int))); |
4755 | m_backend->m_jobs.clear(); |
4756 | - m_backend->mockJobCreated("", "", "", 1, "", true, 100, 1, "", "", 1); |
4757 | + m_backend->mockJobCompleted("", "", "", 1, "", true, 100, 1, "", "", 1); |
4758 | QCOMPARE(removeSpy.count(), 1); |
4759 | |
4760 | // Check item was removed |
4761 | @@ -76,44 +86,451 @@ |
4762 | QCOMPARE(args.at(1).toInt(), 0); |
4763 | QCOMPARE(args.at(2).toInt(), 0); |
4764 | } |
4765 | - void testMove() |
4766 | - { |
4767 | - // Add two jobs. |
4768 | - auto job1 = QSharedPointer<PrinterJob>(new PrinterJob("test-printer", m_backend, 1)); |
4769 | - auto job2 = QSharedPointer<PrinterJob>(new PrinterJob("test-printer", m_backend, 2)); |
4770 | - m_backend->m_jobs << job1 << job2; |
4771 | - m_backend->mockJobCreated("", "", "", 1, "", true, 100, 1, "", "", 1); |
4772 | - |
4773 | - m_backend->m_jobs.move(0, 1); |
4774 | - // Triggers a move. |
4775 | - QSignalSpy moveSpy(m_model, SIGNAL(rowsMoved(const QModelIndex&, int, int, const QModelIndex&, int))); |
4776 | - m_backend->mockJobCreated("", "", "", 1, "", true, 100, 1, "", "", 1); |
4777 | - QCOMPARE(moveSpy.count(), 1); |
4778 | - QList<QVariant> args = moveSpy.at(0); |
4779 | - QCOMPARE(args.at(1).toInt(), 1); |
4780 | - QCOMPARE(args.at(2).toInt(), 1); |
4781 | - QCOMPARE(args.at(4).toInt(), 0); |
4782 | - } |
4783 | void testModify() |
4784 | { |
4785 | auto jobBefore = QSharedPointer<PrinterJob>(new PrinterJob("test-printer", m_backend, 1)); |
4786 | + int impressions_count = 1; |
4787 | |
4788 | m_backend->m_jobs << jobBefore; |
4789 | - m_backend->mockJobCreated("", "", "", 1, "", true, 100, 1, "", "", 1); |
4790 | - |
4791 | - auto jobAfter = QSharedPointer<PrinterJob>(new PrinterJob("test-printer", m_backend, 1)); |
4792 | - jobAfter->setCopies(100); |
4793 | - m_backend->m_jobs.replace(0, jobAfter); |
4794 | + m_backend->mockJobCreated("", "", "", 1, "", true, 100, 1, "", "", impressions_count); |
4795 | |
4796 | // Triggers a change. |
4797 | QSignalSpy changedSpy(m_model, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&, const QVector<int>&))); |
4798 | - m_backend->mockJobCreated("", "", "", 1, "", true, 100, 1, "", "", 1); |
4799 | + |
4800 | + impressions_count = 5; |
4801 | + m_backend->mockJobState("", "", "", 1, "", true, 100, 1, "", "", impressions_count); |
4802 | + |
4803 | QCOMPARE(changedSpy.count(), 1); |
4804 | } |
4805 | |
4806 | + // Tests for the roles in the model exposed to QML |
4807 | + |
4808 | + void testIdRole() |
4809 | + { |
4810 | + auto job = QSharedPointer<PrinterJob>(new PrinterJob("test-printer", m_backend, 100)); |
4811 | + m_backend->m_jobs << job; |
4812 | + m_backend->mockJobCreated("", "", "", 1, "", true, 100, 1, "", "", 1); |
4813 | + |
4814 | + QTRY_COMPARE(m_model->count(), 1); |
4815 | + QCOMPARE(job->jobId(), 100); |
4816 | + QCOMPARE(m_model->data(m_model->index(0), JobModel::IdRole).toInt(), 100); |
4817 | + } |
4818 | + void testCollateRole() |
4819 | + { |
4820 | + auto jobA = QSharedPointer<PrinterJob>(new PrinterJob("test-printer", m_backend, 1)); |
4821 | + jobA->setCollate(false); |
4822 | + |
4823 | + auto jobB = QSharedPointer<PrinterJob>(new PrinterJob("test-printer", m_backend, 2)); |
4824 | + jobB->setCollate(true); |
4825 | + |
4826 | + m_backend->m_jobs << jobA << jobB; |
4827 | + m_backend->mockJobCreated("", "", "test-printer", 1, "", true, 1, 1, "", "", 1); |
4828 | + m_backend->mockJobCreated("", "", "test-printer", 1, "", true, 2, 1, "", "", 1); |
4829 | + |
4830 | + QTRY_COMPARE(m_model->count(), 2); |
4831 | + |
4832 | + QCOMPARE(m_model->data(m_model->index(0), JobModel::CollateRole).toBool(), false); |
4833 | + QCOMPARE(m_model->data(m_model->index(1), JobModel::CollateRole).toBool(), true); |
4834 | + } |
4835 | + void testColorModelRole() |
4836 | + { |
4837 | + // FIXME: read comment in JobModel::updateJob |
4838 | + QSKIP("We are ignoring colorModel for now as it requires a loaded Printer for the PrinterJob."); |
4839 | + |
4840 | + ColorModel a; |
4841 | + a.name = "KGray"; |
4842 | + a.text = "Gray"; |
4843 | + |
4844 | + ColorModel b; |
4845 | + b.name = "RGB"; |
4846 | + b.text = ""; |
4847 | + QList<ColorModel> models({a, b}); |
4848 | + |
4849 | + PrinterBackend* backend = new MockPrinterBackend("a-printer"); |
4850 | + ((MockPrinterBackend*) backend)->printerOptions["a-printer"].insert( |
4851 | + "SupportedColorModels", QVariant::fromValue(models) |
4852 | + ); |
4853 | + |
4854 | + auto printer = QSharedPointer<Printer>(new Printer(backend)); |
4855 | + m_backend->mockPrinterLoaded(printer); |
4856 | +// m_printers->m_printers << printer; |
4857 | + |
4858 | + auto jobA = QSharedPointer<PrinterJob>(new PrinterJob("a-printer", backend, 1)); |
4859 | + jobA->setPrinter(printer); |
4860 | + jobA->loadDefaults(); |
4861 | + jobA->setColorModel(models.indexOf(a)); |
4862 | + |
4863 | + auto jobB = QSharedPointer<PrinterJob>(new PrinterJob("a-printer", backend, 2)); |
4864 | + jobB->setPrinter(printer); |
4865 | + jobB->loadDefaults(); |
4866 | + jobB->setColorModel(models.indexOf(b)); |
4867 | + |
4868 | + m_backend->m_jobs << jobA << jobB; |
4869 | + m_backend->mockJobCreated("", "", "a-printer", 1, "", true, 1, 1, "", "", 1); |
4870 | + m_backend->mockJobCreated("", "", "a-printer", 1, "", true, 2, 1, "", "", 1); |
4871 | + |
4872 | + QTRY_COMPARE(m_model->count(), 2); |
4873 | + QCOMPARE(m_model->data(m_model->index(0), JobModel::ColorModelRole).toString(), |
4874 | + a.text); |
4875 | + QCOMPARE(m_model->data(m_model->index(1), JobModel::ColorModelRole).toString(), |
4876 | + b.name); |
4877 | + } |
4878 | + void testCompletedTimeRole() |
4879 | + { |
4880 | + QDateTime dateTimeA = QDateTime::currentDateTime(); |
4881 | + QDateTime dateTimeB = QDateTime::currentDateTime().addSecs(100); |
4882 | + |
4883 | + auto jobA = QSharedPointer<PrinterJob>(new PrinterJob("test-printer", m_backend, 1)); |
4884 | + jobA->setCompletedTime(dateTimeA); |
4885 | + |
4886 | + auto jobB = QSharedPointer<PrinterJob>(new PrinterJob("test-printer", m_backend, 2)); |
4887 | + jobB->setCompletedTime(dateTimeB); |
4888 | + |
4889 | + m_backend->m_jobs << jobA << jobB; |
4890 | + m_backend->mockJobCreated("", "", "test-printer", 1, "", true, 1, 1, "", "", 1); |
4891 | + m_backend->mockJobCreated("", "", "test-printer", 1, "", true, 2, 1, "", "", 1); |
4892 | + |
4893 | + QTRY_COMPARE(m_model->count(), 2); |
4894 | + QCOMPARE(m_model->data(m_model->index(0), JobModel::CompletedTimeRole).toDateTime(), |
4895 | + dateTimeA); |
4896 | + QCOMPARE(m_model->data(m_model->index(1), JobModel::CompletedTimeRole).toDateTime(), |
4897 | + dateTimeB); |
4898 | + } |
4899 | + void testCopiesRole() |
4900 | + { |
4901 | + auto jobA = QSharedPointer<PrinterJob>(new PrinterJob("test-printer", m_backend, 1)); |
4902 | + jobA->setCopies(2); |
4903 | + |
4904 | + auto jobB = QSharedPointer<PrinterJob>(new PrinterJob("test-printer", m_backend, 2)); |
4905 | + jobB->setCopies(5); |
4906 | + |
4907 | + m_backend->m_jobs << jobA << jobB; |
4908 | + m_backend->mockJobCreated("", "", "test-printer", 1, "", true, 1, 1, "", "", 1); |
4909 | + m_backend->mockJobCreated("", "", "test-printer", 1, "", true, 2, 1, "", "", 1); |
4910 | + |
4911 | + QTRY_COMPARE(m_model->count(), 2); |
4912 | + QCOMPARE(m_model->data(m_model->index(0), JobModel::CopiesRole).toInt(), 2); |
4913 | + QCOMPARE(m_model->data(m_model->index(1), JobModel::CopiesRole).toInt(), 5); |
4914 | + } |
4915 | + void testCreationTimeRole() |
4916 | + { |
4917 | + QDateTime dateTimeA = QDateTime::currentDateTime(); |
4918 | + QDateTime dateTimeB = QDateTime::currentDateTime().addSecs(100); |
4919 | + |
4920 | + auto jobA = QSharedPointer<PrinterJob>(new PrinterJob("test-printer", m_backend, 1)); |
4921 | + jobA->setCreationTime(dateTimeA); |
4922 | + |
4923 | + auto jobB = QSharedPointer<PrinterJob>(new PrinterJob("test-printer", m_backend, 2)); |
4924 | + jobB->setCreationTime(dateTimeB); |
4925 | + |
4926 | + m_backend->m_jobs << jobA << jobB; |
4927 | + m_backend->mockJobCreated("", "", "test-printer", 1, "", true, 1, 1, "", "", 1); |
4928 | + m_backend->mockJobCreated("", "", "test-printer", 1, "", true, 2, 1, "", "", 1); |
4929 | + |
4930 | + QTRY_COMPARE(m_model->count(), 2); |
4931 | + QCOMPARE(m_model->data(m_model->index(0), JobModel::CreationTimeRole).toDateTime(), |
4932 | + dateTimeA); |
4933 | + QCOMPARE(m_model->data(m_model->index(1), JobModel::CreationTimeRole).toDateTime(), |
4934 | + dateTimeB); |
4935 | + } |
4936 | + void testDuplexRole() |
4937 | + { |
4938 | + // FIXME: read comment in JobModel::updateJob |
4939 | + QSKIP("We are ignoring duplex for now as it requires a loaded Printer for the PrinterJob."); |
4940 | + |
4941 | + QList<PrinterEnum::DuplexMode> modes({ |
4942 | + PrinterEnum::DuplexMode::DuplexNone, |
4943 | + PrinterEnum::DuplexMode::DuplexLongSide, |
4944 | + PrinterEnum::DuplexMode::DuplexShortSide, |
4945 | + }); |
4946 | + |
4947 | + ((MockPrinterBackend*) m_backend)->m_supportedDuplexModes = modes; |
4948 | + |
4949 | + PrinterBackend* backend = new MockPrinterBackend("test-printer"); |
4950 | + ((MockPrinterBackend*) backend)->m_supportedDuplexModes = modes; |
4951 | + |
4952 | + auto printer = QSharedPointer<Printer>(new Printer(backend)); |
4953 | + m_backend->mockPrinterLoaded(printer); |
4954 | + |
4955 | + auto jobA = QSharedPointer<PrinterJob>(new PrinterJob("test-printer", backend, 1)); |
4956 | + jobA->setPrinter(printer); |
4957 | + jobA->setDuplexMode(modes.indexOf(PrinterEnum::DuplexMode::DuplexLongSide)); |
4958 | + |
4959 | + auto jobB = QSharedPointer<PrinterJob>(new PrinterJob("test-printer", backend, 2)); |
4960 | + jobB->setPrinter(printer); |
4961 | + jobB->setDuplexMode(modes.indexOf(PrinterEnum::DuplexMode::DuplexNone)); |
4962 | + |
4963 | + m_backend->m_jobs << jobA << jobB; |
4964 | + m_backend->mockJobCreated("", "", "test-printer", 1, "", true, 1, 1, "", "", 1); |
4965 | + m_backend->mockJobCreated("", "", "test-printer", 1, "", true, 2, 1, "", "", 1); |
4966 | + |
4967 | + QTRY_COMPARE(m_model->count(), 2); |
4968 | + QCOMPARE(m_model->data(m_model->index(0), JobModel::DuplexRole).toString(), |
4969 | + Utils::duplexModeToUIString(PrinterEnum::DuplexMode::DuplexLongSide)); |
4970 | + QCOMPARE(m_model->data(m_model->index(1), JobModel::DuplexRole).toString(), |
4971 | + Utils::duplexModeToUIString(PrinterEnum::DuplexMode::DuplexNone)); |
4972 | + } |
4973 | + void testHeldRole() |
4974 | + { |
4975 | + auto jobA = QSharedPointer<PrinterJob>(new PrinterJob("test-printer", m_backend, 1)); |
4976 | + jobA->setState(PrinterEnum::JobState::Pending); |
4977 | + |
4978 | + auto jobB = QSharedPointer<PrinterJob>(new PrinterJob("test-printer", m_backend, 2)); |
4979 | + jobB->setState(PrinterEnum::JobState::Held); |
4980 | + |
4981 | + m_backend->m_jobs << jobA << jobB; |
4982 | + m_backend->mockJobCreated("", "", "test-printer", 1, "", true, 1, static_cast<uint>(jobA->state()), "", "", 1); |
4983 | + m_backend->mockJobCreated("", "", "test-printer", 1, "", true, 2, static_cast<uint>(jobB->state()), "", "", 1); |
4984 | + |
4985 | + QTRY_COMPARE(m_model->count(), 2); |
4986 | + QCOMPARE(m_model->data(m_model->index(0), JobModel::HeldRole).toBool(), false); |
4987 | + QCOMPARE(m_model->data(m_model->index(1), JobModel::HeldRole).toBool(), true); |
4988 | + } |
4989 | + void testImpressionsCompletedRole() |
4990 | + { |
4991 | + auto jobA = QSharedPointer<PrinterJob>(new PrinterJob("test-printer", m_backend, 1)); |
4992 | + jobA->setImpressionsCompleted(2); |
4993 | + |
4994 | + auto jobB = QSharedPointer<PrinterJob>(new PrinterJob("test-printer", m_backend, 2)); |
4995 | + jobB->setImpressionsCompleted(5); |
4996 | + |
4997 | + m_backend->m_jobs << jobA << jobB; |
4998 | + m_backend->mockJobCreated("", "", "test-printer", 1, "", true, 1, 1, "", "", jobA->impressionsCompleted()); |
4999 | + m_backend->mockJobCreated("", "", "test-printer", 1, "", true, 2, 1, "", "", jobB->impressionsCompleted()); |
5000 | + |
I approve all the changes made by Andrew, which is this whole branch minus my own changes.