Merge lp:~jonas-drange/ubuntu-ui-extras/dedupe-requests into lp:ubuntu-ui-extras

Proposed by Jonas G. Drange
Status: Superseded
Proposed branch: lp:~jonas-drange/ubuntu-ui-extras/dedupe-requests
Merge into: lp:ubuntu-ui-extras
Diff against target: 9609 lines (+9257/-4)
55 files modified
debian/control (+3/-0)
modules/Ubuntu/Components/Extras/CMakeLists.txt (+1/-0)
modules/Ubuntu/Components/Extras/Example/PrinterQueue.qml (+117/-0)
modules/Ubuntu/Components/Extras/Example/Printers.qml (+603/-0)
modules/Ubuntu/Components/Extras/Printers/CMakeLists.txt (+68/-0)
modules/Ubuntu/Components/Extras/Printers/backend/backend.cpp (+272/-0)
modules/Ubuntu/Components/Extras/Printers/backend/backend.h (+201/-0)
modules/Ubuntu/Components/Extras/Printers/backend/backend_cups.cpp (+722/-0)
modules/Ubuntu/Components/Extras/Printers/backend/backend_cups.h (+135/-0)
modules/Ubuntu/Components/Extras/Printers/backend/backend_pdf.cpp (+119/-0)
modules/Ubuntu/Components/Extras/Printers/backend/backend_pdf.h (+47/-0)
modules/Ubuntu/Components/Extras/Printers/cups/ippclient.cpp (+988/-0)
modules/Ubuntu/Components/Extras/Printers/cups/ippclient.h (+121/-0)
modules/Ubuntu/Components/Extras/Printers/cups/printerdriverloader.cpp (+127/-0)
modules/Ubuntu/Components/Extras/Printers/cups/printerdriverloader.h (+61/-0)
modules/Ubuntu/Components/Extras/Printers/cups/printerloader.cpp (+53/-0)
modules/Ubuntu/Components/Extras/Printers/cups/printerloader.h (+49/-0)
modules/Ubuntu/Components/Extras/Printers/enums.h (+106/-0)
modules/Ubuntu/Components/Extras/Printers/i18n.cpp (+42/-0)
modules/Ubuntu/Components/Extras/Printers/i18n.h (+29/-0)
modules/Ubuntu/Components/Extras/Printers/models/drivermodel.cpp (+175/-0)
modules/Ubuntu/Components/Extras/Printers/models/drivermodel.h (+82/-0)
modules/Ubuntu/Components/Extras/Printers/models/jobmodel.cpp (+390/-0)
modules/Ubuntu/Components/Extras/Printers/models/jobmodel.h (+125/-0)
modules/Ubuntu/Components/Extras/Printers/models/printermodel.cpp (+503/-0)
modules/Ubuntu/Components/Extras/Printers/models/printermodel.h (+154/-0)
modules/Ubuntu/Components/Extras/Printers/org.cups.cupsd.Notifier.xml (+146/-0)
modules/Ubuntu/Components/Extras/Printers/plugin.cpp (+58/-0)
modules/Ubuntu/Components/Extras/Printers/plugin.h (+33/-0)
modules/Ubuntu/Components/Extras/Printers/printer/printer.cpp (+315/-0)
modules/Ubuntu/Components/Extras/Printers/printer/printer.h (+96/-0)
modules/Ubuntu/Components/Extras/Printers/printer/printerjob.cpp (+514/-0)
modules/Ubuntu/Components/Extras/Printers/printer/printerjob.h (+174/-0)
modules/Ubuntu/Components/Extras/Printers/printer/printersignalhandler.cpp (+69/-0)
modules/Ubuntu/Components/Extras/Printers/printer/printersignalhandler.h (+55/-0)
modules/Ubuntu/Components/Extras/Printers/printers/printers.cpp (+232/-0)
modules/Ubuntu/Components/Extras/Printers/printers/printers.h (+102/-0)
modules/Ubuntu/Components/Extras/Printers/printers_global.h (+23/-0)
modules/Ubuntu/Components/Extras/Printers/qmldir (+2/-0)
modules/Ubuntu/Components/Extras/Printers/structs.h (+97/-0)
modules/Ubuntu/Components/Extras/Printers/utils.h (+110/-0)
po/CMakeLists.txt (+2/-1)
po/ubuntu-ui-extras.pot (+27/-3)
tests/unittests/CMakeLists.txt (+2/-0)
tests/unittests/Printers/CMakeLists.txt (+48/-0)
tests/unittests/Printers/mockbackend.h (+382/-0)
tests/unittests/Printers/tst_drivermodel.cpp (+136/-0)
tests/unittests/Printers/tst_jobfilter.cpp (+55/-0)
tests/unittests/Printers/tst_jobmodel.cpp (+120/-0)
tests/unittests/Printers/tst_printer.cpp (+284/-0)
tests/unittests/Printers/tst_printerfilter.cpp (+126/-0)
tests/unittests/Printers/tst_printerjob.cpp (+318/-0)
tests/unittests/Printers/tst_printermodel.cpp (+161/-0)
tests/unittests/Printers/tst_printers.cpp (+233/-0)
tests/unittests/Printers/tst_signalhandler.cpp (+44/-0)
To merge this branch: bzr merge lp:~jonas-drange/ubuntu-ui-extras/dedupe-requests
Reviewer Review Type Date Requested Status
Ubuntu Phablet Team Pending
Review via email: mp+318217@code.launchpad.net

Commit message

guards against excessive loading of printers and drivers

Description of the change

guards against excessive loading of printers and drivers

To post a comment you must log in.
135. By Jonas G. Drange

qlist->qset

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'debian/control'
--- debian/control 2016-08-24 15:46:52 +0000
+++ debian/control 2017-02-24 12:29:37 +0000
@@ -3,8 +3,10 @@
3Priority: optional3Priority: optional
4Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>4Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>
5Build-Depends: cmake (>= 2.8.9),5Build-Depends: cmake (>= 2.8.9),
6 cmake-extras (>= 0.10),
6 debhelper (>= 9),7 debhelper (>= 9),
7 gettext,8 gettext,
9 libcups2-dev,
8 pkg-config,10 pkg-config,
9 python:any,11 python:any,
10 qt5-default,12 qt5-default,
@@ -27,6 +29,7 @@
27Architecture: any29Architecture: any
28Depends: ${misc:Depends},30Depends: ${misc:Depends},
29 ${shlibs:Depends},31 ${shlibs:Depends},
32 libqt5printsupport5,
30 qml-module-qtquick2,33 qml-module-qtquick2,
31 qml-module-ubuntu-components,34 qml-module-ubuntu-components,
32 qml-module-qtquick-window2,35 qml-module-qtquick-window2,
3336
=== modified file 'modules/Ubuntu/Components/Extras/CMakeLists.txt'
--- modules/Ubuntu/Components/Extras/CMakeLists.txt 2016-12-08 14:26:41 +0000
+++ modules/Ubuntu/Components/Extras/CMakeLists.txt 2017-02-24 12:29:37 +0000
@@ -13,4 +13,5 @@
13add_subdirectory(plugin)13add_subdirectory(plugin)
14add_subdirectory(Example)14add_subdirectory(Example)
15add_subdirectory(PhotoEditor)15add_subdirectory(PhotoEditor)
16add_subdirectory(Printers)
16add_subdirectory(TabsBar)17add_subdirectory(TabsBar)
1718
=== added file 'modules/Ubuntu/Components/Extras/Example/PrinterQueue.qml'
--- modules/Ubuntu/Components/Extras/Example/PrinterQueue.qml 1970-01-01 00:00:00 +0000
+++ modules/Ubuntu/Components/Extras/Example/PrinterQueue.qml 2017-02-24 12:29:37 +0000
@@ -0,0 +1,117 @@
1/*
2 * Copyright 2017 Canonical Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by Jonas G. Drange <jonas.drange@canonical.com>
17 * Andrew Hayzen <andrew.hayzen@canonical.com>
18 */
19
20import QtQuick 2.4
21import QtQuick.Layouts 1.1
22import Ubuntu.Components 1.3
23import Ubuntu.Components.ListItems 1.3 as ListItems
24import Ubuntu.Components.Extras.Printers 0.1
25
26MainView {
27 width: units.gu(50)
28 height: units.gu(90)
29
30 Component {
31 id: queuePage
32
33 Page {
34 header: PageHeader {
35 title: "Queue: " + printer.name
36 flickable: queueView
37 }
38 visible: false
39
40 property var printer
41
42 ListView {
43 id: queueView
44 anchors {
45 fill: parent
46 }
47 delegate: ListItem {
48 height: modelLayout.height + (divider.visible ? divider.height : 0)
49 ListItemLayout {
50 id: modelLayout
51 title.text: displayName
52 subtitle.text: "Job: " + model.id + " State: " + model.state
53 + " Color: " + model.colorModel + " CreationTime: "
54 + model.creationTime + " PageRange: "
55 + model.printRange + " Messages: " + model.messages;
56 subtitle.wrapMode: Text.WrapAtWordBoundaryOrAnywhere
57 subtitle.maximumLineCount: 3
58 }
59 onClicked: {
60 console.debug("Cancel:", printer.name, model.id);
61 Printers.cancelJob(printer.name, model.id);
62 }
63 }
64 model: printer.jobs
65
66 Label {
67 anchors {
68 centerIn: parent
69 }
70 text: "Empty queue"
71 visible: queueView.count === 0
72 }
73 }
74 }
75 }
76
77 PageStack {
78 id: pageStack
79
80 Page {
81 id: printersPage
82 header: PageHeader {
83 title: "Printers"
84 flickable: printerList
85 }
86 visible: false
87
88 ListView {
89 id: printerList
90 anchors { fill: parent }
91 model: Printers.allPrintersWithPdf
92 delegate: ListItem {
93 height: modelLayout.height + (divider.visible ? divider.height : 0)
94 ListItemLayout {
95 id: modelLayout
96 title.text: displayName
97 title.font.bold: model.default
98 subtitle.text: description
99
100 Icon {
101 id: icon
102 width: height
103 height: units.gu(2.5)
104 name: "printer-symbolic"
105 SlotsLayout.position: SlotsLayout.First
106 }
107
108 ProgressionSlot {}
109 }
110 onClicked: pageStack.push(queuePage, { printer: model })
111 }
112 }
113 }
114
115 Component.onCompleted: push(printersPage)
116 }
117}
0118
=== added file 'modules/Ubuntu/Components/Extras/Example/Printers.qml'
--- modules/Ubuntu/Components/Extras/Example/Printers.qml 1970-01-01 00:00:00 +0000
+++ modules/Ubuntu/Components/Extras/Example/Printers.qml 2017-02-24 12:29:37 +0000
@@ -0,0 +1,603 @@
1/*
2 * Copyright 2017 Canonical Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by Jonas G. Drange <jonas.drange@canonical.com>
17 */
18
19import QtQuick 2.4
20import QtQuick.Layouts 1.1
21import Ubuntu.Components 1.3
22import Ubuntu.Components.Popups 1.3
23import Ubuntu.Components.ListItems 1.3 as ListItems
24import Ubuntu.Components.Extras.Printers 0.1
25
26MainView {
27 width: units.gu(50)
28 height: units.gu(90)
29
30 Component {
31 id: printerPage
32
33 Page {
34 visible: false
35 property var printer
36 header: PageHeader {
37 id: printerPageHeader
38 title: printer.name
39 flickable: printerFlickable
40 }
41
42 Component {
43 id: printerPageNotYetLoaded
44
45 Item {
46 anchors.fill: parent
47 ActivityIndicator {
48 anchors.centerIn: parent
49 running: true
50 }
51 }
52 }
53
54 Component.onCompleted: {
55 printer.description;
56 }
57
58 Flickable {
59 id: printerFlickable
60 anchors.fill: parent
61
62 Loader {
63 id: printerPageBitsLoader
64 anchors.fill: parent
65 sourceComponent: printer.isLoaded ? printerPageLoaded : printerPageNotYetLoaded
66 }
67 }
68
69 Component {
70 id: printerPageLoaded
71
72 Column {
73 spacing: units.gu(2)
74 anchors {
75 top: parent.top
76 topMargin: units.gu(2)
77 left: parent.left
78 right: parent.right
79 }
80
81 ListItems.Standard {
82 anchors {
83 left: parent.left
84 right: parent.right
85 }
86 text: "Enabled"
87
88 control: Switch {
89 checked: printer.printerEnabled
90 onCheckedChanged: printer.printerEnabled = checked
91 }
92 }
93
94 ListItems.Standard {
95 anchors {
96 left: parent.left
97 right: parent.right
98 }
99 text: "Accepting jobs"
100
101 control: Switch {
102 checked: printer.acceptJobs
103 onCheckedChanged: printer.acceptJobs = checked
104 }
105 }
106
107 ListItems.Standard {
108 anchors {
109 left: parent.left
110 right: parent.right
111 }
112 text: "Jobs"
113 progression: true
114 onClicked: pageStack.push(jobPage, { printer: printer })
115 }
116
117 Label {
118 anchors {
119 left: parent.left
120 right: parent.right
121 margins: units.gu(2)
122 }
123 text: "Description"
124 }
125
126 ListItems.SingleControl {
127 anchors {
128 left: parent.left
129 right: parent.right
130 }
131
132 control: TextField {
133 anchors {
134 margins: units.gu(1)
135 left: parent.left
136 right: parent.right
137
138 }
139 text: printer.description
140 onTextChanged: printer.description = text
141 }
142 }
143
144
145 ListItems.ValueSelector {
146 anchors {
147 left: parent.left
148 right: parent.right
149 }
150 enabled: values.length > 1
151 text: "Duplex"
152 values: printer.supportedDuplexModes
153 onSelectedIndexChanged: printer.duplexMode = selectedIndex
154 Component.onCompleted: {
155 if (enabled) {
156 selectedIndex = printer.duplexMode
157 }
158 }
159 }
160
161 ListItems.ValueSelector {
162 anchors {
163 left: parent.left
164 right: parent.right
165 }
166 text: "Page size"
167 values: printer.supportedPageSizes
168 onSelectedIndexChanged: printer.pageSize = selectedIndex
169 Component.onCompleted: selectedIndex = printer.supportedPageSizes.indexOf(printer.pageSize)
170 }
171
172 ListItems.ValueSelector {
173 anchors {
174 left: parent.left
175 right: parent.right
176 }
177 visible: printer.supportedColorModels.length
178 text: "Color model"
179 values: printer.supportedColorModels
180 enabled: values.length > 1
181 onSelectedIndexChanged: printer.colorModel = selectedIndex
182 Component.onCompleted: {
183 if (enabled)
184 selectedIndex = printer.colorModel
185 }
186 }
187
188 ListItems.ValueSelector {
189 anchors {
190 left: parent.left
191 right: parent.right
192 }
193 visible: printer.supportedPrintQualities.length
194 text: "Quality"
195 values: printer.supportedPrintQualities
196 enabled: values.length > 1
197 onSelectedIndexChanged: printer.printQuality = selectedIndex
198 Component.onCompleted: {
199 if (enabled)
200 selectedIndex = printer.printQuality
201 }
202 }
203 }
204 }
205 }
206 }
207
208 Component {
209 id: jobPage
210 Page {
211 property var printer
212 header: PageHeader {
213 id: jobPageHeader
214 title: "%1 (%2 jobs)".arg(printer.name).arg(jobList.count)
215 flickable: jobList
216 }
217
218 ListView {
219 id: jobList
220 anchors.fill: parent
221 model: printer.jobs
222 delegate: ListItem {
223 height: jobLayout.height + (divider.visible ? divider.height : 0)
224 ListItemLayout {
225 id: jobLayout
226 title.text: displayName
227
228 Icon {
229 id: icon
230 width: height
231 height: units.gu(2.5)
232 name: "stock_document"
233 SlotsLayout.position: SlotsLayout.First
234 }
235 }
236 }
237 }
238 }
239 }
240
241
242 Component {
243 id: allJobsPage
244 Page {
245 header: PageHeader {
246 id: allJobsHeader
247 title: "Printer jobs"
248 flickable: jobsList
249 }
250
251 ListView {
252 id: jobsList
253 anchors.fill: parent
254 model: Printers.printJobs
255 delegate: ListItem {
256 height: jobsLayout.height + (divider.visible ? divider.height : 0)
257 ListItemLayout {
258 id: jobsLayout
259 title.text: displayName
260
261 Icon {
262 id: icon
263 width: height
264 height: units.gu(2.5)
265 name: "stock_document"
266 SlotsLayout.position: SlotsLayout.First
267 }
268 }
269 }
270 }
271 }
272 }
273
274
275 PageStack {
276 id: pageStack
277
278 Component.onCompleted: push(printersPage)
279
280 Page {
281 id: printersPage
282 header: PageHeader {
283 title: "Printers"
284 flickable: printerList
285 trailingActionBar {
286 actions: [
287 Action {
288 iconName: "add"
289 text: "Add printer"
290 onTriggered: pageStack.push(addPrinterPageComponent)
291 },
292 Action {
293 iconName: "document-print"
294 text: "Printer jobs"
295 onTriggered: pageStack.push(allJobsPage)
296 }
297 ]
298 }
299 }
300 visible: false
301
302 ListView {
303 id: printerList
304 anchors { fill: parent }
305 model: Printers.allPrintersWithPdf
306 delegate: ListItem {
307 height: modelLayout.height + (divider.visible ? divider.height : 0)
308 trailingActions: ListItemActions {
309 actions: [
310 Action {
311 iconName: "delete"
312 onTriggered: {
313 if (!Printers.removePrinter(model.name)) {
314 console.error('failed to remove printer', Printers.lastMessage);
315 }
316 }
317 },
318 Action {
319 iconName: model.default ? "starred" : "non-starred"
320 enabled: !model.default
321 onTriggered: Printers.defaultPrinterName = model.name
322 }
323
324 ]
325 }
326 ListItemLayout {
327 id: modelLayout
328 title.text: displayName
329 title.font.bold: model.default
330
331 Icon {
332 id: icon
333 width: height
334 height: units.gu(2.5)
335 name: "printer-symbolic"
336 SlotsLayout.position: SlotsLayout.First
337 }
338
339 ProgressionSlot {}
340 }
341 onClicked: pageStack.push(printerPage, { printer: model })
342 }
343 }
344 }
345 }
346
347 Component {
348 id: addPrinterPageComponent
349 Page {
350 id: addPrinterPage
351 states: [
352 State {
353 name: "success"
354 PropertyChanges {
355 target: okAction
356 enabled: false
357 }
358 PropertyChanges {
359 target: closeAction
360 enabled: false
361 }
362 PropertyChanges {
363 target: addPrinterCol
364 enabled: false
365 }
366 StateChangeScript {
367 script: okTimer.start()
368 }
369 },
370 State {
371 name: "failure"
372 PropertyChanges {
373 target: errorMessageContainer
374 visible: true
375 }
376 }
377 ]
378 header: PageHeader {
379 title: "Add printer"
380 flickable: addPrinterFlickable
381 leadingActionBar.actions: [
382 Action {
383 id: closeAction
384 iconName: "close"
385 text: "Abort"
386 onTriggered: pageStack.pop()
387 }
388 ]
389 trailingActionBar {
390 actions: [
391 Action {
392 id: okAction
393 iconName: "ok"
394 text: "Complete"
395 onTriggered: {
396 var ret;
397 if (driverSelector.selectedIndex == 0) {
398 ret = Printers.addPrinter(
399 printerName.text,
400 driversView.selectedDriver,
401 printerUri.text,
402 printerDescription.text,
403 printerLocation.text
404 );
405 } else {
406 ret = Printers.addPrinterWithPpdFile(
407 printerName.text,
408 printerPpd.text,
409 printerUri.text,
410 printerDescription.text,
411 printerLocation.text
412 );
413 }
414 if (ret) {
415 addPrinterPage.state = "success"
416 } else {
417 errorMessage.text = Printers.lastMessage;
418 addPrinterPage.state = "failure"
419 }
420 }
421 }
422 ]
423 }
424 }
425
426 Component.onCompleted: {
427 Printers.prepareToAddPrinter();
428 }
429
430 Timer {
431 id: okTimer
432 interval: 2000
433 onTriggered: pageStack.pop();
434 }
435
436 Flickable {
437 id: addPrinterFlickable
438 anchors.fill: parent
439
440 Column {
441 id: addPrinterCol
442 property bool enabled: true
443 anchors {
444 left: parent.left
445 right: parent.right
446 }
447
448 Item {
449 id: errorMessageContainer
450 visible: false
451 anchors {
452 left: parent.left
453 right: parent.right
454 margins: units.gu(2)
455 }
456 height: units.gu(6)
457 Label {
458 id: errorMessage
459 anchors {
460 top: parent.top
461 topMargin: units.gu(2)
462 horizontalCenter: parent.horizontalCenter
463 }
464 }
465
466 }
467
468 ListItems.Standard {
469 text: "Device URI"
470 control: TextField {
471 id: printerUri
472 placeholderText: "ipp://server.local/my-queue"
473 }
474 enabled: parent.enabled
475 }
476
477 ListItems.ValueSelector {
478 id: driverSelector
479 anchors {
480 left: parent.left
481 right: parent.right
482 }
483 text: "Choose driver"
484 values: [
485 "Select printer from database",
486 "Provide PPD file"
487 ]
488 enabled: parent.enabled
489 }
490
491 ListItems.Standard {
492 anchors {
493 left: parent.left
494 right: parent.right
495 }
496 text: "Filter drivers"
497 control: TextField {
498 id: driverFilter
499 onTextChanged: Printers.driverFilter = text
500 }
501 visible: driverSelector.selectedIndex == 0
502 enabled: parent.enabled
503 }
504
505 ListView {
506 id: driversView
507 property string selectedDriver
508 property bool loading: true
509 visible: driverSelector.selectedIndex == 0
510 model: Printers.drivers
511 anchors { left: parent.left; right: parent.right }
512 height: units.gu(30)
513 clip: true
514 enabled: parent.enabled
515 highlightFollowsCurrentItem: false
516 highlight: Rectangle {
517 z: 0
518 y: driversView.currentItem.y
519 width: driversView.currentItem.width
520 height: driversView.currentItem.height
521 color: theme.palette.selected.background
522 }
523 delegate: ListItem {
524 height: driverLayout.height + (divider.visible ? divider.height : 0)
525 ListItemLayout {
526 id: driverLayout
527 title.text: displayName
528 subtitle.text: name
529 summary.text: deviceId
530 }
531 onClicked: {
532 driversView.selectedDriver = name
533 driversView.currentIndex = index
534 }
535 }
536
537 ActivityIndicator {
538 anchors.centerIn: parent
539 running: parent.loading
540 }
541
542 Connections {
543 target: driversView
544 onCountChanged: {
545 target = null;
546 driversView.loading = false;
547 }
548 }
549 }
550
551 ListItems.Standard {
552 text: "PPD File"
553 visible: driverSelector.selectedIndex == 1
554 control: TextField {
555 id: printerPpd
556 placeholderText: "/usr/share/cups/foo.ppd"
557 }
558 enabled: parent.enabled
559 }
560
561 ListItems.Standard {
562 anchors {
563 left: parent.left
564 right: parent.right
565 }
566 text: "Printer name"
567 control: TextField {
568 id: printerName
569 placeholderText: "laserjet"
570 }
571 enabled: parent.enabled
572 }
573
574 ListItems.Standard {
575 anchors {
576 left: parent.left
577 right: parent.right
578 }
579 text: "Description (optional)"
580 control: TextField {
581 id: printerDescription
582 placeholderText: "HP Laserjet with Duplexer"
583 }
584 enabled: parent.enabled
585 }
586
587 ListItems.Standard {
588 anchors {
589 left: parent.left
590 right: parent.right
591 }
592 text: "Location (optional)"
593 control: TextField {
594 id: printerLocation
595 placeholderText: "Lab 1"
596 }
597 enabled: parent.enabled
598 }
599 }
600 }
601 }
602 }
603}
0604
=== added directory 'modules/Ubuntu/Components/Extras/Printers'
=== added file 'modules/Ubuntu/Components/Extras/Printers/CMakeLists.txt'
--- modules/Ubuntu/Components/Extras/Printers/CMakeLists.txt 1970-01-01 00:00:00 +0000
+++ modules/Ubuntu/Components/Extras/Printers/CMakeLists.txt 2017-02-24 12:29:37 +0000
@@ -0,0 +1,68 @@
1project(UbuntuComponentsExtrasPrintersQml)
2
3set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -fno-permissive -pedantic -Wall -Wextra")
4
5add_definitions(-DUBUNTUCOMPONENTSEXTRASPRINTERS_LIBRARY)
6
7include(FindCups)
8
9include_directories(${CMAKE_CURRENT_BINARY_DIR} ${CUPS_INCLUDE_DIR})
10
11find_package(Qt5Gui REQUIRED)
12find_package(Qt5PrintSupport REQUIRED)
13find_package(Qt5Qml REQUIRED)
14find_package(Qt5DBus REQUIRED)
15find_package(Qt5Concurrent REQUIRED)
16
17if(NOT CUPS_FOUND)
18message(FATAL_ERROR "Could not find cups.")
19endif()
20
21qt5_add_dbus_interface(
22 GEN_SOURCES
23 ${CMAKE_CURRENT_SOURCE_DIR}/org.cups.cupsd.Notifier.xml
24 cupsdnotifier)
25
26add_library(UbuntuComponentsExtrasPrintersQml SHARED
27 ${GEN_SOURCES}
28 backend/backend.cpp
29 backend/backend_cups.cpp
30 backend/backend_pdf.cpp
31
32 cups/ippclient.cpp
33 cups/printerdriverloader.cpp
34 cups/printerloader.cpp
35
36 models/drivermodel.cpp
37 models/jobmodel.cpp
38 models/printermodel.cpp
39
40 printer/printer.cpp
41 printer/printerjob.cpp
42 printer/printersignalhandler.cpp
43 printers/printers.cpp
44
45 enums.h
46 i18n.cpp
47 plugin.cpp
48 structs.h
49 utils.h
50)
51
52target_link_libraries(UbuntuComponentsExtrasPrintersQml
53 Qt5::DBus
54 Qt5::Gui
55 Qt5::PrintSupport
56 Qt5::Qml
57 Qt5::Concurrent
58 ${CUPS_LIBRARIES}
59)
60
61find_package(QmlPlugins)
62
63macro(add_plugin PLUGIN VERSION PATH)
64 export_qmlfiles(${PLUGIN} ${PATH} DESTINATION ${QT_IMPORTS_DIR} ${ARGN})
65 export_qmlplugin(${PLUGIN} ${VERSION} ${PATH} DESTINATION ${QT_IMPORTS_DIR} ${ARGN})
66endmacro()
67
68add_plugin(Ubuntu.Components.Extras.Printers 0.1 Ubuntu/Components/Extras/Printers TARGETS UbuntuComponentsExtrasPrintersQml)
069
=== added directory 'modules/Ubuntu/Components/Extras/Printers/backend'
=== added file 'modules/Ubuntu/Components/Extras/Printers/backend/backend.cpp'
--- modules/Ubuntu/Components/Extras/Printers/backend/backend.cpp 1970-01-01 00:00:00 +0000
+++ modules/Ubuntu/Components/Extras/Printers/backend/backend.cpp 2017-02-24 12:29:37 +0000
@@ -0,0 +1,272 @@
1/*
2 * Copyright (C) 2017 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include "backend/backend.h"
18
19PrinterBackend::PrinterBackend(const QString &printerName, QObject *parent)
20 : QObject(parent)
21 , m_printerName(printerName)
22 , m_type(PrinterEnum::PrinterType::ProxyType)
23{
24}
25
26PrinterBackend::~PrinterBackend()
27{
28}
29
30bool PrinterBackend::holdsDefinition() const
31{
32 return false;
33}
34
35QString PrinterBackend::printerAdd(const QString &name,
36 const QString &uri,
37 const QString &ppdFile,
38 const QString &info,
39 const QString &location)
40{
41 Q_UNUSED(name);
42 Q_UNUSED(uri);
43 Q_UNUSED(ppdFile);
44 Q_UNUSED(info);
45 Q_UNUSED(location);
46 return QString();
47}
48
49QString PrinterBackend::printerAddWithPpd(const QString &name,
50 const QString &uri,
51 const QString &ppdFileName,
52 const QString &info,
53 const QString &location)
54{
55 Q_UNUSED(name);
56 Q_UNUSED(uri);
57 Q_UNUSED(ppdFileName);
58 Q_UNUSED(info);
59 Q_UNUSED(location);
60 return QString();
61}
62
63QString PrinterBackend::printerDelete(const QString &name)
64{
65 Q_UNUSED(name);
66 return QString();
67}
68
69QString PrinterBackend::printerSetDefault(const QString &name)
70{
71 Q_UNUSED(name);
72 return QString();
73}
74
75QString PrinterBackend::printerSetEnabled(const QString &name,
76 const bool enabled)
77{
78 Q_UNUSED(name);
79 Q_UNUSED(enabled);
80 return QString();
81}
82
83QString PrinterBackend::printerSetAcceptJobs(
84 const QString &name,
85 const bool enabled,
86 const QString &reason)
87{
88 Q_UNUSED(name);
89 Q_UNUSED(enabled);
90 Q_UNUSED(reason);
91 return QString();
92}
93
94QString PrinterBackend::printerSetInfo(const QString &name,
95 const QString &info)
96{
97 Q_UNUSED(name);
98 Q_UNUSED(info);
99 return QString();
100}
101
102QString PrinterBackend::printerAddOption(const QString &name,
103 const QString &option,
104 const QStringList &values)
105{
106 Q_UNUSED(name);
107 Q_UNUSED(option);
108 Q_UNUSED(values);
109 return QString();
110}
111
112QVariant PrinterBackend::printerGetOption(const QString &name,
113 const QString &option) const
114{
115 Q_UNUSED(name);
116 Q_UNUSED(option);
117 return QVariant();
118}
119
120QMap<QString, QVariant> PrinterBackend::printerGetOptions(
121 const QString &name, const QStringList &options) const
122{
123 Q_UNUSED(name);
124 Q_UNUSED(options);
125 return QMap<QString, QVariant>();
126}
127
128cups_dest_t* PrinterBackend::makeDest(const QString &name,
129 const PrinterJob *options)
130{
131 Q_UNUSED(name);
132 Q_UNUSED(options);
133 return Q_NULLPTR;
134}
135
136void PrinterBackend::cancelJob(const QString &name, const int jobId)
137{
138 Q_UNUSED(jobId);
139 Q_UNUSED(name);
140}
141
142int PrinterBackend::printFileToDest(const QString &filepath,
143 const QString &title,
144 const cups_dest_t *dest)
145{
146 Q_UNUSED(filepath);
147 Q_UNUSED(title);
148 Q_UNUSED(dest);
149 return -1;
150}
151
152QList<QSharedPointer<PrinterJob>> PrinterBackend::printerGetJobs()
153{
154 return QList<QSharedPointer<PrinterJob>>{};
155}
156
157QMap<QString, QVariant> PrinterBackend::printerGetJobAttributes(
158 const QString &name, const int jobId)
159{
160 Q_UNUSED(name);
161 Q_UNUSED(jobId);
162 return QMap<QString, QVariant>();
163}
164
165QString PrinterBackend::printerName() const
166{
167 return m_printerName;
168}
169
170QString PrinterBackend::description() const
171{
172 return QString();
173}
174
175QString PrinterBackend::location() const
176{
177 return QString();
178}
179
180QString PrinterBackend::makeAndModel() const
181{
182 return QString();
183}
184
185PrinterEnum::State PrinterBackend::state() const
186{
187 return PrinterEnum::State::IdleState;
188}
189
190QList<QPageSize> PrinterBackend::supportedPageSizes() const
191{
192 return QList<QPageSize>();
193}
194
195QPageSize PrinterBackend::defaultPageSize() const
196{
197 return QPageSize();
198}
199
200bool PrinterBackend::supportsCustomPageSizes() const
201{
202 return false;
203}
204
205QPageSize PrinterBackend::minimumPhysicalPageSize() const
206{
207 return QPageSize();
208}
209
210QPageSize PrinterBackend::maximumPhysicalPageSize() const
211{
212 return QPageSize();
213}
214
215QList<int> PrinterBackend::supportedResolutions() const
216{
217 return QList<int>();
218}
219
220PrinterEnum::DuplexMode PrinterBackend::defaultDuplexMode() const
221{
222 return PrinterEnum::DuplexMode::DuplexNone;
223}
224
225QList<PrinterEnum::DuplexMode> PrinterBackend::supportedDuplexModes() const
226{
227 return QList<PrinterEnum::DuplexMode>();
228}
229
230QList<QSharedPointer<Printer>> PrinterBackend::availablePrinters()
231{
232 return QList<QSharedPointer<Printer>>();
233}
234
235QStringList PrinterBackend::availablePrinterNames()
236{
237 return QStringList();
238}
239
240QSharedPointer<Printer> PrinterBackend::getPrinter(const QString &printerName)
241{
242 Q_UNUSED(printerName);
243 return QSharedPointer<Printer>(Q_NULLPTR);
244}
245
246QString PrinterBackend::defaultPrinterName()
247{
248 return QString();
249}
250
251void PrinterBackend::requestPrinterDrivers()
252{
253}
254
255void PrinterBackend::requestPrinter(const QString &printerName)
256{
257 Q_UNUSED(printerName);
258}
259
260PrinterEnum::PrinterType PrinterBackend::type() const
261{
262 return m_type;
263}
264
265void PrinterBackend::setPrinterNameInternal(const QString &printerName)
266{
267 m_printerName = printerName;
268}
269
270void PrinterBackend::refresh()
271{
272}
0273
=== added file 'modules/Ubuntu/Components/Extras/Printers/backend/backend.h'
--- modules/Ubuntu/Components/Extras/Printers/backend/backend.h 1970-01-01 00:00:00 +0000
+++ modules/Ubuntu/Components/Extras/Printers/backend/backend.h 2017-02-24 12:29:37 +0000
@@ -0,0 +1,201 @@
1/*
2 * Copyright (C) 2017 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#ifndef USC_PRINTERS_BACKEND_H
18#define USC_PRINTERS_BACKEND_H
19
20#include "printer/printer.h"
21#include "printer/printerjob.h"
22
23// TODO: remove cups specific things from this API
24#include <cups/cups.h>
25
26#include <QObject>
27#include <QPageSize>
28#include <QList>
29#include <QString>
30#include <QStringList>
31
32class Printer;
33class PrinterJob;
34class PRINTERS_DECL_EXPORT PrinterBackend : public QObject
35{
36 Q_OBJECT
37public:
38 explicit PrinterBackend(QObject *parent = Q_NULLPTR);
39 explicit PrinterBackend(const QString &printerName,
40 QObject *parent = Q_NULLPTR);
41 virtual ~PrinterBackend();
42
43 virtual bool holdsDefinition() const;
44
45 // Add a printer using an already existing ppd.
46 virtual QString printerAdd(const QString &name,
47 const QString &uri,
48 const QString &ppdFile,
49 const QString &info,
50 const QString &location);
51
52 // Add a printer and provide a ppd file.
53 virtual QString printerAddWithPpd(const QString &name,
54 const QString &uri,
55 const QString &ppdFileName,
56 const QString &info,
57 const QString &location);
58 virtual QString printerDelete(const QString &name);
59 virtual QString printerSetDefault(const QString &name);
60 virtual QString printerSetEnabled(const QString &name,
61 const bool enabled);
62 virtual QString printerSetAcceptJobs(
63 const QString &name,
64 const bool accept,
65 const QString &reason = QString::null);
66 virtual QString printerSetInfo(const QString &name,
67 const QString &info);
68 virtual QString printerAddOption(const QString &name,
69 const QString &option,
70 const QStringList &values);
71
72 virtual QVariant printerGetOption(const QString &name,
73 const QString &option) const;
74 virtual QMap<QString, QVariant> printerGetOptions(
75 const QString &name, const QStringList &options) const;
76 virtual cups_dest_t* makeDest(const QString &name,
77 const PrinterJob *options);
78
79 virtual void cancelJob(const QString &name, const int jobId);
80 virtual int printFileToDest(const QString &filepath,
81 const QString &title,
82 const cups_dest_t *dest);
83 virtual QList<QSharedPointer<PrinterJob>> printerGetJobs();
84 virtual QMap<QString, QVariant> printerGetJobAttributes(
85 const QString &name, const int jobId);
86
87 virtual QString printerName() const;
88 virtual QString description() const;
89 virtual QString location() const;
90 virtual QString makeAndModel() const;
91
92 virtual PrinterEnum::State state() const;
93 virtual QList<QPageSize> supportedPageSizes() const;
94 virtual QPageSize defaultPageSize() const;
95 virtual bool supportsCustomPageSizes() const;
96
97 virtual QPageSize minimumPhysicalPageSize() const;
98 virtual QPageSize maximumPhysicalPageSize() const;
99 virtual QList<int> supportedResolutions() const;
100 virtual PrinterEnum::DuplexMode defaultDuplexMode() const;
101 virtual QList<PrinterEnum::DuplexMode> supportedDuplexModes() const;
102
103 virtual QList<QSharedPointer<Printer>> availablePrinters();
104 virtual QStringList availablePrinterNames();
105 virtual QSharedPointer<Printer> getPrinter(const QString &printerName);
106 virtual QString defaultPrinterName();
107
108 virtual void requestPrinterDrivers();
109 virtual void requestPrinter(const QString &printerName);
110
111 virtual PrinterEnum::PrinterType type() const;
112
113 virtual void setPrinterNameInternal(const QString &printerName);
114
115public Q_SLOTS:
116 virtual void refresh();
117
118Q_SIGNALS:
119 void printerDriversLoaded(const QList<PrinterDriver> &drivers);
120 void printerDriversFailedToLoad(const QString &errorMessage);
121
122 void printerLoaded(QSharedPointer<Printer> printers);
123
124 void jobCompleted(
125 const QString &text,
126 const QString &printerUri,
127 const QString &printerName,
128 uint printerState,
129 const QString &printerStateReason,
130 bool acceptingJobs,
131 uint jobId,
132 uint jobState,
133 const QString &jobStateReason,
134 const QString &job_name,
135 uint jobImpressionsCompleted
136 );
137 void jobCreated(
138 const QString &text,
139 const QString &printerUri,
140 const QString &printerName,
141 uint printerState,
142 const QString &printerStateReason,
143 bool acceptingJobs,
144 uint jobId,
145 uint jobState,
146 const QString &jobStateReason,
147 const QString &job_name,
148 uint jobImpressionsCompleted
149 );
150 void jobState(
151 const QString &text,
152 const QString &printerUri,
153 const QString &printerName,
154 uint printerState,
155 const QString &printerStateReason,
156 bool acceptingJobs,
157 uint jobId,
158 uint jobState,
159 const QString &jobStateReason,
160 const QString &job_name,
161 uint jobImpressionsCompleted
162 );
163 void printerAdded(
164 const QString &text,
165 const QString &printerUri,
166 const QString &printerName,
167 uint printerState,
168 const QString &printerStateReason,
169 bool acceptingJobs
170 );
171 void printerDeleted(
172 const QString &text,
173 const QString &printerUri,
174 const QString &printerName,
175 uint printerState,
176 const QString &printerStateReason,
177 bool acceptingJobs
178 );
179 void printerModified(
180 const QString &text,
181 const QString &printerUri,
182 const QString &printerName,
183 uint printerState,
184 const QString &printerStateReason,
185 bool acceptingJobs
186 );
187 void printerStateChanged(
188 const QString &text,
189 const QString &printerUri,
190 const QString &printerName,
191 uint printerState,
192 const QString &printerStateReason,
193 bool acceptingJobs
194 );
195
196protected:
197 QString m_printerName;
198 PrinterEnum::PrinterType m_type;
199};
200
201#endif // USC_PRINTERS_BACKEND_H
0202
=== added file 'modules/Ubuntu/Components/Extras/Printers/backend/backend_cups.cpp'
--- modules/Ubuntu/Components/Extras/Printers/backend/backend_cups.cpp 1970-01-01 00:00:00 +0000
+++ modules/Ubuntu/Components/Extras/Printers/backend/backend_cups.cpp 2017-02-24 12:29:37 +0000
@@ -0,0 +1,722 @@
1/*
2 * Copyright (C) 2017 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include "backend/backend_cups.h"
18#include "cups/printerdriverloader.h"
19#include "cups/printerloader.h"
20#include "utils.h"
21
22#include <cups/http.h>
23#include <cups/ipp.h>
24#include <cups/ppd.h>
25
26#include <QLocale>
27#include <QThread>
28#include <QTimeZone>
29
30#define __CUPS_ADD_OPTION(dest, name, value) dest->num_options = \
31 cupsAddOption(name, value, dest->num_options, &dest->options);
32
33#define __CUPS_ATTR_EXISTS(map, attr, type) map.contains(attr) \
34 && map.value(attr).canConvert<type>()
35
36PrinterCupsBackend::PrinterCupsBackend(IppClient *client, QPrinterInfo info,
37 OrgCupsCupsdNotifierInterface *notifier,
38 QObject *parent)
39 : PrinterBackend(info.printerName(), parent)
40 , m_knownQualityOptions({
41 "Quality", "PrintQuality", "HPPrintQuality", "StpQuality",
42 "OutputMode",})
43 , m_client(client)
44 , m_info(info)
45 , m_notifier(notifier)
46 , m_cupsSubscriptionId(-1)
47{
48 m_type = PrinterEnum::PrinterType::CupsType;
49 connect(m_notifier, SIGNAL(JobCompleted(const QString&, const QString&,
50 const QString&, uint,
51 const QString&, bool, uint, uint,
52 const QString&, const QString&, uint)),
53 this, SIGNAL(jobCompleted(const QString&, const QString&,
54 const QString&, uint, const QString&,
55 bool, uint, uint, const QString&,
56 const QString&, uint)));
57 connect(m_notifier, SIGNAL(JobCreated(const QString&, const QString&,
58 const QString&, uint, const QString&,
59 bool, uint, uint, const QString&,
60 const QString&, uint)),
61 this, SIGNAL(jobCreated(const QString&, const QString&,
62 const QString&, uint, const QString&, bool,
63 uint, uint, const QString&, const QString&,
64 uint)));
65 connect(m_notifier, SIGNAL(JobState(const QString&, const QString&,
66 const QString&, uint, const QString&,
67 bool, uint, uint, const QString&,
68 const QString&, uint)),
69 this, SIGNAL(jobState(const QString&, const QString&,
70 const QString&, uint, const QString&, bool,
71 uint, uint, const QString&, const QString&,
72 uint)));
73 connect(m_notifier, SIGNAL(PrinterAdded(const QString&, const QString&,
74 const QString&, uint,
75 const QString&, bool)),
76 this, SIGNAL(printerAdded(const QString&, const QString&,
77 const QString&, uint,
78 const QString&, bool)));
79 connect(m_notifier, SIGNAL(PrinterDeleted(const QString&, const QString&,
80 const QString&, uint,
81 const QString&, bool)),
82 this, SIGNAL(printerDeleted(const QString&, const QString&,
83 const QString&, uint,
84 const QString&, bool)));
85 connect(m_notifier, SIGNAL(PrinterModified(const QString&, const QString&,
86 const QString&, uint,
87 const QString&, bool)),
88 this, SIGNAL(printerModified(const QString&, const QString&,
89 const QString&, uint,
90 const QString&, bool)));
91 connect(m_notifier, SIGNAL(PrinterStateChanged(const QString&,
92 const QString&,
93 const QString&, uint,
94 const QString&, bool)),
95 this, SIGNAL(printerStateChanged(const QString&, const QString&,
96 const QString&, uint,
97 const QString&, bool)));
98
99}
100
101PrinterCupsBackend::~PrinterCupsBackend()
102{
103 Q_FOREACH(auto dest, m_dests) {
104 if (dest)
105 cupsFreeDests(1, dest);
106 }
107 Q_FOREACH(auto ppd, m_ppds) {
108 if (ppd)
109 ppdClose(ppd);
110 }
111
112 cancelSubscription();
113 Q_EMIT cancelWorkers();
114}
115
116QString PrinterCupsBackend::printerAdd(const QString &name,
117 const QString &uri,
118 const QString &ppdFile,
119 const QString &info,
120 const QString &location)
121{
122 if (!m_client->printerAdd(name, uri, ppdFile, info, location)) {
123 return m_client->getLastError();
124 }
125 return QString();
126}
127
128QString PrinterCupsBackend::printerAddWithPpd(const QString &name,
129 const QString &uri,
130 const QString &ppdFileName,
131 const QString &info,
132 const QString &location)
133{
134 if (!m_client->printerAddWithPpdFile(name, uri, ppdFileName, info, location)) {
135 return m_client->getLastError();
136 }
137 return QString();
138}
139
140bool PrinterCupsBackend::holdsDefinition() const
141{
142 return !m_info.isNull();
143}
144
145QString PrinterCupsBackend::printerDelete(const QString &name)
146{
147 if (!m_client->printerDelete(name)) {
148 return m_client->getLastError();
149 }
150 return QString();
151}
152
153QString PrinterCupsBackend::printerSetDefault(const QString &name)
154{
155 if (!m_client->printerSetDefault(name)) {
156 return m_client->getLastError();
157 }
158 return QString();
159}
160
161QString PrinterCupsBackend::printerSetEnabled(const QString &name,
162 const bool enabled)
163{
164 if (!m_client->printerSetEnabled(name, enabled)) {
165 return m_client->getLastError();
166 }
167 return QString();
168}
169
170QString PrinterCupsBackend::printerSetAcceptJobs(
171 const QString &name,
172 const bool accept,
173 const QString &reason)
174{
175 if (!m_client->printerSetAcceptJobs(name, accept, reason)) {
176 return m_client->getLastError();
177 }
178 return QString();
179}
180
181QString PrinterCupsBackend::printerSetInfo(const QString &name,
182 const QString &info)
183{
184 if (!m_client->printerClassSetInfo(name, info)) {
185 return m_client->getLastError();
186 }
187 return QString();
188}
189
190QString PrinterCupsBackend::printerAddOption(const QString &name,
191 const QString &option,
192 const QStringList &values)
193{
194 if (!m_client->printerClassSetOption(name, option, values)) {
195 return m_client->getLastError();
196 }
197
198 return QString();
199}
200
201QVariant PrinterCupsBackend::printerGetOption(const QString &name,
202 const QString &option) const
203{
204 auto res = printerGetOptions(name, QStringList({option}));
205 return res[option];
206}
207
208QMap<QString, QVariant> PrinterCupsBackend::printerGetOptions(
209 const QString &name, const QStringList &options) const
210{
211 QMap<QString, QVariant> ret;
212
213 cups_dest_t *dest = getDest(name);
214 ppd_file_t* ppd = getPpd(name);
215
216 if (!dest || !ppd) {
217 return ret;
218 }
219
220 Q_FOREACH(const QString &option, options) {
221 if (option == QStringLiteral("DefaultColorModel")) {
222 ColorModel model;
223 ppd_option_t *ppdColorModel = ppdFindOption(ppd, "ColorModel");
224 if (ppdColorModel) {
225 ppd_choice_t* def = ppdFindChoice(ppdColorModel,
226 ppdColorModel->defchoice);
227 if (def) {
228 model = Utils::parsePpdColorModel(def->choice,
229 def->text,
230 "ColorModel");
231 }
232 }
233 ret[option] = QVariant::fromValue(model);
234 } else if (option == QStringLiteral("DefaultPrintQuality")) {
235 PrintQuality quality;
236 Q_FOREACH(const QString opt, m_knownQualityOptions) {
237 ppd_option_t *ppdQuality = ppdFindOption(ppd, opt.toUtf8());
238 if (ppdQuality) {
239 ppd_choice_t* def = ppdFindChoice(ppdQuality,
240 ppdQuality->defchoice);
241 if (def) {
242 quality = Utils::parsePpdPrintQuality(def->choice,
243 def->text, opt);
244 }
245 }
246 }
247 ret[option] = QVariant::fromValue(quality);
248 } else if (option == QStringLiteral("SupportedPrintQualities")) {
249 QList<PrintQuality> qualities;
250 Q_FOREACH(const QString &opt, m_knownQualityOptions) {
251 ppd_option_t *qualityOpt = ppdFindOption(ppd, opt.toUtf8());
252 if (qualityOpt) {
253 for (int i = 0; i < qualityOpt->num_choices; ++i) {
254 qualities.append(
255 Utils::parsePpdPrintQuality(
256 qualityOpt->choices[i].choice,
257 qualityOpt->choices[i].text,
258 opt
259 )
260 );
261 }
262 }
263 }
264 ret[option] = QVariant::fromValue(qualities);
265 } else if (option == QStringLiteral("SupportedColorModels")) {
266 QList<ColorModel> models;
267 ppd_option_t *colorModels = ppdFindOption(ppd, "ColorModel");
268 if (colorModels) {
269 for (int i = 0; i < colorModels->num_choices; ++i) {
270 models.append(
271 Utils::parsePpdColorModel(
272 colorModels->choices[i].choice,
273 colorModels->choices[i].text,
274 QStringLiteral("ColorModel")
275 )
276 );
277 }
278 }
279 ret[option] = QVariant::fromValue(models);
280 } else if (option == QStringLiteral("AcceptJobs")) {
281 // "true" if the destination is accepting new jobs, "false" if not.
282 QString res = cupsGetOption("printer-is-accepting-jobs",
283 dest->num_options, dest->options);
284 ret[option] = res.contains("true");
285 } else {
286 ppd_option_t *val = ppdFindOption(ppd, option.toUtf8());
287
288 if (val) {
289 qWarning() << "asking for" << option << "returns" << val->text;
290 } else {
291 qWarning() << "option" << option << "yielded no option";
292 }
293 }
294 }
295 return ret;
296}
297
298cups_dest_t* PrinterCupsBackend::makeDest(const QString &name,
299 const PrinterJob *options)
300{
301 cups_dest_t *dest = getDest(name);
302
303 if (options->collate()) {
304 __CUPS_ADD_OPTION(dest, "Collate", "True");
305 } else {
306 __CUPS_ADD_OPTION(dest, "Collate", "False");
307 }
308
309 if (options->copies() > 1) {
310 __CUPS_ADD_OPTION(dest, "copies", QString::number(options->copies()).toLocal8Bit());
311 }
312
313 __CUPS_ADD_OPTION(dest, "ColorModel", options->getColorModel().name.toLocal8Bit());
314 __CUPS_ADD_OPTION(dest, "Duplex", Utils::duplexModeToPpdChoice(options->getDuplexMode()).toLocal8Bit());
315
316 if (options->landscape()) {
317 __CUPS_ADD_OPTION(dest, "landscape", "");
318 }
319
320 if (options->printRangeMode() == PrinterEnum::PrintRange::PageRange
321 && !options->printRange().isEmpty()) {
322 __CUPS_ADD_OPTION(dest, "page-ranges", options->printRange().toLocal8Bit());
323 }
324
325 PrintQuality quality = options->getPrintQuality();
326 __CUPS_ADD_OPTION(dest, quality.originalOption.toLocal8Bit(),
327 quality.name.toLocal8Bit());
328
329 if (options->reverse()) {
330 __CUPS_ADD_OPTION(dest, "OutputOrder", "Reverse");
331 } else {
332 __CUPS_ADD_OPTION(dest, "OutputOrder", "Normal");
333 }
334
335 // Always scale to fit the page for now
336 __CUPS_ADD_OPTION(dest, "fit-to-page", "True");
337
338 return dest;
339}
340
341void PrinterCupsBackend::cancelJob(const QString &name, const int jobId)
342{
343 int ret = cupsCancelJob(name.toLocal8Bit(), jobId);
344
345 if (!ret) {
346 qWarning() << "Failed to cancel job:" << jobId << "for" << name;
347 }
348}
349
350int PrinterCupsBackend::printFileToDest(const QString &filepath,
351 const QString &title,
352 const cups_dest_t *dest)
353{
354 qDebug() << "Printing:" << filepath << title << dest->name << dest->num_options;
355 return cupsPrintFile(dest->name,
356 filepath.toLocal8Bit(),
357 title.toLocal8Bit(),
358 dest->num_options,
359 dest->options);
360}
361
362
363QList<cups_job_t *> PrinterCupsBackend::getCupsJobs(const QString &name)
364{
365 QList<cups_job_t *> list;
366 cups_job_t *jobs;
367
368 // Get a list of the jobs that are 'mine' and only active ones
369 // https://www.cups.org/doc/api-cups.html#cupsGetJobs
370 int count;
371 if (name.isEmpty()) {
372 count = cupsGetJobs(&jobs, NULL, 1, CUPS_WHICHJOBS_ACTIVE);
373 } else {
374 count = cupsGetJobs(&jobs, name.toLocal8Bit(), 1, CUPS_WHICHJOBS_ACTIVE);
375 }
376
377 for (int i=0; i < count; i++) {
378 list.append(&jobs[i]);
379 }
380
381 return list;
382}
383
384QMap<QString, QVariant> PrinterCupsBackend::printerGetJobAttributes(
385 const QString &name, const int jobId)
386{
387 Q_UNUSED(name);
388 QMap<QString, QVariant> rawMap = m_client->printerGetJobAttributes(jobId);
389 QMap<QString, QVariant> map;
390
391 // Filter attributes to know values
392 // Do this here so we can use things such as m_knownQualityOptions
393
394 if (__CUPS_ATTR_EXISTS(rawMap, "Collate", bool)) {
395 map.insert("Collate", rawMap.value("Collate"));
396 } else {
397 map.insert("Collate", QVariant(true));
398 }
399
400 if (__CUPS_ATTR_EXISTS(rawMap, "copies", int)) {
401 map.insert("copies", rawMap.value("copies"));
402 } else {
403 map.insert("copies", QVariant(1));
404 }
405
406 if (__CUPS_ATTR_EXISTS(rawMap, "ColorModel", QString)) {
407 map.insert("ColorModel", rawMap.value("ColorModel"));
408 } else {
409 map.insert("ColorModel", QVariant(""));
410 }
411
412 if (__CUPS_ATTR_EXISTS(rawMap, "Duplex", QString)) {
413 map.insert("Duplex", rawMap.value("Duplex"));
414 } else {
415 map.insert("Duplex", QVariant(""));
416 }
417
418 if (__CUPS_ATTR_EXISTS(rawMap, "landscape", bool)) {
419 map.insert("landscape", rawMap.value("landscape"));
420 } else {
421 map.insert("landscape", QVariant(false));
422 }
423
424 if (__CUPS_ATTR_EXISTS(rawMap, "page-ranges", QList<QVariant>)) {
425 QList<QVariant> range = rawMap.value("page-ranges").toList();
426 QStringList rangeStrings;
427
428 Q_FOREACH(QVariant var, range) {
429 rangeStrings << var.toString();
430 }
431
432 map.insert("page-ranges", QVariant(rangeStrings));
433 } else {
434 map.insert("page-ranges", QVariant(QStringList()));
435 }
436
437 Q_FOREACH(QString qualityOption, m_knownQualityOptions) {
438 if (rawMap.contains(qualityOption)
439 && rawMap.value(qualityOption).canConvert<QString>()) {
440 map.insert("quality", rawMap.value(qualityOption).toString());
441 }
442 }
443
444 if (!map.contains("quality")) {
445 map.insert("quality", QVariant(""));
446 }
447
448 if (__CUPS_ATTR_EXISTS(rawMap, "OutputOrder", QString)) {
449 map.insert("OutputOrder", rawMap.value("OutputOrder"));
450 } else {
451 map.insert("OutputOrder", "Normal");
452 }
453
454 // Generate a list of messages
455 // TODO: for now just using job-printer-state-message, are there others?
456 QStringList messages;
457
458 if (__CUPS_ATTR_EXISTS(rawMap, "job-printer-state-message", QString)) {
459 messages << rawMap.value("job-printer-state-message").toString();
460 }
461
462 map.insert("messages", QVariant(messages));
463
464 return map;
465}
466
467
468QList<QSharedPointer<PrinterJob>> PrinterCupsBackend::printerGetJobs()
469{
470 auto jobs = getCupsJobs();
471 QList<QSharedPointer<PrinterJob>> list;
472
473 Q_FOREACH(auto job, jobs) {
474 auto newJob = QSharedPointer<PrinterJob>(
475 new PrinterJob(QString::fromUtf8(job->dest), this, job->id)
476 );
477
478 // Extract the times
479 QDateTime completedTime;
480 completedTime.setTimeZone(QTimeZone::systemTimeZone());
481 completedTime.setTime_t(job->completed_time);
482
483 QDateTime creationTime;
484 creationTime.setTimeZone(QTimeZone::systemTimeZone());
485 creationTime.setTime_t(job->creation_time);
486
487 QDateTime processingTime;
488 processingTime.setTimeZone(QTimeZone::systemTimeZone());
489 processingTime.setTime_t(job->processing_time);
490
491 // Load the information from the cups struct
492 newJob->setCompletedTime(completedTime);
493 newJob->setCreationTime(creationTime);
494 newJob->setProcessingTime(processingTime);
495 newJob->setSize(job->size);
496 newJob->setState(static_cast<PrinterEnum::JobState>(job->state));
497 newJob->setTitle(QString::fromLocal8Bit(job->title));
498 newJob->setUser(QString::fromLocal8Bit(job->user));
499
500 list.append(newJob);
501 }
502 if (!list.isEmpty())
503 cupsFreeJobs(list.size(), jobs.first());
504
505 return list;
506}
507
508QString PrinterCupsBackend::printerName() const
509{
510 return m_printerName;
511}
512
513QString PrinterCupsBackend::description() const
514{
515 return m_info.description();
516}
517
518QString PrinterCupsBackend::location() const
519{
520 return m_info.location();
521}
522
523QString PrinterCupsBackend::makeAndModel() const
524{
525 return m_info.makeAndModel();
526}
527
528PrinterEnum::State PrinterCupsBackend::state() const
529{
530 switch (m_info.state()) {
531 case QPrinter::Active:
532 return PrinterEnum::State::ActiveState;
533 case QPrinter::Aborted:
534 return PrinterEnum::State::AbortedState;
535 case QPrinter::Error:
536 return PrinterEnum::State::ErrorState;
537 case QPrinter::Idle:
538 default:
539 return PrinterEnum::State::IdleState;
540 }
541}
542
543QList<QPageSize> PrinterCupsBackend::supportedPageSizes() const
544{
545 return m_info.supportedPageSizes();
546}
547
548QPageSize PrinterCupsBackend::defaultPageSize() const
549{
550 return m_info.defaultPageSize();
551}
552
553bool PrinterCupsBackend::supportsCustomPageSizes() const
554{
555 return m_info.supportsCustomPageSizes();
556}
557
558QPageSize PrinterCupsBackend::minimumPhysicalPageSize() const
559{
560 return m_info.minimumPhysicalPageSize();
561}
562
563QPageSize PrinterCupsBackend::maximumPhysicalPageSize() const
564{
565 return m_info.maximumPhysicalPageSize();
566}
567
568QList<int> PrinterCupsBackend::supportedResolutions() const
569{
570 return m_info.supportedResolutions();
571}
572
573PrinterEnum::DuplexMode PrinterCupsBackend::defaultDuplexMode() const
574{
575 return Utils::qDuplexModeToDuplexMode(m_info.defaultDuplexMode());
576}
577
578QList<PrinterEnum::DuplexMode> PrinterCupsBackend::supportedDuplexModes() const
579{
580 QList<PrinterEnum::DuplexMode> list;
581 Q_FOREACH(const QPrinter::DuplexMode mode, m_info.supportedDuplexModes()) {
582 if (mode != QPrinter::DuplexAuto) {
583 list.append(Utils::qDuplexModeToDuplexMode(mode));
584 }
585 }
586
587 if (list.isEmpty())
588 list.append(PrinterEnum::DuplexMode::DuplexNone);
589
590 return list;
591}
592
593QList<QSharedPointer<Printer>> PrinterCupsBackend::availablePrinters()
594{
595 return QList<QSharedPointer<Printer>>();
596}
597
598QStringList PrinterCupsBackend::availablePrinterNames()
599{
600 return QPrinterInfo::availablePrinterNames();
601}
602
603QSharedPointer<Printer> PrinterCupsBackend::getPrinter(const QString &printerName)
604{
605 QPrinterInfo info = QPrinterInfo::printerInfo(printerName);
606 return QSharedPointer<Printer>(new Printer(new PrinterCupsBackend(m_client, info, m_notifier)));
607}
608
609QString PrinterCupsBackend::defaultPrinterName()
610{
611 return QPrinterInfo::defaultPrinterName();
612}
613
614void PrinterCupsBackend::requestPrinter(const QString &printerName)
615{
616 if (m_activeRequests.contains(printerName)) {
617 return;
618 }
619
620 auto thread = new QThread;
621 auto loader = new PrinterLoader(printerName, m_client, m_notifier);
622 loader->moveToThread(thread);
623 connect(thread, SIGNAL(started()), loader, SLOT(load()));
624 connect(loader, SIGNAL(finished()), thread, SLOT(quit()));
625 connect(loader, SIGNAL(finished()), loader, SLOT(deleteLater()));
626 connect(loader, SIGNAL(loaded(QSharedPointer<Printer>)),
627 this, SIGNAL(printerLoaded(QSharedPointer<Printer>)));
628 connect(loader, SIGNAL(loaded(QSharedPointer<Printer>)),
629 this, SLOT(onPrinterLoaded(QSharedPointer<Printer>)));
630 connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
631 thread->start();
632
633 m_activeRequests << printerName;
634}
635
636void PrinterCupsBackend::requestPrinterDrivers()
637{
638 auto thread = new QThread;
639 auto loader = new PrinterDriverLoader();
640 loader->moveToThread(thread);
641 connect(loader, SIGNAL(error(const QString&)),
642 this, SIGNAL(printerDriversFailedToLoad(const QString&)));
643 connect(this, SIGNAL(requestPrinterDriverCancel()), loader, SLOT(cancel()));
644 connect(thread, SIGNAL(started()), loader, SLOT(process()));
645 connect(loader, SIGNAL(finished()), thread, SLOT(quit()));
646 connect(loader, SIGNAL(finished()), loader, SLOT(deleteLater()));
647 connect(loader, SIGNAL(loaded(const QList<PrinterDriver>&)),
648 this, SIGNAL(printerDriversLoaded(const QList<PrinterDriver>&)));
649 connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
650 thread->start();
651}
652
653void PrinterCupsBackend::cancelPrinterDriverRequest()
654{
655 Q_EMIT requestPrinterDriverCancel();
656}
657
658void PrinterCupsBackend::refresh()
659{
660 if (m_printerName.isEmpty()) {
661 throw std::invalid_argument("Trying to refresh unnamed printer.");
662 } else {
663 m_info = QPrinterInfo::printerInfo(m_printerName);
664 }
665}
666
667void PrinterCupsBackend::createSubscription()
668{
669 m_cupsSubscriptionId = m_client->createSubscription();;
670}
671
672void PrinterCupsBackend::cancelSubscription()
673{
674 if (m_cupsSubscriptionId > 0)
675 m_client->cancelSubscription(m_cupsSubscriptionId);
676}
677
678QString PrinterCupsBackend::getPrinterInstance(const QString &name) const
679{
680 const auto parts = name.splitRef(QLatin1Char('/'));
681 QString instance;
682 if (parts.size() > 1)
683 instance = parts.at(1).toString();
684
685 return instance;
686}
687
688QString PrinterCupsBackend::getPrinterName(const QString &name) const
689{
690 return name.splitRef(QLatin1Char('/')).first().toString();
691}
692
693cups_dest_t* PrinterCupsBackend::getDest(const QString &name) const
694{
695 QString printerName = getPrinterName(name);
696 QString instance = getPrinterInstance(name);
697
698 if (m_dests.contains(name)) {
699 return m_dests[name];
700 } else {
701 m_dests[name] = m_client->getDest(printerName, instance);
702 return m_dests[name];
703 }
704}
705
706ppd_file_t* PrinterCupsBackend::getPpd(const QString &name) const
707{
708 QString printerName = getPrinterName(name);
709 QString instance = getPrinterInstance(name);
710
711 if (m_ppds.contains(name)) {
712 return m_ppds[name];
713 } else {
714 m_ppds[name] = m_client->getPpdFile(printerName, instance);
715 return m_ppds[name];
716 }
717}
718
719void PrinterCupsBackend::onPrinterLoaded(QSharedPointer<Printer> printer)
720{
721 m_activeRequests.removeOne(printer->name());
722}
0723
=== added file 'modules/Ubuntu/Components/Extras/Printers/backend/backend_cups.h'
--- modules/Ubuntu/Components/Extras/Printers/backend/backend_cups.h 1970-01-01 00:00:00 +0000
+++ modules/Ubuntu/Components/Extras/Printers/backend/backend_cups.h 2017-02-24 12:29:37 +0000
@@ -0,0 +1,135 @@
1/*
2 * Copyright (C) 2017 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#ifndef USC_PRINTERS_CUPS_BACKEND_H
18#define USC_PRINTERS_CUPS_BACKEND_H
19
20#include "backend/backend.h"
21#include "cups/ippclient.h"
22#include "cupsdnotifier.h" // Note: this file was generated.
23
24#include <cups/cups.h>
25
26#include <QPrinterInfo>
27
28class PRINTERS_DECL_EXPORT PrinterCupsBackend : public PrinterBackend
29{
30 Q_OBJECT
31public:
32 explicit PrinterCupsBackend(IppClient *client, QPrinterInfo info,
33 OrgCupsCupsdNotifierInterface* notifier,
34 QObject *parent = Q_NULLPTR);
35 virtual ~PrinterCupsBackend() override;
36
37 virtual bool holdsDefinition() const override;
38
39 virtual QString printerAdd(const QString &name,
40 const QString &uri,
41 const QString &ppdFile,
42 const QString &info,
43 const QString &location) override;
44 virtual QString printerAddWithPpd(const QString &name,
45 const QString &uri,
46 const QString &ppdFileName,
47 const QString &info,
48 const QString &location) override;
49 virtual QString printerDelete(const QString &name) override;
50 virtual QString printerSetDefault(const QString &name) override;
51 virtual QString printerSetEnabled(const QString &name,
52 const bool enabled) override;
53 virtual QString printerSetAcceptJobs(
54 const QString &name,
55 const bool accept,
56 const QString &reason = QString::null) override;
57 virtual QString printerSetInfo(const QString &name,
58 const QString &info) override;
59 virtual QString printerAddOption(const QString &name,
60 const QString &option,
61 const QStringList &values) override;
62
63 virtual QVariant printerGetOption(const QString &name,
64 const QString &option) const override;
65 virtual QMap<QString, QVariant> printerGetOptions(
66 const QString &name, const QStringList &options
67 ) const override;
68 // FIXME: maybe have a PrinterDest iface that has a CupsDest impl?
69 virtual cups_dest_t* makeDest(const QString &name,
70 const PrinterJob *options) override;
71
72 virtual void cancelJob(const QString &name, const int jobId) override;
73 virtual int printFileToDest(const QString &filepath,
74 const QString &title,
75 const cups_dest_t *dest) override;
76 virtual QList<QSharedPointer<PrinterJob>> printerGetJobs() override;
77
78 virtual QString printerName() const override;
79 virtual QString description() const override;
80 virtual QString location() const override;
81 virtual QString makeAndModel() const override;
82
83 virtual PrinterEnum::State state() const override;
84 virtual QList<QPageSize> supportedPageSizes() const override;
85 virtual QPageSize defaultPageSize() const override;
86 virtual bool supportsCustomPageSizes() const override;
87
88 virtual QPageSize minimumPhysicalPageSize() const override;
89 virtual QPageSize maximumPhysicalPageSize() const override;
90 virtual QList<int> supportedResolutions() const override;
91 virtual PrinterEnum::DuplexMode defaultDuplexMode() const override;
92 virtual QList<PrinterEnum::DuplexMode> supportedDuplexModes() const override;
93
94 virtual QList<QSharedPointer<Printer>> availablePrinters() override;
95 virtual QStringList availablePrinterNames() override;
96 virtual QSharedPointer<Printer> getPrinter(const QString &printerName) override;
97 virtual QString defaultPrinterName() override;
98 virtual void requestPrinterDrivers() override;
99 virtual void requestPrinter(const QString &printerName) override;
100 virtual QMap<QString, QVariant> printerGetJobAttributes(
101 const QString &name, const int jobId) override;
102
103public Q_SLOTS:
104 virtual void refresh() override;
105 void createSubscription();
106
107Q_SIGNALS:
108 void cancelWorkers();
109 void printerDriversLoaded(const QList<PrinterDriver> &drivers);
110 void printerDriversFailedToLoad(const QString &errorMessage);
111 void requestPrinterDriverCancel();
112
113private:
114 void cancelSubscription();
115 void cancelPrinterDriverRequest();
116 QList<cups_job_t *> getCupsJobs(const QString &name = QStringLiteral());
117
118 QString getPrinterName(const QString &name) const;
119 QString getPrinterInstance(const QString &name) const;
120 cups_dest_t* getDest(const QString &name) const;
121 ppd_file_t* getPpd(const QString &name) const;
122 const QStringList m_knownQualityOptions;
123 IppClient *m_client;
124 QPrinterInfo m_info;
125 OrgCupsCupsdNotifierInterface *m_notifier;
126 int m_cupsSubscriptionId;
127 mutable QMap<QString, cups_dest_t*> m_dests; // Printer name, dest.
128 mutable QMap<QString, ppd_file_t*> m_ppds; // Printer name, ppd.
129 QList<QString> m_activeRequests;
130
131private Q_SLOTS:
132 void onPrinterLoaded(QSharedPointer<Printer> printer);
133};
134
135#endif // USC_PRINTERS_CUPS_BACKEND_H
0136
=== added file 'modules/Ubuntu/Components/Extras/Printers/backend/backend_pdf.cpp'
--- modules/Ubuntu/Components/Extras/Printers/backend/backend_pdf.cpp 1970-01-01 00:00:00 +0000
+++ modules/Ubuntu/Components/Extras/Printers/backend/backend_pdf.cpp 2017-02-24 12:29:37 +0000
@@ -0,0 +1,119 @@
1/*
2 * Copyright (C) 2017 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include "i18n.h"
18#include "backend/backend_pdf.h"
19
20PrinterPdfBackend::PrinterPdfBackend(const QString &printerName,
21 QObject *parent)
22 : PrinterBackend(printerName, parent)
23{
24 m_type = PrinterEnum::PrinterType::PdfType;
25}
26
27QVariant PrinterPdfBackend::printerGetOption(const QString &name,
28 const QString &option) const
29{
30 auto res = printerGetOptions(name, QStringList({option}));
31 return res[option];
32}
33
34QMap<QString, QVariant> PrinterPdfBackend::printerGetOptions(
35 const QString &name, const QStringList &options) const
36{
37 Q_UNUSED(name);
38
39 QMap<QString, QVariant> ret;
40
41 ColorModel rgb;
42 rgb.colorType = PrinterEnum::ColorModelType::ColorType;
43 rgb.name = "RGB";
44 rgb.text = __("Color");
45
46 PrintQuality quality;
47 quality.name = __("Normal");
48
49 Q_FOREACH(const QString &option, options) {
50 if (option == QLatin1String("DefaultColorModel")) {
51 ret[option] = QVariant::fromValue(rgb);
52 } else if (option == QLatin1String("DefaultPrintQuality")) {
53 ret[option] = QVariant::fromValue(quality);
54 } else if (option == QLatin1String("SupportedPrintQualities")) {
55 auto qualities = QList<PrintQuality>({quality});
56 ret[option] = QVariant::fromValue(qualities);
57 } else if (option == QLatin1String("SupportedColorModels")) {
58 auto models = QList<ColorModel>{rgb};
59 ret[option] = QVariant::fromValue(models);
60 } else if (option == QLatin1String("AcceptJobs")) {
61 ret[option] = true;
62 } else {
63 throw std::invalid_argument("Invalid value for PDF printer: " + option.toStdString());
64 }
65 }
66
67 return ret;
68}
69
70QString PrinterPdfBackend::printerName() const
71{
72 return m_printerName;
73}
74
75PrinterEnum::State PrinterPdfBackend::state() const
76{
77 return PrinterEnum::State::IdleState;
78}
79
80QList<QPageSize> PrinterPdfBackend::supportedPageSizes() const
81{
82 return QList<QPageSize>{QPageSize(QPageSize::A4)};
83}
84
85QPageSize PrinterPdfBackend::defaultPageSize() const
86{
87 return QPageSize(QPageSize::A4);
88}
89
90bool PrinterPdfBackend::supportsCustomPageSizes() const
91{
92 return false;
93}
94
95QPageSize PrinterPdfBackend::minimumPhysicalPageSize() const
96{
97 return QPageSize(QPageSize::A4);
98}
99
100QPageSize PrinterPdfBackend::maximumPhysicalPageSize() const
101{
102 return QPageSize(QPageSize::A4);
103}
104
105QList<int> PrinterPdfBackend::supportedResolutions() const
106{
107 return QList<int>{};
108}
109
110PrinterEnum::DuplexMode PrinterPdfBackend::defaultDuplexMode() const
111{
112 return PrinterEnum::DuplexMode::DuplexNone;
113}
114
115QList<PrinterEnum::DuplexMode> PrinterPdfBackend::supportedDuplexModes() const
116{
117 return QList<PrinterEnum::DuplexMode>{PrinterEnum::DuplexMode::DuplexNone};
118}
119
0120
=== added file 'modules/Ubuntu/Components/Extras/Printers/backend/backend_pdf.h'
--- modules/Ubuntu/Components/Extras/Printers/backend/backend_pdf.h 1970-01-01 00:00:00 +0000
+++ modules/Ubuntu/Components/Extras/Printers/backend/backend_pdf.h 2017-02-24 12:29:37 +0000
@@ -0,0 +1,47 @@
1/*
2 * Copyright (C) 2017 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#ifndef USC_PRINTERS_PDF_BACKEND_H
18#define USC_PRINTERS_PDF_BACKEND_H
19
20#include "backend/backend.h"
21
22class PRINTERS_DECL_EXPORT PrinterPdfBackend : public PrinterBackend
23{
24public:
25 explicit PrinterPdfBackend(const QString &printerName,
26 QObject *parent = Q_NULLPTR);
27 virtual QVariant printerGetOption(const QString &name,
28 const QString &option) const override;
29 virtual QMap<QString, QVariant> printerGetOptions(
30 const QString &name, const QStringList &options
31 ) const override;
32
33 virtual QString printerName() const override;
34
35 virtual PrinterEnum::State state() const override;
36 virtual QList<QPageSize> supportedPageSizes() const override;
37 virtual QPageSize defaultPageSize() const override;
38 virtual bool supportsCustomPageSizes() const override;
39
40 virtual QPageSize minimumPhysicalPageSize() const override;
41 virtual QPageSize maximumPhysicalPageSize() const override;
42 virtual QList<int> supportedResolutions() const override;
43 virtual PrinterEnum::DuplexMode defaultDuplexMode() const override;
44 virtual QList<PrinterEnum::DuplexMode> supportedDuplexModes() const override;
45};
46
47#endif // USC_PRINTERS_PDF_BACKEND_H
048
=== added directory 'modules/Ubuntu/Components/Extras/Printers/cups'
=== added file 'modules/Ubuntu/Components/Extras/Printers/cups/ippclient.cpp'
--- modules/Ubuntu/Components/Extras/Printers/cups/ippclient.cpp 1970-01-01 00:00:00 +0000
+++ modules/Ubuntu/Components/Extras/Printers/cups/ippclient.cpp 2017-02-24 12:29:37 +0000
@@ -0,0 +1,988 @@
1/*
2 * Copyright (C) 2017 Canonical, Ltd.
3 * Copyright (C) 2014 John Layt <jlayt@kde.org>
4 * Copyright (C) 2002, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2013, 2014 Red Hat, Inc.
5 * Copyright (C) 2008 Novell, Inc.
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU Lesser General Public License as published by
9 * the Free Software Foundation; version 3.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include "cups/ippclient.h"
21
22#include <errno.h>
23#include <string.h>
24#include <unistd.h>
25
26#include <QDebug>
27#include <QDateTime>
28#include <QTimeZone>
29#include <QUrl>
30
31IppClient::IppClient()
32 : m_connection(httpConnectEncrypt(cupsServer(),
33 ippPort(),
34 cupsEncryption()))
35{
36 if (!m_connection) {
37 qCritical("Failed to connect to cupsd");
38 } else {
39 qDebug("Successfully connected to cupsd.");
40 }
41}
42
43IppClient::~IppClient()
44{
45 if (m_connection)
46 httpClose(m_connection);
47}
48
49bool IppClient::printerDelete(const QString &printerName)
50{
51 return sendNewSimpleRequest(CUPS_DELETE_PRINTER, printerName.toUtf8(),
52 CupsResource::CupsResourceAdmin);
53}
54
55bool IppClient::printerAdd(const QString &printerName,
56 const QString &printerUri,
57 const QString &ppdFile,
58 const QString &info,
59 const QString &location)
60{
61 ipp_t *request;
62
63 if (!isPrinterNameValid(printerName)) {
64 setInternalStatus(QString("%1 is not a valid printer name.").arg(printerName));
65 return false;
66 }
67
68 if (!isStringValid(info)) {
69 setInternalStatus(QString("%1 is not a valid description.").arg(info));
70 return false;
71 }
72
73 if (!isStringValid(location)) {
74 setInternalStatus(QString("%1 is not a valid location.").arg(location));
75 return false;
76 }
77
78 if (!isStringValid(ppdFile)) {
79 setInternalStatus(QString("%1 is not a valid ppd file.").arg(ppdFile));
80 return false;
81 }
82
83 if (!isStringValid(printerUri)) {
84 setInternalStatus(QString("%1 is not a valid printer uri.").arg(printerUri));
85 return false;
86 }
87
88
89 request = ippNewRequest (CUPS_ADD_MODIFY_PRINTER);
90 addPrinterUri(request, printerName);
91 addRequestingUsername(request, NULL);
92
93 ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_NAME,
94 "printer-name", NULL, printerName.toUtf8());
95
96 if (!ppdFile.isEmpty()) {
97 ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_NAME,
98 "ppd-name", NULL, ppdFile.toUtf8());
99 }
100 if (!printerUri.isEmpty()) {
101 ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_URI,
102 "device-uri", NULL, printerUri.toUtf8());
103 }
104 if (!info.isEmpty()) {
105 ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_TEXT,
106 "printer-info", NULL, info.toUtf8());
107 }
108 if (!location.isEmpty()) {
109 ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_TEXT,
110 "printer-location", NULL, location.toUtf8());
111 }
112
113 return sendRequest(request, CupsResourceAdmin);
114}
115
116bool IppClient::printerAddWithPpdFile(const QString &printerName,
117 const QString &printerUri,
118 const QString &ppdFileName,
119 const QString &info,
120 const QString &location)
121{
122 ipp_t *request;
123
124 if (!isPrinterNameValid(printerName)) {
125 setInternalStatus(QString("%1 is not a valid printer name.").arg(printerName));
126 return false;
127 }
128
129 if (!isStringValid(info)) {
130 setInternalStatus(QString("%1 is not a valid description.").arg(info));
131 return false;
132 }
133
134 if (!isStringValid(location)) {
135 setInternalStatus(QString("%1 is not a valid location.").arg(location));
136 return false;
137 }
138
139 if (!isStringValid(ppdFileName)) {
140 setInternalStatus(QString("%1 is not a valid ppd file name.").arg(ppdFileName));
141 return false;
142 }
143
144 if (!isStringValid(printerUri)) {
145 setInternalStatus(QString("%1 is not a valid printer uri.").arg(printerUri));
146 return false;
147 }
148
149 request = ippNewRequest(CUPS_ADD_MODIFY_PRINTER);
150 addPrinterUri(request, printerName);
151 addRequestingUsername(request, NULL);
152
153 ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_NAME,
154 "printer-name", NULL, printerName.toUtf8());
155
156 /* In this specific case of ADD_MODIFY, the URI can be NULL/empty since
157 * we provide a complete PPD. And cups fails if we pass an empty
158 * string. */
159 if (!printerUri.isEmpty()) {
160 ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_URI,
161 "device-uri", NULL, printerUri.toUtf8());
162 }
163
164 if (!info.isEmpty()) {
165 ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_TEXT,
166 "printer-info", NULL, info.toUtf8());
167 }
168 if (!location.isEmpty()) {
169 ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_TEXT,
170 "printer-location", NULL, location.toUtf8());
171 }
172
173 return postRequest(request, ppdFileName.toUtf8(), CupsResourceAdmin);
174}
175
176bool IppClient::printerSetDefault(const QString &printerName)
177{
178 return sendNewSimpleRequest(CUPS_SET_DEFAULT, printerName.toUtf8(),
179 CupsResource::CupsResourceAdmin);
180}
181
182bool IppClient::printerSetEnabled(const QString &printerName,
183 const bool enabled)
184{
185 ipp_op_t op;
186 op = enabled ? IPP_RESUME_PRINTER : IPP_PAUSE_PRINTER;
187 return sendNewSimpleRequest(op, printerName, CupsResourceAdmin);
188}
189
190/* reason must be empty if accept is true */
191bool IppClient::printerSetAcceptJobs(const QString &printerName,
192 const bool accept,
193 const QString &reason)
194{
195 ipp_t *request;
196
197 if (accept && !reason.isEmpty()) {
198 setInternalStatus("Accepting jobs does not take a reason.");
199 return false;
200 }
201
202 if (!isPrinterNameValid(printerName)) {
203 setInternalStatus(QString("%1 is not a valid printer name.").arg(printerName));
204 return false;
205 }
206
207 if (!isStringValid(reason)) {
208 setInternalStatus(QString("%1 is not a valid reason.").arg(reason));
209 return false;
210 }
211
212 if (accept) {
213 return sendNewSimpleRequest(CUPS_ACCEPT_JOBS, printerName.toUtf8(),
214 CupsResourceAdmin);
215 } else {
216 request = ippNewRequest(CUPS_REJECT_JOBS);
217 addPrinterUri(request, printerName);
218 addRequestingUsername(request, NULL);
219
220 if (!reason.isEmpty())
221 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_TEXT,
222 "printer-state-message", NULL, reason.toUtf8());
223
224 return sendRequest(request, CupsResourceAdmin);
225 }
226}
227
228
229bool IppClient::printerClassSetInfo(const QString &name,
230 const QString &info)
231{
232 if (!isPrinterNameValid(name)) {
233 setInternalStatus(QString("%1 is not a valid printer name.").arg(name));
234 return false;
235 }
236
237 if (!isStringValid(info)) {
238 setInternalStatus(QString("%1 is not a valid description.").arg(info));
239 return false;
240 }
241
242 return sendNewPrinterClassRequest(name, IPP_TAG_PRINTER, IPP_TAG_TEXT,
243 "printer-info", info);
244}
245
246bool IppClient::printerClassSetOption(const QString &name,
247 const QString &option,
248 const QStringList &values)
249{
250 bool isClass;
251 int length = 0;
252 ipp_t *request;
253 ipp_attribute_t *attr;
254 QString newPpdFile;
255 bool retval;
256
257 if (!isPrinterNameValid(name)) {
258 setInternalStatus(QString("%1 is not a valid printer name.").arg(name));
259 return false;
260 }
261
262 if (!isStringValid(option)) {
263 setInternalStatus(QString("%1 is not a valid option.").arg(option));
264 return false;
265 }
266
267 Q_FOREACH(const QString &val, values) {
268 if (!isStringValid(val)) {
269 setInternalStatus(QString("%1 is not a valid value.").arg(val));
270 return false;
271 }
272 length++;
273 }
274
275 if (length == 0) {
276 setInternalStatus("No valid values.");
277 return false;
278 }
279
280 isClass = printerIsClass(name);
281
282 /* We permit only one value to change in PPD file because we are setting
283 * default value in it. */
284 if (!isClass && length == 1) {
285 cups_option_t *options = NULL;
286 int numOptions = 0;
287 QString ppdfile;
288
289 numOptions = cupsAddOption(option.toUtf8(),
290 values[0].toUtf8(),
291 numOptions, &options);
292
293 ppdfile = QString(cupsGetPPD(name.toUtf8()));
294
295 newPpdFile = preparePpdForOptions(ppdfile.toUtf8(),
296 options, numOptions).toLatin1().data();
297
298 unlink(ppdfile.toUtf8());
299 cupsFreeOptions(numOptions, options);
300 }
301
302 if (isClass) {
303 request = ippNewRequest(CUPS_ADD_MODIFY_CLASS);
304 addClassUri(request, name);
305 } else {
306 request = ippNewRequest(CUPS_ADD_MODIFY_PRINTER);
307 addPrinterUri(request, name);
308 }
309
310 addRequestingUsername(request, NULL);
311
312 if (length == 1) {
313 ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_NAME,
314 option.toUtf8(),
315 NULL,
316 values[0].toUtf8());
317 } else {
318 int i;
319
320 attr = ippAddStrings(request, IPP_TAG_PRINTER, IPP_TAG_NAME,
321 option.toUtf8(), length, NULL, NULL);
322
323 for (i = 0; i < length; i++)
324 ippSetString(request, &attr, i, values[i].toUtf8());
325 }
326
327 if (!newPpdFile.isEmpty()) {
328 retval = postRequest(request, newPpdFile, CupsResourceAdmin);
329
330 unlink(newPpdFile.toUtf8());
331 // TODO: fix leak here.
332 } else {
333 retval = sendRequest(request, CupsResourceAdmin);
334 }
335
336 return retval;
337}
338
339QMap<QString, QVariant> IppClient::printerGetJobAttributes(const int jobId)
340{
341 ipp_t *request;
342 QMap<QString, QVariant> map;
343
344 // Construct request
345 request = ippNewRequest(IPP_GET_JOB_ATTRIBUTES);
346 QString uri = QStringLiteral("ipp://localhost/jobs/") + QString::number(jobId);
347 qDebug() << "URI:" << uri;
348
349 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", NULL, uri.toStdString().data());
350
351
352 // Send request and construct reply
353 ipp_t *reply;
354 const QString resourceChar = getResource(CupsResourceRoot);
355 reply = cupsDoRequest(m_connection, request,
356 resourceChar.toUtf8());
357
358 // Check if the reply is OK
359 if (isReplyOk(reply, false)) {
360 // Loop through the attributes
361 ipp_attribute_t *attr;
362
363 for (attr = ippFirstAttribute(reply); attr; attr = ippNextAttribute(reply)) {
364 QVariant value = getAttributeValue(attr);
365 map.insert(ippGetName(attr), value);
366 }
367 } else {
368 qWarning() << "Not able to get attributes of job:" << jobId;
369 }
370
371 // Destruct the reply if valid
372 if (reply) {
373 ippDelete(reply);
374 }
375
376 return map;
377}
378
379
380/* This function sets given options to specified values in file 'ppdfile'.
381 * This needs to be done because of applications which use content of PPD files
382 * instead of IPP attributes.
383 * CUPS doesn't do this automatically (but hopefully will starting with 1.6) */
384QString IppClient::preparePpdForOptions(const QString &ppdfile,
385 cups_option_t *options, int numOptions)
386{
387 auto ppdfile_c = ppdfile.toUtf8();
388 ppd_file_t *ppd;
389 bool ppdchanged = false;
390 QString result;
391 QString error;
392 char newppdfile[PATH_MAX];
393 cups_file_t *in = NULL;
394 cups_file_t *out = NULL;
395 char line[CPH_STR_MAXLEN];
396 char keyword[CPH_STR_MAXLEN];
397 char *keyptr;
398 ppd_choice_t *choice;
399 QString value;
400 QLatin1String defaultStr("*Default");
401
402 ppd = ppdOpenFile(ppdfile_c);
403 if (!ppd) {
404 error = QString("Unable to open PPD file \"%1\": %2")
405 .arg(ppdfile).arg(strerror(errno));
406 setInternalStatus(error);
407 goto out;
408 }
409
410 in = cupsFileOpen(ppdfile_c, "r");
411 if (!in) {
412 error = QString("Unable to open PPD file \"%1\": %2")
413 .arg(ppdfile).arg(strerror(errno));
414 setInternalStatus(error);
415 goto out;
416 }
417
418 out = cupsTempFile2(newppdfile, sizeof(newppdfile));
419 if (!out) {
420 setInternalStatus("Unable to create temporary file");
421 goto out;
422 }
423
424 /* Mark default values and values of options we are changing. */
425 ppdMarkDefaults(ppd);
426 cupsMarkOptions(ppd, numOptions, options);
427
428 while (cupsFileGets(in, line, sizeof(line))) {
429 QString line_qs(line);
430 if (!line_qs.startsWith(defaultStr)) {
431 cupsFilePrintf(out, "%s\n", line);
432 } else {
433 /* This part parses lines with *Default on their
434 * beginning. For instance:
435 * "*DefaultResolution: 1200dpi" becomes:
436 * - keyword: Resolution
437 * - keyptr: 1200dpi
438 */
439 strncpy(keyword, line + defaultStr.size(), sizeof(keyword));
440
441 for (keyptr = keyword; *keyptr; keyptr++)
442 if (*keyptr == ':' || isspace (*keyptr & 255))
443 break;
444
445 *keyptr++ = '\0';
446 while (isspace (*keyptr & 255))
447 keyptr++;
448
449 QString keyword_sq(keyword);
450 QString keyptr_qs(keyptr);
451
452 /* We have to change PageSize if any of PageRegion,
453 * PageSize, PaperDimension or ImageableArea changes.
454 * We change PageRegion if PageSize is not available. */
455 if (keyword_sq == "PageRegion" ||
456 keyword_sq == "PageSize" ||
457 keyword_sq == "PaperDimension" ||
458 keyword_sq == "ImageableArea") {
459
460 choice = ppdFindMarkedChoice(ppd, "PageSize");
461 if (!choice)
462 choice = ppdFindMarkedChoice(ppd, "PageRegion");
463 } else {
464 choice = ppdFindMarkedChoice(ppd, keyword);
465 }
466
467
468 QString choice_qs;
469 if (choice) {
470 choice_qs = choice->choice;
471 }
472
473 if (choice && choice_qs != keyptr_qs) {
474 /* We have to set the value in PPD manually if
475 * a custom value was passed in:
476 * cupsMarkOptions() marks the choice as
477 * "Custom". We want to set this value with our
478 * input. */
479 if (choice_qs != "Custom") {
480 cupsFilePrintf(out,
481 "*Default%s: %s\n",
482 keyword,
483 choice->choice);
484 ppdchanged = true;
485 } else {
486 value = cupsGetOption(keyword, numOptions, options);
487 if (!value.isEmpty()) {
488 cupsFilePrintf(out,
489 "*Default%s: %s\n",
490 keyword,
491 value.toStdString().c_str());
492 ppdchanged = true;
493 } else {
494 cupsFilePrintf(out, "%s\n", line);
495 }
496 }
497 } else {
498 cupsFilePrintf(out, "%s\n", line);
499 }
500 }
501 }
502
503 if (ppdchanged)
504 result = QString::fromUtf8(newppdfile);
505 else
506 unlink(newppdfile);
507
508out:
509 if (in)
510 cupsFileClose(in);
511 if (out)
512 cupsFileClose(out);
513 if (ppd)
514 ppdClose(ppd);
515
516 return result;
517}
518
519
520bool IppClient::sendNewPrinterClassRequest(const QString &printerName,
521 ipp_tag_t group, ipp_tag_t type,
522 const QString &name,
523 const QString &value)
524{
525 ipp_t *request;
526
527 request = ippNewRequest(CUPS_ADD_MODIFY_PRINTER);
528 addPrinterUri(request, printerName);
529 addRequestingUsername(request, QString());
530 ippAddString(request, group, type, name.toUtf8(), NULL,
531 value.toUtf8());
532
533 if (sendRequest(request, CupsResource::CupsResourceAdmin))
534 return true;
535
536 // it failed, maybe it was a class?
537 if (m_lastStatus != IPP_NOT_POSSIBLE) {
538 return false;
539 }
540
541 // TODO: implement class modification <here>.
542 return false;
543}
544
545void IppClient::addPrinterUri(ipp_t *request, const QString &name)
546{
547 QUrl uri(QString("ipp://localhost/printers/%1").arg(name));
548 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
549 "printer-uri", NULL, uri.toEncoded().data());
550}
551
552void IppClient::addRequestingUsername(ipp_t *request, const QString &username)
553{
554 if (!username.isEmpty())
555 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
556 "requesting-user-name", NULL,
557 username.toUtf8());
558 else
559 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
560 "requesting-user-name", NULL, cupsUser());
561}
562
563QString IppClient::getLastError() const
564{
565 return m_internalStatus;
566}
567
568const QString IppClient::getResource(const IppClient::CupsResource &resource)
569{
570 switch (resource) {
571 case CupsResourceRoot:
572 return "/";
573 case CupsResourceAdmin:
574 return "/admin/";
575 case CupsResourceJobs:
576 return "/jobs/";
577 default:
578 qCritical("Asking for a resource with no match.");
579 return "/";
580 }
581}
582
583bool IppClient::isPrinterNameValid(const QString &name)
584{
585 int i;
586 int len;
587
588 /* Quoting the lpadmin man page:
589 * CUPS allows printer names to contain any printable character
590 * except SPACE, TAB, "/", or "#".
591 * On top of that, validate_name() in lpadmin.c (from cups) checks that
592 * the string is 127 characters long, or shorter. */
593
594 /* no empty string */
595 if (name.isEmpty())
596 return false;
597
598 len = name.size();
599 /* no string that is too long; see comment at the beginning of the
600 * validation code block */
601 if (len > 127)
602 return false;
603
604 /* only printable characters, no space, no /, no # */
605 for (i = 0; i < len; i++) {
606 const QChar c = name.at(i);
607 if (!c.isPrint())
608 return false;
609 if (c.isSpace())
610 return false;
611 if (c == '/' || c == '#')
612 return false;
613 }
614 return true;
615}
616
617bool IppClient::isStringValid(const QString &string, const bool checkNull,
618 const int maxLength)
619{
620 if (isStringPrintable(string, checkNull, maxLength))
621 return true;
622 return false;
623}
624
625bool IppClient::isStringPrintable(const QString &string, const bool checkNull,
626 const int maxLength)
627{
628 int i;
629 int len;
630
631 /* no null string */
632 if (string.isNull())
633 return !checkNull;
634
635 len = string.size();
636 if (maxLength > 0 && len > maxLength)
637 return false;
638
639 /* only printable characters */
640 for (i = 0; i < len; i++) {
641 const QChar c = string.at(i);
642 if (!c.isPrint())
643 return false;
644 }
645 return true;
646}
647
648void IppClient::setInternalStatus(const QString &status)
649{
650 if (!m_internalStatus.isNull()) {
651 m_internalStatus = QString::null;
652 }
653
654 if (status.isNull()) {
655 m_internalStatus = QString::null;
656 } else {
657 m_internalStatus = status;
658
659 // Only used for errors for now.
660 qCritical() << status;
661 }
662}
663
664bool IppClient::postRequest(ipp_t *request, const QString &file,
665 const CupsResource &resource)
666{
667 ipp_t *reply;
668 QString resourceChar;
669
670 resourceChar = getResource(resource);
671
672 if (!file.isEmpty())
673 reply = cupsDoFileRequest(m_connection, request, resourceChar.toUtf8(),
674 file.toUtf8());
675 else
676 reply = cupsDoFileRequest(m_connection, request, resourceChar.toUtf8(),
677 NULL);
678
679 return handleReply(reply);
680}
681
682
683bool IppClient::sendRequest(ipp_t *request, const CupsResource &resource)
684{
685 ipp_t *reply;
686 const QString resourceChar = getResource(resource);
687 reply = cupsDoRequest(m_connection, request,
688 resourceChar.toUtf8());
689 return handleReply(reply);
690}
691
692bool IppClient::sendNewSimpleRequest(ipp_op_t op, const QString &printerName,
693 const IppClient::CupsResource &resource)
694{
695 ipp_t *request;
696
697 if (!isPrinterNameValid(printerName))
698 return false;
699
700 request = ippNewRequest(op);
701 addPrinterUri(request, printerName);
702 addRequestingUsername(request, NULL);
703
704 return sendRequest(request, resource);
705}
706
707bool IppClient::handleReply(ipp_t *reply)
708{
709 bool retval;
710 retval = isReplyOk(reply, false);
711 if (reply)
712 ippDelete(reply);
713
714 return retval;
715}
716
717bool IppClient::isReplyOk(ipp_t *reply, bool deleteIfReplyNotOk)
718{
719 /* reset the internal status: we'll use the cups status */
720 m_lastStatus = IPP_STATUS_CUPS_INVALID;
721
722 if (reply && ippGetStatusCode(reply) <= IPP_OK_CONFLICT) {
723 m_lastStatus = IPP_OK;
724 return true;
725 } else {
726 setErrorFromReply(reply);
727 qWarning() << Q_FUNC_INFO << "Cups HTTP error:" << cupsLastErrorString();
728
729 if (deleteIfReplyNotOk && reply)
730 ippDelete(reply);
731
732 return false;
733 }
734}
735
736void IppClient::setErrorFromReply(ipp_t *reply)
737{
738 if (reply)
739 m_lastStatus = ippGetStatusCode(reply);
740 else
741 m_lastStatus = cupsLastError();
742}
743
744bool IppClient::printerIsClass(const QString &name)
745{
746 const char * const attrs[1] = { "member-names" };
747 ipp_t *request;
748 QString resource;
749 ipp_t *reply;
750 bool retval;
751
752 // Class/Printer name validation is equal.
753 if (!isPrinterNameValid(name)) {
754 setInternalStatus(QString("%1 is not a valid printer name.").arg(name));
755 return false;
756 }
757
758 request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
759 addClassUri(request, name);
760 addRequestingUsername(request, QString());
761 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
762 "requested-attributes", 1, NULL, attrs);
763
764 resource = getResource(CupsResource::CupsResourceRoot);
765 reply = cupsDoRequest(m_connection, request, resource.toUtf8());
766
767 if (!isReplyOk(reply, true))
768 return true;
769
770 /* Note: we need to look if the attribute is there, since we get a
771 * reply if the name is a printer name and not a class name. The
772 * attribute is the only way to distinguish the two cases. */
773 retval = ippFindAttribute(reply, attrs[0], IPP_TAG_NAME) != NULL;
774
775 if (reply)
776 ippDelete(reply);
777
778 return retval;
779}
780
781void IppClient::addClassUri(ipp_t *request, const QString &name)
782{
783 QUrl uri(QString("ipp://localhost/printers/%1").arg(name));
784 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
785 "printer-uri", NULL, uri.toEncoded().data());
786}
787
788ppd_file_t* IppClient::getPpdFile(const QString &name,
789 const QString &instance) const
790{
791 Q_UNUSED(instance);
792
793 ppd_file_t* file = 0;
794 const char *ppdFile = cupsGetPPD(name.toUtf8());
795 if (ppdFile) {
796 file = ppdOpenFile(ppdFile);
797 unlink(ppdFile);
798 }
799 if (file) {
800 ppdMarkDefaults(file);
801 } else {
802 file = 0;
803 }
804
805 return file;
806}
807
808cups_dest_t* IppClient::getDest(const QString &name,
809 const QString &instance) const
810{
811 cups_dest_t *dest = 0;
812
813 if (instance.isEmpty()) {
814 dest = cupsGetNamedDest(m_connection, name.toUtf8(), NULL);
815 } else {
816 dest = cupsGetNamedDest(m_connection, name.toUtf8(), instance.toUtf8());
817 }
818 return dest;
819}
820
821ipp_t* IppClient::createPrinterDriversRequest(
822 const QString &deviceId, const QString &language, const QString &makeModel,
823 const QString &product, const QStringList &includeSchemes,
824 const QStringList &excludeSchemes
825)
826{
827 Q_UNUSED(includeSchemes);
828 Q_UNUSED(excludeSchemes);
829
830 ipp_t *request;
831
832 request = ippNewRequest(CUPS_GET_PPDS);
833
834 if (!deviceId.isEmpty())
835 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_TEXT, "ppd-device-id",
836 NULL, deviceId.toUtf8());
837 if (!language.isEmpty())
838 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE, "ppd-language",
839 NULL, language.toUtf8());
840 if (!makeModel.isEmpty())
841 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_TEXT, "ppd-make-and-model",
842 NULL, makeModel.toUtf8());
843 if (!product.isEmpty())
844 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_TEXT, "ppd-product",
845 NULL, product.toUtf8());
846
847 // Do the request and get return the response.
848 const QString resourceChar = getResource(CupsResourceRoot);
849 return cupsDoRequest(m_connection, request,
850 resourceChar.toUtf8());
851}
852
853int IppClient::createSubscription()
854{
855 ipp_t *req;
856 ipp_t *resp;
857 ipp_attribute_t *attr;
858 int subscriptionId = -1;
859
860 req = ippNewRequest(IPP_CREATE_PRINTER_SUBSCRIPTION);
861 ippAddString(req, IPP_TAG_OPERATION, IPP_TAG_URI,
862 "printer-uri", NULL, "/");
863 ippAddString(req, IPP_TAG_SUBSCRIPTION, IPP_TAG_KEYWORD,
864 "notify-events", NULL, "all");
865 ippAddString(req, IPP_TAG_SUBSCRIPTION, IPP_TAG_URI,
866 "notify-recipient-uri", NULL, "dbus://");
867 ippAddInteger(req, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
868 "notify-lease-duration", 0);
869
870 resp = cupsDoRequest(m_connection, req,
871 getResource(CupsResourceRoot).toUtf8());
872 if (!isReplyOk(resp, true)) {
873 return subscriptionId;
874 }
875
876 attr = ippFindAttribute(resp, "notify-subscription-id", IPP_TAG_INTEGER);
877
878 if (!attr) {
879 qWarning() << "ipp-create-printer-subscription response doesn't"
880 " contain subscription id.";
881 } else {
882 subscriptionId = ippGetInteger(attr, 0);
883 }
884
885 ippDelete (resp);
886
887 return subscriptionId;
888}
889
890void IppClient::cancelSubscription(const int &subscriptionId)
891{
892 ipp_t *req;
893 ipp_t *resp;
894
895 if (subscriptionId <= 0) {
896 return;
897 }
898
899 req = ippNewRequest(IPP_CANCEL_SUBSCRIPTION);
900 ippAddString(req, IPP_TAG_OPERATION, IPP_TAG_URI,
901 "printer-uri", NULL, "/");
902 ippAddInteger(req, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
903 "notify-subscription-id", subscriptionId);
904
905 resp = cupsDoRequest(m_connection, req,
906 getResource(CupsResourceRoot).toUtf8());
907 if (!isReplyOk(resp, true)) {
908 return;
909 }
910
911 ippDelete(resp);
912}
913
914QVariant IppClient::getAttributeValue(ipp_attribute_t *attr, int index) const
915{
916 QVariant var;
917
918 if (ippGetCount(attr) > 1 && index < 0) {
919 QList<QVariant> list;
920
921 for (int i=0; i < ippGetCount(attr); i++) {
922 list.append(getAttributeValue(attr, i));
923 }
924
925 var = QVariant::fromValue<QList<QVariant>>(list);
926 } else {
927 if (index == -1) {
928 index = 0;
929 }
930
931 switch (ippGetValueTag(attr)) {
932 case IPP_TAG_NAME:
933 case IPP_TAG_TEXT:
934 case IPP_TAG_KEYWORD:
935 case IPP_TAG_URI:
936 case IPP_TAG_CHARSET:
937 case IPP_TAG_MIMETYPE:
938 case IPP_TAG_LANGUAGE:
939 var = QVariant::fromValue<QString>(ippGetString(attr, index, NULL));
940 break;
941 case IPP_TAG_INTEGER:
942 case IPP_TAG_ENUM:
943 var = QVariant::fromValue<int>(ippGetInteger(attr, index));
944 break;
945 case IPP_TAG_BOOLEAN:
946 var = QVariant::fromValue<bool>(ippGetBoolean(attr, index));
947 break;
948 case IPP_TAG_RANGE: {
949 QString range;
950 int upper;
951 int lower = ippGetRange(attr, index, &upper);
952
953 // Build a string similar to "1-3" "5-" "8" "-4"
954 if (lower != INT_MIN) {
955 range += QString::number(lower);
956 }
957
958 if (lower != upper) {
959 range += QStringLiteral("-");
960
961 if (upper != INT_MAX) {
962 range += QString::number(upper);
963 }
964 }
965
966 var = QVariant(range);
967 break;
968 }
969 case IPP_TAG_NOVALUE:
970 var = QVariant();
971 break;
972 case IPP_TAG_DATE: {
973 time_t time = ippDateToTime(ippGetDate(attr, index));
974 QDateTime datetime;
975 datetime.setTimeZone(QTimeZone::systemTimeZone());
976 datetime.setTime_t(time);
977
978 var = QVariant::fromValue<QDateTime>(datetime);
979 break;
980 }
981 default:
982 qWarning() << "Unknown IPP value tab 0x" << ippGetValueTag(attr);
983 break;
984 }
985 }
986
987 return var;
988}
0989
=== added file 'modules/Ubuntu/Components/Extras/Printers/cups/ippclient.h'
--- modules/Ubuntu/Components/Extras/Printers/cups/ippclient.h 1970-01-01 00:00:00 +0000
+++ modules/Ubuntu/Components/Extras/Printers/cups/ippclient.h 2017-02-24 12:29:37 +0000
@@ -0,0 +1,121 @@
1/*
2 * Copyright (C) 2017 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#ifndef USC_PRINTERS_CUPS_IPPCLIENT_H
18#define USC_PRINTERS_CUPS_IPPCLIENT_H
19
20#include "structs.h"
21
22#include <cups/cups.h>
23#include <cups/http.h>
24#include <cups/ipp.h>
25#include <cups/ppd.h>
26
27#include <QString>
28#include <QStringList>
29
30/* From https://bugzilla.novell.com/show_bug.cgi?id=447444#c5
31 * We need to define a maximum length for strings to avoid cups
32 * thinking there are multiple lines.
33 */
34#define CPH_STR_MAXLEN 512
35
36class IppClient
37{
38public:
39 explicit IppClient();
40 ~IppClient();
41
42 bool printerDelete(const QString &printerName);
43 bool printerAdd(const QString &printerName,
44 const QString &printerUri,
45 const QString &ppdFile,
46 const QString &info,
47 const QString &location);
48
49 bool printerAddWithPpdFile(const QString &printerName,
50 const QString &printerUri,
51 const QString &ppdFileName,
52 const QString &info,
53 const QString &location);
54 bool printerSetDefault(const QString &printerName);
55 bool printerSetEnabled(const QString &printerName, const bool enabled);
56 bool printerSetAcceptJobs(const QString &printerName, const bool accept,
57 const QString &reason);
58 bool printerClassSetInfo(const QString &name, const QString &info);
59 bool printerClassSetOption(const QString &name, const QString &option,
60 const QStringList &values);
61 ppd_file_t* getPpdFile(const QString &name, const QString &instance) const;
62 cups_dest_t* getDest(const QString &name, const QString &instance) const;
63
64 QMap<QString, QVariant> printerGetJobAttributes(const int jobId);
65
66 QString getLastError() const;
67
68 // Note: This response needs to be free by the caller.
69 ipp_t* createPrinterDriversRequest(
70 const QString &deviceId, const QString &language,
71 const QString &makeModel, const QString &product,
72 const QStringList &includeSchemes, const QStringList &excludeSchemes
73 );
74 int createSubscription();
75 void cancelSubscription(const int &subscriptionId);
76
77private:
78 enum CupsResource
79 {
80 CupsResourceRoot = 0,
81 CupsResourceAdmin,
82 CupsResourceJobs,
83 };
84
85 bool sendNewPrinterClassRequest(const QString &printerName,
86 ipp_tag_t group,
87 ipp_tag_t type,
88 const QString &name,
89 const QString &value);
90 static void addPrinterUri(ipp_t *request, const QString &name);
91 static void addRequestingUsername(ipp_t *request, const QString &username);
92 static const QString getResource(const CupsResource &resource);
93 static bool isPrinterNameValid(const QString &name);
94 static void addClassUri(ipp_t *request, const QString &name);
95 static bool isStringValid(const QString &string,
96 const bool checkNull = false,
97 const int maxLength = 512);
98 static bool isStringPrintable(const QString &string, const bool checkNull,
99 const int maxLength);
100 QString preparePpdForOptions(const QString &ppdfile,
101 cups_option_t *options,
102 int numOptions);
103 bool printerIsClass(const QString &name);
104 void setInternalStatus(const QString &status);
105 bool postRequest(ipp_t *request, const QString &file,
106 const CupsResource &resource);
107 bool sendRequest(ipp_t *request, const CupsResource &resource);
108 bool sendNewSimpleRequest(ipp_op_t op, const QString &printerName,
109 const CupsResource &resource);
110 bool handleReply(ipp_t *reply);
111 bool isReplyOk(ipp_t *reply, bool deleteIfReplyNotOk);
112 void setErrorFromReply(ipp_t *reply);
113 QVariant getAttributeValue(ipp_attribute_t *attr, int index=-1) const;
114
115 http_t *m_connection;
116 ipp_status_t m_lastStatus = IPP_OK;
117 mutable QString m_internalStatus = QString::null;
118};
119
120
121#endif // USC_PRINTERS_CUPS_IPPCLIENT_H
0122
=== added file 'modules/Ubuntu/Components/Extras/Printers/cups/printerdriverloader.cpp'
--- modules/Ubuntu/Components/Extras/Printers/cups/printerdriverloader.cpp 1970-01-01 00:00:00 +0000
+++ modules/Ubuntu/Components/Extras/Printers/cups/printerdriverloader.cpp 2017-02-24 12:29:37 +0000
@@ -0,0 +1,127 @@
1/*
2 * Copyright (C) 2017 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include "printerdriverloader.h"
18
19PrinterDriverLoader::PrinterDriverLoader(
20 const QString &deviceId, const QString &language,
21 const QString &makeModel, const QString &product,
22 const QStringList &includeSchemes, const QStringList &excludeSchemes)
23 : m_deviceId(deviceId)
24 , m_language(language)
25 , m_makeModel(makeModel)
26 , m_product(product)
27 , m_includeSchemes(includeSchemes)
28 , m_excludeSchemes(excludeSchemes)
29{
30}
31
32PrinterDriverLoader::~PrinterDriverLoader()
33{
34}
35
36void PrinterDriverLoader::process()
37{
38 m_running = true;
39
40 ipp_t* response = client.createPrinterDriversRequest(
41 m_deviceId, m_language, m_makeModel, m_product, m_includeSchemes,
42 m_excludeSchemes
43 );
44
45 // Note: if the response somehow fails, we return.
46 if (!response || ippGetStatusCode(response) > IPP_OK_CONFLICT) {
47 QString err(cupsLastErrorString());
48 qWarning() << Q_FUNC_INFO << "Cups HTTP error:" << err;
49
50 if (response)
51 ippDelete(response);
52
53 Q_EMIT error(err);
54 Q_EMIT finished();
55 return;
56 }
57
58 ipp_attribute_t *attr;
59 QByteArray ppdDeviceId;
60 QByteArray ppdLanguage;
61 QByteArray ppdMakeModel;
62 QByteArray ppdName;
63
64 QList<PrinterDriver> drivers;
65
66 for (attr = ippFirstAttribute(response); attr != NULL && m_running; attr = ippNextAttribute(response)) {
67
68 while (attr != NULL && ippGetGroupTag(attr) != IPP_TAG_PRINTER)
69 attr = ippNextAttribute(response);
70
71 if (attr == NULL)
72 break;
73
74 // Pull the needed attributes from this PPD...
75 ppdDeviceId = "NONE";
76 ppdLanguage.clear();
77 ppdMakeModel.clear();
78 ppdName.clear();
79
80 while (attr != NULL && ippGetGroupTag(attr) == IPP_TAG_PRINTER) {
81 if (!strcmp(ippGetName(attr), "ppd-device-id") &&
82 ippGetValueTag(attr) == IPP_TAG_TEXT) {
83 ppdDeviceId = ippGetString(attr, 0, NULL);
84 } else if (!strcmp(ippGetName(attr), "ppd-natural-language") &&
85 ippGetValueTag(attr) == IPP_TAG_LANGUAGE) {
86 ppdLanguage = ippGetString(attr, 0, NULL);
87
88 } else if (!strcmp(ippGetName(attr), "ppd-make-and-model") &&
89 ippGetValueTag(attr) == IPP_TAG_TEXT) {
90 ppdMakeModel = ippGetString(attr, 0, NULL);
91 } else if (!strcmp(ippGetName(attr), "ppd-name") &&
92 ippGetValueTag(attr) == IPP_TAG_NAME) {
93
94 ppdName = ippGetString(attr, 0, NULL);
95 }
96
97 attr = ippNextAttribute(response);
98 }
99
100 // See if we have everything needed...
101 if (ppdLanguage.isEmpty() || ppdMakeModel.isEmpty() ||
102 ppdName.isEmpty()) {
103 if (attr == NULL)
104 break;
105 else
106 continue;
107 }
108
109 PrinterDriver m;
110 m.name = ppdName;
111 m.deviceId = ppdDeviceId;
112 m.makeModel = ppdMakeModel;
113 m.language = ppdLanguage;
114
115 drivers.append(m);
116 }
117
118 ippDelete(response);
119
120 Q_EMIT loaded(drivers);
121 Q_EMIT finished();
122}
123
124void PrinterDriverLoader::cancel()
125{
126 m_running = false;
127}
0128
=== added file 'modules/Ubuntu/Components/Extras/Printers/cups/printerdriverloader.h'
--- modules/Ubuntu/Components/Extras/Printers/cups/printerdriverloader.h 1970-01-01 00:00:00 +0000
+++ modules/Ubuntu/Components/Extras/Printers/cups/printerdriverloader.h 2017-02-24 12:29:37 +0000
@@ -0,0 +1,61 @@
1/*
2 * Copyright (C) 2017 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#ifndef USC_PRINTERS_CUPS_DRIVERLOADER_H
18#define USC_PRINTERS_CUPS_DRIVERLOADER_H
19
20#include "ippclient.h"
21#include "structs.h"
22
23#include <QObject>
24#include <QString>
25#include <QStringList>
26
27class PrinterDriverLoader : public QObject
28{
29 Q_OBJECT
30public:
31 PrinterDriverLoader(
32 const QString &deviceId = "",
33 const QString &language = "",
34 const QString &makeModel = "",
35 const QString &product = "",
36 const QStringList &includeSchemes = QStringList(),
37 const QStringList &excludeSchemes = QStringList());
38 ~PrinterDriverLoader();
39
40public Q_SLOTS:
41 void process();
42 void cancel();
43
44Q_SIGNALS:
45 void finished();
46 void loaded(const QList<PrinterDriver> &drivers);
47 void error(const QString &error);
48
49private:
50 QString m_deviceId = QString::null;
51 QString m_language = QString::null;
52 QString m_makeModel = QString::null;
53 QString m_product = QString::null;
54 QStringList m_includeSchemes;
55 QStringList m_excludeSchemes;
56
57 bool m_running = false;
58 IppClient client;
59};
60
61#endif // USC_PRINTERS_CUPS_DRIVERLOADER_H
062
=== added file 'modules/Ubuntu/Components/Extras/Printers/cups/printerloader.cpp'
--- modules/Ubuntu/Components/Extras/Printers/cups/printerloader.cpp 1970-01-01 00:00:00 +0000
+++ modules/Ubuntu/Components/Extras/Printers/cups/printerloader.cpp 2017-02-24 12:29:37 +0000
@@ -0,0 +1,53 @@
1/*
2 * Copyright (C) 2017 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include "backend/backend_pdf.h"
18#include "backend/backend_cups.h"
19#include "printerloader.h"
20
21#include <QPrinterInfo>
22
23class PrinterCupsBackend;
24PrinterLoader::PrinterLoader(const QString &printerName,
25 IppClient *client,
26 OrgCupsCupsdNotifierInterface* notifier,
27 QObject *parent)
28 : QObject(parent)
29 , m_printerName(printerName)
30 , m_client(client)
31 , m_notifier(notifier)
32{
33}
34
35PrinterLoader::~PrinterLoader()
36{
37}
38
39void PrinterLoader::load()
40{
41 QPrinterInfo info = QPrinterInfo::printerInfo(m_printerName);
42 auto backend = new PrinterCupsBackend(m_client, info, m_notifier);
43
44 // Dest or PPD was null, but we know it's name so we will use it.
45 if (info.printerName().isEmpty()) {
46 backend->setPrinterNameInternal(m_printerName);
47 }
48
49 auto p = QSharedPointer<Printer>(new Printer(backend));
50
51 Q_EMIT loaded(p);
52 Q_EMIT finished();
53}
054
=== added file 'modules/Ubuntu/Components/Extras/Printers/cups/printerloader.h'
--- modules/Ubuntu/Components/Extras/Printers/cups/printerloader.h 1970-01-01 00:00:00 +0000
+++ modules/Ubuntu/Components/Extras/Printers/cups/printerloader.h 2017-02-24 12:29:37 +0000
@@ -0,0 +1,49 @@
1/*
2 * Copyright (C) 2017 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#ifndef USC_PRINTERS_CUPS_PRINTERLOADER_H
18#define USC_PRINTERS_CUPS_PRINTERLOADER_H
19
20#include "cups/ippclient.h"
21#include "cupsdnotifier.h" // Note: this file was generated.
22#include "printer/printer.h"
23
24#include <QList>
25#include <QObject>
26#include <QSharedPointer>
27
28class PrinterLoader : public QObject
29{
30 Q_OBJECT
31 const QString m_printerName;
32 IppClient *m_client;
33 OrgCupsCupsdNotifierInterface *m_notifier;
34public:
35 explicit PrinterLoader(const QString &printerName,
36 IppClient *client,
37 OrgCupsCupsdNotifierInterface* notifier,
38 QObject *parent = Q_NULLPTR);
39 ~PrinterLoader();
40
41public Q_SLOTS:
42 void load();
43
44Q_SIGNALS:
45 void finished();
46 void loaded(QSharedPointer<Printer> printer);
47};
48
49#endif // USC_PRINTERS_CUPS_PRINTERLOADER_H
050
=== added file 'modules/Ubuntu/Components/Extras/Printers/enums.h'
--- modules/Ubuntu/Components/Extras/Printers/enums.h 1970-01-01 00:00:00 +0000
+++ modules/Ubuntu/Components/Extras/Printers/enums.h 2017-02-24 12:29:37 +0000
@@ -0,0 +1,106 @@
1/*
2 * Copyright (C) 2017 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#ifndef USC_PRINTERS_ENUMS_H
18#define USC_PRINTERS_ENUMS_H
19
20#include "printers_global.h"
21
22#include <QtCore/QObject>
23
24class PRINTERS_DECL_EXPORT PrinterEnum
25{
26 Q_GADGET
27
28public:
29 enum class AccessControl
30 {
31 AccessAllow = 0,
32 AccessDeny,
33 };
34 Q_ENUM(AccessControl)
35
36 enum class ColorModelType
37 {
38 GrayType = 0,
39 ColorType,
40 UnknownType,
41 };
42 Q_ENUM(ColorModelType)
43
44 enum class DuplexMode
45 {
46 DuplexNone = 0,
47 DuplexLongSide,
48 DuplexShortSide,
49 };
50 Q_ENUM(DuplexMode)
51
52 enum class ErrorPolicy
53 {
54 RetryOnError = 0,
55 AbortOnError,
56 StopPrinterOnError,
57 RetryCurrentOnError,
58 };
59 Q_ENUM(ErrorPolicy)
60
61 // Match enums from ipp_jstate_t
62 enum class JobState
63 {
64 Pending = 3,
65 Held,
66 Processing,
67 Stopped,
68 Canceled,
69 Aborted,
70 Complete,
71 };
72 Q_ENUM(JobState)
73
74 enum class OperationPolicy
75 {
76 DefaultOperation = 0,
77 AuthenticatedOperation,
78 };
79 Q_ENUM(OperationPolicy)
80
81 enum class PrintRange
82 {
83 AllPages = 0,
84 PageRange,
85 };
86 Q_ENUM(PrintRange)
87
88 enum class State
89 {
90 IdleState = 0,
91 ActiveState,
92 AbortedState,
93 ErrorState,
94 };
95 Q_ENUM(State)
96
97 enum class PrinterType
98 {
99 ProxyType = 0, // Represents a printer not yet loaded.
100 CupsType,
101 PdfType,
102 };
103 Q_ENUM(PrinterType)
104};
105
106#endif // USC_PRINTERS_ENUMS_H
0107
=== added file 'modules/Ubuntu/Components/Extras/Printers/i18n.cpp'
--- modules/Ubuntu/Components/Extras/Printers/i18n.cpp 1970-01-01 00:00:00 +0000
+++ modules/Ubuntu/Components/Extras/Printers/i18n.cpp 2017-02-24 12:29:37 +0000
@@ -0,0 +1,42 @@
1/*
2 * Copyright (C) 2014, 2017 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by: Ken VanDine <ken.vandine@canonical.com>
17 * Andrew Hayzen <andrew.hayzen@canonical.com>
18 */
19
20#define NO_TR_OVERRIDE
21#include "i18n.h"
22
23#include <libintl.h>
24
25const char *thisDomain = "";
26
27void initTr(const char *domain, const char *localeDir)
28{
29 // Don't bind the domain or set textdomain as it changes the Apps domain
30 // as well. Instead store the domain and use it in the lookups
31 Q_UNUSED(localeDir);
32
33 thisDomain = domain;
34}
35
36QString __(const char *text, const char *domain)
37{
38 Q_UNUSED(domain);
39
40 // Use the stored domain
41 return QString::fromUtf8(dgettext(thisDomain, text));
42}
043
=== added file 'modules/Ubuntu/Components/Extras/Printers/i18n.h'
--- modules/Ubuntu/Components/Extras/Printers/i18n.h 1970-01-01 00:00:00 +0000
+++ modules/Ubuntu/Components/Extras/Printers/i18n.h 2017-02-24 12:29:37 +0000
@@ -0,0 +1,29 @@
1/*
2 * Copyright (C) 2014, 2017 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by: Ken VanDine <ken.vandine@canonical.com>
17 * Andrew Hayzen <andrew.hayzen@canonical.com>
18 */
19
20#ifndef I18N_H
21#define I18N_H
22
23#include <QtCore/QString>
24
25void initTr(const char *domain, const char *localeDir);
26QString __(const char *text, const char *domain = 0);
27
28#endif // I18N_H
29
030
=== added directory 'modules/Ubuntu/Components/Extras/Printers/models'
=== added file 'modules/Ubuntu/Components/Extras/Printers/models/drivermodel.cpp'
--- modules/Ubuntu/Components/Extras/Printers/models/drivermodel.cpp 1970-01-01 00:00:00 +0000
+++ modules/Ubuntu/Components/Extras/Printers/models/drivermodel.cpp 2017-02-24 12:29:37 +0000
@@ -0,0 +1,175 @@
1/*
2 * Copyright (C) 2017 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include "backend/backend_cups.h"
18#include "models/drivermodel.h"
19
20#include <QDebug>
21#include <QtConcurrent>
22
23DriverModel::DriverModel(PrinterBackend *backend, QObject *parent)
24 : QAbstractListModel(parent)
25 , m_backend(backend)
26{
27 connect(m_backend, SIGNAL(printerDriversLoaded(const QList<PrinterDriver>&)),
28 this, SLOT(printerDriversLoaded(const QList<PrinterDriver>&)));
29
30 QObject::connect(&m_watcher,
31 &QFutureWatcher<PrinterDriver>::finished,
32 this,
33 &DriverModel::filterFinished);
34
35}
36
37DriverModel::~DriverModel()
38{
39 cancel();
40}
41
42int DriverModel::rowCount(const QModelIndex &parent) const
43{
44 Q_UNUSED(parent);
45 return m_drivers.size();
46}
47
48int DriverModel::count() const
49{
50 return rowCount();
51}
52
53QVariant DriverModel::data(const QModelIndex &index, int role) const
54{
55 QVariant ret;
56
57 if ((0 <= index.row()) && (index.row() < m_drivers.size())) {
58
59 auto driver = m_drivers[index.row()];
60
61 switch (role) {
62 case Qt::DisplayRole:
63 ret = driver.toString();
64 break;
65 case NameRole:
66 ret = driver.name;
67 break;
68 case DeviceIdRole:
69 ret = driver.deviceId;
70 break;
71 case LanguageRole:
72 ret = driver.language;
73 break;
74 case MakeModelRole:
75 ret = driver.makeModel;
76 break;
77 }
78 }
79
80 return ret;
81}
82
83QHash<int, QByteArray> DriverModel::roleNames() const
84{
85 static QHash<int,QByteArray> names;
86
87 if (Q_UNLIKELY(names.empty())) {
88 names[Qt::DisplayRole] = "displayName";
89 names[NameRole] = "name";
90 names[DeviceIdRole] = "deviceId";
91 names[LanguageRole] = "language";
92 names[MakeModelRole] = "makeModel";
93 }
94
95 return names;
96}
97
98void DriverModel::setFilter(const QString& pattern)
99{
100 QList<QByteArray> needles;
101 Q_FOREACH(const QString patternPart, pattern.toLower().split(" ")) {
102 needles.append(patternPart.toUtf8());
103 }
104 QList<PrinterDriver> list;
105
106 if (m_watcher.isRunning())
107 m_watcher.cancel();
108
109 if (pattern.isEmpty()) {
110 setModel(m_originalDrivers);
111 m_filter = pattern;
112 return;
113 }
114
115 if (!m_filter.isEmpty() && !m_drivers.isEmpty() &&
116 pattern.startsWith(m_filter))
117 list = m_drivers; // search in the smaller list
118 else
119 list = m_originalDrivers; //search in the whole list
120
121 m_filter = pattern;
122
123 QFuture<PrinterDriver> future(QtConcurrent::filtered(list,
124 [needles] (const PrinterDriver &driver) {
125 QByteArray haystack = driver.makeModel.toLower();
126 Q_FOREACH(const QByteArray needle, needles) {
127 if (!haystack.contains(needle)) {
128 return false;
129 }
130 }
131 return true;
132 }
133 )
134 );
135
136 Q_EMIT filterBegin();
137
138 m_watcher.setFuture(future);
139}
140
141QString DriverModel::filter() const
142{
143 return m_filter;
144}
145
146void DriverModel::filterFinished()
147{
148 setModel(m_watcher.future().results());
149}
150
151void DriverModel::load()
152{
153 m_backend->requestPrinterDrivers();
154}
155
156void DriverModel::cancel()
157{
158 if (m_watcher.isRunning())
159 m_watcher.cancel();
160}
161
162void DriverModel::printerDriversLoaded(const QList<PrinterDriver> &drivers)
163{
164 m_originalDrivers = drivers;
165 setModel(m_originalDrivers);
166}
167
168void DriverModel::setModel(const QList<PrinterDriver> &drivers)
169{
170 beginResetModel();
171 m_drivers = drivers;
172 endResetModel();
173
174 Q_EMIT filterComplete();
175}
0176
=== added file 'modules/Ubuntu/Components/Extras/Printers/models/drivermodel.h'
--- modules/Ubuntu/Components/Extras/Printers/models/drivermodel.h 1970-01-01 00:00:00 +0000
+++ modules/Ubuntu/Components/Extras/Printers/models/drivermodel.h 2017-02-24 12:29:37 +0000
@@ -0,0 +1,82 @@
1/*
2 * Copyright (C) 2017 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#ifndef USC_PRINTER_DRIVERMODEL_H
18#define USC_PRINTER_DRIVERMODEL_H
19
20#include "printers_global.h"
21
22#include "structs.h"
23
24#include <QAbstractListModel>
25#include <QFutureWatcher>
26#include <QModelIndex>
27#include <QObject>
28#include <QVariant>
29
30class PRINTERS_DECL_EXPORT DriverModel : public QAbstractListModel
31{
32 Q_OBJECT
33 Q_PROPERTY(int count READ count NOTIFY countChanged)
34public:
35 explicit DriverModel(PrinterBackend *backend, QObject *parent = Q_NULLPTR);
36 ~DriverModel();
37
38 enum Roles
39 {
40 // Qt::DisplayRole holds driver name
41 NameRole = Qt::UserRole,
42 DeviceIdRole,
43 LanguageRole,
44 MakeModelRole,
45 LastRole = MakeModelRole,
46 };
47
48 virtual int rowCount(const QModelIndex &parent = QModelIndex()) const override;
49 virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
50 virtual QHash<int, QByteArray> roleNames() const override;
51
52 int count() const;
53
54 QString filter() const;
55 void setFilter(const QString& pattern);
56
57public Q_SLOTS:
58 // Start loading the model.
59 void load();
60
61 // Cancel loading of the model.
62 void cancel();
63
64private Q_SLOTS:
65 void printerDriversLoaded(const QList<PrinterDriver> &drivers);
66 void filterFinished();
67
68Q_SIGNALS:
69 void countChanged();
70 void filterBegin();
71 void filterComplete();
72
73private:
74 void setModel(const QList<PrinterDriver> &drivers);
75 PrinterBackend *m_backend;
76 QList<PrinterDriver> m_drivers;
77 QList<PrinterDriver> m_originalDrivers;
78 QString m_filter;
79 QFutureWatcher<PrinterDriver> m_watcher;
80};
81
82#endif // USC_PRINTER_DRIVERMODEL_H
083
=== added file 'modules/Ubuntu/Components/Extras/Printers/models/jobmodel.cpp'
--- modules/Ubuntu/Components/Extras/Printers/models/jobmodel.cpp 1970-01-01 00:00:00 +0000
+++ modules/Ubuntu/Components/Extras/Printers/models/jobmodel.cpp 2017-02-24 12:29:37 +0000
@@ -0,0 +1,390 @@
1/*
2 * Copyright (C) 2017 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include "utils.h"
18
19#include "backend/backend_cups.h"
20
21#include "models/jobmodel.h"
22
23#include <QDebug>
24
25JobModel::JobModel(QObject *parent) : QAbstractListModel(parent)
26{
27}
28
29JobModel::JobModel(PrinterBackend *backend,
30 QObject *parent)
31 : QAbstractListModel(parent)
32 , m_backend(backend)
33{
34 update();
35
36 QObject::connect(m_backend, &PrinterBackend::jobCreated,
37 this, &JobModel::jobSignalCatchAll);
38 QObject::connect(m_backend, &PrinterBackend::jobState,
39 this, &JobModel::jobSignalCatchAll);
40 QObject::connect(m_backend, &PrinterBackend::jobCompleted,
41 this, &JobModel::jobSignalCatchAll);
42}
43
44JobModel::~JobModel()
45{
46}
47
48void JobModel::jobSignalCatchAll(
49 const QString &text, const QString &printer_uri,
50 const QString &printer_name, uint printer_state,
51 const QString &printer_state_reasons, bool printer_is_accepting_jobs,
52 uint job_id, uint job_state, const QString &job_state_reasons,
53 const QString &job_name, uint job_impressions_completed)
54{
55 Q_UNUSED(text);
56 Q_UNUSED(printer_uri);
57 Q_UNUSED(printer_name);
58 Q_UNUSED(printer_state);
59 Q_UNUSED(printer_state_reasons);
60 Q_UNUSED(printer_is_accepting_jobs);
61 Q_UNUSED(job_id);
62 Q_UNUSED(job_state);
63 Q_UNUSED(job_state_reasons);
64 Q_UNUSED(job_name);
65
66 auto job = getJobById(job_id);
67 if (job)
68 job->setImpressionsCompleted(job_impressions_completed);
69
70 update();
71}
72
73void JobModel::update()
74{
75 // Store the old count and get the new printers
76 int oldCount = m_jobs.size();
77 QList<QSharedPointer<PrinterJob>> newJobs = m_backend->printerGetJobs();
78
79 // Go through the old model
80 for (int i=0; i < m_jobs.count(); i++) {
81 // Determine if the old printer exists in the new model
82 bool exists = false;
83
84 Q_FOREACH(QSharedPointer<PrinterJob> p, newJobs) {
85 if (p->jobId() == m_jobs.at(i)->jobId()) {
86 exists = true;
87
88 // Ensure the other properties of the job are up to date
89 if (!m_jobs.at(i)->deepCompare(p)) {
90 m_jobs.at(i)->updateFrom(p);
91
92 Q_EMIT dataChanged(index(i), index(i));
93 }
94
95 break;
96 }
97 }
98
99 // If it doesn't exist then remove it from the old model
100 if (!exists) {
101 beginRemoveRows(QModelIndex(), i, i);
102 QSharedPointer<PrinterJob> p = m_jobs.takeAt(i);
103 endRemoveRows();
104
105 i--; // as we have removed an item decrement
106 }
107 }
108
109 // Go through the new model
110 for (int i=0; i < newJobs.count(); i++) {
111 // Determine if the new printer exists in the old model
112 bool exists = false;
113 int j;
114
115 for (j=0; j < m_jobs.count(); j++) {
116 if (m_jobs.at(j)->jobId() == newJobs.at(i)->jobId()) {
117 exists = true;
118 break;
119 }
120 }
121
122 if (exists) {
123 if (j == i) { // New printer exists and in correct position
124 continue;
125 } else {
126 // New printer does exist but needs to be moved in old model
127 beginMoveRows(QModelIndex(), j, 1, QModelIndex(), i);
128 m_jobs.move(j, i);
129 endMoveRows();
130 }
131 } else {
132 // New printer does not exist insert into model
133 beginInsertRows(QModelIndex(), i, i);
134 m_jobs.insert(i, newJobs.at(i));
135 endInsertRows();
136 }
137 }
138
139 if (oldCount != m_jobs.size()) {
140 Q_EMIT countChanged();
141 }
142}
143
144int JobModel::rowCount(const QModelIndex &parent) const
145{
146 Q_UNUSED(parent);
147 return m_jobs.size();
148}
149
150int JobModel::count() const
151{
152 return rowCount();
153}
154
155QVariant JobModel::data(const QModelIndex &index, int role) const
156{
157 QVariant ret;
158
159 if ((0<=index.row()) && (index.row()<m_jobs.size())) {
160
161 auto job = m_jobs[index.row()];
162
163 switch (role) {
164 case CollateRole:
165 ret = job->collate();
166 break;
167 case ColorModelRole: {
168 if (job->printer()) {
169 ret = job->printer()->supportedColorModels().at(job->colorModel()).text;
170 } else {
171 qWarning() << "Printer is undefined, no colorModel";
172 ret = "";
173 }
174 break;
175 }
176 case CompletedTimeRole:
177 ret = job->completedTime().toString(QLocale::system().dateTimeFormat());
178 break;
179 case CopiesRole:
180 ret = job->copies();
181 break;
182 case CreationTimeRole:
183 ret = job->creationTime().toString(QLocale::system().dateTimeFormat());
184 break;
185 case DuplexRole: {
186 if (job->printer()) {
187 ret = job->printer()->supportedDuplexStrings().at(job->duplexMode());
188 } else {
189 qWarning() << "Printer is undefined, no duplexMode";
190 ret = "";
191 }
192 break;
193 }
194 case IdRole:
195 ret = job->jobId();
196 break;
197 case ImpressionsCompletedRole:
198 ret = job->impressionsCompleted();
199 break;
200 case LandscapeRole:
201 ret = job->landscape();
202 break;
203 case MessagesRole:
204 ret = job->messages();
205 break;
206 case PrinterNameRole:
207 ret = job->printerName();
208 break;
209 case PrintRangeRole:
210 ret = job->printRange();
211 break;
212 case PrintRangeModeRole:
213 ret = QVariant::fromValue<PrinterEnum::PrintRange>(job->printRangeMode());
214 break;
215 case ProcessingTimeRole:
216 ret = job->processingTime().toString(QLocale::system().dateTimeFormat());
217 break;
218 case QualityRole: {
219 if (job->printer()) {
220 ret = job->printer()->supportedPrintQualities().at(job->quality()).text;
221 } else {
222 qWarning() << "Printer is undefined, no quality";
223 ret = "";
224 }
225 break;
226 }
227 case ReverseRole:
228 ret = job->reverse();
229 break;
230 case SizeRole:
231 ret = job->size();
232 break;
233 case StateRole:
234 // TODO: improve, for now have a switch
235 switch (job->state()) {
236 case PrinterEnum::JobState::Aborted:
237 ret = "Aborted";
238 break;
239 case PrinterEnum::JobState::Canceled:
240 ret = "Canceled";
241 break;
242 case PrinterEnum::JobState::Complete:
243 ret = "Compelete";
244 break;
245 case PrinterEnum::JobState::Held:
246 ret = "Held";
247 break;
248 case PrinterEnum::JobState::Pending:
249 ret = "Pending";
250 break;
251 case PrinterEnum::JobState::Processing:
252 ret = "Processing";
253 break;
254 case PrinterEnum::JobState::Stopped:
255 ret = "Stopped";
256 break;
257 }
258 break;
259 case Qt::DisplayRole:
260 case TitleRole:
261 ret = job->title();
262 break;
263 case UserRole:
264 ret = job->user();
265 break;
266 }
267 }
268
269 return ret;
270}
271
272QHash<int, QByteArray> JobModel::roleNames() const
273{
274 static QHash<int,QByteArray> names;
275
276 if (Q_UNLIKELY(names.empty())) {
277 names[Qt::DisplayRole] = "displayName";
278 names[IdRole] = "id";
279 names[CollateRole] = "collate";
280 names[ColorModelRole] = "colorModel";
281 names[CompletedTimeRole] = "completedTime";
282 names[CopiesRole] = "copies";
283 names[CreationTimeRole] = "creationTime";
284 names[DuplexRole] = "duplexMode";
285 names[ImpressionsCompletedRole] = "impressionsCompleted";
286 names[LandscapeRole] = "landscape";
287 names[MessagesRole] = "messages";
288 names[PrinterNameRole] = "printerName";
289 names[PrintRangeRole] = "printRange";
290 names[PrintRangeModeRole] = "printRangeMode";
291 names[ProcessingTimeRole] = "processingTime";
292 names[QualityRole] = "quality";
293 names[ReverseRole] = "reverse";
294 names[SizeRole] = "size";
295 names[StateRole] = "state";
296 names[TitleRole] = "title";
297 names[UserRole] = "user";
298 names[LastStateMessageRole] = "lastStateMessage";
299 }
300
301 return names;
302}
303
304QVariantMap JobModel::get(const int row) const
305{
306 QHashIterator<int, QByteArray> iterator(roleNames());
307 QVariantMap result;
308 QModelIndex modelIndex = index(row, 0);
309
310 while (iterator.hasNext()) {
311 iterator.next();
312 result[iterator.value()] = modelIndex.data(iterator.key());
313 }
314
315 return result;
316}
317
318QSharedPointer<PrinterJob> JobModel::getJobById(const int &id)
319{
320 Q_FOREACH(auto job, m_jobs) {
321 if (job->jobId() == id) {
322 return job;
323 }
324 }
325 return QSharedPointer<PrinterJob>(Q_NULLPTR);
326}
327
328
329JobFilter::JobFilter(QObject *parent) : QSortFilterProxyModel(parent)
330{
331 connect(this, SIGNAL(sourceModelChanged()), SLOT(onSourceModelChanged()));
332}
333
334JobFilter::~JobFilter()
335{
336}
337
338QVariantMap JobFilter::get(const int row) const
339{
340 QHashIterator<int, QByteArray> iterator(roleNames());
341 QVariantMap result;
342 QModelIndex modelIndex = index(row, 0);
343
344 while (iterator.hasNext()) {
345 iterator.next();
346 result[iterator.value()] = modelIndex.data(iterator.key());
347 }
348
349 return result;
350}
351
352void JobFilter::onSourceModelChanged()
353{
354 connect((JobModel*) sourceModel(),
355 SIGNAL(countChanged()),
356 this,
357 SIGNAL(countChanged()));
358}
359
360void JobFilter::onSourceModelCountChanged()
361{
362 Q_EMIT countChanged();
363}
364
365int JobFilter::count() const
366{
367 return rowCount();
368}
369
370void JobFilter::filterOnPrinterName(const QString &name)
371{
372 m_printerName = name;
373 m_printerNameFilterEnabled = true;
374 invalidate();
375}
376
377bool JobFilter::filterAcceptsRow(int sourceRow,
378 const QModelIndex &sourceParent) const
379{
380 bool accepts = true;
381 QModelIndex childIndex = sourceModel()->index(sourceRow, 0, sourceParent);
382
383 if (accepts && m_printerNameFilterEnabled) {
384 QString printerName = childIndex.model()->data(
385 childIndex, JobModel::PrinterNameRole).toString();
386 accepts = m_printerName == printerName;
387 }
388
389 return accepts;
390}
0391
=== added file 'modules/Ubuntu/Components/Extras/Printers/models/jobmodel.h'
--- modules/Ubuntu/Components/Extras/Printers/models/jobmodel.h 1970-01-01 00:00:00 +0000
+++ modules/Ubuntu/Components/Extras/Printers/models/jobmodel.h 2017-02-24 12:29:37 +0000
@@ -0,0 +1,125 @@
1/*
2 * Copyright (C) 2017 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#ifndef USC_JOB_MODEL_H
18#define USC_JOB_MODEL_H
19
20#include "printers_global.h"
21#include "backend/backend.h"
22#include "printer/printerjob.h"
23
24#include <QAbstractListModel>
25#include <QByteArray>
26#include <QModelIndex>
27#include <QObject>
28#include <QSharedPointer>
29#include <QSortFilterProxyModel>
30#include <QTimer>
31#include <QVariant>
32
33class PRINTERS_DECL_EXPORT JobModel : public QAbstractListModel
34{
35 Q_OBJECT
36
37 Q_PROPERTY(int count READ count NOTIFY countChanged)
38public:
39 explicit JobModel(QObject *parent = Q_NULLPTR);
40 explicit JobModel(PrinterBackend *backend,
41 QObject *parent = Q_NULLPTR);
42 ~JobModel();
43
44 enum Roles
45 {
46 // Qt::DisplayRole holds job title
47 IdRole = Qt::UserRole,
48 CollateRole,
49 ColorModelRole,
50 CompletedTimeRole,
51 CopiesRole,
52 CreationTimeRole,
53 DuplexRole,
54 ImpressionsCompletedRole,
55 LandscapeRole,
56 MessagesRole,
57 PrinterNameRole,
58 PrintRangeRole,
59 PrintRangeModeRole,
60 ProcessingTimeRole,
61 QualityRole,
62 ReverseRole,
63 SizeRole,
64 StateRole,
65 TitleRole,
66 UserRole,
67 LastStateMessageRole,
68 LastRole = LastStateMessageRole,
69 };
70
71 virtual int rowCount(const QModelIndex &parent = QModelIndex()) const override;
72 virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
73 virtual QHash<int, QByteArray> roleNames() const override;
74
75 int count() const;
76
77 Q_INVOKABLE QVariantMap get(const int row) const;
78 QSharedPointer<PrinterJob> getJobById(const int &id);
79private:
80 PrinterBackend *m_backend;
81
82 QList<QSharedPointer<PrinterJob>> m_jobs;
83private Q_SLOTS:
84 void update();
85 void jobSignalCatchAll(const QString &text, const QString &printer_uri,
86 const QString &printer_name, uint printer_state,
87 const QString &printer_state_reasons,
88 bool printer_is_accepting_jobs, uint job_id,
89 uint job_state, const QString &job_state_reasons,
90 const QString &job_name,
91 uint job_impressions_completed);
92
93Q_SIGNALS:
94 void countChanged();
95};
96
97class PRINTERS_DECL_EXPORT JobFilter : public QSortFilterProxyModel
98{
99 Q_OBJECT
100 Q_PROPERTY(int count READ count NOTIFY countChanged)
101public:
102 explicit JobFilter(QObject *parent = Q_NULLPTR);
103 ~JobFilter();
104
105 Q_INVOKABLE QVariantMap get(const int row) const;
106
107 void filterOnPrinterName(const QString &name);
108 int count() const;
109protected:
110 virtual bool filterAcceptsRow(
111 int sourceRow, const QModelIndex &sourceParent) const override;
112
113Q_SIGNALS:
114 void countChanged();
115
116private Q_SLOTS:
117 void onSourceModelChanged();
118 void onSourceModelCountChanged();
119
120private:
121 QString m_printerName = QString::null;
122 bool m_printerNameFilterEnabled = false;
123};
124
125#endif // USC_JOB_MODEL_H
0126
=== added file 'modules/Ubuntu/Components/Extras/Printers/models/printermodel.cpp'
--- modules/Ubuntu/Components/Extras/Printers/models/printermodel.cpp 1970-01-01 00:00:00 +0000
+++ modules/Ubuntu/Components/Extras/Printers/models/printermodel.cpp 2017-02-24 12:29:37 +0000
@@ -0,0 +1,503 @@
1/*
2 * Copyright (C) 2017 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include "backend/backend_cups.h"
18#include "backend/backend_pdf.h"
19#include "i18n.h"
20#include "models/jobmodel.h"
21#include "models/printermodel.h"
22#include "utils.h"
23
24#include <QDebug>
25
26PrinterModel::PrinterModel(PrinterBackend *backend, QObject *parent)
27 : QAbstractListModel(parent)
28 , m_backend(backend)
29{
30
31 QObject::connect(m_backend, &PrinterBackend::printerAdded,
32 this, &PrinterModel::printerAdded);
33 QObject::connect(m_backend, &PrinterBackend::printerModified,
34 &m_signalHandler, &PrinterSignalHandler::onPrinterModified);
35 QObject::connect(m_backend, &PrinterBackend::printerStateChanged,
36 &m_signalHandler, &PrinterSignalHandler::onPrinterModified);
37 QObject::connect(m_backend, &PrinterBackend::printerDeleted,
38 this, &PrinterModel::printerDeleted);
39
40 connect(&m_signalHandler, SIGNAL(printerModified(const QString&)),
41 this, SLOT(printerModified(const QString&)));
42 connect(m_backend, SIGNAL(printerLoaded(QSharedPointer<Printer>)),
43 this, SLOT(printerLoaded(QSharedPointer<Printer>)));
44
45 // Create printer proxies for every printerName.
46 Q_FOREACH(auto printerName, m_backend->availablePrinterNames()) {
47 auto p = QSharedPointer<Printer>(new Printer(new PrinterBackend(printerName)));
48 addPrinter(p, CountChangeSignal::Defer);
49 }
50
51 // Add a PDF printer.
52 auto pdfPrinter = QSharedPointer<Printer>(
53 new Printer(new PrinterPdfBackend(__("Create PDF")))
54 );
55 addPrinter(pdfPrinter, CountChangeSignal::Defer);
56
57 Q_EMIT countChanged();
58}
59
60PrinterModel::~PrinterModel()
61{
62}
63
64void PrinterModel::printerLoaded(QSharedPointer<Printer> printer)
65{
66 // Find and possibly replace an old printer.
67 for (int i=0; i < m_printers.count(); i++) {
68 auto oldPrinter = m_printers.at(i);
69 if (printer->name() == oldPrinter->name()) {
70 if (!oldPrinter->deepCompare(printer)) {
71 updatePrinter(oldPrinter, printer);
72 }
73
74 // We're done.
75 return;
76 }
77 }
78
79 addPrinter(printer, CountChangeSignal::Emit);
80}
81
82void PrinterModel::printerModified(const QString &printerName)
83{
84 // These signals might be emitted of a now deleted printer.
85 if (getPrinterByName(printerName))
86 m_backend->requestPrinter(printerName);
87}
88
89void PrinterModel::printerAdded(
90 const QString &text, const QString &printerUri,
91 const QString &printerName, uint printerState,
92 const QString &printerStateReason, bool acceptingJobs)
93{
94 Q_UNUSED(text);
95 Q_UNUSED(printerUri);
96 Q_UNUSED(printerState);
97 Q_UNUSED(printerStateReason);
98 Q_UNUSED(acceptingJobs);
99
100 m_backend->requestPrinter(printerName);
101}
102
103void PrinterModel::printerDeleted(
104 const QString &text, const QString &printerUri,
105 const QString &printerName, uint printerState,
106 const QString &printerStateReason, bool acceptingJobs)
107{
108 Q_UNUSED(text);
109 Q_UNUSED(printerUri);
110 Q_UNUSED(printerState);
111 Q_UNUSED(printerStateReason);
112 Q_UNUSED(acceptingJobs);
113
114 auto printer = getPrinterByName(printerName);
115 if (printer) {
116 removePrinter(printer, CountChangeSignal::Emit);
117 }
118}
119
120QSharedPointer<Printer> PrinterModel::getPrinterByName(const QString &printerName)
121{
122 Q_FOREACH(auto p, m_printers) {
123 if (p->name() == printerName)
124 return p;
125 }
126 return QSharedPointer<Printer>(Q_NULLPTR);
127}
128
129void PrinterModel::movePrinter(const int &from, const int &to)
130{
131 int size = m_printers.size();
132 if (from < 0 || to < 0 || from >= size || to >= size) {
133 qWarning() << Q_FUNC_INFO << "Illegal move operation from"
134 << from << "to" << to << ". Size was" << size;
135 return;
136 }
137 if (!beginMoveRows(QModelIndex(), from, from, QModelIndex(), to)) {
138 qWarning() << Q_FUNC_INFO << "failed to move rows.";
139 return;
140 }
141 m_printers.move(from, to);
142 endMoveRows();
143}
144
145void PrinterModel::removePrinter(QSharedPointer<Printer> printer, const CountChangeSignal &notify)
146{
147 int idx = m_printers.indexOf(printer);
148 beginRemoveRows(QModelIndex(), idx, idx);
149 m_printers.removeAt(idx);
150 endRemoveRows();
151
152 if (notify == CountChangeSignal::Emit)
153 Q_EMIT countChanged();
154}
155
156void PrinterModel::updatePrinter(QSharedPointer<Printer> old,
157 QSharedPointer<Printer> newPrinter)
158{
159 int i = m_printers.indexOf(old);
160 QModelIndex idx = index(i);
161 old->updateFrom(newPrinter);
162 Q_EMIT dataChanged(idx, idx);
163}
164
165void PrinterModel::addPrinter(QSharedPointer<Printer> printer, const CountChangeSignal &notify)
166{
167 int i = m_printers.size();
168 beginInsertRows(QModelIndex(), i, i);
169 m_printers.append(printer);
170 endInsertRows();
171
172 if (notify == CountChangeSignal::Emit)
173 Q_EMIT countChanged();
174}
175
176int PrinterModel::rowCount(const QModelIndex &parent) const
177{
178 Q_UNUSED(parent);
179 return m_printers.size();
180}
181
182int PrinterModel::count() const
183{
184 return rowCount();
185}
186
187QVariant PrinterModel::data(const QModelIndex &index, int role) const
188{
189 QVariant ret;
190
191 if ((0<=index.row()) && (index.row()<m_printers.size())) {
192
193 auto printer = m_printers[index.row()];
194
195
196 /* If printer is a proxy (not loaded), determine if the requested role
197 is something async and that we need to request the data. */
198 if (printer->type() == PrinterEnum::PrinterType::ProxyType) {
199 switch (role) {
200 case Qt::DisplayRole:
201 case NameRole:
202 case DefaultPrinterRole:
203 case PrinterRole:
204 case IsPdfRole:
205 case IsLoadedRole:
206 break; // All of these can be inferred from the name (lazily).
207 default:
208 m_backend->requestPrinter(printer->name());
209 }
210 }
211
212 switch (role) {
213 case NameRole:
214 case Qt::DisplayRole:
215 ret = printer->name();
216 break;
217 case ColorModelRole:
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches