Merge lp:~jonas-drange/ubuntu-ui-extras/add-cmake-extras into lp:ubuntu-ui-extras

Proposed by Jonas G. Drange
Status: Superseded
Proposed branch: lp:~jonas-drange/ubuntu-ui-extras/add-cmake-extras
Merge into: lp:ubuntu-ui-extras
Diff against target: 9817 lines (+9465/-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 (+350/-0)
modules/Ubuntu/Components/Extras/Printers/backend/backend.h (+223/-0)
modules/Ubuntu/Components/Extras/Printers/backend/backend_cups.cpp (+787/-0)
modules/Ubuntu/Components/Extras/Printers/backend/backend_cups.h (+152/-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 (+128/-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 (+141/-0)
modules/Ubuntu/Components/Extras/Printers/i18n.cpp (+44/-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 (+391/-0)
modules/Ubuntu/Components/Extras/Printers/models/jobmodel.h (+125/-0)
modules/Ubuntu/Components/Extras/Printers/models/printermodel.cpp (+533/-0)
modules/Ubuntu/Components/Extras/Printers/models/printermodel.h (+161/-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 (+358/-0)
modules/Ubuntu/Components/Extras/Printers/printer/printer.h (+109/-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 (+251/-0)
modules/Ubuntu/Components/Extras/Printers/printers/printers.h (+107/-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 (+103/-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 (+443/-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 (+117/-0)
tests/unittests/Printers/tst_printerjob.cpp (+207/-0)
tests/unittests/Printers/tst_printermodel.cpp (+161/-0)
tests/unittests/Printers/tst_printers.cpp (+156/-0)
tests/unittests/Printers/tst_signalhandler.cpp (+44/-0)
To merge this branch: bzr merge lp:~jonas-drange/ubuntu-ui-extras/add-cmake-extras
Reviewer Review Type Date Requested Status
Ubuntu Phablet Team Pending
Review via email: mp+317968@code.launchpad.net

Commit message

adds cmake extras, required by printer stuff

To post a comment you must log in.

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-22 12:44:26 +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-22 12:44:26 +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-22 12:44:26 +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-22 12:44:26 +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-22 12:44:26 +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-22 12:44:26 +0000
@@ -0,0 +1,350 @@
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::printerSetLocation(const QString &name,
103 const QString &location)
104{
105 Q_UNUSED(name);
106 Q_UNUSED(location);
107 return QString();
108}
109
110QString PrinterBackend::printerSetShared(const QString &name,
111 const bool shared)
112{
113 Q_UNUSED(name);
114 Q_UNUSED(shared);
115 return QString();
116}
117
118QString PrinterBackend::printerSetJobSheets(const QString &name,
119 const QString &start,
120 const QString &end)
121{
122 Q_UNUSED(name);
123 Q_UNUSED(start);
124 Q_UNUSED(end);
125 return QString();
126}
127
128QString PrinterBackend::printerSetErrorPolicy(const QString &name,
129 const PrinterEnum::ErrorPolicy &policy)
130{
131 Q_UNUSED(name);
132 Q_UNUSED(policy);
133 return QString();
134}
135
136
137QString PrinterBackend::printerSetOpPolicy(const QString &name,
138 const PrinterEnum::OperationPolicy &policy)
139{
140 Q_UNUSED(name);
141 Q_UNUSED(policy);
142 return QString();
143}
144
145QString PrinterBackend::printerSetUsersAllowed(const QString &name,
146 const QStringList &users)
147{
148 Q_UNUSED(name);
149 Q_UNUSED(users);
150 return QString();
151}
152
153QString PrinterBackend::printerSetUsersDenied(const QString &name,
154 const QStringList &users)
155{
156 Q_UNUSED(name);
157 Q_UNUSED(users);
158 return QString();
159}
160
161QString PrinterBackend::printerAddOptionDefault(const QString &name,
162 const QString &option,
163 const QStringList &values)
164{
165 Q_UNUSED(name);
166 Q_UNUSED(option);
167 Q_UNUSED(values);
168 return QString();
169}
170
171QString PrinterBackend::printerDeleteOptionDefault(const QString &name,
172 const QString &value)
173{
174 Q_UNUSED(name);
175 Q_UNUSED(value);
176 return QString();
177}
178
179QString PrinterBackend::printerAddOption(const QString &name,
180 const QString &option,
181 const QStringList &values)
182{
183 Q_UNUSED(name);
184 Q_UNUSED(option);
185 Q_UNUSED(values);
186 return QString();
187}
188
189QVariant PrinterBackend::printerGetOption(const QString &name,
190 const QString &option) const
191{
192 Q_UNUSED(name);
193 Q_UNUSED(option);
194 return QVariant();
195}
196
197QMap<QString, QVariant> PrinterBackend::printerGetOptions(
198 const QString &name, const QStringList &options) const
199{
200 Q_UNUSED(name);
201 Q_UNUSED(options);
202 return QMap<QString, QVariant>();
203}
204
205// FIXME: maybe have a PrinterDest iface that has a CupsDest impl?
206cups_dest_t* PrinterBackend::makeDest(const QString &name,
207 const PrinterJob *options)
208{
209 Q_UNUSED(name);
210 Q_UNUSED(options);
211 return Q_NULLPTR;
212}
213
214void PrinterBackend::cancelJob(const QString &name, const int jobId)
215{
216 Q_UNUSED(jobId);
217 Q_UNUSED(name);
218}
219
220int PrinterBackend::printFileToDest(const QString &filepath,
221 const QString &title,
222 const cups_dest_t *dest)
223{
224 Q_UNUSED(filepath);
225 Q_UNUSED(title);
226 Q_UNUSED(dest);
227 return -1;
228}
229
230QList<QSharedPointer<PrinterJob>> PrinterBackend::printerGetJobs()
231{
232 return QList<QSharedPointer<PrinterJob>>{};
233}
234
235QMap<QString, QVariant> PrinterBackend::printerGetJobAttributes(
236 const QString &name, const int jobId)
237{
238 Q_UNUSED(name);
239 Q_UNUSED(jobId);
240 return QMap<QString, QVariant>();
241}
242
243QString PrinterBackend::printerName() const
244{
245 return m_printerName;
246}
247
248QString PrinterBackend::description() const
249{
250 return QString();
251}
252
253QString PrinterBackend::location() const
254{
255 return QString();
256}
257
258QString PrinterBackend::makeAndModel() const
259{
260 return QString();
261}
262
263PrinterEnum::State PrinterBackend::state() const
264{
265 return PrinterEnum::State::IdleState;
266}
267
268QList<QPageSize> PrinterBackend::supportedPageSizes() const
269{
270 return QList<QPageSize>();
271}
272
273QPageSize PrinterBackend::defaultPageSize() const
274{
275 return QPageSize();
276}
277
278bool PrinterBackend::supportsCustomPageSizes() const
279{
280 return false;
281}
282
283QPageSize PrinterBackend::minimumPhysicalPageSize() const
284{
285 return QPageSize();
286}
287
288QPageSize PrinterBackend::maximumPhysicalPageSize() const
289{
290 return QPageSize();
291}
292
293QList<int> PrinterBackend::supportedResolutions() const
294{
295 return QList<int>();
296}
297
298PrinterEnum::DuplexMode PrinterBackend::defaultDuplexMode() const
299{
300 return PrinterEnum::DuplexMode::DuplexNone;
301}
302
303QList<PrinterEnum::DuplexMode> PrinterBackend::supportedDuplexModes() const
304{
305 return QList<PrinterEnum::DuplexMode>();
306}
307
308QList<QSharedPointer<Printer>> PrinterBackend::availablePrinters()
309{
310 return QList<QSharedPointer<Printer>>();
311}
312
313QStringList PrinterBackend::availablePrinterNames()
314{
315 return QStringList();
316}
317
318QSharedPointer<Printer> PrinterBackend::getPrinter(const QString &printerName)
319{
320 Q_UNUSED(printerName);
321 return QSharedPointer<Printer>(Q_NULLPTR);
322}
323
324QString PrinterBackend::defaultPrinterName()
325{
326 return QString();
327}
328
329void PrinterBackend::requestPrinterDrivers()
330{
331}
332
333void PrinterBackend::requestPrinter(const QString &printerName)
334{
335 Q_UNUSED(printerName);
336}
337
338PrinterEnum::PrinterType PrinterBackend::type() const
339{
340 return m_type;
341}
342
343void PrinterBackend::setPrinterNameInternal(const QString &printerName)
344{
345 m_printerName = printerName;
346}
347
348void PrinterBackend::refresh()
349{
350}
0351
=== 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-22 12:44:26 +0000
@@ -0,0 +1,223 @@
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 printerSetLocation(const QString &name,
69 const QString &location);
70 virtual QString printerSetShared(const QString &name,
71 const bool shared);
72 virtual QString printerSetJobSheets(const QString &name,
73 const QString &start,
74 const QString &end);
75 virtual QString printerSetErrorPolicy(const QString &name,
76 const PrinterEnum::ErrorPolicy &policy);
77
78 virtual QString printerSetOpPolicy(const QString &name,
79 const PrinterEnum::OperationPolicy &policy);
80 virtual QString printerSetUsersAllowed(const QString &name,
81 const QStringList &users);
82 virtual QString printerSetUsersDenied(const QString &name,
83 const QStringList &users);
84 virtual QString printerAddOptionDefault(const QString &name,
85 const QString &option,
86 const QStringList &values);
87 virtual QString printerDeleteOptionDefault(const QString &name,
88 const QString &value);
89 virtual QString printerAddOption(const QString &name,
90 const QString &option,
91 const QStringList &values);
92
93 virtual QVariant printerGetOption(const QString &name,
94 const QString &option) const;
95 virtual QMap<QString, QVariant> printerGetOptions(
96 const QString &name, const QStringList &options) const;
97 // FIXME: maybe have a PrinterDest iface that has a CupsDest impl?
98 virtual cups_dest_t* makeDest(const QString &name,
99 const PrinterJob *options);
100
101 virtual void cancelJob(const QString &name, const int jobId);
102 virtual int printFileToDest(const QString &filepath,
103 const QString &title,
104 const cups_dest_t *dest);
105 virtual QList<QSharedPointer<PrinterJob>> printerGetJobs();
106 virtual QMap<QString, QVariant> printerGetJobAttributes(
107 const QString &name, const int jobId);
108
109 virtual QString printerName() const;
110 virtual QString description() const;
111 virtual QString location() const;
112 virtual QString makeAndModel() const;
113
114 virtual PrinterEnum::State state() const;
115 virtual QList<QPageSize> supportedPageSizes() const;
116 virtual QPageSize defaultPageSize() const;
117 virtual bool supportsCustomPageSizes() const;
118
119 virtual QPageSize minimumPhysicalPageSize() const;
120 virtual QPageSize maximumPhysicalPageSize() const;
121 virtual QList<int> supportedResolutions() const;
122 virtual PrinterEnum::DuplexMode defaultDuplexMode() const;
123 virtual QList<PrinterEnum::DuplexMode> supportedDuplexModes() const;
124
125 virtual QList<QSharedPointer<Printer>> availablePrinters();
126 virtual QStringList availablePrinterNames();
127 virtual QSharedPointer<Printer> getPrinter(const QString &printerName);
128 virtual QString defaultPrinterName();
129
130 virtual void requestPrinterDrivers();
131 virtual void requestPrinter(const QString &printerName);
132
133 virtual PrinterEnum::PrinterType type() const;
134
135 virtual void setPrinterNameInternal(const QString &printerName);
136
137public Q_SLOTS:
138 virtual void refresh();
139
140Q_SIGNALS:
141 void printerDriversLoaded(const QList<PrinterDriver> &drivers);
142 void printerDriversFailedToLoad(const QString &errorMessage);
143
144 void printerLoaded(QSharedPointer<Printer> printers);
145
146 void jobCompleted(
147 const QString &text,
148 const QString &printerUri,
149 const QString &printerName,
150 uint printerState,
151 const QString &printerStateReason,
152 bool acceptingJobs,
153 uint jobId,
154 uint jobState,
155 const QString &jobStateReason,
156 const QString &job_name,
157 uint jobImpressionsCompleted
158 );
159 void jobCreated(
160 const QString &text,
161 const QString &printerUri,
162 const QString &printerName,
163 uint printerState,
164 const QString &printerStateReason,
165 bool acceptingJobs,
166 uint jobId,
167 uint jobState,
168 const QString &jobStateReason,
169 const QString &job_name,
170 uint jobImpressionsCompleted
171 );
172 void jobState(
173 const QString &text,
174 const QString &printerUri,
175 const QString &printerName,
176 uint printerState,
177 const QString &printerStateReason,
178 bool acceptingJobs,
179 uint jobId,
180 uint jobState,
181 const QString &jobStateReason,
182 const QString &job_name,
183 uint jobImpressionsCompleted
184 );
185 void printerAdded(
186 const QString &text,
187 const QString &printerUri,
188 const QString &printerName,
189 uint printerState,
190 const QString &printerStateReason,
191 bool acceptingJobs
192 );
193 void printerDeleted(
194 const QString &text,
195 const QString &printerUri,
196 const QString &printerName,
197 uint printerState,
198 const QString &printerStateReason,
199 bool acceptingJobs
200 );
201 void printerModified(
202 const QString &text,
203 const QString &printerUri,
204 const QString &printerName,
205 uint printerState,
206 const QString &printerStateReason,
207 bool acceptingJobs
208 );
209 void printerStateChanged(
210 const QString &text,
211 const QString &printerUri,
212 const QString &printerName,
213 uint printerState,
214 const QString &printerStateReason,
215 bool acceptingJobs
216 );
217
218protected:
219 QString m_printerName;
220 PrinterEnum::PrinterType m_type;
221};
222
223#endif // USC_PRINTERS_BACKEND_H
0224
=== 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-22 12:44:26 +0000
@@ -0,0 +1,787 @@
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::printerSetLocation(const QString &name,
191 const QString &location)
192{
193 Q_UNUSED(name);
194 Q_UNUSED(location);
195 return QString();
196}
197
198QString PrinterCupsBackend::printerSetShared(const QString &name,
199 const bool shared)
200{
201 Q_UNUSED(name);
202 Q_UNUSED(shared);
203 return QString();
204}
205
206QString PrinterCupsBackend::printerSetJobSheets(const QString &name,
207 const QString &start,
208 const QString &end)
209{
210 Q_UNUSED(name);
211 Q_UNUSED(start);
212 Q_UNUSED(end);
213 return QString();
214}
215
216QString PrinterCupsBackend::printerSetErrorPolicy(const QString &name,
217 const PrinterEnum::ErrorPolicy &policy)
218{
219 Q_UNUSED(name);
220 Q_UNUSED(policy);
221 return QString();
222}
223
224QString PrinterCupsBackend::printerSetOpPolicy(const QString &name,
225 const PrinterEnum::OperationPolicy &policy)
226{
227 Q_UNUSED(name);
228 Q_UNUSED(policy);
229 return QString();
230}
231
232QString PrinterCupsBackend::printerSetUsersAllowed(const QString &name,
233 const QStringList &users)
234{
235 Q_UNUSED(name);
236 Q_UNUSED(users);
237 return QString();
238}
239
240QString PrinterCupsBackend::printerSetUsersDenied(const QString &name,
241 const QStringList &users)
242{
243 Q_UNUSED(name);
244 Q_UNUSED(users);
245 return QString();
246}
247
248QString PrinterCupsBackend::printerAddOptionDefault(const QString &name,
249 const QString &option,
250 const QStringList &values)
251{
252 Q_UNUSED(name);
253 Q_UNUSED(option);
254 Q_UNUSED(values);
255 return QString();
256}
257
258QString PrinterCupsBackend::printerDeleteOptionDefault(const QString &name,
259 const QString &value)
260{
261 Q_UNUSED(name);
262 Q_UNUSED(value);
263 return QString();
264}
265
266QString PrinterCupsBackend::printerAddOption(const QString &name,
267 const QString &option,
268 const QStringList &values)
269{
270 if (!m_client->printerClassSetOption(name, option, values)) {
271 return m_client->getLastError();
272 }
273
274 return QString();
275}
276
277QVariant PrinterCupsBackend::printerGetOption(const QString &name,
278 const QString &option) const
279{
280 auto res = printerGetOptions(name, QStringList({option}));
281 return res[option];
282}
283
284QMap<QString, QVariant> PrinterCupsBackend::printerGetOptions(
285 const QString &name, const QStringList &options) const
286{
287 QMap<QString, QVariant> ret;
288
289 cups_dest_t *dest = getDest(name);
290 ppd_file_t* ppd = getPpd(name);
291
292 if (!dest || !ppd) {
293 return ret;
294 }
295
296 Q_FOREACH(const QString &option, options) {
297 if (option == QStringLiteral("DefaultColorModel")) {
298 ColorModel model;
299 ppd_option_t *ppdColorModel = ppdFindOption(ppd, "ColorModel");
300 if (ppdColorModel) {
301 ppd_choice_t* def = ppdFindChoice(ppdColorModel,
302 ppdColorModel->defchoice);
303 if (def) {
304 model = Utils::parsePpdColorModel(def->choice,
305 def->text,
306 "ColorModel");
307 }
308 }
309 ret[option] = QVariant::fromValue(model);
310 } else if (option == QStringLiteral("DefaultPrintQuality")) {
311 PrintQuality quality;
312 Q_FOREACH(const QString opt, m_knownQualityOptions) {
313 ppd_option_t *ppdQuality = ppdFindOption(ppd, opt.toUtf8());
314 if (ppdQuality) {
315 ppd_choice_t* def = ppdFindChoice(ppdQuality,
316 ppdQuality->defchoice);
317 if (def) {
318 quality = Utils::parsePpdPrintQuality(def->choice,
319 def->text, opt);
320 }
321 }
322 }
323 ret[option] = QVariant::fromValue(quality);
324 } else if (option == QStringLiteral("SupportedPrintQualities")) {
325 QList<PrintQuality> qualities;
326 Q_FOREACH(const QString &opt, m_knownQualityOptions) {
327 ppd_option_t *qualityOpt = ppdFindOption(ppd, opt.toUtf8());
328 if (qualityOpt) {
329 for (int i = 0; i < qualityOpt->num_choices; ++i) {
330 qualities.append(
331 Utils::parsePpdPrintQuality(
332 qualityOpt->choices[i].choice,
333 qualityOpt->choices[i].text,
334 opt
335 )
336 );
337 }
338 }
339 }
340 ret[option] = QVariant::fromValue(qualities);
341 } else if (option == QStringLiteral("SupportedColorModels")) {
342 QList<ColorModel> models;
343 ppd_option_t *colorModels = ppdFindOption(ppd, "ColorModel");
344 if (colorModels) {
345 for (int i = 0; i < colorModels->num_choices; ++i) {
346 models.append(
347 Utils::parsePpdColorModel(
348 colorModels->choices[i].choice,
349 colorModels->choices[i].text,
350 QStringLiteral("ColorModel")
351 )
352 );
353 }
354 }
355 ret[option] = QVariant::fromValue(models);
356 } else if (option == QStringLiteral("AcceptJobs")) {
357 // "true" if the destination is accepting new jobs, "false" if not.
358 QString res = cupsGetOption("printer-is-accepting-jobs",
359 dest->num_options, dest->options);
360 ret[option] = res.contains("true");
361 } else {
362 ppd_option_t *val = ppdFindOption(ppd, option.toUtf8());
363
364 if (val) {
365 qWarning() << "asking for" << option << "returns" << val->text;
366 } else {
367 qWarning() << "option" << option << "yielded no option";
368 }
369 }
370 }
371 return ret;
372}
373
374// FIXME: maybe have a PrinterDest iface that has a CupsDest impl?
375cups_dest_t* PrinterCupsBackend::makeDest(const QString &name,
376 const PrinterJob *options)
377{
378 // Get the cups dest
379 cups_dest_t *dest = getDest(name);
380
381 if (options->collate()) {
382 __CUPS_ADD_OPTION(dest, "Collate", "True");
383 } else {
384 __CUPS_ADD_OPTION(dest, "Collate", "False");
385 }
386
387 if (options->copies() > 1) {
388 __CUPS_ADD_OPTION(dest, "copies", QString::number(options->copies()).toLocal8Bit());
389 }
390
391 __CUPS_ADD_OPTION(dest, "ColorModel", options->getColorModel().name.toLocal8Bit());
392 __CUPS_ADD_OPTION(dest, "Duplex", Utils::duplexModeToPpdChoice(options->getDuplexMode()).toLocal8Bit());
393
394 if (options->landscape()) {
395 __CUPS_ADD_OPTION(dest, "landscape", "");
396 }
397
398 if (options->printRangeMode() == PrinterEnum::PrintRange::PageRange
399 && !options->printRange().isEmpty()) {
400 __CUPS_ADD_OPTION(dest, "page-ranges", options->printRange().toLocal8Bit());
401 }
402
403 PrintQuality quality = options->getPrintQuality();
404 __CUPS_ADD_OPTION(dest, quality.originalOption.toLocal8Bit(),
405 quality.name.toLocal8Bit());
406
407 if (options->reverse()) {
408 __CUPS_ADD_OPTION(dest, "OutputOrder", "Reverse");
409 } else {
410 __CUPS_ADD_OPTION(dest, "OutputOrder", "Normal");
411 }
412
413 // Always scale to fit the page for now
414 __CUPS_ADD_OPTION(dest, "fit-to-page", "True");
415
416 return dest;
417}
418
419void PrinterCupsBackend::cancelJob(const QString &name, const int jobId)
420{
421 int ret = cupsCancelJob(name.toLocal8Bit(), jobId);
422
423 if (!ret) {
424 qWarning() << "Failed to cancel job:" << jobId << "for" << name;
425 }
426}
427
428int PrinterCupsBackend::printFileToDest(const QString &filepath,
429 const QString &title,
430 const cups_dest_t *dest)
431{
432 qDebug() << "Printing:" << filepath << title << dest->name << dest->num_options;
433 return cupsPrintFile(dest->name,
434 filepath.toLocal8Bit(),
435 title.toLocal8Bit(),
436 dest->num_options,
437 dest->options);
438}
439
440
441QList<cups_job_t *> PrinterCupsBackend::getCupsJobs(const QString &name)
442{
443 QList<cups_job_t *> list;
444 cups_job_t *jobs;
445
446 // Get a list of the jobs that are 'mine' and only active ones
447 // https://www.cups.org/doc/api-cups.html#cupsGetJobs
448 int count;
449 if (name.isEmpty()) {
450 count = cupsGetJobs(&jobs, NULL, 1, CUPS_WHICHJOBS_ACTIVE);
451 } else {
452 count = cupsGetJobs(&jobs, name.toLocal8Bit(), 1, CUPS_WHICHJOBS_ACTIVE);
453 }
454
455 for (int i=0; i < count; i++) {
456 list.append(&jobs[i]);
457 }
458
459 return list;
460}
461
462QMap<QString, QVariant> PrinterCupsBackend::printerGetJobAttributes(
463 const QString &name, const int jobId)
464{
465 Q_UNUSED(name);
466 QMap<QString, QVariant> rawMap = m_client->printerGetJobAttributes(jobId);
467 QMap<QString, QVariant> map;
468
469 // Filter attributes to know values
470 // Do this here so we can use things such as m_knownQualityOptions
471
472 if (__CUPS_ATTR_EXISTS(rawMap, "Collate", bool)) {
473 map.insert("Collate", rawMap.value("Collate"));
474 } else {
475 map.insert("Collate", QVariant(true));
476 }
477
478 if (__CUPS_ATTR_EXISTS(rawMap, "copies", int)) {
479 map.insert("copies", rawMap.value("copies"));
480 } else {
481 map.insert("copies", QVariant(1));
482 }
483
484 if (__CUPS_ATTR_EXISTS(rawMap, "ColorModel", QString)) {
485 map.insert("ColorModel", rawMap.value("ColorModel"));
486 } else {
487 map.insert("ColorModel", QVariant(""));
488 }
489
490 if (__CUPS_ATTR_EXISTS(rawMap, "Duplex", QString)) {
491 map.insert("Duplex", rawMap.value("Duplex"));
492 } else {
493 map.insert("Duplex", QVariant(""));
494 }
495
496 if (__CUPS_ATTR_EXISTS(rawMap, "landscape", bool)) {
497 map.insert("landscape", rawMap.value("landscape"));
498 } else {
499 map.insert("landscape", QVariant(false));
500 }
501
502 if (__CUPS_ATTR_EXISTS(rawMap, "page-ranges", QList<QVariant>)) {
503 QList<QVariant> range = rawMap.value("page-ranges").toList();
504 QStringList rangeStrings;
505
506 Q_FOREACH(QVariant var, range) {
507 rangeStrings << var.toString();
508 }
509
510 map.insert("page-ranges", QVariant(rangeStrings));
511 } else {
512 map.insert("page-ranges", QVariant(QStringList()));
513 }
514
515 Q_FOREACH(QString qualityOption, m_knownQualityOptions) {
516 if (rawMap.contains(qualityOption)
517 && rawMap.value(qualityOption).canConvert<QString>()) {
518 map.insert("quality", rawMap.value(qualityOption).toString());
519 }
520 }
521
522 if (!map.contains("quality")) {
523 map.insert("quality", QVariant(""));
524 }
525
526 if (__CUPS_ATTR_EXISTS(rawMap, "OutputOrder", QString)) {
527 map.insert("OutputOrder", rawMap.value("OutputOrder"));
528 } else {
529 map.insert("OutputOrder", "Normal");
530 }
531
532 // Generate a list of messages
533 // TODO: for now just using job-printer-state-message, are there others?
534 QStringList messages;
535
536 if (__CUPS_ATTR_EXISTS(rawMap, "job-printer-state-message", QString)) {
537 messages << rawMap.value("job-printer-state-message").toString();
538 }
539
540 map.insert("messages", QVariant(messages));
541
542 return map;
543}
544
545
546QList<QSharedPointer<PrinterJob>> PrinterCupsBackend::printerGetJobs()
547{
548 auto jobs = getCupsJobs();
549 QList<QSharedPointer<PrinterJob>> list;
550
551 Q_FOREACH(auto job, jobs) {
552 auto newJob = QSharedPointer<PrinterJob>(
553 new PrinterJob(QString::fromUtf8(job->dest), this, job->id)
554 );
555
556 // Extract the times
557 QDateTime completedTime;
558 completedTime.setTimeZone(QTimeZone::systemTimeZone());
559 completedTime.setTime_t(job->completed_time);
560
561 QDateTime creationTime;
562 creationTime.setTimeZone(QTimeZone::systemTimeZone());
563 creationTime.setTime_t(job->creation_time);
564
565 QDateTime processingTime;
566 processingTime.setTimeZone(QTimeZone::systemTimeZone());
567 processingTime.setTime_t(job->processing_time);
568
569 // Load the information from the cups struct
570 newJob->setCompletedTime(completedTime);
571 newJob->setCreationTime(creationTime);
572 newJob->setProcessingTime(processingTime);
573 newJob->setSize(job->size);
574 newJob->setState(static_cast<PrinterEnum::JobState>(job->state));
575 newJob->setTitle(QString::fromLocal8Bit(job->title));
576 newJob->setUser(QString::fromLocal8Bit(job->user));
577
578 list.append(newJob);
579 }
580 if (!list.isEmpty())
581 cupsFreeJobs(list.size(), jobs.first());
582
583 return list;
584}
585
586QString PrinterCupsBackend::printerName() const
587{
588 return m_printerName;
589}
590
591QString PrinterCupsBackend::description() const
592{
593 return m_info.description();
594}
595
596QString PrinterCupsBackend::location() const
597{
598 return m_info.location();
599}
600
601QString PrinterCupsBackend::makeAndModel() const
602{
603 return m_info.makeAndModel();
604}
605
606PrinterEnum::State PrinterCupsBackend::state() const
607{
608 switch (m_info.state()) {
609 case QPrinter::Active:
610 return PrinterEnum::State::ActiveState;
611 case QPrinter::Aborted:
612 return PrinterEnum::State::AbortedState;
613 case QPrinter::Error:
614 return PrinterEnum::State::ErrorState;
615 case QPrinter::Idle:
616 default:
617 return PrinterEnum::State::IdleState;
618 }
619}
620
621QList<QPageSize> PrinterCupsBackend::supportedPageSizes() const
622{
623 return m_info.supportedPageSizes();
624}
625
626QPageSize PrinterCupsBackend::defaultPageSize() const
627{
628 return m_info.defaultPageSize();
629}
630
631bool PrinterCupsBackend::supportsCustomPageSizes() const
632{
633 return m_info.supportsCustomPageSizes();
634}
635
636QPageSize PrinterCupsBackend::minimumPhysicalPageSize() const
637{
638 return m_info.minimumPhysicalPageSize();
639}
640
641QPageSize PrinterCupsBackend::maximumPhysicalPageSize() const
642{
643 return m_info.maximumPhysicalPageSize();
644}
645
646QList<int> PrinterCupsBackend::supportedResolutions() const
647{
648 return m_info.supportedResolutions();
649}
650
651PrinterEnum::DuplexMode PrinterCupsBackend::defaultDuplexMode() const
652{
653 return Utils::qDuplexModeToDuplexMode(m_info.defaultDuplexMode());
654}
655
656QList<PrinterEnum::DuplexMode> PrinterCupsBackend::supportedDuplexModes() const
657{
658 QList<PrinterEnum::DuplexMode> list;
659 Q_FOREACH(const QPrinter::DuplexMode mode, m_info.supportedDuplexModes()) {
660 if (mode != QPrinter::DuplexAuto) {
661 list.append(Utils::qDuplexModeToDuplexMode(mode));
662 }
663 }
664
665 if (list.isEmpty())
666 list.append(PrinterEnum::DuplexMode::DuplexNone);
667
668 return list;
669}
670
671QList<QSharedPointer<Printer>> PrinterCupsBackend::availablePrinters()
672{
673 return QList<QSharedPointer<Printer>>();
674}
675
676QStringList PrinterCupsBackend::availablePrinterNames()
677{
678 return QPrinterInfo::availablePrinterNames();
679}
680
681QSharedPointer<Printer> PrinterCupsBackend::getPrinter(const QString &printerName)
682{
683 QPrinterInfo info = QPrinterInfo::printerInfo(printerName);
684 return QSharedPointer<Printer>(new Printer(new PrinterCupsBackend(m_client, info, m_notifier)));
685}
686
687QString PrinterCupsBackend::defaultPrinterName()
688{
689 return QPrinterInfo::defaultPrinterName();
690}
691
692void PrinterCupsBackend::requestPrinter(const QString &printerName)
693{
694 auto thread = new QThread;
695 auto loader = new PrinterLoader(printerName, m_client, m_notifier);
696 loader->moveToThread(thread);
697 connect(thread, SIGNAL(started()), loader, SLOT(load()));
698 connect(loader, SIGNAL(finished()), thread, SLOT(quit()));
699 connect(loader, SIGNAL(finished()), loader, SLOT(deleteLater()));
700 connect(loader, SIGNAL(loaded(QSharedPointer<Printer>)),
701 this, SIGNAL(printerLoaded(QSharedPointer<Printer>)));
702 connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
703 thread->start();
704}
705
706void PrinterCupsBackend::requestPrinterDrivers()
707{
708 auto thread = new QThread;
709 auto loader = new PrinterDriverLoader();
710 loader->moveToThread(thread);
711 connect(loader, SIGNAL(error(const QString&)),
712 this, SIGNAL(printerDriversFailedToLoad(const QString&)));
713 connect(this, SIGNAL(requestPrinterDriverCancel()), loader, SLOT(cancel()));
714 connect(thread, SIGNAL(started()), loader, SLOT(process()));
715 connect(loader, SIGNAL(finished()), thread, SLOT(quit()));
716 connect(loader, SIGNAL(finished()), loader, SLOT(deleteLater()));
717 connect(loader, SIGNAL(loaded(const QList<PrinterDriver>&)),
718 this, SIGNAL(printerDriversLoaded(const QList<PrinterDriver>&)));
719 connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
720 thread->start();
721}
722
723void PrinterCupsBackend::cancelPrinterDriverRequest()
724{
725 Q_EMIT requestPrinterDriverCancel();
726}
727
728void PrinterCupsBackend::refresh()
729{
730 if (m_printerName.isEmpty()) {
731 throw std::invalid_argument("Trying to refresh unnamed printer.");
732 } else {
733 m_info = QPrinterInfo::printerInfo(m_printerName);
734 }
735}
736
737void PrinterCupsBackend::createSubscription()
738{
739 m_cupsSubscriptionId = m_client->createSubscription();;
740}
741
742void PrinterCupsBackend::cancelSubscription()
743{
744 if (m_cupsSubscriptionId > 0)
745 m_client->cancelSubscription(m_cupsSubscriptionId);
746}
747
748QString PrinterCupsBackend::getPrinterInstance(const QString &name) const
749{
750 const auto parts = name.splitRef(QLatin1Char('/'));
751 QString instance;
752 if (parts.size() > 1)
753 instance = parts.at(1).toString();
754
755 return instance;
756}
757
758QString PrinterCupsBackend::getPrinterName(const QString &name) const
759{
760 return name.splitRef(QLatin1Char('/')).first().toString();
761}
762
763cups_dest_t* PrinterCupsBackend::getDest(const QString &name) const
764{
765 QString printerName = getPrinterName(name);
766 QString instance = getPrinterInstance(name);
767
768 if (m_dests.contains(name)) {
769 return m_dests[name];
770 } else {
771 m_dests[name] = m_client->getDest(printerName, instance);
772 return m_dests[name];
773 }
774}
775
776ppd_file_t* PrinterCupsBackend::getPpd(const QString &name) const
777{
778 QString printerName = getPrinterName(name);
779 QString instance = getPrinterInstance(name);
780
781 if (m_ppds.contains(name)) {
782 return m_ppds[name];
783 } else {
784 m_ppds[name] = m_client->getPpdFile(printerName, instance);
785 return m_ppds[name];
786 }
787}
0788
=== 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-22 12:44:26 +0000
@@ -0,0 +1,152 @@
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 printerSetLocation(const QString &name,
60 const QString &location) override;
61 virtual QString printerSetShared(const QString &name,
62 const bool shared) override;
63 virtual QString printerSetJobSheets(const QString &name,
64 const QString &start,
65 const QString &end) override;
66 virtual QString printerSetErrorPolicy(const QString &name,
67 const PrinterEnum::ErrorPolicy &policy) override;
68
69 virtual QString printerSetOpPolicy(const QString &name,
70 const PrinterEnum::OperationPolicy &policy) override;
71 virtual QString printerSetUsersAllowed(const QString &name,
72 const QStringList &users) override;
73 virtual QString printerSetUsersDenied(const QString &name,
74 const QStringList &users) override;
75 virtual QString printerAddOptionDefault(const QString &name,
76 const QString &option,
77 const QStringList &values) override;
78 virtual QString printerDeleteOptionDefault(const QString &name,
79 const QString &value) override;
80 virtual QString printerAddOption(const QString &name,
81 const QString &option,
82 const QStringList &values) override;
83
84 virtual QVariant printerGetOption(const QString &name,
85 const QString &option) const override;
86 virtual QMap<QString, QVariant> printerGetOptions(
87 const QString &name, const QStringList &options
88 ) const override;
89 // FIXME: maybe have a PrinterDest iface that has a CupsDest impl?
90 virtual cups_dest_t* makeDest(const QString &name,
91 const PrinterJob *options) override;
92
93 virtual void cancelJob(const QString &name, const int jobId) override;
94 virtual int printFileToDest(const QString &filepath,
95 const QString &title,
96 const cups_dest_t *dest) override;
97 virtual QList<QSharedPointer<PrinterJob>> printerGetJobs() override;
98
99 virtual QString printerName() const override;
100 virtual QString description() const override;
101 virtual QString location() const override;
102 virtual QString makeAndModel() const override;
103
104 virtual PrinterEnum::State state() const override;
105 virtual QList<QPageSize> supportedPageSizes() const override;
106 virtual QPageSize defaultPageSize() const override;
107 virtual bool supportsCustomPageSizes() const override;
108
109 virtual QPageSize minimumPhysicalPageSize() const override;
110 virtual QPageSize maximumPhysicalPageSize() const override;
111 virtual QList<int> supportedResolutions() const override;
112 virtual PrinterEnum::DuplexMode defaultDuplexMode() const override;
113 virtual QList<PrinterEnum::DuplexMode> supportedDuplexModes() const override;
114
115 virtual QList<QSharedPointer<Printer>> availablePrinters() override;
116 virtual QStringList availablePrinterNames() override;
117 virtual QSharedPointer<Printer> getPrinter(const QString &printerName) override;
118 virtual QString defaultPrinterName() override;
119 virtual void requestPrinterDrivers() override;
120 virtual void requestPrinter(const QString &printerName) override;
121 virtual QMap<QString, QVariant> printerGetJobAttributes(
122 const QString &name, const int jobId) override;
123
124public Q_SLOTS:
125 virtual void refresh() override;
126 void createSubscription();
127
128Q_SIGNALS:
129 void cancelWorkers();
130 void printerDriversLoaded(const QList<PrinterDriver> &drivers);
131 void printerDriversFailedToLoad(const QString &errorMessage);
132 void requestPrinterDriverCancel();
133
134private:
135 void cancelSubscription();
136 void cancelPrinterDriverRequest();
137 QList<cups_job_t *> getCupsJobs(const QString &name = QStringLiteral());
138
139 QString getPrinterName(const QString &name) const;
140 QString getPrinterInstance(const QString &name) const;
141 cups_dest_t* getDest(const QString &name) const;
142 ppd_file_t* getPpd(const QString &name) const;
143 const QStringList m_knownQualityOptions;
144 IppClient *m_client;
145 QPrinterInfo m_info;
146 OrgCupsCupsdNotifierInterface *m_notifier;
147 int m_cupsSubscriptionId;
148 mutable QMap<QString, cups_dest_t*> m_dests; // Printer name, dest.
149 mutable QMap<QString, ppd_file_t*> m_ppds; // Printer name, ppd.
150};
151
152#endif // USC_PRINTERS_CUPS_BACKEND_H
0153
=== 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-22 12:44:26 +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-22 12:44:26 +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-22 12:44:26 +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-22 12:44:26 +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-22 12:44:26 +0000
@@ -0,0 +1,128 @@
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 // cups_option_t option;
65 QList<PrinterDriver> drivers;
66
67 for (attr = ippFirstAttribute(response); attr != NULL && m_running; attr = ippNextAttribute(response)) {
68
69 while (attr != NULL && ippGetGroupTag(attr) != IPP_TAG_PRINTER)
70 attr = ippNextAttribute(response);
71
72 if (attr == NULL)
73 break;
74
75 // Pull the needed attributes from this PPD...
76 ppdDeviceId = "NONE";
77 ppdLanguage.clear();
78 ppdMakeModel.clear();
79 ppdName.clear();
80
81 while (attr != NULL && ippGetGroupTag(attr) == IPP_TAG_PRINTER) {
82 if (!strcmp(ippGetName(attr), "ppd-device-id") &&
83 ippGetValueTag(attr) == IPP_TAG_TEXT) {
84 ppdDeviceId = ippGetString(attr, 0, NULL);
85 } else if (!strcmp(ippGetName(attr), "ppd-natural-language") &&
86 ippGetValueTag(attr) == IPP_TAG_LANGUAGE) {
87 ppdLanguage = ippGetString(attr, 0, NULL);
88
89 } else if (!strcmp(ippGetName(attr), "ppd-make-and-model") &&
90 ippGetValueTag(attr) == IPP_TAG_TEXT) {
91 ppdMakeModel = ippGetString(attr, 0, NULL);
92 } else if (!strcmp(ippGetName(attr), "ppd-name") &&
93 ippGetValueTag(attr) == IPP_TAG_NAME) {
94
95 ppdName = ippGetString(attr, 0, NULL);
96 }
97
98 attr = ippNextAttribute(response);
99 }
100
101 // See if we have everything needed...
102 if (ppdLanguage.isEmpty() || ppdMakeModel.isEmpty() ||
103 ppdName.isEmpty()) {
104 if (attr == NULL)
105 break;
106 else
107 continue;
108 }
109
110 PrinterDriver m;
111 m.name = ppdName;
112 m.deviceId = ppdDeviceId;
113 m.makeModel = ppdMakeModel;
114 m.language = ppdLanguage;
115
116 drivers.append(m);
117 }
118
119 ippDelete(response);
120
121 Q_EMIT loaded(drivers);
122 Q_EMIT finished();
123}
124
125void PrinterDriverLoader::cancel()
126{
127 m_running = false;
128}
0129
=== 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-22 12:44:26 +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-22 12:44:26 +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-22 12:44:26 +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-22 12:44:26 +0000
@@ -0,0 +1,141 @@
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 ColorSpace
45 {
46 NSpace = 0,
47 RGBSpace,
48 RGBKSpace,
49 GraySpace,
50 CMYSpace,
51 CMYKSpace,
52 UnknownSpace,
53 };
54 Q_ENUM(ColorSpace)
55
56 enum class ColorOrganization
57 {
58 ChunkyOrganization = 0,
59 BandedOrganization,
60 PlanarOrganization,
61 UnknownOrganization,
62 };
63 Q_ENUM(ColorOrganization)
64
65 enum class CartridgeType
66 {
67 BlackCartridge = 0,
68 CyanCartridge,
69 MagentaCartridge,
70 YellowCartridge,
71 RedCartridge,
72 GreenCartridge,
73 BlueCartridge,
74 UnknownCartridge,
75 WhiteCartridge,
76 };
77 Q_ENUM(CartridgeType)
78
79 enum class DuplexMode
80 {
81 DuplexNone = 0,
82 DuplexLongSide,
83 DuplexShortSide,
84 };
85 Q_ENUM(DuplexMode)
86
87 enum class ErrorPolicy
88 {
89 RetryOnError = 0,
90 AbortOnError,
91 StopPrinterOnError,
92 RetryCurrentOnError,
93 };
94 Q_ENUM(ErrorPolicy)
95
96 // Match enums from ipp_jstate_t
97 enum class JobState
98 {
99 Pending = 3,
100 Held,
101 Processing,
102 Stopped,
103 Canceled,
104 Aborted,
105 Complete,
106 };
107 Q_ENUM(JobState)
108
109 enum class OperationPolicy
110 {
111 DefaultOperation = 0,
112 AuthenticatedOperation,
113 };
114 Q_ENUM(OperationPolicy)
115
116 enum class PrintRange
117 {
118 AllPages = 0,
119 PageRange,
120 };
121 Q_ENUM(PrintRange)
122
123 enum class State
124 {
125 IdleState = 0,
126 ActiveState,
127 AbortedState,
128 ErrorState,
129 };
130 Q_ENUM(State)
131
132 enum class PrinterType
133 {
134 ProxyType = 0, // Represents a printer not yet loaded.
135 CupsType,
136 PdfType,
137 };
138 Q_ENUM(PrinterType)
139};
140
141#endif // USC_PRINTERS_ENUMS_H
0142
=== 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-22 12:44:26 +0000
@@ -0,0 +1,44 @@
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// bindtextdomain(domain, localeDir);
32// textdomain(domain);
33 Q_UNUSED(localeDir);
34
35 thisDomain = domain;
36}
37
38QString __(const char *text, const char *domain)
39{
40 Q_UNUSED(domain);
41
42 // Use the stored domain
43 return QString::fromUtf8(dgettext(thisDomain, text));
44}
045
=== 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-22 12:44:26 +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-22 12:44:26 +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-22 12:44:26 +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-22 12:44:26 +0000
@@ -0,0 +1,391 @@
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 Q_UNUSED(job_impressions_completed);
66
67 auto job = getJobById(job_id);
68 if (job)
69 job->setImpressionsCompleted(job_impressions_completed);
70
71 update();
72}
73
74void JobModel::update()
75{
76 // Store the old count and get the new printers
77 int oldCount = m_jobs.size();
78 QList<QSharedPointer<PrinterJob>> newJobs = m_backend->printerGetJobs();
79
80 // Go through the old model
81 for (int i=0; i < m_jobs.count(); i++) {
82 // Determine if the old printer exists in the new model
83 bool exists = false;
84
85 Q_FOREACH(QSharedPointer<PrinterJob> p, newJobs) {
86 if (p->jobId() == m_jobs.at(i)->jobId()) {
87 exists = true;
88
89 // Ensure the other properties of the job are up to date
90 if (!m_jobs.at(i)->deepCompare(p)) {
91 m_jobs.at(i)->updateFrom(p);
92
93 Q_EMIT dataChanged(index(i), index(i));
94 }
95
96 break;
97 }
98 }
99
100 // If it doesn't exist then remove it from the old model
101 if (!exists) {
102 beginRemoveRows(QModelIndex(), i, i);
103 QSharedPointer<PrinterJob> p = m_jobs.takeAt(i);
104 endRemoveRows();
105
106 i--; // as we have removed an item decrement
107 }
108 }
109
110 // Go through the new model
111 for (int i=0; i < newJobs.count(); i++) {
112 // Determine if the new printer exists in the old model
113 bool exists = false;
114 int j;
115
116 for (j=0; j < m_jobs.count(); j++) {
117 if (m_jobs.at(j)->jobId() == newJobs.at(i)->jobId()) {
118 exists = true;
119 break;
120 }
121 }
122
123 if (exists) {
124 if (j == i) { // New printer exists and in correct position
125 continue;
126 } else {
127 // New printer does exist but needs to be moved in old model
128 beginMoveRows(QModelIndex(), j, 1, QModelIndex(), i);
129 m_jobs.move(j, i);
130 endMoveRows();
131 }
132 } else {
133 // New printer does not exist insert into model
134 beginInsertRows(QModelIndex(), i, i);
135 m_jobs.insert(i, newJobs.at(i));
136 endInsertRows();
137 }
138 }
139
140 if (oldCount != m_jobs.size()) {
141 Q_EMIT countChanged();
142 }
143}
144
145int JobModel::rowCount(const QModelIndex &parent) const
146{
147 Q_UNUSED(parent);
148 return m_jobs.size();
149}
150
151int JobModel::count() const
152{
153 return rowCount();
154}
155
156QVariant JobModel::data(const QModelIndex &index, int role) const
157{
158 QVariant ret;
159
160 if ((0<=index.row()) && (index.row()<m_jobs.size())) {
161
162 auto job = m_jobs[index.row()];
163
164 switch (role) {
165 case CollateRole:
166 ret = job->collate();
167 break;
168 case ColorModelRole: {
169 if (job->printer()) {
170 ret = job->printer()->supportedColorModels().at(job->colorModel()).text;
171 } else {
172 qWarning() << "Printer is undefined, no colorModel";
173 ret = "";
174 }
175 break;
176 }
177 case CompletedTimeRole:
178 ret = job->completedTime().toString(QLocale::system().dateTimeFormat());
179 break;
180 case CopiesRole:
181 ret = job->copies();
182 break;
183 case CreationTimeRole:
184 ret = job->creationTime().toString(QLocale::system().dateTimeFormat());
185 break;
186 case DuplexRole: {
187 if (job->printer()) {
188 ret = job->printer()->supportedDuplexStrings().at(job->duplexMode());
189 } else {
190 qWarning() << "Printer is undefined, no duplexMode";
191 ret = "";
192 }
193 break;
194 }
195 case IdRole:
196 ret = job->jobId();
197 break;
198 case ImpressionsCompletedRole:
199 ret = job->impressionsCompleted();
200 break;
201 case LandscapeRole:
202 ret = job->landscape();
203 break;
204 case MessagesRole:
205 ret = job->messages();
206 break;
207 case PrinterNameRole:
208 ret = job->printerName();
209 break;
210 case PrintRangeRole:
211 ret = job->printRange();
212 break;
213 case PrintRangeModeRole:
214 ret = QVariant::fromValue<PrinterEnum::PrintRange>(job->printRangeMode());
215 break;
216 case ProcessingTimeRole:
217 ret = job->processingTime().toString(QLocale::system().dateTimeFormat());
218 break;
219 case QualityRole: {
220 if (job->printer()) {
221 ret = job->printer()->supportedPrintQualities().at(job->quality()).text;
222 } else {
223 qWarning() << "Printer is undefined, no quality";
224 ret = "";
225 }
226 break;
227 }
228 case ReverseRole:
229 ret = job->reverse();
230 break;
231 case SizeRole:
232 ret = job->size();
233 break;
234 case StateRole:
235 // TODO: improve, for now have a switch
236 switch (job->state()) {
237 case PrinterEnum::JobState::Aborted:
238 ret = "Aborted";
239 break;
240 case PrinterEnum::JobState::Canceled:
241 ret = "Canceled";
242 break;
243 case PrinterEnum::JobState::Complete:
244 ret = "Compelete";
245 break;
246 case PrinterEnum::JobState::Held:
247 ret = "Held";
248 break;
249 case PrinterEnum::JobState::Pending:
250 ret = "Pending";
251 break;
252 case PrinterEnum::JobState::Processing:
253 ret = "Processing";
254 break;
255 case PrinterEnum::JobState::Stopped:
256 ret = "Stopped";
257 break;
258 }
259 break;
260 case Qt::DisplayRole:
261 case TitleRole:
262 ret = job->title();
263 break;
264 case UserRole:
265 ret = job->user();
266 break;
267 }
268 }
269
270 return ret;
271}
272
273QHash<int, QByteArray> JobModel::roleNames() const
274{
275 static QHash<int,QByteArray> names;
276
277 if (Q_UNLIKELY(names.empty())) {
278 names[Qt::DisplayRole] = "displayName";
279 names[IdRole] = "id";
280 names[CollateRole] = "collate";
281 names[ColorModelRole] = "colorModel";
282 names[CompletedTimeRole] = "completedTime";
283 names[CopiesRole] = "copies";
284 names[CreationTimeRole] = "creationTime";
285 names[DuplexRole] = "duplexMode";
286 names[ImpressionsCompletedRole] = "impressionsCompleted";
287 names[LandscapeRole] = "landscape";
288 names[MessagesRole] = "messages";
289 names[PrinterNameRole] = "printerName";
290 names[PrintRangeRole] = "printRange";
291 names[PrintRangeModeRole] = "printRangeMode";
292 names[ProcessingTimeRole] = "processingTime";
293 names[QualityRole] = "quality";
294 names[ReverseRole] = "reverse";
295 names[SizeRole] = "size";
296 names[StateRole] = "state";
297 names[TitleRole] = "title";
298 names[UserRole] = "user";
299 names[LastStateMessageRole] = "lastStateMessage";
300 }
301
302 return names;
303}
304
305QVariantMap JobModel::get(const int row) const
306{
307 QHashIterator<int, QByteArray> iterator(roleNames());
308 QVariantMap result;
309 QModelIndex modelIndex = index(row, 0);
310
311 while (iterator.hasNext()) {
312 iterator.next();
313 result[iterator.value()] = modelIndex.data(iterator.key());
314 }
315
316 return result;
317}
318
319QSharedPointer<PrinterJob> JobModel::getJobById(const int &id)
320{
321 Q_FOREACH(auto job, m_jobs) {
322 if (job->jobId() == id) {
323 return job;
324 }
325 }
326 return QSharedPointer<PrinterJob>(Q_NULLPTR);
327}
328
329
330JobFilter::JobFilter(QObject *parent) : QSortFilterProxyModel(parent)
331{
332 connect(this, SIGNAL(sourceModelChanged()), SLOT(onSourceModelChanged()));
333}
334
335JobFilter::~JobFilter()
336{
337}
338
339QVariantMap JobFilter::get(const int row) const
340{
341 QHashIterator<int, QByteArray> iterator(roleNames());
342 QVariantMap result;
343 QModelIndex modelIndex = index(row, 0);
344
345 while (iterator.hasNext()) {
346 iterator.next();
347 result[iterator.value()] = modelIndex.data(iterator.key());
348 }
349
350 return result;
351}
352
353void JobFilter::onSourceModelChanged()
354{
355 connect((JobModel*) sourceModel(),
356 SIGNAL(countChanged()),
357 this,
358 SIGNAL(countChanged()));
359}
360
361void JobFilter::onSourceModelCountChanged()
362{
363 Q_EMIT countChanged();
364}
365
366int JobFilter::count() const
367{
368 return rowCount();
369}
370
371void JobFilter::filterOnPrinterName(const QString &name)
372{
373 m_printerName = name;
374 m_printerNameFilterEnabled = true;
375 invalidate();
376}
377
378bool JobFilter::filterAcceptsRow(int sourceRow,
379 const QModelIndex &sourceParent) const
380{
381 bool accepts = true;
382 QModelIndex childIndex = sourceModel()->index(sourceRow, 0, sourceParent);
383
384 if (accepts && m_printerNameFilterEnabled) {
385 QString printerName = childIndex.model()->data(
386 childIndex, JobModel::PrinterNameRole).toString();
387 accepts = m_printerName == printerName;
388 }
389
390 return accepts;
391}
0392
=== 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-22 12:44:26 +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
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches