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
1=== modified file 'debian/control'
2--- debian/control 2016-08-24 15:46:52 +0000
3+++ debian/control 2017-02-22 12:44:26 +0000
4@@ -3,8 +3,10 @@
5 Priority: optional
6 Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>
7 Build-Depends: cmake (>= 2.8.9),
8+ cmake-extras (>= 0.10),
9 debhelper (>= 9),
10 gettext,
11+ libcups2-dev,
12 pkg-config,
13 python:any,
14 qt5-default,
15@@ -27,6 +29,7 @@
16 Architecture: any
17 Depends: ${misc:Depends},
18 ${shlibs:Depends},
19+ libqt5printsupport5,
20 qml-module-qtquick2,
21 qml-module-ubuntu-components,
22 qml-module-qtquick-window2,
23
24=== modified file 'modules/Ubuntu/Components/Extras/CMakeLists.txt'
25--- modules/Ubuntu/Components/Extras/CMakeLists.txt 2016-12-08 14:26:41 +0000
26+++ modules/Ubuntu/Components/Extras/CMakeLists.txt 2017-02-22 12:44:26 +0000
27@@ -13,4 +13,5 @@
28 add_subdirectory(plugin)
29 add_subdirectory(Example)
30 add_subdirectory(PhotoEditor)
31+add_subdirectory(Printers)
32 add_subdirectory(TabsBar)
33
34=== added file 'modules/Ubuntu/Components/Extras/Example/PrinterQueue.qml'
35--- modules/Ubuntu/Components/Extras/Example/PrinterQueue.qml 1970-01-01 00:00:00 +0000
36+++ modules/Ubuntu/Components/Extras/Example/PrinterQueue.qml 2017-02-22 12:44:26 +0000
37@@ -0,0 +1,117 @@
38+/*
39+ * Copyright 2017 Canonical Ltd.
40+ *
41+ * This program is free software; you can redistribute it and/or modify
42+ * it under the terms of the GNU Lesser General Public License as published by
43+ * the Free Software Foundation; version 3.
44+ *
45+ * This program is distributed in the hope that it will be useful,
46+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
47+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
48+ * GNU Lesser General Public License for more details.
49+ *
50+ * You should have received a copy of the GNU Lesser General Public License
51+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
52+ *
53+ * Authored by Jonas G. Drange <jonas.drange@canonical.com>
54+ * Andrew Hayzen <andrew.hayzen@canonical.com>
55+ */
56+
57+import QtQuick 2.4
58+import QtQuick.Layouts 1.1
59+import Ubuntu.Components 1.3
60+import Ubuntu.Components.ListItems 1.3 as ListItems
61+import Ubuntu.Components.Extras.Printers 0.1
62+
63+MainView {
64+ width: units.gu(50)
65+ height: units.gu(90)
66+
67+ Component {
68+ id: queuePage
69+
70+ Page {
71+ header: PageHeader {
72+ title: "Queue: " + printer.name
73+ flickable: queueView
74+ }
75+ visible: false
76+
77+ property var printer
78+
79+ ListView {
80+ id: queueView
81+ anchors {
82+ fill: parent
83+ }
84+ delegate: ListItem {
85+ height: modelLayout.height + (divider.visible ? divider.height : 0)
86+ ListItemLayout {
87+ id: modelLayout
88+ title.text: displayName
89+ subtitle.text: "Job: " + model.id + " State: " + model.state
90+ + " Color: " + model.colorModel + " CreationTime: "
91+ + model.creationTime + " PageRange: "
92+ + model.printRange + " Messages: " + model.messages;
93+ subtitle.wrapMode: Text.WrapAtWordBoundaryOrAnywhere
94+ subtitle.maximumLineCount: 3
95+ }
96+ onClicked: {
97+ console.debug("Cancel:", printer.name, model.id);
98+ Printers.cancelJob(printer.name, model.id);
99+ }
100+ }
101+ model: printer.jobs
102+
103+ Label {
104+ anchors {
105+ centerIn: parent
106+ }
107+ text: "Empty queue"
108+ visible: queueView.count === 0
109+ }
110+ }
111+ }
112+ }
113+
114+ PageStack {
115+ id: pageStack
116+
117+ Page {
118+ id: printersPage
119+ header: PageHeader {
120+ title: "Printers"
121+ flickable: printerList
122+ }
123+ visible: false
124+
125+ ListView {
126+ id: printerList
127+ anchors { fill: parent }
128+ model: Printers.allPrintersWithPdf
129+ delegate: ListItem {
130+ height: modelLayout.height + (divider.visible ? divider.height : 0)
131+ ListItemLayout {
132+ id: modelLayout
133+ title.text: displayName
134+ title.font.bold: model.default
135+ subtitle.text: description
136+
137+ Icon {
138+ id: icon
139+ width: height
140+ height: units.gu(2.5)
141+ name: "printer-symbolic"
142+ SlotsLayout.position: SlotsLayout.First
143+ }
144+
145+ ProgressionSlot {}
146+ }
147+ onClicked: pageStack.push(queuePage, { printer: model })
148+ }
149+ }
150+ }
151+
152+ Component.onCompleted: push(printersPage)
153+ }
154+}
155
156=== added file 'modules/Ubuntu/Components/Extras/Example/Printers.qml'
157--- modules/Ubuntu/Components/Extras/Example/Printers.qml 1970-01-01 00:00:00 +0000
158+++ modules/Ubuntu/Components/Extras/Example/Printers.qml 2017-02-22 12:44:26 +0000
159@@ -0,0 +1,603 @@
160+/*
161+ * Copyright 2017 Canonical Ltd.
162+ *
163+ * This program is free software; you can redistribute it and/or modify
164+ * it under the terms of the GNU Lesser General Public License as published by
165+ * the Free Software Foundation; version 3.
166+ *
167+ * This program is distributed in the hope that it will be useful,
168+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
169+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
170+ * GNU Lesser General Public License for more details.
171+ *
172+ * You should have received a copy of the GNU Lesser General Public License
173+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
174+ *
175+ * Authored by Jonas G. Drange <jonas.drange@canonical.com>
176+ */
177+
178+import QtQuick 2.4
179+import QtQuick.Layouts 1.1
180+import Ubuntu.Components 1.3
181+import Ubuntu.Components.Popups 1.3
182+import Ubuntu.Components.ListItems 1.3 as ListItems
183+import Ubuntu.Components.Extras.Printers 0.1
184+
185+MainView {
186+ width: units.gu(50)
187+ height: units.gu(90)
188+
189+ Component {
190+ id: printerPage
191+
192+ Page {
193+ visible: false
194+ property var printer
195+ header: PageHeader {
196+ id: printerPageHeader
197+ title: printer.name
198+ flickable: printerFlickable
199+ }
200+
201+ Component {
202+ id: printerPageNotYetLoaded
203+
204+ Item {
205+ anchors.fill: parent
206+ ActivityIndicator {
207+ anchors.centerIn: parent
208+ running: true
209+ }
210+ }
211+ }
212+
213+ Component.onCompleted: {
214+ printer.description;
215+ }
216+
217+ Flickable {
218+ id: printerFlickable
219+ anchors.fill: parent
220+
221+ Loader {
222+ id: printerPageBitsLoader
223+ anchors.fill: parent
224+ sourceComponent: printer.isLoaded ? printerPageLoaded : printerPageNotYetLoaded
225+ }
226+ }
227+
228+ Component {
229+ id: printerPageLoaded
230+
231+ Column {
232+ spacing: units.gu(2)
233+ anchors {
234+ top: parent.top
235+ topMargin: units.gu(2)
236+ left: parent.left
237+ right: parent.right
238+ }
239+
240+ ListItems.Standard {
241+ anchors {
242+ left: parent.left
243+ right: parent.right
244+ }
245+ text: "Enabled"
246+
247+ control: Switch {
248+ checked: printer.printerEnabled
249+ onCheckedChanged: printer.printerEnabled = checked
250+ }
251+ }
252+
253+ ListItems.Standard {
254+ anchors {
255+ left: parent.left
256+ right: parent.right
257+ }
258+ text: "Accepting jobs"
259+
260+ control: Switch {
261+ checked: printer.acceptJobs
262+ onCheckedChanged: printer.acceptJobs = checked
263+ }
264+ }
265+
266+ ListItems.Standard {
267+ anchors {
268+ left: parent.left
269+ right: parent.right
270+ }
271+ text: "Jobs"
272+ progression: true
273+ onClicked: pageStack.push(jobPage, { printer: printer })
274+ }
275+
276+ Label {
277+ anchors {
278+ left: parent.left
279+ right: parent.right
280+ margins: units.gu(2)
281+ }
282+ text: "Description"
283+ }
284+
285+ ListItems.SingleControl {
286+ anchors {
287+ left: parent.left
288+ right: parent.right
289+ }
290+
291+ control: TextField {
292+ anchors {
293+ margins: units.gu(1)
294+ left: parent.left
295+ right: parent.right
296+
297+ }
298+ text: printer.description
299+ onTextChanged: printer.description = text
300+ }
301+ }
302+
303+
304+ ListItems.ValueSelector {
305+ anchors {
306+ left: parent.left
307+ right: parent.right
308+ }
309+ enabled: values.length > 1
310+ text: "Duplex"
311+ values: printer.supportedDuplexModes
312+ onSelectedIndexChanged: printer.duplexMode = selectedIndex
313+ Component.onCompleted: {
314+ if (enabled) {
315+ selectedIndex = printer.duplexMode
316+ }
317+ }
318+ }
319+
320+ ListItems.ValueSelector {
321+ anchors {
322+ left: parent.left
323+ right: parent.right
324+ }
325+ text: "Page size"
326+ values: printer.supportedPageSizes
327+ onSelectedIndexChanged: printer.pageSize = selectedIndex
328+ Component.onCompleted: selectedIndex = printer.supportedPageSizes.indexOf(printer.pageSize)
329+ }
330+
331+ ListItems.ValueSelector {
332+ anchors {
333+ left: parent.left
334+ right: parent.right
335+ }
336+ visible: printer.supportedColorModels.length
337+ text: "Color model"
338+ values: printer.supportedColorModels
339+ enabled: values.length > 1
340+ onSelectedIndexChanged: printer.colorModel = selectedIndex
341+ Component.onCompleted: {
342+ if (enabled)
343+ selectedIndex = printer.colorModel
344+ }
345+ }
346+
347+ ListItems.ValueSelector {
348+ anchors {
349+ left: parent.left
350+ right: parent.right
351+ }
352+ visible: printer.supportedPrintQualities.length
353+ text: "Quality"
354+ values: printer.supportedPrintQualities
355+ enabled: values.length > 1
356+ onSelectedIndexChanged: printer.printQuality = selectedIndex
357+ Component.onCompleted: {
358+ if (enabled)
359+ selectedIndex = printer.printQuality
360+ }
361+ }
362+ }
363+ }
364+ }
365+ }
366+
367+ Component {
368+ id: jobPage
369+ Page {
370+ property var printer
371+ header: PageHeader {
372+ id: jobPageHeader
373+ title: "%1 (%2 jobs)".arg(printer.name).arg(jobList.count)
374+ flickable: jobList
375+ }
376+
377+ ListView {
378+ id: jobList
379+ anchors.fill: parent
380+ model: printer.jobs
381+ delegate: ListItem {
382+ height: jobLayout.height + (divider.visible ? divider.height : 0)
383+ ListItemLayout {
384+ id: jobLayout
385+ title.text: displayName
386+
387+ Icon {
388+ id: icon
389+ width: height
390+ height: units.gu(2.5)
391+ name: "stock_document"
392+ SlotsLayout.position: SlotsLayout.First
393+ }
394+ }
395+ }
396+ }
397+ }
398+ }
399+
400+
401+ Component {
402+ id: allJobsPage
403+ Page {
404+ header: PageHeader {
405+ id: allJobsHeader
406+ title: "Printer jobs"
407+ flickable: jobsList
408+ }
409+
410+ ListView {
411+ id: jobsList
412+ anchors.fill: parent
413+ model: Printers.printJobs
414+ delegate: ListItem {
415+ height: jobsLayout.height + (divider.visible ? divider.height : 0)
416+ ListItemLayout {
417+ id: jobsLayout
418+ title.text: displayName
419+
420+ Icon {
421+ id: icon
422+ width: height
423+ height: units.gu(2.5)
424+ name: "stock_document"
425+ SlotsLayout.position: SlotsLayout.First
426+ }
427+ }
428+ }
429+ }
430+ }
431+ }
432+
433+
434+ PageStack {
435+ id: pageStack
436+
437+ Component.onCompleted: push(printersPage)
438+
439+ Page {
440+ id: printersPage
441+ header: PageHeader {
442+ title: "Printers"
443+ flickable: printerList
444+ trailingActionBar {
445+ actions: [
446+ Action {
447+ iconName: "add"
448+ text: "Add printer"
449+ onTriggered: pageStack.push(addPrinterPageComponent)
450+ },
451+ Action {
452+ iconName: "document-print"
453+ text: "Printer jobs"
454+ onTriggered: pageStack.push(allJobsPage)
455+ }
456+ ]
457+ }
458+ }
459+ visible: false
460+
461+ ListView {
462+ id: printerList
463+ anchors { fill: parent }
464+ model: Printers.allPrintersWithPdf
465+ delegate: ListItem {
466+ height: modelLayout.height + (divider.visible ? divider.height : 0)
467+ trailingActions: ListItemActions {
468+ actions: [
469+ Action {
470+ iconName: "delete"
471+ onTriggered: {
472+ if (!Printers.removePrinter(model.name)) {
473+ console.error('failed to remove printer', Printers.lastMessage);
474+ }
475+ }
476+ },
477+ Action {
478+ iconName: model.default ? "starred" : "non-starred"
479+ enabled: !model.default
480+ onTriggered: Printers.defaultPrinterName = model.name
481+ }
482+
483+ ]
484+ }
485+ ListItemLayout {
486+ id: modelLayout
487+ title.text: displayName
488+ title.font.bold: model.default
489+
490+ Icon {
491+ id: icon
492+ width: height
493+ height: units.gu(2.5)
494+ name: "printer-symbolic"
495+ SlotsLayout.position: SlotsLayout.First
496+ }
497+
498+ ProgressionSlot {}
499+ }
500+ onClicked: pageStack.push(printerPage, { printer: model })
501+ }
502+ }
503+ }
504+ }
505+
506+ Component {
507+ id: addPrinterPageComponent
508+ Page {
509+ id: addPrinterPage
510+ states: [
511+ State {
512+ name: "success"
513+ PropertyChanges {
514+ target: okAction
515+ enabled: false
516+ }
517+ PropertyChanges {
518+ target: closeAction
519+ enabled: false
520+ }
521+ PropertyChanges {
522+ target: addPrinterCol
523+ enabled: false
524+ }
525+ StateChangeScript {
526+ script: okTimer.start()
527+ }
528+ },
529+ State {
530+ name: "failure"
531+ PropertyChanges {
532+ target: errorMessageContainer
533+ visible: true
534+ }
535+ }
536+ ]
537+ header: PageHeader {
538+ title: "Add printer"
539+ flickable: addPrinterFlickable
540+ leadingActionBar.actions: [
541+ Action {
542+ id: closeAction
543+ iconName: "close"
544+ text: "Abort"
545+ onTriggered: pageStack.pop()
546+ }
547+ ]
548+ trailingActionBar {
549+ actions: [
550+ Action {
551+ id: okAction
552+ iconName: "ok"
553+ text: "Complete"
554+ onTriggered: {
555+ var ret;
556+ if (driverSelector.selectedIndex == 0) {
557+ ret = Printers.addPrinter(
558+ printerName.text,
559+ driversView.selectedDriver,
560+ printerUri.text,
561+ printerDescription.text,
562+ printerLocation.text
563+ );
564+ } else {
565+ ret = Printers.addPrinterWithPpdFile(
566+ printerName.text,
567+ printerPpd.text,
568+ printerUri.text,
569+ printerDescription.text,
570+ printerLocation.text
571+ );
572+ }
573+ if (ret) {
574+ addPrinterPage.state = "success"
575+ } else {
576+ errorMessage.text = Printers.lastMessage;
577+ addPrinterPage.state = "failure"
578+ }
579+ }
580+ }
581+ ]
582+ }
583+ }
584+
585+ Component.onCompleted: {
586+ Printers.prepareToAddPrinter();
587+ }
588+
589+ Timer {
590+ id: okTimer
591+ interval: 2000
592+ onTriggered: pageStack.pop();
593+ }
594+
595+ Flickable {
596+ id: addPrinterFlickable
597+ anchors.fill: parent
598+
599+ Column {
600+ id: addPrinterCol
601+ property bool enabled: true
602+ anchors {
603+ left: parent.left
604+ right: parent.right
605+ }
606+
607+ Item {
608+ id: errorMessageContainer
609+ visible: false
610+ anchors {
611+ left: parent.left
612+ right: parent.right
613+ margins: units.gu(2)
614+ }
615+ height: units.gu(6)
616+ Label {
617+ id: errorMessage
618+ anchors {
619+ top: parent.top
620+ topMargin: units.gu(2)
621+ horizontalCenter: parent.horizontalCenter
622+ }
623+ }
624+
625+ }
626+
627+ ListItems.Standard {
628+ text: "Device URI"
629+ control: TextField {
630+ id: printerUri
631+ placeholderText: "ipp://server.local/my-queue"
632+ }
633+ enabled: parent.enabled
634+ }
635+
636+ ListItems.ValueSelector {
637+ id: driverSelector
638+ anchors {
639+ left: parent.left
640+ right: parent.right
641+ }
642+ text: "Choose driver"
643+ values: [
644+ "Select printer from database",
645+ "Provide PPD file"
646+ ]
647+ enabled: parent.enabled
648+ }
649+
650+ ListItems.Standard {
651+ anchors {
652+ left: parent.left
653+ right: parent.right
654+ }
655+ text: "Filter drivers"
656+ control: TextField {
657+ id: driverFilter
658+ onTextChanged: Printers.driverFilter = text
659+ }
660+ visible: driverSelector.selectedIndex == 0
661+ enabled: parent.enabled
662+ }
663+
664+ ListView {
665+ id: driversView
666+ property string selectedDriver
667+ property bool loading: true
668+ visible: driverSelector.selectedIndex == 0
669+ model: Printers.drivers
670+ anchors { left: parent.left; right: parent.right }
671+ height: units.gu(30)
672+ clip: true
673+ enabled: parent.enabled
674+ highlightFollowsCurrentItem: false
675+ highlight: Rectangle {
676+ z: 0
677+ y: driversView.currentItem.y
678+ width: driversView.currentItem.width
679+ height: driversView.currentItem.height
680+ color: theme.palette.selected.background
681+ }
682+ delegate: ListItem {
683+ height: driverLayout.height + (divider.visible ? divider.height : 0)
684+ ListItemLayout {
685+ id: driverLayout
686+ title.text: displayName
687+ subtitle.text: name
688+ summary.text: deviceId
689+ }
690+ onClicked: {
691+ driversView.selectedDriver = name
692+ driversView.currentIndex = index
693+ }
694+ }
695+
696+ ActivityIndicator {
697+ anchors.centerIn: parent
698+ running: parent.loading
699+ }
700+
701+ Connections {
702+ target: driversView
703+ onCountChanged: {
704+ target = null;
705+ driversView.loading = false;
706+ }
707+ }
708+ }
709+
710+ ListItems.Standard {
711+ text: "PPD File"
712+ visible: driverSelector.selectedIndex == 1
713+ control: TextField {
714+ id: printerPpd
715+ placeholderText: "/usr/share/cups/foo.ppd"
716+ }
717+ enabled: parent.enabled
718+ }
719+
720+ ListItems.Standard {
721+ anchors {
722+ left: parent.left
723+ right: parent.right
724+ }
725+ text: "Printer name"
726+ control: TextField {
727+ id: printerName
728+ placeholderText: "laserjet"
729+ }
730+ enabled: parent.enabled
731+ }
732+
733+ ListItems.Standard {
734+ anchors {
735+ left: parent.left
736+ right: parent.right
737+ }
738+ text: "Description (optional)"
739+ control: TextField {
740+ id: printerDescription
741+ placeholderText: "HP Laserjet with Duplexer"
742+ }
743+ enabled: parent.enabled
744+ }
745+
746+ ListItems.Standard {
747+ anchors {
748+ left: parent.left
749+ right: parent.right
750+ }
751+ text: "Location (optional)"
752+ control: TextField {
753+ id: printerLocation
754+ placeholderText: "Lab 1"
755+ }
756+ enabled: parent.enabled
757+ }
758+ }
759+ }
760+ }
761+ }
762+}
763
764=== added directory 'modules/Ubuntu/Components/Extras/Printers'
765=== added file 'modules/Ubuntu/Components/Extras/Printers/CMakeLists.txt'
766--- modules/Ubuntu/Components/Extras/Printers/CMakeLists.txt 1970-01-01 00:00:00 +0000
767+++ modules/Ubuntu/Components/Extras/Printers/CMakeLists.txt 2017-02-22 12:44:26 +0000
768@@ -0,0 +1,68 @@
769+project(UbuntuComponentsExtrasPrintersQml)
770+
771+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -fno-permissive -pedantic -Wall -Wextra")
772+
773+add_definitions(-DUBUNTUCOMPONENTSEXTRASPRINTERS_LIBRARY)
774+
775+include(FindCups)
776+
777+include_directories(${CMAKE_CURRENT_BINARY_DIR} ${CUPS_INCLUDE_DIR})
778+
779+find_package(Qt5Gui REQUIRED)
780+find_package(Qt5PrintSupport REQUIRED)
781+find_package(Qt5Qml REQUIRED)
782+find_package(Qt5DBus REQUIRED)
783+find_package(Qt5Concurrent REQUIRED)
784+
785+if(NOT CUPS_FOUND)
786+message(FATAL_ERROR "Could not find cups.")
787+endif()
788+
789+qt5_add_dbus_interface(
790+ GEN_SOURCES
791+ ${CMAKE_CURRENT_SOURCE_DIR}/org.cups.cupsd.Notifier.xml
792+ cupsdnotifier)
793+
794+add_library(UbuntuComponentsExtrasPrintersQml SHARED
795+ ${GEN_SOURCES}
796+ backend/backend.cpp
797+ backend/backend_cups.cpp
798+ backend/backend_pdf.cpp
799+
800+ cups/ippclient.cpp
801+ cups/printerdriverloader.cpp
802+ cups/printerloader.cpp
803+
804+ models/drivermodel.cpp
805+ models/jobmodel.cpp
806+ models/printermodel.cpp
807+
808+ printer/printer.cpp
809+ printer/printerjob.cpp
810+ printer/printersignalhandler.cpp
811+ printers/printers.cpp
812+
813+ enums.h
814+ i18n.cpp
815+ plugin.cpp
816+ structs.h
817+ utils.h
818+)
819+
820+target_link_libraries(UbuntuComponentsExtrasPrintersQml
821+ Qt5::DBus
822+ Qt5::Gui
823+ Qt5::PrintSupport
824+ Qt5::Qml
825+ Qt5::Concurrent
826+ ${CUPS_LIBRARIES}
827+)
828+
829+find_package(QmlPlugins)
830+
831+macro(add_plugin PLUGIN VERSION PATH)
832+ export_qmlfiles(${PLUGIN} ${PATH} DESTINATION ${QT_IMPORTS_DIR} ${ARGN})
833+ export_qmlplugin(${PLUGIN} ${VERSION} ${PATH} DESTINATION ${QT_IMPORTS_DIR} ${ARGN})
834+endmacro()
835+
836+add_plugin(Ubuntu.Components.Extras.Printers 0.1 Ubuntu/Components/Extras/Printers TARGETS UbuntuComponentsExtrasPrintersQml)
837
838=== added directory 'modules/Ubuntu/Components/Extras/Printers/backend'
839=== added file 'modules/Ubuntu/Components/Extras/Printers/backend/backend.cpp'
840--- modules/Ubuntu/Components/Extras/Printers/backend/backend.cpp 1970-01-01 00:00:00 +0000
841+++ modules/Ubuntu/Components/Extras/Printers/backend/backend.cpp 2017-02-22 12:44:26 +0000
842@@ -0,0 +1,350 @@
843+/*
844+ * Copyright (C) 2017 Canonical, Ltd.
845+ *
846+ * This program is free software; you can redistribute it and/or modify
847+ * it under the terms of the GNU Lesser General Public License as published by
848+ * the Free Software Foundation; version 3.
849+ *
850+ * This program is distributed in the hope that it will be useful,
851+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
852+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
853+ * GNU Lesser General Public License for more details.
854+ *
855+ * You should have received a copy of the GNU Lesser General Public License
856+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
857+ */
858+
859+#include "backend/backend.h"
860+
861+PrinterBackend::PrinterBackend(const QString &printerName, QObject *parent)
862+ : QObject(parent)
863+ , m_printerName(printerName)
864+ , m_type(PrinterEnum::PrinterType::ProxyType)
865+{
866+}
867+
868+PrinterBackend::~PrinterBackend()
869+{
870+}
871+
872+bool PrinterBackend::holdsDefinition() const
873+{
874+ return false;
875+}
876+
877+QString PrinterBackend::printerAdd(const QString &name,
878+ const QString &uri,
879+ const QString &ppdFile,
880+ const QString &info,
881+ const QString &location)
882+{
883+ Q_UNUSED(name);
884+ Q_UNUSED(uri);
885+ Q_UNUSED(ppdFile);
886+ Q_UNUSED(info);
887+ Q_UNUSED(location);
888+ return QString();
889+}
890+
891+QString PrinterBackend::printerAddWithPpd(const QString &name,
892+ const QString &uri,
893+ const QString &ppdFileName,
894+ const QString &info,
895+ const QString &location)
896+{
897+ Q_UNUSED(name);
898+ Q_UNUSED(uri);
899+ Q_UNUSED(ppdFileName);
900+ Q_UNUSED(info);
901+ Q_UNUSED(location);
902+ return QString();
903+}
904+
905+QString PrinterBackend::printerDelete(const QString &name)
906+{
907+ Q_UNUSED(name);
908+ return QString();
909+}
910+
911+QString PrinterBackend::printerSetDefault(const QString &name)
912+{
913+ Q_UNUSED(name);
914+ return QString();
915+}
916+
917+QString PrinterBackend::printerSetEnabled(const QString &name,
918+ const bool enabled)
919+{
920+ Q_UNUSED(name);
921+ Q_UNUSED(enabled);
922+ return QString();
923+}
924+
925+QString PrinterBackend::printerSetAcceptJobs(
926+ const QString &name,
927+ const bool enabled,
928+ const QString &reason)
929+{
930+ Q_UNUSED(name);
931+ Q_UNUSED(enabled);
932+ Q_UNUSED(reason);
933+ return QString();
934+}
935+
936+QString PrinterBackend::printerSetInfo(const QString &name,
937+ const QString &info)
938+{
939+ Q_UNUSED(name);
940+ Q_UNUSED(info);
941+ return QString();
942+}
943+
944+QString PrinterBackend::printerSetLocation(const QString &name,
945+ const QString &location)
946+{
947+ Q_UNUSED(name);
948+ Q_UNUSED(location);
949+ return QString();
950+}
951+
952+QString PrinterBackend::printerSetShared(const QString &name,
953+ const bool shared)
954+{
955+ Q_UNUSED(name);
956+ Q_UNUSED(shared);
957+ return QString();
958+}
959+
960+QString PrinterBackend::printerSetJobSheets(const QString &name,
961+ const QString &start,
962+ const QString &end)
963+{
964+ Q_UNUSED(name);
965+ Q_UNUSED(start);
966+ Q_UNUSED(end);
967+ return QString();
968+}
969+
970+QString PrinterBackend::printerSetErrorPolicy(const QString &name,
971+ const PrinterEnum::ErrorPolicy &policy)
972+{
973+ Q_UNUSED(name);
974+ Q_UNUSED(policy);
975+ return QString();
976+}
977+
978+
979+QString PrinterBackend::printerSetOpPolicy(const QString &name,
980+ const PrinterEnum::OperationPolicy &policy)
981+{
982+ Q_UNUSED(name);
983+ Q_UNUSED(policy);
984+ return QString();
985+}
986+
987+QString PrinterBackend::printerSetUsersAllowed(const QString &name,
988+ const QStringList &users)
989+{
990+ Q_UNUSED(name);
991+ Q_UNUSED(users);
992+ return QString();
993+}
994+
995+QString PrinterBackend::printerSetUsersDenied(const QString &name,
996+ const QStringList &users)
997+{
998+ Q_UNUSED(name);
999+ Q_UNUSED(users);
1000+ return QString();
1001+}
1002+
1003+QString PrinterBackend::printerAddOptionDefault(const QString &name,
1004+ const QString &option,
1005+ const QStringList &values)
1006+{
1007+ Q_UNUSED(name);
1008+ Q_UNUSED(option);
1009+ Q_UNUSED(values);
1010+ return QString();
1011+}
1012+
1013+QString PrinterBackend::printerDeleteOptionDefault(const QString &name,
1014+ const QString &value)
1015+{
1016+ Q_UNUSED(name);
1017+ Q_UNUSED(value);
1018+ return QString();
1019+}
1020+
1021+QString PrinterBackend::printerAddOption(const QString &name,
1022+ const QString &option,
1023+ const QStringList &values)
1024+{
1025+ Q_UNUSED(name);
1026+ Q_UNUSED(option);
1027+ Q_UNUSED(values);
1028+ return QString();
1029+}
1030+
1031+QVariant PrinterBackend::printerGetOption(const QString &name,
1032+ const QString &option) const
1033+{
1034+ Q_UNUSED(name);
1035+ Q_UNUSED(option);
1036+ return QVariant();
1037+}
1038+
1039+QMap<QString, QVariant> PrinterBackend::printerGetOptions(
1040+ const QString &name, const QStringList &options) const
1041+{
1042+ Q_UNUSED(name);
1043+ Q_UNUSED(options);
1044+ return QMap<QString, QVariant>();
1045+}
1046+
1047+// FIXME: maybe have a PrinterDest iface that has a CupsDest impl?
1048+cups_dest_t* PrinterBackend::makeDest(const QString &name,
1049+ const PrinterJob *options)
1050+{
1051+ Q_UNUSED(name);
1052+ Q_UNUSED(options);
1053+ return Q_NULLPTR;
1054+}
1055+
1056+void PrinterBackend::cancelJob(const QString &name, const int jobId)
1057+{
1058+ Q_UNUSED(jobId);
1059+ Q_UNUSED(name);
1060+}
1061+
1062+int PrinterBackend::printFileToDest(const QString &filepath,
1063+ const QString &title,
1064+ const cups_dest_t *dest)
1065+{
1066+ Q_UNUSED(filepath);
1067+ Q_UNUSED(title);
1068+ Q_UNUSED(dest);
1069+ return -1;
1070+}
1071+
1072+QList<QSharedPointer<PrinterJob>> PrinterBackend::printerGetJobs()
1073+{
1074+ return QList<QSharedPointer<PrinterJob>>{};
1075+}
1076+
1077+QMap<QString, QVariant> PrinterBackend::printerGetJobAttributes(
1078+ const QString &name, const int jobId)
1079+{
1080+ Q_UNUSED(name);
1081+ Q_UNUSED(jobId);
1082+ return QMap<QString, QVariant>();
1083+}
1084+
1085+QString PrinterBackend::printerName() const
1086+{
1087+ return m_printerName;
1088+}
1089+
1090+QString PrinterBackend::description() const
1091+{
1092+ return QString();
1093+}
1094+
1095+QString PrinterBackend::location() const
1096+{
1097+ return QString();
1098+}
1099+
1100+QString PrinterBackend::makeAndModel() const
1101+{
1102+ return QString();
1103+}
1104+
1105+PrinterEnum::State PrinterBackend::state() const
1106+{
1107+ return PrinterEnum::State::IdleState;
1108+}
1109+
1110+QList<QPageSize> PrinterBackend::supportedPageSizes() const
1111+{
1112+ return QList<QPageSize>();
1113+}
1114+
1115+QPageSize PrinterBackend::defaultPageSize() const
1116+{
1117+ return QPageSize();
1118+}
1119+
1120+bool PrinterBackend::supportsCustomPageSizes() const
1121+{
1122+ return false;
1123+}
1124+
1125+QPageSize PrinterBackend::minimumPhysicalPageSize() const
1126+{
1127+ return QPageSize();
1128+}
1129+
1130+QPageSize PrinterBackend::maximumPhysicalPageSize() const
1131+{
1132+ return QPageSize();
1133+}
1134+
1135+QList<int> PrinterBackend::supportedResolutions() const
1136+{
1137+ return QList<int>();
1138+}
1139+
1140+PrinterEnum::DuplexMode PrinterBackend::defaultDuplexMode() const
1141+{
1142+ return PrinterEnum::DuplexMode::DuplexNone;
1143+}
1144+
1145+QList<PrinterEnum::DuplexMode> PrinterBackend::supportedDuplexModes() const
1146+{
1147+ return QList<PrinterEnum::DuplexMode>();
1148+}
1149+
1150+QList<QSharedPointer<Printer>> PrinterBackend::availablePrinters()
1151+{
1152+ return QList<QSharedPointer<Printer>>();
1153+}
1154+
1155+QStringList PrinterBackend::availablePrinterNames()
1156+{
1157+ return QStringList();
1158+}
1159+
1160+QSharedPointer<Printer> PrinterBackend::getPrinter(const QString &printerName)
1161+{
1162+ Q_UNUSED(printerName);
1163+ return QSharedPointer<Printer>(Q_NULLPTR);
1164+}
1165+
1166+QString PrinterBackend::defaultPrinterName()
1167+{
1168+ return QString();
1169+}
1170+
1171+void PrinterBackend::requestPrinterDrivers()
1172+{
1173+}
1174+
1175+void PrinterBackend::requestPrinter(const QString &printerName)
1176+{
1177+ Q_UNUSED(printerName);
1178+}
1179+
1180+PrinterEnum::PrinterType PrinterBackend::type() const
1181+{
1182+ return m_type;
1183+}
1184+
1185+void PrinterBackend::setPrinterNameInternal(const QString &printerName)
1186+{
1187+ m_printerName = printerName;
1188+}
1189+
1190+void PrinterBackend::refresh()
1191+{
1192+}
1193
1194=== added file 'modules/Ubuntu/Components/Extras/Printers/backend/backend.h'
1195--- modules/Ubuntu/Components/Extras/Printers/backend/backend.h 1970-01-01 00:00:00 +0000
1196+++ modules/Ubuntu/Components/Extras/Printers/backend/backend.h 2017-02-22 12:44:26 +0000
1197@@ -0,0 +1,223 @@
1198+/*
1199+ * Copyright (C) 2017 Canonical, Ltd.
1200+ *
1201+ * This program is free software; you can redistribute it and/or modify
1202+ * it under the terms of the GNU Lesser General Public License as published by
1203+ * the Free Software Foundation; version 3.
1204+ *
1205+ * This program is distributed in the hope that it will be useful,
1206+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1207+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1208+ * GNU Lesser General Public License for more details.
1209+ *
1210+ * You should have received a copy of the GNU Lesser General Public License
1211+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1212+ */
1213+
1214+#ifndef USC_PRINTERS_BACKEND_H
1215+#define USC_PRINTERS_BACKEND_H
1216+
1217+#include "printer/printer.h"
1218+#include "printer/printerjob.h"
1219+
1220+// TODO: remove cups specific things from this API
1221+#include <cups/cups.h>
1222+
1223+#include <QObject>
1224+#include <QPageSize>
1225+#include <QList>
1226+#include <QString>
1227+#include <QStringList>
1228+
1229+class Printer;
1230+class PrinterJob;
1231+class PRINTERS_DECL_EXPORT PrinterBackend : public QObject
1232+{
1233+ Q_OBJECT
1234+public:
1235+ explicit PrinterBackend(QObject *parent = Q_NULLPTR);
1236+ explicit PrinterBackend(const QString &printerName,
1237+ QObject *parent = Q_NULLPTR);
1238+ virtual ~PrinterBackend();
1239+
1240+ virtual bool holdsDefinition() const;
1241+
1242+ // Add a printer using an already existing ppd.
1243+ virtual QString printerAdd(const QString &name,
1244+ const QString &uri,
1245+ const QString &ppdFile,
1246+ const QString &info,
1247+ const QString &location);
1248+
1249+ // Add a printer and provide a ppd file.
1250+ virtual QString printerAddWithPpd(const QString &name,
1251+ const QString &uri,
1252+ const QString &ppdFileName,
1253+ const QString &info,
1254+ const QString &location);
1255+ virtual QString printerDelete(const QString &name);
1256+ virtual QString printerSetDefault(const QString &name);
1257+ virtual QString printerSetEnabled(const QString &name,
1258+ const bool enabled);
1259+ virtual QString printerSetAcceptJobs(
1260+ const QString &name,
1261+ const bool accept,
1262+ const QString &reason = QString::null);
1263+ virtual QString printerSetInfo(const QString &name,
1264+ const QString &info);
1265+ virtual QString printerSetLocation(const QString &name,
1266+ const QString &location);
1267+ virtual QString printerSetShared(const QString &name,
1268+ const bool shared);
1269+ virtual QString printerSetJobSheets(const QString &name,
1270+ const QString &start,
1271+ const QString &end);
1272+ virtual QString printerSetErrorPolicy(const QString &name,
1273+ const PrinterEnum::ErrorPolicy &policy);
1274+
1275+ virtual QString printerSetOpPolicy(const QString &name,
1276+ const PrinterEnum::OperationPolicy &policy);
1277+ virtual QString printerSetUsersAllowed(const QString &name,
1278+ const QStringList &users);
1279+ virtual QString printerSetUsersDenied(const QString &name,
1280+ const QStringList &users);
1281+ virtual QString printerAddOptionDefault(const QString &name,
1282+ const QString &option,
1283+ const QStringList &values);
1284+ virtual QString printerDeleteOptionDefault(const QString &name,
1285+ const QString &value);
1286+ virtual QString printerAddOption(const QString &name,
1287+ const QString &option,
1288+ const QStringList &values);
1289+
1290+ virtual QVariant printerGetOption(const QString &name,
1291+ const QString &option) const;
1292+ virtual QMap<QString, QVariant> printerGetOptions(
1293+ const QString &name, const QStringList &options) const;
1294+ // FIXME: maybe have a PrinterDest iface that has a CupsDest impl?
1295+ virtual cups_dest_t* makeDest(const QString &name,
1296+ const PrinterJob *options);
1297+
1298+ virtual void cancelJob(const QString &name, const int jobId);
1299+ virtual int printFileToDest(const QString &filepath,
1300+ const QString &title,
1301+ const cups_dest_t *dest);
1302+ virtual QList<QSharedPointer<PrinterJob>> printerGetJobs();
1303+ virtual QMap<QString, QVariant> printerGetJobAttributes(
1304+ const QString &name, const int jobId);
1305+
1306+ virtual QString printerName() const;
1307+ virtual QString description() const;
1308+ virtual QString location() const;
1309+ virtual QString makeAndModel() const;
1310+
1311+ virtual PrinterEnum::State state() const;
1312+ virtual QList<QPageSize> supportedPageSizes() const;
1313+ virtual QPageSize defaultPageSize() const;
1314+ virtual bool supportsCustomPageSizes() const;
1315+
1316+ virtual QPageSize minimumPhysicalPageSize() const;
1317+ virtual QPageSize maximumPhysicalPageSize() const;
1318+ virtual QList<int> supportedResolutions() const;
1319+ virtual PrinterEnum::DuplexMode defaultDuplexMode() const;
1320+ virtual QList<PrinterEnum::DuplexMode> supportedDuplexModes() const;
1321+
1322+ virtual QList<QSharedPointer<Printer>> availablePrinters();
1323+ virtual QStringList availablePrinterNames();
1324+ virtual QSharedPointer<Printer> getPrinter(const QString &printerName);
1325+ virtual QString defaultPrinterName();
1326+
1327+ virtual void requestPrinterDrivers();
1328+ virtual void requestPrinter(const QString &printerName);
1329+
1330+ virtual PrinterEnum::PrinterType type() const;
1331+
1332+ virtual void setPrinterNameInternal(const QString &printerName);
1333+
1334+public Q_SLOTS:
1335+ virtual void refresh();
1336+
1337+Q_SIGNALS:
1338+ void printerDriversLoaded(const QList<PrinterDriver> &drivers);
1339+ void printerDriversFailedToLoad(const QString &errorMessage);
1340+
1341+ void printerLoaded(QSharedPointer<Printer> printers);
1342+
1343+ void jobCompleted(
1344+ const QString &text,
1345+ const QString &printerUri,
1346+ const QString &printerName,
1347+ uint printerState,
1348+ const QString &printerStateReason,
1349+ bool acceptingJobs,
1350+ uint jobId,
1351+ uint jobState,
1352+ const QString &jobStateReason,
1353+ const QString &job_name,
1354+ uint jobImpressionsCompleted
1355+ );
1356+ void jobCreated(
1357+ const QString &text,
1358+ const QString &printerUri,
1359+ const QString &printerName,
1360+ uint printerState,
1361+ const QString &printerStateReason,
1362+ bool acceptingJobs,
1363+ uint jobId,
1364+ uint jobState,
1365+ const QString &jobStateReason,
1366+ const QString &job_name,
1367+ uint jobImpressionsCompleted
1368+ );
1369+ void jobState(
1370+ const QString &text,
1371+ const QString &printerUri,
1372+ const QString &printerName,
1373+ uint printerState,
1374+ const QString &printerStateReason,
1375+ bool acceptingJobs,
1376+ uint jobId,
1377+ uint jobState,
1378+ const QString &jobStateReason,
1379+ const QString &job_name,
1380+ uint jobImpressionsCompleted
1381+ );
1382+ void printerAdded(
1383+ const QString &text,
1384+ const QString &printerUri,
1385+ const QString &printerName,
1386+ uint printerState,
1387+ const QString &printerStateReason,
1388+ bool acceptingJobs
1389+ );
1390+ void printerDeleted(
1391+ const QString &text,
1392+ const QString &printerUri,
1393+ const QString &printerName,
1394+ uint printerState,
1395+ const QString &printerStateReason,
1396+ bool acceptingJobs
1397+ );
1398+ void printerModified(
1399+ const QString &text,
1400+ const QString &printerUri,
1401+ const QString &printerName,
1402+ uint printerState,
1403+ const QString &printerStateReason,
1404+ bool acceptingJobs
1405+ );
1406+ void printerStateChanged(
1407+ const QString &text,
1408+ const QString &printerUri,
1409+ const QString &printerName,
1410+ uint printerState,
1411+ const QString &printerStateReason,
1412+ bool acceptingJobs
1413+ );
1414+
1415+protected:
1416+ QString m_printerName;
1417+ PrinterEnum::PrinterType m_type;
1418+};
1419+
1420+#endif // USC_PRINTERS_BACKEND_H
1421
1422=== added file 'modules/Ubuntu/Components/Extras/Printers/backend/backend_cups.cpp'
1423--- modules/Ubuntu/Components/Extras/Printers/backend/backend_cups.cpp 1970-01-01 00:00:00 +0000
1424+++ modules/Ubuntu/Components/Extras/Printers/backend/backend_cups.cpp 2017-02-22 12:44:26 +0000
1425@@ -0,0 +1,787 @@
1426+/*
1427+ * Copyright (C) 2017 Canonical, Ltd.
1428+ *
1429+ * This program is free software; you can redistribute it and/or modify
1430+ * it under the terms of the GNU Lesser General Public License as published by
1431+ * the Free Software Foundation; version 3.
1432+ *
1433+ * This program is distributed in the hope that it will be useful,
1434+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1435+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1436+ * GNU Lesser General Public License for more details.
1437+ *
1438+ * You should have received a copy of the GNU Lesser General Public License
1439+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1440+ */
1441+
1442+#include "backend/backend_cups.h"
1443+#include "cups/printerdriverloader.h"
1444+#include "cups/printerloader.h"
1445+#include "utils.h"
1446+
1447+#include <cups/http.h>
1448+#include <cups/ipp.h>
1449+#include <cups/ppd.h>
1450+
1451+#include <QLocale>
1452+#include <QThread>
1453+#include <QTimeZone>
1454+
1455+#define __CUPS_ADD_OPTION(dest, name, value) dest->num_options = \
1456+ cupsAddOption(name, value, dest->num_options, &dest->options);
1457+
1458+#define __CUPS_ATTR_EXISTS(map, attr, type) map.contains(attr) \
1459+ && map.value(attr).canConvert<type>()
1460+
1461+PrinterCupsBackend::PrinterCupsBackend(IppClient *client, QPrinterInfo info,
1462+ OrgCupsCupsdNotifierInterface *notifier,
1463+ QObject *parent)
1464+ : PrinterBackend(info.printerName(), parent)
1465+ , m_knownQualityOptions({
1466+ "Quality", "PrintQuality", "HPPrintQuality", "StpQuality",
1467+ "OutputMode",})
1468+ , m_client(client)
1469+ , m_info(info)
1470+ , m_notifier(notifier)
1471+ , m_cupsSubscriptionId(-1)
1472+{
1473+ m_type = PrinterEnum::PrinterType::CupsType;
1474+ connect(m_notifier, SIGNAL(JobCompleted(const QString&, const QString&,
1475+ const QString&, uint,
1476+ const QString&, bool, uint, uint,
1477+ const QString&, const QString&, uint)),
1478+ this, SIGNAL(jobCompleted(const QString&, const QString&,
1479+ const QString&, uint, const QString&,
1480+ bool, uint, uint, const QString&,
1481+ const QString&, uint)));
1482+ connect(m_notifier, SIGNAL(JobCreated(const QString&, const QString&,
1483+ const QString&, uint, const QString&,
1484+ bool, uint, uint, const QString&,
1485+ const QString&, uint)),
1486+ this, SIGNAL(jobCreated(const QString&, const QString&,
1487+ const QString&, uint, const QString&, bool,
1488+ uint, uint, const QString&, const QString&,
1489+ uint)));
1490+ connect(m_notifier, SIGNAL(JobState(const QString&, const QString&,
1491+ const QString&, uint, const QString&,
1492+ bool, uint, uint, const QString&,
1493+ const QString&, uint)),
1494+ this, SIGNAL(jobState(const QString&, const QString&,
1495+ const QString&, uint, const QString&, bool,
1496+ uint, uint, const QString&, const QString&,
1497+ uint)));
1498+ connect(m_notifier, SIGNAL(PrinterAdded(const QString&, const QString&,
1499+ const QString&, uint,
1500+ const QString&, bool)),
1501+ this, SIGNAL(printerAdded(const QString&, const QString&,
1502+ const QString&, uint,
1503+ const QString&, bool)));
1504+ connect(m_notifier, SIGNAL(PrinterDeleted(const QString&, const QString&,
1505+ const QString&, uint,
1506+ const QString&, bool)),
1507+ this, SIGNAL(printerDeleted(const QString&, const QString&,
1508+ const QString&, uint,
1509+ const QString&, bool)));
1510+ connect(m_notifier, SIGNAL(PrinterModified(const QString&, const QString&,
1511+ const QString&, uint,
1512+ const QString&, bool)),
1513+ this, SIGNAL(printerModified(const QString&, const QString&,
1514+ const QString&, uint,
1515+ const QString&, bool)));
1516+ connect(m_notifier, SIGNAL(PrinterStateChanged(const QString&,
1517+ const QString&,
1518+ const QString&, uint,
1519+ const QString&, bool)),
1520+ this, SIGNAL(printerStateChanged(const QString&, const QString&,
1521+ const QString&, uint,
1522+ const QString&, bool)));
1523+
1524+}
1525+
1526+PrinterCupsBackend::~PrinterCupsBackend()
1527+{
1528+ Q_FOREACH(auto dest, m_dests) {
1529+ if (dest)
1530+ cupsFreeDests(1, dest);
1531+ }
1532+ Q_FOREACH(auto ppd, m_ppds) {
1533+ if (ppd)
1534+ ppdClose(ppd);
1535+ }
1536+
1537+ cancelSubscription();
1538+ Q_EMIT cancelWorkers();
1539+}
1540+
1541+QString PrinterCupsBackend::printerAdd(const QString &name,
1542+ const QString &uri,
1543+ const QString &ppdFile,
1544+ const QString &info,
1545+ const QString &location)
1546+{
1547+ if (!m_client->printerAdd(name, uri, ppdFile, info, location)) {
1548+ return m_client->getLastError();
1549+ }
1550+ return QString();
1551+}
1552+
1553+QString PrinterCupsBackend::printerAddWithPpd(const QString &name,
1554+ const QString &uri,
1555+ const QString &ppdFileName,
1556+ const QString &info,
1557+ const QString &location)
1558+{
1559+ if (!m_client->printerAddWithPpdFile(name, uri, ppdFileName, info, location)) {
1560+ return m_client->getLastError();
1561+ }
1562+ return QString();
1563+}
1564+
1565+bool PrinterCupsBackend::holdsDefinition() const
1566+{
1567+ return !m_info.isNull();
1568+}
1569+
1570+QString PrinterCupsBackend::printerDelete(const QString &name)
1571+{
1572+ if (!m_client->printerDelete(name)) {
1573+ return m_client->getLastError();
1574+ }
1575+ return QString();
1576+}
1577+
1578+QString PrinterCupsBackend::printerSetDefault(const QString &name)
1579+{
1580+ if (!m_client->printerSetDefault(name)) {
1581+ return m_client->getLastError();
1582+ }
1583+ return QString();
1584+}
1585+
1586+QString PrinterCupsBackend::printerSetEnabled(const QString &name,
1587+ const bool enabled)
1588+{
1589+ if (!m_client->printerSetEnabled(name, enabled)) {
1590+ return m_client->getLastError();
1591+ }
1592+ return QString();
1593+}
1594+
1595+QString PrinterCupsBackend::printerSetAcceptJobs(
1596+ const QString &name,
1597+ const bool accept,
1598+ const QString &reason)
1599+{
1600+ if (!m_client->printerSetAcceptJobs(name, accept, reason)) {
1601+ return m_client->getLastError();
1602+ }
1603+ return QString();
1604+}
1605+
1606+QString PrinterCupsBackend::printerSetInfo(const QString &name,
1607+ const QString &info)
1608+{
1609+ if (!m_client->printerClassSetInfo(name, info)) {
1610+ return m_client->getLastError();
1611+ }
1612+ return QString();
1613+}
1614+
1615+QString PrinterCupsBackend::printerSetLocation(const QString &name,
1616+ const QString &location)
1617+{
1618+ Q_UNUSED(name);
1619+ Q_UNUSED(location);
1620+ return QString();
1621+}
1622+
1623+QString PrinterCupsBackend::printerSetShared(const QString &name,
1624+ const bool shared)
1625+{
1626+ Q_UNUSED(name);
1627+ Q_UNUSED(shared);
1628+ return QString();
1629+}
1630+
1631+QString PrinterCupsBackend::printerSetJobSheets(const QString &name,
1632+ const QString &start,
1633+ const QString &end)
1634+{
1635+ Q_UNUSED(name);
1636+ Q_UNUSED(start);
1637+ Q_UNUSED(end);
1638+ return QString();
1639+}
1640+
1641+QString PrinterCupsBackend::printerSetErrorPolicy(const QString &name,
1642+ const PrinterEnum::ErrorPolicy &policy)
1643+{
1644+ Q_UNUSED(name);
1645+ Q_UNUSED(policy);
1646+ return QString();
1647+}
1648+
1649+QString PrinterCupsBackend::printerSetOpPolicy(const QString &name,
1650+ const PrinterEnum::OperationPolicy &policy)
1651+{
1652+ Q_UNUSED(name);
1653+ Q_UNUSED(policy);
1654+ return QString();
1655+}
1656+
1657+QString PrinterCupsBackend::printerSetUsersAllowed(const QString &name,
1658+ const QStringList &users)
1659+{
1660+ Q_UNUSED(name);
1661+ Q_UNUSED(users);
1662+ return QString();
1663+}
1664+
1665+QString PrinterCupsBackend::printerSetUsersDenied(const QString &name,
1666+ const QStringList &users)
1667+{
1668+ Q_UNUSED(name);
1669+ Q_UNUSED(users);
1670+ return QString();
1671+}
1672+
1673+QString PrinterCupsBackend::printerAddOptionDefault(const QString &name,
1674+ const QString &option,
1675+ const QStringList &values)
1676+{
1677+ Q_UNUSED(name);
1678+ Q_UNUSED(option);
1679+ Q_UNUSED(values);
1680+ return QString();
1681+}
1682+
1683+QString PrinterCupsBackend::printerDeleteOptionDefault(const QString &name,
1684+ const QString &value)
1685+{
1686+ Q_UNUSED(name);
1687+ Q_UNUSED(value);
1688+ return QString();
1689+}
1690+
1691+QString PrinterCupsBackend::printerAddOption(const QString &name,
1692+ const QString &option,
1693+ const QStringList &values)
1694+{
1695+ if (!m_client->printerClassSetOption(name, option, values)) {
1696+ return m_client->getLastError();
1697+ }
1698+
1699+ return QString();
1700+}
1701+
1702+QVariant PrinterCupsBackend::printerGetOption(const QString &name,
1703+ const QString &option) const
1704+{
1705+ auto res = printerGetOptions(name, QStringList({option}));
1706+ return res[option];
1707+}
1708+
1709+QMap<QString, QVariant> PrinterCupsBackend::printerGetOptions(
1710+ const QString &name, const QStringList &options) const
1711+{
1712+ QMap<QString, QVariant> ret;
1713+
1714+ cups_dest_t *dest = getDest(name);
1715+ ppd_file_t* ppd = getPpd(name);
1716+
1717+ if (!dest || !ppd) {
1718+ return ret;
1719+ }
1720+
1721+ Q_FOREACH(const QString &option, options) {
1722+ if (option == QStringLiteral("DefaultColorModel")) {
1723+ ColorModel model;
1724+ ppd_option_t *ppdColorModel = ppdFindOption(ppd, "ColorModel");
1725+ if (ppdColorModel) {
1726+ ppd_choice_t* def = ppdFindChoice(ppdColorModel,
1727+ ppdColorModel->defchoice);
1728+ if (def) {
1729+ model = Utils::parsePpdColorModel(def->choice,
1730+ def->text,
1731+ "ColorModel");
1732+ }
1733+ }
1734+ ret[option] = QVariant::fromValue(model);
1735+ } else if (option == QStringLiteral("DefaultPrintQuality")) {
1736+ PrintQuality quality;
1737+ Q_FOREACH(const QString opt, m_knownQualityOptions) {
1738+ ppd_option_t *ppdQuality = ppdFindOption(ppd, opt.toUtf8());
1739+ if (ppdQuality) {
1740+ ppd_choice_t* def = ppdFindChoice(ppdQuality,
1741+ ppdQuality->defchoice);
1742+ if (def) {
1743+ quality = Utils::parsePpdPrintQuality(def->choice,
1744+ def->text, opt);
1745+ }
1746+ }
1747+ }
1748+ ret[option] = QVariant::fromValue(quality);
1749+ } else if (option == QStringLiteral("SupportedPrintQualities")) {
1750+ QList<PrintQuality> qualities;
1751+ Q_FOREACH(const QString &opt, m_knownQualityOptions) {
1752+ ppd_option_t *qualityOpt = ppdFindOption(ppd, opt.toUtf8());
1753+ if (qualityOpt) {
1754+ for (int i = 0; i < qualityOpt->num_choices; ++i) {
1755+ qualities.append(
1756+ Utils::parsePpdPrintQuality(
1757+ qualityOpt->choices[i].choice,
1758+ qualityOpt->choices[i].text,
1759+ opt
1760+ )
1761+ );
1762+ }
1763+ }
1764+ }
1765+ ret[option] = QVariant::fromValue(qualities);
1766+ } else if (option == QStringLiteral("SupportedColorModels")) {
1767+ QList<ColorModel> models;
1768+ ppd_option_t *colorModels = ppdFindOption(ppd, "ColorModel");
1769+ if (colorModels) {
1770+ for (int i = 0; i < colorModels->num_choices; ++i) {
1771+ models.append(
1772+ Utils::parsePpdColorModel(
1773+ colorModels->choices[i].choice,
1774+ colorModels->choices[i].text,
1775+ QStringLiteral("ColorModel")
1776+ )
1777+ );
1778+ }
1779+ }
1780+ ret[option] = QVariant::fromValue(models);
1781+ } else if (option == QStringLiteral("AcceptJobs")) {
1782+ // "true" if the destination is accepting new jobs, "false" if not.
1783+ QString res = cupsGetOption("printer-is-accepting-jobs",
1784+ dest->num_options, dest->options);
1785+ ret[option] = res.contains("true");
1786+ } else {
1787+ ppd_option_t *val = ppdFindOption(ppd, option.toUtf8());
1788+
1789+ if (val) {
1790+ qWarning() << "asking for" << option << "returns" << val->text;
1791+ } else {
1792+ qWarning() << "option" << option << "yielded no option";
1793+ }
1794+ }
1795+ }
1796+ return ret;
1797+}
1798+
1799+// FIXME: maybe have a PrinterDest iface that has a CupsDest impl?
1800+cups_dest_t* PrinterCupsBackend::makeDest(const QString &name,
1801+ const PrinterJob *options)
1802+{
1803+ // Get the cups dest
1804+ cups_dest_t *dest = getDest(name);
1805+
1806+ if (options->collate()) {
1807+ __CUPS_ADD_OPTION(dest, "Collate", "True");
1808+ } else {
1809+ __CUPS_ADD_OPTION(dest, "Collate", "False");
1810+ }
1811+
1812+ if (options->copies() > 1) {
1813+ __CUPS_ADD_OPTION(dest, "copies", QString::number(options->copies()).toLocal8Bit());
1814+ }
1815+
1816+ __CUPS_ADD_OPTION(dest, "ColorModel", options->getColorModel().name.toLocal8Bit());
1817+ __CUPS_ADD_OPTION(dest, "Duplex", Utils::duplexModeToPpdChoice(options->getDuplexMode()).toLocal8Bit());
1818+
1819+ if (options->landscape()) {
1820+ __CUPS_ADD_OPTION(dest, "landscape", "");
1821+ }
1822+
1823+ if (options->printRangeMode() == PrinterEnum::PrintRange::PageRange
1824+ && !options->printRange().isEmpty()) {
1825+ __CUPS_ADD_OPTION(dest, "page-ranges", options->printRange().toLocal8Bit());
1826+ }
1827+
1828+ PrintQuality quality = options->getPrintQuality();
1829+ __CUPS_ADD_OPTION(dest, quality.originalOption.toLocal8Bit(),
1830+ quality.name.toLocal8Bit());
1831+
1832+ if (options->reverse()) {
1833+ __CUPS_ADD_OPTION(dest, "OutputOrder", "Reverse");
1834+ } else {
1835+ __CUPS_ADD_OPTION(dest, "OutputOrder", "Normal");
1836+ }
1837+
1838+ // Always scale to fit the page for now
1839+ __CUPS_ADD_OPTION(dest, "fit-to-page", "True");
1840+
1841+ return dest;
1842+}
1843+
1844+void PrinterCupsBackend::cancelJob(const QString &name, const int jobId)
1845+{
1846+ int ret = cupsCancelJob(name.toLocal8Bit(), jobId);
1847+
1848+ if (!ret) {
1849+ qWarning() << "Failed to cancel job:" << jobId << "for" << name;
1850+ }
1851+}
1852+
1853+int PrinterCupsBackend::printFileToDest(const QString &filepath,
1854+ const QString &title,
1855+ const cups_dest_t *dest)
1856+{
1857+ qDebug() << "Printing:" << filepath << title << dest->name << dest->num_options;
1858+ return cupsPrintFile(dest->name,
1859+ filepath.toLocal8Bit(),
1860+ title.toLocal8Bit(),
1861+ dest->num_options,
1862+ dest->options);
1863+}
1864+
1865+
1866+QList<cups_job_t *> PrinterCupsBackend::getCupsJobs(const QString &name)
1867+{
1868+ QList<cups_job_t *> list;
1869+ cups_job_t *jobs;
1870+
1871+ // Get a list of the jobs that are 'mine' and only active ones
1872+ // https://www.cups.org/doc/api-cups.html#cupsGetJobs
1873+ int count;
1874+ if (name.isEmpty()) {
1875+ count = cupsGetJobs(&jobs, NULL, 1, CUPS_WHICHJOBS_ACTIVE);
1876+ } else {
1877+ count = cupsGetJobs(&jobs, name.toLocal8Bit(), 1, CUPS_WHICHJOBS_ACTIVE);
1878+ }
1879+
1880+ for (int i=0; i < count; i++) {
1881+ list.append(&jobs[i]);
1882+ }
1883+
1884+ return list;
1885+}
1886+
1887+QMap<QString, QVariant> PrinterCupsBackend::printerGetJobAttributes(
1888+ const QString &name, const int jobId)
1889+{
1890+ Q_UNUSED(name);
1891+ QMap<QString, QVariant> rawMap = m_client->printerGetJobAttributes(jobId);
1892+ QMap<QString, QVariant> map;
1893+
1894+ // Filter attributes to know values
1895+ // Do this here so we can use things such as m_knownQualityOptions
1896+
1897+ if (__CUPS_ATTR_EXISTS(rawMap, "Collate", bool)) {
1898+ map.insert("Collate", rawMap.value("Collate"));
1899+ } else {
1900+ map.insert("Collate", QVariant(true));
1901+ }
1902+
1903+ if (__CUPS_ATTR_EXISTS(rawMap, "copies", int)) {
1904+ map.insert("copies", rawMap.value("copies"));
1905+ } else {
1906+ map.insert("copies", QVariant(1));
1907+ }
1908+
1909+ if (__CUPS_ATTR_EXISTS(rawMap, "ColorModel", QString)) {
1910+ map.insert("ColorModel", rawMap.value("ColorModel"));
1911+ } else {
1912+ map.insert("ColorModel", QVariant(""));
1913+ }
1914+
1915+ if (__CUPS_ATTR_EXISTS(rawMap, "Duplex", QString)) {
1916+ map.insert("Duplex", rawMap.value("Duplex"));
1917+ } else {
1918+ map.insert("Duplex", QVariant(""));
1919+ }
1920+
1921+ if (__CUPS_ATTR_EXISTS(rawMap, "landscape", bool)) {
1922+ map.insert("landscape", rawMap.value("landscape"));
1923+ } else {
1924+ map.insert("landscape", QVariant(false));
1925+ }
1926+
1927+ if (__CUPS_ATTR_EXISTS(rawMap, "page-ranges", QList<QVariant>)) {
1928+ QList<QVariant> range = rawMap.value("page-ranges").toList();
1929+ QStringList rangeStrings;
1930+
1931+ Q_FOREACH(QVariant var, range) {
1932+ rangeStrings << var.toString();
1933+ }
1934+
1935+ map.insert("page-ranges", QVariant(rangeStrings));
1936+ } else {
1937+ map.insert("page-ranges", QVariant(QStringList()));
1938+ }
1939+
1940+ Q_FOREACH(QString qualityOption, m_knownQualityOptions) {
1941+ if (rawMap.contains(qualityOption)
1942+ && rawMap.value(qualityOption).canConvert<QString>()) {
1943+ map.insert("quality", rawMap.value(qualityOption).toString());
1944+ }
1945+ }
1946+
1947+ if (!map.contains("quality")) {
1948+ map.insert("quality", QVariant(""));
1949+ }
1950+
1951+ if (__CUPS_ATTR_EXISTS(rawMap, "OutputOrder", QString)) {
1952+ map.insert("OutputOrder", rawMap.value("OutputOrder"));
1953+ } else {
1954+ map.insert("OutputOrder", "Normal");
1955+ }
1956+
1957+ // Generate a list of messages
1958+ // TODO: for now just using job-printer-state-message, are there others?
1959+ QStringList messages;
1960+
1961+ if (__CUPS_ATTR_EXISTS(rawMap, "job-printer-state-message", QString)) {
1962+ messages << rawMap.value("job-printer-state-message").toString();
1963+ }
1964+
1965+ map.insert("messages", QVariant(messages));
1966+
1967+ return map;
1968+}
1969+
1970+
1971+QList<QSharedPointer<PrinterJob>> PrinterCupsBackend::printerGetJobs()
1972+{
1973+ auto jobs = getCupsJobs();
1974+ QList<QSharedPointer<PrinterJob>> list;
1975+
1976+ Q_FOREACH(auto job, jobs) {
1977+ auto newJob = QSharedPointer<PrinterJob>(
1978+ new PrinterJob(QString::fromUtf8(job->dest), this, job->id)
1979+ );
1980+
1981+ // Extract the times
1982+ QDateTime completedTime;
1983+ completedTime.setTimeZone(QTimeZone::systemTimeZone());
1984+ completedTime.setTime_t(job->completed_time);
1985+
1986+ QDateTime creationTime;
1987+ creationTime.setTimeZone(QTimeZone::systemTimeZone());
1988+ creationTime.setTime_t(job->creation_time);
1989+
1990+ QDateTime processingTime;
1991+ processingTime.setTimeZone(QTimeZone::systemTimeZone());
1992+ processingTime.setTime_t(job->processing_time);
1993+
1994+ // Load the information from the cups struct
1995+ newJob->setCompletedTime(completedTime);
1996+ newJob->setCreationTime(creationTime);
1997+ newJob->setProcessingTime(processingTime);
1998+ newJob->setSize(job->size);
1999+ newJob->setState(static_cast<PrinterEnum::JobState>(job->state));
2000+ newJob->setTitle(QString::fromLocal8Bit(job->title));
2001+ newJob->setUser(QString::fromLocal8Bit(job->user));
2002+
2003+ list.append(newJob);
2004+ }
2005+ if (!list.isEmpty())
2006+ cupsFreeJobs(list.size(), jobs.first());
2007+
2008+ return list;
2009+}
2010+
2011+QString PrinterCupsBackend::printerName() const
2012+{
2013+ return m_printerName;
2014+}
2015+
2016+QString PrinterCupsBackend::description() const
2017+{
2018+ return m_info.description();
2019+}
2020+
2021+QString PrinterCupsBackend::location() const
2022+{
2023+ return m_info.location();
2024+}
2025+
2026+QString PrinterCupsBackend::makeAndModel() const
2027+{
2028+ return m_info.makeAndModel();
2029+}
2030+
2031+PrinterEnum::State PrinterCupsBackend::state() const
2032+{
2033+ switch (m_info.state()) {
2034+ case QPrinter::Active:
2035+ return PrinterEnum::State::ActiveState;
2036+ case QPrinter::Aborted:
2037+ return PrinterEnum::State::AbortedState;
2038+ case QPrinter::Error:
2039+ return PrinterEnum::State::ErrorState;
2040+ case QPrinter::Idle:
2041+ default:
2042+ return PrinterEnum::State::IdleState;
2043+ }
2044+}
2045+
2046+QList<QPageSize> PrinterCupsBackend::supportedPageSizes() const
2047+{
2048+ return m_info.supportedPageSizes();
2049+}
2050+
2051+QPageSize PrinterCupsBackend::defaultPageSize() const
2052+{
2053+ return m_info.defaultPageSize();
2054+}
2055+
2056+bool PrinterCupsBackend::supportsCustomPageSizes() const
2057+{
2058+ return m_info.supportsCustomPageSizes();
2059+}
2060+
2061+QPageSize PrinterCupsBackend::minimumPhysicalPageSize() const
2062+{
2063+ return m_info.minimumPhysicalPageSize();
2064+}
2065+
2066+QPageSize PrinterCupsBackend::maximumPhysicalPageSize() const
2067+{
2068+ return m_info.maximumPhysicalPageSize();
2069+}
2070+
2071+QList<int> PrinterCupsBackend::supportedResolutions() const
2072+{
2073+ return m_info.supportedResolutions();
2074+}
2075+
2076+PrinterEnum::DuplexMode PrinterCupsBackend::defaultDuplexMode() const
2077+{
2078+ return Utils::qDuplexModeToDuplexMode(m_info.defaultDuplexMode());
2079+}
2080+
2081+QList<PrinterEnum::DuplexMode> PrinterCupsBackend::supportedDuplexModes() const
2082+{
2083+ QList<PrinterEnum::DuplexMode> list;
2084+ Q_FOREACH(const QPrinter::DuplexMode mode, m_info.supportedDuplexModes()) {
2085+ if (mode != QPrinter::DuplexAuto) {
2086+ list.append(Utils::qDuplexModeToDuplexMode(mode));
2087+ }
2088+ }
2089+
2090+ if (list.isEmpty())
2091+ list.append(PrinterEnum::DuplexMode::DuplexNone);
2092+
2093+ return list;
2094+}
2095+
2096+QList<QSharedPointer<Printer>> PrinterCupsBackend::availablePrinters()
2097+{
2098+ return QList<QSharedPointer<Printer>>();
2099+}
2100+
2101+QStringList PrinterCupsBackend::availablePrinterNames()
2102+{
2103+ return QPrinterInfo::availablePrinterNames();
2104+}
2105+
2106+QSharedPointer<Printer> PrinterCupsBackend::getPrinter(const QString &printerName)
2107+{
2108+ QPrinterInfo info = QPrinterInfo::printerInfo(printerName);
2109+ return QSharedPointer<Printer>(new Printer(new PrinterCupsBackend(m_client, info, m_notifier)));
2110+}
2111+
2112+QString PrinterCupsBackend::defaultPrinterName()
2113+{
2114+ return QPrinterInfo::defaultPrinterName();
2115+}
2116+
2117+void PrinterCupsBackend::requestPrinter(const QString &printerName)
2118+{
2119+ auto thread = new QThread;
2120+ auto loader = new PrinterLoader(printerName, m_client, m_notifier);
2121+ loader->moveToThread(thread);
2122+ connect(thread, SIGNAL(started()), loader, SLOT(load()));
2123+ connect(loader, SIGNAL(finished()), thread, SLOT(quit()));
2124+ connect(loader, SIGNAL(finished()), loader, SLOT(deleteLater()));
2125+ connect(loader, SIGNAL(loaded(QSharedPointer<Printer>)),
2126+ this, SIGNAL(printerLoaded(QSharedPointer<Printer>)));
2127+ connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
2128+ thread->start();
2129+}
2130+
2131+void PrinterCupsBackend::requestPrinterDrivers()
2132+{
2133+ auto thread = new QThread;
2134+ auto loader = new PrinterDriverLoader();
2135+ loader->moveToThread(thread);
2136+ connect(loader, SIGNAL(error(const QString&)),
2137+ this, SIGNAL(printerDriversFailedToLoad(const QString&)));
2138+ connect(this, SIGNAL(requestPrinterDriverCancel()), loader, SLOT(cancel()));
2139+ connect(thread, SIGNAL(started()), loader, SLOT(process()));
2140+ connect(loader, SIGNAL(finished()), thread, SLOT(quit()));
2141+ connect(loader, SIGNAL(finished()), loader, SLOT(deleteLater()));
2142+ connect(loader, SIGNAL(loaded(const QList<PrinterDriver>&)),
2143+ this, SIGNAL(printerDriversLoaded(const QList<PrinterDriver>&)));
2144+ connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
2145+ thread->start();
2146+}
2147+
2148+void PrinterCupsBackend::cancelPrinterDriverRequest()
2149+{
2150+ Q_EMIT requestPrinterDriverCancel();
2151+}
2152+
2153+void PrinterCupsBackend::refresh()
2154+{
2155+ if (m_printerName.isEmpty()) {
2156+ throw std::invalid_argument("Trying to refresh unnamed printer.");
2157+ } else {
2158+ m_info = QPrinterInfo::printerInfo(m_printerName);
2159+ }
2160+}
2161+
2162+void PrinterCupsBackend::createSubscription()
2163+{
2164+ m_cupsSubscriptionId = m_client->createSubscription();;
2165+}
2166+
2167+void PrinterCupsBackend::cancelSubscription()
2168+{
2169+ if (m_cupsSubscriptionId > 0)
2170+ m_client->cancelSubscription(m_cupsSubscriptionId);
2171+}
2172+
2173+QString PrinterCupsBackend::getPrinterInstance(const QString &name) const
2174+{
2175+ const auto parts = name.splitRef(QLatin1Char('/'));
2176+ QString instance;
2177+ if (parts.size() > 1)
2178+ instance = parts.at(1).toString();
2179+
2180+ return instance;
2181+}
2182+
2183+QString PrinterCupsBackend::getPrinterName(const QString &name) const
2184+{
2185+ return name.splitRef(QLatin1Char('/')).first().toString();
2186+}
2187+
2188+cups_dest_t* PrinterCupsBackend::getDest(const QString &name) const
2189+{
2190+ QString printerName = getPrinterName(name);
2191+ QString instance = getPrinterInstance(name);
2192+
2193+ if (m_dests.contains(name)) {
2194+ return m_dests[name];
2195+ } else {
2196+ m_dests[name] = m_client->getDest(printerName, instance);
2197+ return m_dests[name];
2198+ }
2199+}
2200+
2201+ppd_file_t* PrinterCupsBackend::getPpd(const QString &name) const
2202+{
2203+ QString printerName = getPrinterName(name);
2204+ QString instance = getPrinterInstance(name);
2205+
2206+ if (m_ppds.contains(name)) {
2207+ return m_ppds[name];
2208+ } else {
2209+ m_ppds[name] = m_client->getPpdFile(printerName, instance);
2210+ return m_ppds[name];
2211+ }
2212+}
2213
2214=== added file 'modules/Ubuntu/Components/Extras/Printers/backend/backend_cups.h'
2215--- modules/Ubuntu/Components/Extras/Printers/backend/backend_cups.h 1970-01-01 00:00:00 +0000
2216+++ modules/Ubuntu/Components/Extras/Printers/backend/backend_cups.h 2017-02-22 12:44:26 +0000
2217@@ -0,0 +1,152 @@
2218+/*
2219+ * Copyright (C) 2017 Canonical, Ltd.
2220+ *
2221+ * This program is free software; you can redistribute it and/or modify
2222+ * it under the terms of the GNU Lesser General Public License as published by
2223+ * the Free Software Foundation; version 3.
2224+ *
2225+ * This program is distributed in the hope that it will be useful,
2226+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2227+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2228+ * GNU Lesser General Public License for more details.
2229+ *
2230+ * You should have received a copy of the GNU Lesser General Public License
2231+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2232+ */
2233+
2234+#ifndef USC_PRINTERS_CUPS_BACKEND_H
2235+#define USC_PRINTERS_CUPS_BACKEND_H
2236+
2237+#include "backend/backend.h"
2238+#include "cups/ippclient.h"
2239+#include "cupsdnotifier.h" // Note: this file was generated.
2240+
2241+#include <cups/cups.h>
2242+
2243+#include <QPrinterInfo>
2244+
2245+class PRINTERS_DECL_EXPORT PrinterCupsBackend : public PrinterBackend
2246+{
2247+ Q_OBJECT
2248+public:
2249+ explicit PrinterCupsBackend(IppClient *client, QPrinterInfo info,
2250+ OrgCupsCupsdNotifierInterface* notifier,
2251+ QObject *parent = Q_NULLPTR);
2252+ virtual ~PrinterCupsBackend() override;
2253+
2254+ virtual bool holdsDefinition() const override;
2255+
2256+ virtual QString printerAdd(const QString &name,
2257+ const QString &uri,
2258+ const QString &ppdFile,
2259+ const QString &info,
2260+ const QString &location) override;
2261+ virtual QString printerAddWithPpd(const QString &name,
2262+ const QString &uri,
2263+ const QString &ppdFileName,
2264+ const QString &info,
2265+ const QString &location) override;
2266+ virtual QString printerDelete(const QString &name) override;
2267+ virtual QString printerSetDefault(const QString &name) override;
2268+ virtual QString printerSetEnabled(const QString &name,
2269+ const bool enabled) override;
2270+ virtual QString printerSetAcceptJobs(
2271+ const QString &name,
2272+ const bool accept,
2273+ const QString &reason = QString::null) override;
2274+ virtual QString printerSetInfo(const QString &name,
2275+ const QString &info) override;
2276+ virtual QString printerSetLocation(const QString &name,
2277+ const QString &location) override;
2278+ virtual QString printerSetShared(const QString &name,
2279+ const bool shared) override;
2280+ virtual QString printerSetJobSheets(const QString &name,
2281+ const QString &start,
2282+ const QString &end) override;
2283+ virtual QString printerSetErrorPolicy(const QString &name,
2284+ const PrinterEnum::ErrorPolicy &policy) override;
2285+
2286+ virtual QString printerSetOpPolicy(const QString &name,
2287+ const PrinterEnum::OperationPolicy &policy) override;
2288+ virtual QString printerSetUsersAllowed(const QString &name,
2289+ const QStringList &users) override;
2290+ virtual QString printerSetUsersDenied(const QString &name,
2291+ const QStringList &users) override;
2292+ virtual QString printerAddOptionDefault(const QString &name,
2293+ const QString &option,
2294+ const QStringList &values) override;
2295+ virtual QString printerDeleteOptionDefault(const QString &name,
2296+ const QString &value) override;
2297+ virtual QString printerAddOption(const QString &name,
2298+ const QString &option,
2299+ const QStringList &values) override;
2300+
2301+ virtual QVariant printerGetOption(const QString &name,
2302+ const QString &option) const override;
2303+ virtual QMap<QString, QVariant> printerGetOptions(
2304+ const QString &name, const QStringList &options
2305+ ) const override;
2306+ // FIXME: maybe have a PrinterDest iface that has a CupsDest impl?
2307+ virtual cups_dest_t* makeDest(const QString &name,
2308+ const PrinterJob *options) override;
2309+
2310+ virtual void cancelJob(const QString &name, const int jobId) override;
2311+ virtual int printFileToDest(const QString &filepath,
2312+ const QString &title,
2313+ const cups_dest_t *dest) override;
2314+ virtual QList<QSharedPointer<PrinterJob>> printerGetJobs() override;
2315+
2316+ virtual QString printerName() const override;
2317+ virtual QString description() const override;
2318+ virtual QString location() const override;
2319+ virtual QString makeAndModel() const override;
2320+
2321+ virtual PrinterEnum::State state() const override;
2322+ virtual QList<QPageSize> supportedPageSizes() const override;
2323+ virtual QPageSize defaultPageSize() const override;
2324+ virtual bool supportsCustomPageSizes() const override;
2325+
2326+ virtual QPageSize minimumPhysicalPageSize() const override;
2327+ virtual QPageSize maximumPhysicalPageSize() const override;
2328+ virtual QList<int> supportedResolutions() const override;
2329+ virtual PrinterEnum::DuplexMode defaultDuplexMode() const override;
2330+ virtual QList<PrinterEnum::DuplexMode> supportedDuplexModes() const override;
2331+
2332+ virtual QList<QSharedPointer<Printer>> availablePrinters() override;
2333+ virtual QStringList availablePrinterNames() override;
2334+ virtual QSharedPointer<Printer> getPrinter(const QString &printerName) override;
2335+ virtual QString defaultPrinterName() override;
2336+ virtual void requestPrinterDrivers() override;
2337+ virtual void requestPrinter(const QString &printerName) override;
2338+ virtual QMap<QString, QVariant> printerGetJobAttributes(
2339+ const QString &name, const int jobId) override;
2340+
2341+public Q_SLOTS:
2342+ virtual void refresh() override;
2343+ void createSubscription();
2344+
2345+Q_SIGNALS:
2346+ void cancelWorkers();
2347+ void printerDriversLoaded(const QList<PrinterDriver> &drivers);
2348+ void printerDriversFailedToLoad(const QString &errorMessage);
2349+ void requestPrinterDriverCancel();
2350+
2351+private:
2352+ void cancelSubscription();
2353+ void cancelPrinterDriverRequest();
2354+ QList<cups_job_t *> getCupsJobs(const QString &name = QStringLiteral());
2355+
2356+ QString getPrinterName(const QString &name) const;
2357+ QString getPrinterInstance(const QString &name) const;
2358+ cups_dest_t* getDest(const QString &name) const;
2359+ ppd_file_t* getPpd(const QString &name) const;
2360+ const QStringList m_knownQualityOptions;
2361+ IppClient *m_client;
2362+ QPrinterInfo m_info;
2363+ OrgCupsCupsdNotifierInterface *m_notifier;
2364+ int m_cupsSubscriptionId;
2365+ mutable QMap<QString, cups_dest_t*> m_dests; // Printer name, dest.
2366+ mutable QMap<QString, ppd_file_t*> m_ppds; // Printer name, ppd.
2367+};
2368+
2369+#endif // USC_PRINTERS_CUPS_BACKEND_H
2370
2371=== added file 'modules/Ubuntu/Components/Extras/Printers/backend/backend_pdf.cpp'
2372--- modules/Ubuntu/Components/Extras/Printers/backend/backend_pdf.cpp 1970-01-01 00:00:00 +0000
2373+++ modules/Ubuntu/Components/Extras/Printers/backend/backend_pdf.cpp 2017-02-22 12:44:26 +0000
2374@@ -0,0 +1,119 @@
2375+/*
2376+ * Copyright (C) 2017 Canonical, Ltd.
2377+ *
2378+ * This program is free software; you can redistribute it and/or modify
2379+ * it under the terms of the GNU Lesser General Public License as published by
2380+ * the Free Software Foundation; version 3.
2381+ *
2382+ * This program is distributed in the hope that it will be useful,
2383+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2384+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2385+ * GNU Lesser General Public License for more details.
2386+ *
2387+ * You should have received a copy of the GNU Lesser General Public License
2388+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2389+ */
2390+
2391+#include "i18n.h"
2392+#include "backend/backend_pdf.h"
2393+
2394+PrinterPdfBackend::PrinterPdfBackend(const QString &printerName,
2395+ QObject *parent)
2396+ : PrinterBackend(printerName, parent)
2397+{
2398+ m_type = PrinterEnum::PrinterType::PdfType;
2399+}
2400+
2401+QVariant PrinterPdfBackend::printerGetOption(const QString &name,
2402+ const QString &option) const
2403+{
2404+ auto res = printerGetOptions(name, QStringList({option}));
2405+ return res[option];
2406+}
2407+
2408+QMap<QString, QVariant> PrinterPdfBackend::printerGetOptions(
2409+ const QString &name, const QStringList &options) const
2410+{
2411+ Q_UNUSED(name);
2412+
2413+ QMap<QString, QVariant> ret;
2414+
2415+ ColorModel rgb;
2416+ rgb.colorType = PrinterEnum::ColorModelType::ColorType;
2417+ rgb.name = "RGB";
2418+ rgb.text = __("Color");
2419+
2420+ PrintQuality quality;
2421+ quality.name = __("Normal");
2422+
2423+ Q_FOREACH(const QString &option, options) {
2424+ if (option == QLatin1String("DefaultColorModel")) {
2425+ ret[option] = QVariant::fromValue(rgb);
2426+ } else if (option == QLatin1String("DefaultPrintQuality")) {
2427+ ret[option] = QVariant::fromValue(quality);
2428+ } else if (option == QLatin1String("SupportedPrintQualities")) {
2429+ auto qualities = QList<PrintQuality>({quality});
2430+ ret[option] = QVariant::fromValue(qualities);
2431+ } else if (option == QLatin1String("SupportedColorModels")) {
2432+ auto models = QList<ColorModel>{rgb};
2433+ ret[option] = QVariant::fromValue(models);
2434+ } else if (option == QLatin1String("AcceptJobs")) {
2435+ ret[option] = true;
2436+ } else {
2437+ throw std::invalid_argument("Invalid value for PDF printer: " + option.toStdString());
2438+ }
2439+ }
2440+
2441+ return ret;
2442+}
2443+
2444+QString PrinterPdfBackend::printerName() const
2445+{
2446+ return m_printerName;
2447+}
2448+
2449+PrinterEnum::State PrinterPdfBackend::state() const
2450+{
2451+ return PrinterEnum::State::IdleState;
2452+}
2453+
2454+QList<QPageSize> PrinterPdfBackend::supportedPageSizes() const
2455+{
2456+ return QList<QPageSize>{QPageSize(QPageSize::A4)};
2457+}
2458+
2459+QPageSize PrinterPdfBackend::defaultPageSize() const
2460+{
2461+ return QPageSize(QPageSize::A4);
2462+}
2463+
2464+bool PrinterPdfBackend::supportsCustomPageSizes() const
2465+{
2466+ return false;
2467+}
2468+
2469+QPageSize PrinterPdfBackend::minimumPhysicalPageSize() const
2470+{
2471+ return QPageSize(QPageSize::A4);
2472+}
2473+
2474+QPageSize PrinterPdfBackend::maximumPhysicalPageSize() const
2475+{
2476+ return QPageSize(QPageSize::A4);
2477+}
2478+
2479+QList<int> PrinterPdfBackend::supportedResolutions() const
2480+{
2481+ return QList<int>{};
2482+}
2483+
2484+PrinterEnum::DuplexMode PrinterPdfBackend::defaultDuplexMode() const
2485+{
2486+ return PrinterEnum::DuplexMode::DuplexNone;
2487+}
2488+
2489+QList<PrinterEnum::DuplexMode> PrinterPdfBackend::supportedDuplexModes() const
2490+{
2491+ return QList<PrinterEnum::DuplexMode>{PrinterEnum::DuplexMode::DuplexNone};
2492+}
2493+
2494
2495=== added file 'modules/Ubuntu/Components/Extras/Printers/backend/backend_pdf.h'
2496--- modules/Ubuntu/Components/Extras/Printers/backend/backend_pdf.h 1970-01-01 00:00:00 +0000
2497+++ modules/Ubuntu/Components/Extras/Printers/backend/backend_pdf.h 2017-02-22 12:44:26 +0000
2498@@ -0,0 +1,47 @@
2499+/*
2500+ * Copyright (C) 2017 Canonical, Ltd.
2501+ *
2502+ * This program is free software; you can redistribute it and/or modify
2503+ * it under the terms of the GNU Lesser General Public License as published by
2504+ * the Free Software Foundation; version 3.
2505+ *
2506+ * This program is distributed in the hope that it will be useful,
2507+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2508+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2509+ * GNU Lesser General Public License for more details.
2510+ *
2511+ * You should have received a copy of the GNU Lesser General Public License
2512+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2513+ */
2514+
2515+#ifndef USC_PRINTERS_PDF_BACKEND_H
2516+#define USC_PRINTERS_PDF_BACKEND_H
2517+
2518+#include "backend/backend.h"
2519+
2520+class PRINTERS_DECL_EXPORT PrinterPdfBackend : public PrinterBackend
2521+{
2522+public:
2523+ explicit PrinterPdfBackend(const QString &printerName,
2524+ QObject *parent = Q_NULLPTR);
2525+ virtual QVariant printerGetOption(const QString &name,
2526+ const QString &option) const override;
2527+ virtual QMap<QString, QVariant> printerGetOptions(
2528+ const QString &name, const QStringList &options
2529+ ) const override;
2530+
2531+ virtual QString printerName() const override;
2532+
2533+ virtual PrinterEnum::State state() const override;
2534+ virtual QList<QPageSize> supportedPageSizes() const override;
2535+ virtual QPageSize defaultPageSize() const override;
2536+ virtual bool supportsCustomPageSizes() const override;
2537+
2538+ virtual QPageSize minimumPhysicalPageSize() const override;
2539+ virtual QPageSize maximumPhysicalPageSize() const override;
2540+ virtual QList<int> supportedResolutions() const override;
2541+ virtual PrinterEnum::DuplexMode defaultDuplexMode() const override;
2542+ virtual QList<PrinterEnum::DuplexMode> supportedDuplexModes() const override;
2543+};
2544+
2545+#endif // USC_PRINTERS_PDF_BACKEND_H
2546
2547=== added directory 'modules/Ubuntu/Components/Extras/Printers/cups'
2548=== added file 'modules/Ubuntu/Components/Extras/Printers/cups/ippclient.cpp'
2549--- modules/Ubuntu/Components/Extras/Printers/cups/ippclient.cpp 1970-01-01 00:00:00 +0000
2550+++ modules/Ubuntu/Components/Extras/Printers/cups/ippclient.cpp 2017-02-22 12:44:26 +0000
2551@@ -0,0 +1,988 @@
2552+/*
2553+ * Copyright (C) 2017 Canonical, Ltd.
2554+ * Copyright (C) 2014 John Layt <jlayt@kde.org>
2555+ * Copyright (C) 2002, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2013, 2014 Red Hat, Inc.
2556+ * Copyright (C) 2008 Novell, Inc.
2557+ *
2558+ * This program is free software; you can redistribute it and/or modify
2559+ * it under the terms of the GNU Lesser General Public License as published by
2560+ * the Free Software Foundation; version 3.
2561+ *
2562+ * This program is distributed in the hope that it will be useful,
2563+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2564+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2565+ * GNU Lesser General Public License for more details.
2566+ *
2567+ * You should have received a copy of the GNU Lesser General Public License
2568+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2569+ */
2570+
2571+#include "cups/ippclient.h"
2572+
2573+#include <errno.h>
2574+#include <string.h>
2575+#include <unistd.h>
2576+
2577+#include <QDebug>
2578+#include <QDateTime>
2579+#include <QTimeZone>
2580+#include <QUrl>
2581+
2582+IppClient::IppClient()
2583+ : m_connection(httpConnectEncrypt(cupsServer(),
2584+ ippPort(),
2585+ cupsEncryption()))
2586+{
2587+ if (!m_connection) {
2588+ qCritical("Failed to connect to cupsd");
2589+ } else {
2590+ qDebug("Successfully connected to cupsd.");
2591+ }
2592+}
2593+
2594+IppClient::~IppClient()
2595+{
2596+ if (m_connection)
2597+ httpClose(m_connection);
2598+}
2599+
2600+bool IppClient::printerDelete(const QString &printerName)
2601+{
2602+ return sendNewSimpleRequest(CUPS_DELETE_PRINTER, printerName.toUtf8(),
2603+ CupsResource::CupsResourceAdmin);
2604+}
2605+
2606+bool IppClient::printerAdd(const QString &printerName,
2607+ const QString &printerUri,
2608+ const QString &ppdFile,
2609+ const QString &info,
2610+ const QString &location)
2611+{
2612+ ipp_t *request;
2613+
2614+ if (!isPrinterNameValid(printerName)) {
2615+ setInternalStatus(QString("%1 is not a valid printer name.").arg(printerName));
2616+ return false;
2617+ }
2618+
2619+ if (!isStringValid(info)) {
2620+ setInternalStatus(QString("%1 is not a valid description.").arg(info));
2621+ return false;
2622+ }
2623+
2624+ if (!isStringValid(location)) {
2625+ setInternalStatus(QString("%1 is not a valid location.").arg(location));
2626+ return false;
2627+ }
2628+
2629+ if (!isStringValid(ppdFile)) {
2630+ setInternalStatus(QString("%1 is not a valid ppd file.").arg(ppdFile));
2631+ return false;
2632+ }
2633+
2634+ if (!isStringValid(printerUri)) {
2635+ setInternalStatus(QString("%1 is not a valid printer uri.").arg(printerUri));
2636+ return false;
2637+ }
2638+
2639+
2640+ request = ippNewRequest (CUPS_ADD_MODIFY_PRINTER);
2641+ addPrinterUri(request, printerName);
2642+ addRequestingUsername(request, NULL);
2643+
2644+ ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_NAME,
2645+ "printer-name", NULL, printerName.toUtf8());
2646+
2647+ if (!ppdFile.isEmpty()) {
2648+ ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_NAME,
2649+ "ppd-name", NULL, ppdFile.toUtf8());
2650+ }
2651+ if (!printerUri.isEmpty()) {
2652+ ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_URI,
2653+ "device-uri", NULL, printerUri.toUtf8());
2654+ }
2655+ if (!info.isEmpty()) {
2656+ ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_TEXT,
2657+ "printer-info", NULL, info.toUtf8());
2658+ }
2659+ if (!location.isEmpty()) {
2660+ ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_TEXT,
2661+ "printer-location", NULL, location.toUtf8());
2662+ }
2663+
2664+ return sendRequest(request, CupsResourceAdmin);
2665+}
2666+
2667+bool IppClient::printerAddWithPpdFile(const QString &printerName,
2668+ const QString &printerUri,
2669+ const QString &ppdFileName,
2670+ const QString &info,
2671+ const QString &location)
2672+{
2673+ ipp_t *request;
2674+
2675+ if (!isPrinterNameValid(printerName)) {
2676+ setInternalStatus(QString("%1 is not a valid printer name.").arg(printerName));
2677+ return false;
2678+ }
2679+
2680+ if (!isStringValid(info)) {
2681+ setInternalStatus(QString("%1 is not a valid description.").arg(info));
2682+ return false;
2683+ }
2684+
2685+ if (!isStringValid(location)) {
2686+ setInternalStatus(QString("%1 is not a valid location.").arg(location));
2687+ return false;
2688+ }
2689+
2690+ if (!isStringValid(ppdFileName)) {
2691+ setInternalStatus(QString("%1 is not a valid ppd file name.").arg(ppdFileName));
2692+ return false;
2693+ }
2694+
2695+ if (!isStringValid(printerUri)) {
2696+ setInternalStatus(QString("%1 is not a valid printer uri.").arg(printerUri));
2697+ return false;
2698+ }
2699+
2700+ request = ippNewRequest(CUPS_ADD_MODIFY_PRINTER);
2701+ addPrinterUri(request, printerName);
2702+ addRequestingUsername(request, NULL);
2703+
2704+ ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_NAME,
2705+ "printer-name", NULL, printerName.toUtf8());
2706+
2707+ /* In this specific case of ADD_MODIFY, the URI can be NULL/empty since
2708+ * we provide a complete PPD. And cups fails if we pass an empty
2709+ * string. */
2710+ if (!printerUri.isEmpty()) {
2711+ ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_URI,
2712+ "device-uri", NULL, printerUri.toUtf8());
2713+ }
2714+
2715+ if (!info.isEmpty()) {
2716+ ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_TEXT,
2717+ "printer-info", NULL, info.toUtf8());
2718+ }
2719+ if (!location.isEmpty()) {
2720+ ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_TEXT,
2721+ "printer-location", NULL, location.toUtf8());
2722+ }
2723+
2724+ return postRequest(request, ppdFileName.toUtf8(), CupsResourceAdmin);
2725+}
2726+
2727+bool IppClient::printerSetDefault(const QString &printerName)
2728+{
2729+ return sendNewSimpleRequest(CUPS_SET_DEFAULT, printerName.toUtf8(),
2730+ CupsResource::CupsResourceAdmin);
2731+}
2732+
2733+bool IppClient::printerSetEnabled(const QString &printerName,
2734+ const bool enabled)
2735+{
2736+ ipp_op_t op;
2737+ op = enabled ? IPP_RESUME_PRINTER : IPP_PAUSE_PRINTER;
2738+ return sendNewSimpleRequest(op, printerName, CupsResourceAdmin);
2739+}
2740+
2741+/* reason must be empty if accept is true */
2742+bool IppClient::printerSetAcceptJobs(const QString &printerName,
2743+ const bool accept,
2744+ const QString &reason)
2745+{
2746+ ipp_t *request;
2747+
2748+ if (accept && !reason.isEmpty()) {
2749+ setInternalStatus("Accepting jobs does not take a reason.");
2750+ return false;
2751+ }
2752+
2753+ if (!isPrinterNameValid(printerName)) {
2754+ setInternalStatus(QString("%1 is not a valid printer name.").arg(printerName));
2755+ return false;
2756+ }
2757+
2758+ if (!isStringValid(reason)) {
2759+ setInternalStatus(QString("%1 is not a valid reason.").arg(reason));
2760+ return false;
2761+ }
2762+
2763+ if (accept) {
2764+ return sendNewSimpleRequest(CUPS_ACCEPT_JOBS, printerName.toUtf8(),
2765+ CupsResourceAdmin);
2766+ } else {
2767+ request = ippNewRequest(CUPS_REJECT_JOBS);
2768+ addPrinterUri(request, printerName);
2769+ addRequestingUsername(request, NULL);
2770+
2771+ if (!reason.isEmpty())
2772+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_TEXT,
2773+ "printer-state-message", NULL, reason.toUtf8());
2774+
2775+ return sendRequest(request, CupsResourceAdmin);
2776+ }
2777+}
2778+
2779+
2780+bool IppClient::printerClassSetInfo(const QString &name,
2781+ const QString &info)
2782+{
2783+ if (!isPrinterNameValid(name)) {
2784+ setInternalStatus(QString("%1 is not a valid printer name.").arg(name));
2785+ return false;
2786+ }
2787+
2788+ if (!isStringValid(info)) {
2789+ setInternalStatus(QString("%1 is not a valid description.").arg(info));
2790+ return false;
2791+ }
2792+
2793+ return sendNewPrinterClassRequest(name, IPP_TAG_PRINTER, IPP_TAG_TEXT,
2794+ "printer-info", info);
2795+}
2796+
2797+bool IppClient::printerClassSetOption(const QString &name,
2798+ const QString &option,
2799+ const QStringList &values)
2800+{
2801+ bool isClass;
2802+ int length = 0;
2803+ ipp_t *request;
2804+ ipp_attribute_t *attr;
2805+ QString newPpdFile;
2806+ bool retval;
2807+
2808+ if (!isPrinterNameValid(name)) {
2809+ setInternalStatus(QString("%1 is not a valid printer name.").arg(name));
2810+ return false;
2811+ }
2812+
2813+ if (!isStringValid(option)) {
2814+ setInternalStatus(QString("%1 is not a valid option.").arg(option));
2815+ return false;
2816+ }
2817+
2818+ Q_FOREACH(const QString &val, values) {
2819+ if (!isStringValid(val)) {
2820+ setInternalStatus(QString("%1 is not a valid value.").arg(val));
2821+ return false;
2822+ }
2823+ length++;
2824+ }
2825+
2826+ if (length == 0) {
2827+ setInternalStatus("No valid values.");
2828+ return false;
2829+ }
2830+
2831+ isClass = printerIsClass(name);
2832+
2833+ /* We permit only one value to change in PPD file because we are setting
2834+ * default value in it. */
2835+ if (!isClass && length == 1) {
2836+ cups_option_t *options = NULL;
2837+ int numOptions = 0;
2838+ QString ppdfile;
2839+
2840+ numOptions = cupsAddOption(option.toUtf8(),
2841+ values[0].toUtf8(),
2842+ numOptions, &options);
2843+
2844+ ppdfile = QString(cupsGetPPD(name.toUtf8()));
2845+
2846+ newPpdFile = preparePpdForOptions(ppdfile.toUtf8(),
2847+ options, numOptions).toLatin1().data();
2848+
2849+ unlink(ppdfile.toUtf8());
2850+ cupsFreeOptions(numOptions, options);
2851+ }
2852+
2853+ if (isClass) {
2854+ request = ippNewRequest(CUPS_ADD_MODIFY_CLASS);
2855+ addClassUri(request, name);
2856+ } else {
2857+ request = ippNewRequest(CUPS_ADD_MODIFY_PRINTER);
2858+ addPrinterUri(request, name);
2859+ }
2860+
2861+ addRequestingUsername(request, NULL);
2862+
2863+ if (length == 1) {
2864+ ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_NAME,
2865+ option.toUtf8(),
2866+ NULL,
2867+ values[0].toUtf8());
2868+ } else {
2869+ int i;
2870+
2871+ attr = ippAddStrings(request, IPP_TAG_PRINTER, IPP_TAG_NAME,
2872+ option.toUtf8(), length, NULL, NULL);
2873+
2874+ for (i = 0; i < length; i++)
2875+ ippSetString(request, &attr, i, values[i].toUtf8());
2876+ }
2877+
2878+ if (!newPpdFile.isEmpty()) {
2879+ retval = postRequest(request, newPpdFile, CupsResourceAdmin);
2880+
2881+ unlink(newPpdFile.toUtf8());
2882+ // TODO: fix leak here.
2883+ } else {
2884+ retval = sendRequest(request, CupsResourceAdmin);
2885+ }
2886+
2887+ return retval;
2888+}
2889+
2890+QMap<QString, QVariant> IppClient::printerGetJobAttributes(const int jobId)
2891+{
2892+ ipp_t *request;
2893+ QMap<QString, QVariant> map;
2894+
2895+ // Construct request
2896+ request = ippNewRequest(IPP_GET_JOB_ATTRIBUTES);
2897+ QString uri = QStringLiteral("ipp://localhost/jobs/") + QString::number(jobId);
2898+ qDebug() << "URI:" << uri;
2899+
2900+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", NULL, uri.toStdString().data());
2901+
2902+
2903+ // Send request and construct reply
2904+ ipp_t *reply;
2905+ const QString resourceChar = getResource(CupsResourceRoot);
2906+ reply = cupsDoRequest(m_connection, request,
2907+ resourceChar.toUtf8());
2908+
2909+ // Check if the reply is OK
2910+ if (isReplyOk(reply, false)) {
2911+ // Loop through the attributes
2912+ ipp_attribute_t *attr;
2913+
2914+ for (attr = ippFirstAttribute(reply); attr; attr = ippNextAttribute(reply)) {
2915+ QVariant value = getAttributeValue(attr);
2916+ map.insert(ippGetName(attr), value);
2917+ }
2918+ } else {
2919+ qWarning() << "Not able to get attributes of job:" << jobId;
2920+ }
2921+
2922+ // Destruct the reply if valid
2923+ if (reply) {
2924+ ippDelete(reply);
2925+ }
2926+
2927+ return map;
2928+}
2929+
2930+
2931+/* This function sets given options to specified values in file 'ppdfile'.
2932+ * This needs to be done because of applications which use content of PPD files
2933+ * instead of IPP attributes.
2934+ * CUPS doesn't do this automatically (but hopefully will starting with 1.6) */
2935+QString IppClient::preparePpdForOptions(const QString &ppdfile,
2936+ cups_option_t *options, int numOptions)
2937+{
2938+ auto ppdfile_c = ppdfile.toUtf8();
2939+ ppd_file_t *ppd;
2940+ bool ppdchanged = false;
2941+ QString result;
2942+ QString error;
2943+ char newppdfile[PATH_MAX];
2944+ cups_file_t *in = NULL;
2945+ cups_file_t *out = NULL;
2946+ char line[CPH_STR_MAXLEN];
2947+ char keyword[CPH_STR_MAXLEN];
2948+ char *keyptr;
2949+ ppd_choice_t *choice;
2950+ QString value;
2951+ QLatin1String defaultStr("*Default");
2952+
2953+ ppd = ppdOpenFile(ppdfile_c);
2954+ if (!ppd) {
2955+ error = QString("Unable to open PPD file \"%1\": %2")
2956+ .arg(ppdfile).arg(strerror(errno));
2957+ setInternalStatus(error);
2958+ goto out;
2959+ }
2960+
2961+ in = cupsFileOpen(ppdfile_c, "r");
2962+ if (!in) {
2963+ error = QString("Unable to open PPD file \"%1\": %2")
2964+ .arg(ppdfile).arg(strerror(errno));
2965+ setInternalStatus(error);
2966+ goto out;
2967+ }
2968+
2969+ out = cupsTempFile2(newppdfile, sizeof(newppdfile));
2970+ if (!out) {
2971+ setInternalStatus("Unable to create temporary file");
2972+ goto out;
2973+ }
2974+
2975+ /* Mark default values and values of options we are changing. */
2976+ ppdMarkDefaults(ppd);
2977+ cupsMarkOptions(ppd, numOptions, options);
2978+
2979+ while (cupsFileGets(in, line, sizeof(line))) {
2980+ QString line_qs(line);
2981+ if (!line_qs.startsWith(defaultStr)) {
2982+ cupsFilePrintf(out, "%s\n", line);
2983+ } else {
2984+ /* This part parses lines with *Default on their
2985+ * beginning. For instance:
2986+ * "*DefaultResolution: 1200dpi" becomes:
2987+ * - keyword: Resolution
2988+ * - keyptr: 1200dpi
2989+ */
2990+ strncpy(keyword, line + defaultStr.size(), sizeof(keyword));
2991+
2992+ for (keyptr = keyword; *keyptr; keyptr++)
2993+ if (*keyptr == ':' || isspace (*keyptr & 255))
2994+ break;
2995+
2996+ *keyptr++ = '\0';
2997+ while (isspace (*keyptr & 255))
2998+ keyptr++;
2999+
3000+ QString keyword_sq(keyword);
3001+ QString keyptr_qs(keyptr);
3002+
3003+ /* We have to change PageSize if any of PageRegion,
3004+ * PageSize, PaperDimension or ImageableArea changes.
3005+ * We change PageRegion if PageSize is not available. */
3006+ if (keyword_sq == "PageRegion" ||
3007+ keyword_sq == "PageSize" ||
3008+ keyword_sq == "PaperDimension" ||
3009+ keyword_sq == "ImageableArea") {
3010+
3011+ choice = ppdFindMarkedChoice(ppd, "PageSize");
3012+ if (!choice)
3013+ choice = ppdFindMarkedChoice(ppd, "PageRegion");
3014+ } else {
3015+ choice = ppdFindMarkedChoice(ppd, keyword);
3016+ }
3017+
3018+
3019+ QString choice_qs;
3020+ if (choice) {
3021+ choice_qs = choice->choice;
3022+ }
3023+
3024+ if (choice && choice_qs != keyptr_qs) {
3025+ /* We have to set the value in PPD manually if
3026+ * a custom value was passed in:
3027+ * cupsMarkOptions() marks the choice as
3028+ * "Custom". We want to set this value with our
3029+ * input. */
3030+ if (choice_qs != "Custom") {
3031+ cupsFilePrintf(out,
3032+ "*Default%s: %s\n",
3033+ keyword,
3034+ choice->choice);
3035+ ppdchanged = true;
3036+ } else {
3037+ value = cupsGetOption(keyword, numOptions, options);
3038+ if (!value.isEmpty()) {
3039+ cupsFilePrintf(out,
3040+ "*Default%s: %s\n",
3041+ keyword,
3042+ value.toStdString().c_str());
3043+ ppdchanged = true;
3044+ } else {
3045+ cupsFilePrintf(out, "%s\n", line);
3046+ }
3047+ }
3048+ } else {
3049+ cupsFilePrintf(out, "%s\n", line);
3050+ }
3051+ }
3052+ }
3053+
3054+ if (ppdchanged)
3055+ result = QString::fromUtf8(newppdfile);
3056+ else
3057+ unlink(newppdfile);
3058+
3059+out:
3060+ if (in)
3061+ cupsFileClose(in);
3062+ if (out)
3063+ cupsFileClose(out);
3064+ if (ppd)
3065+ ppdClose(ppd);
3066+
3067+ return result;
3068+}
3069+
3070+
3071+bool IppClient::sendNewPrinterClassRequest(const QString &printerName,
3072+ ipp_tag_t group, ipp_tag_t type,
3073+ const QString &name,
3074+ const QString &value)
3075+{
3076+ ipp_t *request;
3077+
3078+ request = ippNewRequest(CUPS_ADD_MODIFY_PRINTER);
3079+ addPrinterUri(request, printerName);
3080+ addRequestingUsername(request, QString());
3081+ ippAddString(request, group, type, name.toUtf8(), NULL,
3082+ value.toUtf8());
3083+
3084+ if (sendRequest(request, CupsResource::CupsResourceAdmin))
3085+ return true;
3086+
3087+ // it failed, maybe it was a class?
3088+ if (m_lastStatus != IPP_NOT_POSSIBLE) {
3089+ return false;
3090+ }
3091+
3092+ // TODO: implement class modification <here>.
3093+ return false;
3094+}
3095+
3096+void IppClient::addPrinterUri(ipp_t *request, const QString &name)
3097+{
3098+ QUrl uri(QString("ipp://localhost/printers/%1").arg(name));
3099+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
3100+ "printer-uri", NULL, uri.toEncoded().data());
3101+}
3102+
3103+void IppClient::addRequestingUsername(ipp_t *request, const QString &username)
3104+{
3105+ if (!username.isEmpty())
3106+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
3107+ "requesting-user-name", NULL,
3108+ username.toUtf8());
3109+ else
3110+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
3111+ "requesting-user-name", NULL, cupsUser());
3112+}
3113+
3114+QString IppClient::getLastError() const
3115+{
3116+ return m_internalStatus;
3117+}
3118+
3119+const QString IppClient::getResource(const IppClient::CupsResource &resource)
3120+{
3121+ switch (resource) {
3122+ case CupsResourceRoot:
3123+ return "/";
3124+ case CupsResourceAdmin:
3125+ return "/admin/";
3126+ case CupsResourceJobs:
3127+ return "/jobs/";
3128+ default:
3129+ qCritical("Asking for a resource with no match.");
3130+ return "/";
3131+ }
3132+}
3133+
3134+bool IppClient::isPrinterNameValid(const QString &name)
3135+{
3136+ int i;
3137+ int len;
3138+
3139+ /* Quoting the lpadmin man page:
3140+ * CUPS allows printer names to contain any printable character
3141+ * except SPACE, TAB, "/", or "#".
3142+ * On top of that, validate_name() in lpadmin.c (from cups) checks that
3143+ * the string is 127 characters long, or shorter. */
3144+
3145+ /* no empty string */
3146+ if (name.isEmpty())
3147+ return false;
3148+
3149+ len = name.size();
3150+ /* no string that is too long; see comment at the beginning of the
3151+ * validation code block */
3152+ if (len > 127)
3153+ return false;
3154+
3155+ /* only printable characters, no space, no /, no # */
3156+ for (i = 0; i < len; i++) {
3157+ const QChar c = name.at(i);
3158+ if (!c.isPrint())
3159+ return false;
3160+ if (c.isSpace())
3161+ return false;
3162+ if (c == '/' || c == '#')
3163+ return false;
3164+ }
3165+ return true;
3166+}
3167+
3168+bool IppClient::isStringValid(const QString &string, const bool checkNull,
3169+ const int maxLength)
3170+{
3171+ if (isStringPrintable(string, checkNull, maxLength))
3172+ return true;
3173+ return false;
3174+}
3175+
3176+bool IppClient::isStringPrintable(const QString &string, const bool checkNull,
3177+ const int maxLength)
3178+{
3179+ int i;
3180+ int len;
3181+
3182+ /* no null string */
3183+ if (string.isNull())
3184+ return !checkNull;
3185+
3186+ len = string.size();
3187+ if (maxLength > 0 && len > maxLength)
3188+ return false;
3189+
3190+ /* only printable characters */
3191+ for (i = 0; i < len; i++) {
3192+ const QChar c = string.at(i);
3193+ if (!c.isPrint())
3194+ return false;
3195+ }
3196+ return true;
3197+}
3198+
3199+void IppClient::setInternalStatus(const QString &status)
3200+{
3201+ if (!m_internalStatus.isNull()) {
3202+ m_internalStatus = QString::null;
3203+ }
3204+
3205+ if (status.isNull()) {
3206+ m_internalStatus = QString::null;
3207+ } else {
3208+ m_internalStatus = status;
3209+
3210+ // Only used for errors for now.
3211+ qCritical() << status;
3212+ }
3213+}
3214+
3215+bool IppClient::postRequest(ipp_t *request, const QString &file,
3216+ const CupsResource &resource)
3217+{
3218+ ipp_t *reply;
3219+ QString resourceChar;
3220+
3221+ resourceChar = getResource(resource);
3222+
3223+ if (!file.isEmpty())
3224+ reply = cupsDoFileRequest(m_connection, request, resourceChar.toUtf8(),
3225+ file.toUtf8());
3226+ else
3227+ reply = cupsDoFileRequest(m_connection, request, resourceChar.toUtf8(),
3228+ NULL);
3229+
3230+ return handleReply(reply);
3231+}
3232+
3233+
3234+bool IppClient::sendRequest(ipp_t *request, const CupsResource &resource)
3235+{
3236+ ipp_t *reply;
3237+ const QString resourceChar = getResource(resource);
3238+ reply = cupsDoRequest(m_connection, request,
3239+ resourceChar.toUtf8());
3240+ return handleReply(reply);
3241+}
3242+
3243+bool IppClient::sendNewSimpleRequest(ipp_op_t op, const QString &printerName,
3244+ const IppClient::CupsResource &resource)
3245+{
3246+ ipp_t *request;
3247+
3248+ if (!isPrinterNameValid(printerName))
3249+ return false;
3250+
3251+ request = ippNewRequest(op);
3252+ addPrinterUri(request, printerName);
3253+ addRequestingUsername(request, NULL);
3254+
3255+ return sendRequest(request, resource);
3256+}
3257+
3258+bool IppClient::handleReply(ipp_t *reply)
3259+{
3260+ bool retval;
3261+ retval = isReplyOk(reply, false);
3262+ if (reply)
3263+ ippDelete(reply);
3264+
3265+ return retval;
3266+}
3267+
3268+bool IppClient::isReplyOk(ipp_t *reply, bool deleteIfReplyNotOk)
3269+{
3270+ /* reset the internal status: we'll use the cups status */
3271+ m_lastStatus = IPP_STATUS_CUPS_INVALID;
3272+
3273+ if (reply && ippGetStatusCode(reply) <= IPP_OK_CONFLICT) {
3274+ m_lastStatus = IPP_OK;
3275+ return true;
3276+ } else {
3277+ setErrorFromReply(reply);
3278+ qWarning() << Q_FUNC_INFO << "Cups HTTP error:" << cupsLastErrorString();
3279+
3280+ if (deleteIfReplyNotOk && reply)
3281+ ippDelete(reply);
3282+
3283+ return false;
3284+ }
3285+}
3286+
3287+void IppClient::setErrorFromReply(ipp_t *reply)
3288+{
3289+ if (reply)
3290+ m_lastStatus = ippGetStatusCode(reply);
3291+ else
3292+ m_lastStatus = cupsLastError();
3293+}
3294+
3295+bool IppClient::printerIsClass(const QString &name)
3296+{
3297+ const char * const attrs[1] = { "member-names" };
3298+ ipp_t *request;
3299+ QString resource;
3300+ ipp_t *reply;
3301+ bool retval;
3302+
3303+ // Class/Printer name validation is equal.
3304+ if (!isPrinterNameValid(name)) {
3305+ setInternalStatus(QString("%1 is not a valid printer name.").arg(name));
3306+ return false;
3307+ }
3308+
3309+ request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
3310+ addClassUri(request, name);
3311+ addRequestingUsername(request, QString());
3312+ ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
3313+ "requested-attributes", 1, NULL, attrs);
3314+
3315+ resource = getResource(CupsResource::CupsResourceRoot);
3316+ reply = cupsDoRequest(m_connection, request, resource.toUtf8());
3317+
3318+ if (!isReplyOk(reply, true))
3319+ return true;
3320+
3321+ /* Note: we need to look if the attribute is there, since we get a
3322+ * reply if the name is a printer name and not a class name. The
3323+ * attribute is the only way to distinguish the two cases. */
3324+ retval = ippFindAttribute(reply, attrs[0], IPP_TAG_NAME) != NULL;
3325+
3326+ if (reply)
3327+ ippDelete(reply);
3328+
3329+ return retval;
3330+}
3331+
3332+void IppClient::addClassUri(ipp_t *request, const QString &name)
3333+{
3334+ QUrl uri(QString("ipp://localhost/printers/%1").arg(name));
3335+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
3336+ "printer-uri", NULL, uri.toEncoded().data());
3337+}
3338+
3339+ppd_file_t* IppClient::getPpdFile(const QString &name,
3340+ const QString &instance) const
3341+{
3342+ Q_UNUSED(instance);
3343+
3344+ ppd_file_t* file = 0;
3345+ const char *ppdFile = cupsGetPPD(name.toUtf8());
3346+ if (ppdFile) {
3347+ file = ppdOpenFile(ppdFile);
3348+ unlink(ppdFile);
3349+ }
3350+ if (file) {
3351+ ppdMarkDefaults(file);
3352+ } else {
3353+ file = 0;
3354+ }
3355+
3356+ return file;
3357+}
3358+
3359+cups_dest_t* IppClient::getDest(const QString &name,
3360+ const QString &instance) const
3361+{
3362+ cups_dest_t *dest = 0;
3363+
3364+ if (instance.isEmpty()) {
3365+ dest = cupsGetNamedDest(m_connection, name.toUtf8(), NULL);
3366+ } else {
3367+ dest = cupsGetNamedDest(m_connection, name.toUtf8(), instance.toUtf8());
3368+ }
3369+ return dest;
3370+}
3371+
3372+ipp_t* IppClient::createPrinterDriversRequest(
3373+ const QString &deviceId, const QString &language, const QString &makeModel,
3374+ const QString &product, const QStringList &includeSchemes,
3375+ const QStringList &excludeSchemes
3376+)
3377+{
3378+ Q_UNUSED(includeSchemes);
3379+ Q_UNUSED(excludeSchemes);
3380+
3381+ ipp_t *request;
3382+
3383+ request = ippNewRequest(CUPS_GET_PPDS);
3384+
3385+ if (!deviceId.isEmpty())
3386+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_TEXT, "ppd-device-id",
3387+ NULL, deviceId.toUtf8());
3388+ if (!language.isEmpty())
3389+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE, "ppd-language",
3390+ NULL, language.toUtf8());
3391+ if (!makeModel.isEmpty())
3392+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_TEXT, "ppd-make-and-model",
3393+ NULL, makeModel.toUtf8());
3394+ if (!product.isEmpty())
3395+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_TEXT, "ppd-product",
3396+ NULL, product.toUtf8());
3397+
3398+ // Do the request and get return the response.
3399+ const QString resourceChar = getResource(CupsResourceRoot);
3400+ return cupsDoRequest(m_connection, request,
3401+ resourceChar.toUtf8());
3402+}
3403+
3404+int IppClient::createSubscription()
3405+{
3406+ ipp_t *req;
3407+ ipp_t *resp;
3408+ ipp_attribute_t *attr;
3409+ int subscriptionId = -1;
3410+
3411+ req = ippNewRequest(IPP_CREATE_PRINTER_SUBSCRIPTION);
3412+ ippAddString(req, IPP_TAG_OPERATION, IPP_TAG_URI,
3413+ "printer-uri", NULL, "/");
3414+ ippAddString(req, IPP_TAG_SUBSCRIPTION, IPP_TAG_KEYWORD,
3415+ "notify-events", NULL, "all");
3416+ ippAddString(req, IPP_TAG_SUBSCRIPTION, IPP_TAG_URI,
3417+ "notify-recipient-uri", NULL, "dbus://");
3418+ ippAddInteger(req, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
3419+ "notify-lease-duration", 0);
3420+
3421+ resp = cupsDoRequest(m_connection, req,
3422+ getResource(CupsResourceRoot).toUtf8());
3423+ if (!isReplyOk(resp, true)) {
3424+ return subscriptionId;
3425+ }
3426+
3427+ attr = ippFindAttribute(resp, "notify-subscription-id", IPP_TAG_INTEGER);
3428+
3429+ if (!attr) {
3430+ qWarning() << "ipp-create-printer-subscription response doesn't"
3431+ " contain subscription id.";
3432+ } else {
3433+ subscriptionId = ippGetInteger(attr, 0);
3434+ }
3435+
3436+ ippDelete (resp);
3437+
3438+ return subscriptionId;
3439+}
3440+
3441+void IppClient::cancelSubscription(const int &subscriptionId)
3442+{
3443+ ipp_t *req;
3444+ ipp_t *resp;
3445+
3446+ if (subscriptionId <= 0) {
3447+ return;
3448+ }
3449+
3450+ req = ippNewRequest(IPP_CANCEL_SUBSCRIPTION);
3451+ ippAddString(req, IPP_TAG_OPERATION, IPP_TAG_URI,
3452+ "printer-uri", NULL, "/");
3453+ ippAddInteger(req, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
3454+ "notify-subscription-id", subscriptionId);
3455+
3456+ resp = cupsDoRequest(m_connection, req,
3457+ getResource(CupsResourceRoot).toUtf8());
3458+ if (!isReplyOk(resp, true)) {
3459+ return;
3460+ }
3461+
3462+ ippDelete(resp);
3463+}
3464+
3465+QVariant IppClient::getAttributeValue(ipp_attribute_t *attr, int index) const
3466+{
3467+ QVariant var;
3468+
3469+ if (ippGetCount(attr) > 1 && index < 0) {
3470+ QList<QVariant> list;
3471+
3472+ for (int i=0; i < ippGetCount(attr); i++) {
3473+ list.append(getAttributeValue(attr, i));
3474+ }
3475+
3476+ var = QVariant::fromValue<QList<QVariant>>(list);
3477+ } else {
3478+ if (index == -1) {
3479+ index = 0;
3480+ }
3481+
3482+ switch (ippGetValueTag(attr)) {
3483+ case IPP_TAG_NAME:
3484+ case IPP_TAG_TEXT:
3485+ case IPP_TAG_KEYWORD:
3486+ case IPP_TAG_URI:
3487+ case IPP_TAG_CHARSET:
3488+ case IPP_TAG_MIMETYPE:
3489+ case IPP_TAG_LANGUAGE:
3490+ var = QVariant::fromValue<QString>(ippGetString(attr, index, NULL));
3491+ break;
3492+ case IPP_TAG_INTEGER:
3493+ case IPP_TAG_ENUM:
3494+ var = QVariant::fromValue<int>(ippGetInteger(attr, index));
3495+ break;
3496+ case IPP_TAG_BOOLEAN:
3497+ var = QVariant::fromValue<bool>(ippGetBoolean(attr, index));
3498+ break;
3499+ case IPP_TAG_RANGE: {
3500+ QString range;
3501+ int upper;
3502+ int lower = ippGetRange(attr, index, &upper);
3503+
3504+ // Build a string similar to "1-3" "5-" "8" "-4"
3505+ if (lower != INT_MIN) {
3506+ range += QString::number(lower);
3507+ }
3508+
3509+ if (lower != upper) {
3510+ range += QStringLiteral("-");
3511+
3512+ if (upper != INT_MAX) {
3513+ range += QString::number(upper);
3514+ }
3515+ }
3516+
3517+ var = QVariant(range);
3518+ break;
3519+ }
3520+ case IPP_TAG_NOVALUE:
3521+ var = QVariant();
3522+ break;
3523+ case IPP_TAG_DATE: {
3524+ time_t time = ippDateToTime(ippGetDate(attr, index));
3525+ QDateTime datetime;
3526+ datetime.setTimeZone(QTimeZone::systemTimeZone());
3527+ datetime.setTime_t(time);
3528+
3529+ var = QVariant::fromValue<QDateTime>(datetime);
3530+ break;
3531+ }
3532+ default:
3533+ qWarning() << "Unknown IPP value tab 0x" << ippGetValueTag(attr);
3534+ break;
3535+ }
3536+ }
3537+
3538+ return var;
3539+}
3540
3541=== added file 'modules/Ubuntu/Components/Extras/Printers/cups/ippclient.h'
3542--- modules/Ubuntu/Components/Extras/Printers/cups/ippclient.h 1970-01-01 00:00:00 +0000
3543+++ modules/Ubuntu/Components/Extras/Printers/cups/ippclient.h 2017-02-22 12:44:26 +0000
3544@@ -0,0 +1,121 @@
3545+/*
3546+ * Copyright (C) 2017 Canonical, Ltd.
3547+ *
3548+ * This program is free software; you can redistribute it and/or modify
3549+ * it under the terms of the GNU Lesser General Public License as published by
3550+ * the Free Software Foundation; version 3.
3551+ *
3552+ * This program is distributed in the hope that it will be useful,
3553+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
3554+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3555+ * GNU Lesser General Public License for more details.
3556+ *
3557+ * You should have received a copy of the GNU Lesser General Public License
3558+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
3559+ */
3560+
3561+#ifndef USC_PRINTERS_CUPS_IPPCLIENT_H
3562+#define USC_PRINTERS_CUPS_IPPCLIENT_H
3563+
3564+#include "structs.h"
3565+
3566+#include <cups/cups.h>
3567+#include <cups/http.h>
3568+#include <cups/ipp.h>
3569+#include <cups/ppd.h>
3570+
3571+#include <QString>
3572+#include <QStringList>
3573+
3574+/* From https://bugzilla.novell.com/show_bug.cgi?id=447444#c5
3575+ * We need to define a maximum length for strings to avoid cups
3576+ * thinking there are multiple lines.
3577+ */
3578+#define CPH_STR_MAXLEN 512
3579+
3580+class IppClient
3581+{
3582+public:
3583+ explicit IppClient();
3584+ ~IppClient();
3585+
3586+ bool printerDelete(const QString &printerName);
3587+ bool printerAdd(const QString &printerName,
3588+ const QString &printerUri,
3589+ const QString &ppdFile,
3590+ const QString &info,
3591+ const QString &location);
3592+
3593+ bool printerAddWithPpdFile(const QString &printerName,
3594+ const QString &printerUri,
3595+ const QString &ppdFileName,
3596+ const QString &info,
3597+ const QString &location);
3598+ bool printerSetDefault(const QString &printerName);
3599+ bool printerSetEnabled(const QString &printerName, const bool enabled);
3600+ bool printerSetAcceptJobs(const QString &printerName, const bool accept,
3601+ const QString &reason);
3602+ bool printerClassSetInfo(const QString &name, const QString &info);
3603+ bool printerClassSetOption(const QString &name, const QString &option,
3604+ const QStringList &values);
3605+ ppd_file_t* getPpdFile(const QString &name, const QString &instance) const;
3606+ cups_dest_t* getDest(const QString &name, const QString &instance) const;
3607+
3608+ QMap<QString, QVariant> printerGetJobAttributes(const int jobId);
3609+
3610+ QString getLastError() const;
3611+
3612+ // Note: This response needs to be free by the caller.
3613+ ipp_t* createPrinterDriversRequest(
3614+ const QString &deviceId, const QString &language,
3615+ const QString &makeModel, const QString &product,
3616+ const QStringList &includeSchemes, const QStringList &excludeSchemes
3617+ );
3618+ int createSubscription();
3619+ void cancelSubscription(const int &subscriptionId);
3620+
3621+private:
3622+ enum CupsResource
3623+ {
3624+ CupsResourceRoot = 0,
3625+ CupsResourceAdmin,
3626+ CupsResourceJobs,
3627+ };
3628+
3629+ bool sendNewPrinterClassRequest(const QString &printerName,
3630+ ipp_tag_t group,
3631+ ipp_tag_t type,
3632+ const QString &name,
3633+ const QString &value);
3634+ static void addPrinterUri(ipp_t *request, const QString &name);
3635+ static void addRequestingUsername(ipp_t *request, const QString &username);
3636+ static const QString getResource(const CupsResource &resource);
3637+ static bool isPrinterNameValid(const QString &name);
3638+ static void addClassUri(ipp_t *request, const QString &name);
3639+ static bool isStringValid(const QString &string,
3640+ const bool checkNull = false,
3641+ const int maxLength = 512);
3642+ static bool isStringPrintable(const QString &string, const bool checkNull,
3643+ const int maxLength);
3644+ QString preparePpdForOptions(const QString &ppdfile,
3645+ cups_option_t *options,
3646+ int numOptions);
3647+ bool printerIsClass(const QString &name);
3648+ void setInternalStatus(const QString &status);
3649+ bool postRequest(ipp_t *request, const QString &file,
3650+ const CupsResource &resource);
3651+ bool sendRequest(ipp_t *request, const CupsResource &resource);
3652+ bool sendNewSimpleRequest(ipp_op_t op, const QString &printerName,
3653+ const CupsResource &resource);
3654+ bool handleReply(ipp_t *reply);
3655+ bool isReplyOk(ipp_t *reply, bool deleteIfReplyNotOk);
3656+ void setErrorFromReply(ipp_t *reply);
3657+ QVariant getAttributeValue(ipp_attribute_t *attr, int index=-1) const;
3658+
3659+ http_t *m_connection;
3660+ ipp_status_t m_lastStatus = IPP_OK;
3661+ mutable QString m_internalStatus = QString::null;
3662+};
3663+
3664+
3665+#endif // USC_PRINTERS_CUPS_IPPCLIENT_H
3666
3667=== added file 'modules/Ubuntu/Components/Extras/Printers/cups/printerdriverloader.cpp'
3668--- modules/Ubuntu/Components/Extras/Printers/cups/printerdriverloader.cpp 1970-01-01 00:00:00 +0000
3669+++ modules/Ubuntu/Components/Extras/Printers/cups/printerdriverloader.cpp 2017-02-22 12:44:26 +0000
3670@@ -0,0 +1,128 @@
3671+/*
3672+ * Copyright (C) 2017 Canonical, Ltd.
3673+ *
3674+ * This program is free software; you can redistribute it and/or modify
3675+ * it under the terms of the GNU Lesser General Public License as published by
3676+ * the Free Software Foundation; version 3.
3677+ *
3678+ * This program is distributed in the hope that it will be useful,
3679+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
3680+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3681+ * GNU Lesser General Public License for more details.
3682+ *
3683+ * You should have received a copy of the GNU Lesser General Public License
3684+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
3685+ */
3686+
3687+#include "printerdriverloader.h"
3688+
3689+PrinterDriverLoader::PrinterDriverLoader(
3690+ const QString &deviceId, const QString &language,
3691+ const QString &makeModel, const QString &product,
3692+ const QStringList &includeSchemes, const QStringList &excludeSchemes)
3693+ : m_deviceId(deviceId)
3694+ , m_language(language)
3695+ , m_makeModel(makeModel)
3696+ , m_product(product)
3697+ , m_includeSchemes(includeSchemes)
3698+ , m_excludeSchemes(excludeSchemes)
3699+{
3700+}
3701+
3702+PrinterDriverLoader::~PrinterDriverLoader()
3703+{
3704+}
3705+
3706+void PrinterDriverLoader::process()
3707+{
3708+ m_running = true;
3709+
3710+ ipp_t* response = client.createPrinterDriversRequest(
3711+ m_deviceId, m_language, m_makeModel, m_product, m_includeSchemes,
3712+ m_excludeSchemes
3713+ );
3714+
3715+ // Note: if the response somehow fails, we return.
3716+ if (!response || ippGetStatusCode(response) > IPP_OK_CONFLICT) {
3717+ QString err(cupsLastErrorString());
3718+ qWarning() << Q_FUNC_INFO << "Cups HTTP error:" << err;
3719+
3720+ if (response)
3721+ ippDelete(response);
3722+
3723+ Q_EMIT error(err);
3724+ Q_EMIT finished();
3725+ return;
3726+ }
3727+
3728+ ipp_attribute_t *attr;
3729+ QByteArray ppdDeviceId;
3730+ QByteArray ppdLanguage;
3731+ QByteArray ppdMakeModel;
3732+ QByteArray ppdName;
3733+
3734+ // cups_option_t option;
3735+ QList<PrinterDriver> drivers;
3736+
3737+ for (attr = ippFirstAttribute(response); attr != NULL && m_running; attr = ippNextAttribute(response)) {
3738+
3739+ while (attr != NULL && ippGetGroupTag(attr) != IPP_TAG_PRINTER)
3740+ attr = ippNextAttribute(response);
3741+
3742+ if (attr == NULL)
3743+ break;
3744+
3745+ // Pull the needed attributes from this PPD...
3746+ ppdDeviceId = "NONE";
3747+ ppdLanguage.clear();
3748+ ppdMakeModel.clear();
3749+ ppdName.clear();
3750+
3751+ while (attr != NULL && ippGetGroupTag(attr) == IPP_TAG_PRINTER) {
3752+ if (!strcmp(ippGetName(attr), "ppd-device-id") &&
3753+ ippGetValueTag(attr) == IPP_TAG_TEXT) {
3754+ ppdDeviceId = ippGetString(attr, 0, NULL);
3755+ } else if (!strcmp(ippGetName(attr), "ppd-natural-language") &&
3756+ ippGetValueTag(attr) == IPP_TAG_LANGUAGE) {
3757+ ppdLanguage = ippGetString(attr, 0, NULL);
3758+
3759+ } else if (!strcmp(ippGetName(attr), "ppd-make-and-model") &&
3760+ ippGetValueTag(attr) == IPP_TAG_TEXT) {
3761+ ppdMakeModel = ippGetString(attr, 0, NULL);
3762+ } else if (!strcmp(ippGetName(attr), "ppd-name") &&
3763+ ippGetValueTag(attr) == IPP_TAG_NAME) {
3764+
3765+ ppdName = ippGetString(attr, 0, NULL);
3766+ }
3767+
3768+ attr = ippNextAttribute(response);
3769+ }
3770+
3771+ // See if we have everything needed...
3772+ if (ppdLanguage.isEmpty() || ppdMakeModel.isEmpty() ||
3773+ ppdName.isEmpty()) {
3774+ if (attr == NULL)
3775+ break;
3776+ else
3777+ continue;
3778+ }
3779+
3780+ PrinterDriver m;
3781+ m.name = ppdName;
3782+ m.deviceId = ppdDeviceId;
3783+ m.makeModel = ppdMakeModel;
3784+ m.language = ppdLanguage;
3785+
3786+ drivers.append(m);
3787+ }
3788+
3789+ ippDelete(response);
3790+
3791+ Q_EMIT loaded(drivers);
3792+ Q_EMIT finished();
3793+}
3794+
3795+void PrinterDriverLoader::cancel()
3796+{
3797+ m_running = false;
3798+}
3799
3800=== added file 'modules/Ubuntu/Components/Extras/Printers/cups/printerdriverloader.h'
3801--- modules/Ubuntu/Components/Extras/Printers/cups/printerdriverloader.h 1970-01-01 00:00:00 +0000
3802+++ modules/Ubuntu/Components/Extras/Printers/cups/printerdriverloader.h 2017-02-22 12:44:26 +0000
3803@@ -0,0 +1,61 @@
3804+/*
3805+ * Copyright (C) 2017 Canonical, Ltd.
3806+ *
3807+ * This program is free software; you can redistribute it and/or modify
3808+ * it under the terms of the GNU Lesser General Public License as published by
3809+ * the Free Software Foundation; version 3.
3810+ *
3811+ * This program is distributed in the hope that it will be useful,
3812+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
3813+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3814+ * GNU Lesser General Public License for more details.
3815+ *
3816+ * You should have received a copy of the GNU Lesser General Public License
3817+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
3818+ */
3819+
3820+#ifndef USC_PRINTERS_CUPS_DRIVERLOADER_H
3821+#define USC_PRINTERS_CUPS_DRIVERLOADER_H
3822+
3823+#include "ippclient.h"
3824+#include "structs.h"
3825+
3826+#include <QObject>
3827+#include <QString>
3828+#include <QStringList>
3829+
3830+class PrinterDriverLoader : public QObject
3831+{
3832+ Q_OBJECT
3833+public:
3834+ PrinterDriverLoader(
3835+ const QString &deviceId = "",
3836+ const QString &language = "",
3837+ const QString &makeModel = "",
3838+ const QString &product = "",
3839+ const QStringList &includeSchemes = QStringList(),
3840+ const QStringList &excludeSchemes = QStringList());
3841+ ~PrinterDriverLoader();
3842+
3843+public Q_SLOTS:
3844+ void process();
3845+ void cancel();
3846+
3847+Q_SIGNALS:
3848+ void finished();
3849+ void loaded(const QList<PrinterDriver> &drivers);
3850+ void error(const QString &error);
3851+
3852+private:
3853+ QString m_deviceId = QString::null;
3854+ QString m_language = QString::null;
3855+ QString m_makeModel = QString::null;
3856+ QString m_product = QString::null;
3857+ QStringList m_includeSchemes;
3858+ QStringList m_excludeSchemes;
3859+
3860+ bool m_running = false;
3861+ IppClient client;
3862+};
3863+
3864+#endif // USC_PRINTERS_CUPS_DRIVERLOADER_H
3865
3866=== added file 'modules/Ubuntu/Components/Extras/Printers/cups/printerloader.cpp'
3867--- modules/Ubuntu/Components/Extras/Printers/cups/printerloader.cpp 1970-01-01 00:00:00 +0000
3868+++ modules/Ubuntu/Components/Extras/Printers/cups/printerloader.cpp 2017-02-22 12:44:26 +0000
3869@@ -0,0 +1,53 @@
3870+/*
3871+ * Copyright (C) 2017 Canonical, Ltd.
3872+ *
3873+ * This program is free software; you can redistribute it and/or modify
3874+ * it under the terms of the GNU Lesser General Public License as published by
3875+ * the Free Software Foundation; version 3.
3876+ *
3877+ * This program is distributed in the hope that it will be useful,
3878+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
3879+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3880+ * GNU Lesser General Public License for more details.
3881+ *
3882+ * You should have received a copy of the GNU Lesser General Public License
3883+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
3884+ */
3885+
3886+#include "backend/backend_pdf.h"
3887+#include "backend/backend_cups.h"
3888+#include "printerloader.h"
3889+
3890+#include <QPrinterInfo>
3891+
3892+class PrinterCupsBackend;
3893+PrinterLoader::PrinterLoader(const QString &printerName,
3894+ IppClient *client,
3895+ OrgCupsCupsdNotifierInterface* notifier,
3896+ QObject *parent)
3897+ : QObject(parent)
3898+ , m_printerName(printerName)
3899+ , m_client(client)
3900+ , m_notifier(notifier)
3901+{
3902+}
3903+
3904+PrinterLoader::~PrinterLoader()
3905+{
3906+}
3907+
3908+void PrinterLoader::load()
3909+{
3910+ QPrinterInfo info = QPrinterInfo::printerInfo(m_printerName);
3911+ auto backend = new PrinterCupsBackend(m_client, info, m_notifier);
3912+
3913+ // Dest or PPD was null, but we know it's name so we will use it.
3914+ if (info.printerName().isEmpty()) {
3915+ backend->setPrinterNameInternal(m_printerName);
3916+ }
3917+
3918+ auto p = QSharedPointer<Printer>(new Printer(backend));
3919+
3920+ Q_EMIT loaded(p);
3921+ Q_EMIT finished();
3922+}
3923
3924=== added file 'modules/Ubuntu/Components/Extras/Printers/cups/printerloader.h'
3925--- modules/Ubuntu/Components/Extras/Printers/cups/printerloader.h 1970-01-01 00:00:00 +0000
3926+++ modules/Ubuntu/Components/Extras/Printers/cups/printerloader.h 2017-02-22 12:44:26 +0000
3927@@ -0,0 +1,49 @@
3928+/*
3929+ * Copyright (C) 2017 Canonical, Ltd.
3930+ *
3931+ * This program is free software; you can redistribute it and/or modify
3932+ * it under the terms of the GNU Lesser General Public License as published by
3933+ * the Free Software Foundation; version 3.
3934+ *
3935+ * This program is distributed in the hope that it will be useful,
3936+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
3937+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3938+ * GNU Lesser General Public License for more details.
3939+ *
3940+ * You should have received a copy of the GNU Lesser General Public License
3941+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
3942+ */
3943+
3944+#ifndef USC_PRINTERS_CUPS_PRINTERLOADER_H
3945+#define USC_PRINTERS_CUPS_PRINTERLOADER_H
3946+
3947+#include "cups/ippclient.h"
3948+#include "cupsdnotifier.h" // Note: this file was generated.
3949+#include "printer/printer.h"
3950+
3951+#include <QList>
3952+#include <QObject>
3953+#include <QSharedPointer>
3954+
3955+class PrinterLoader : public QObject
3956+{
3957+ Q_OBJECT
3958+ const QString m_printerName;
3959+ IppClient *m_client;
3960+ OrgCupsCupsdNotifierInterface *m_notifier;
3961+public:
3962+ explicit PrinterLoader(const QString &printerName,
3963+ IppClient *client,
3964+ OrgCupsCupsdNotifierInterface* notifier,
3965+ QObject *parent = Q_NULLPTR);
3966+ ~PrinterLoader();
3967+
3968+public Q_SLOTS:
3969+ void load();
3970+
3971+Q_SIGNALS:
3972+ void finished();
3973+ void loaded(QSharedPointer<Printer> printer);
3974+};
3975+
3976+#endif // USC_PRINTERS_CUPS_PRINTERLOADER_H
3977
3978=== added file 'modules/Ubuntu/Components/Extras/Printers/enums.h'
3979--- modules/Ubuntu/Components/Extras/Printers/enums.h 1970-01-01 00:00:00 +0000
3980+++ modules/Ubuntu/Components/Extras/Printers/enums.h 2017-02-22 12:44:26 +0000
3981@@ -0,0 +1,141 @@
3982+/*
3983+ * Copyright (C) 2017 Canonical, Ltd.
3984+ *
3985+ * This program is free software; you can redistribute it and/or modify
3986+ * it under the terms of the GNU Lesser General Public License as published by
3987+ * the Free Software Foundation; version 3.
3988+ *
3989+ * This program is distributed in the hope that it will be useful,
3990+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
3991+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3992+ * GNU Lesser General Public License for more details.
3993+ *
3994+ * You should have received a copy of the GNU Lesser General Public License
3995+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
3996+ */
3997+
3998+#ifndef USC_PRINTERS_ENUMS_H
3999+#define USC_PRINTERS_ENUMS_H
4000+
4001+#include "printers_global.h"
4002+
4003+#include <QtCore/QObject>
4004+
4005+class PRINTERS_DECL_EXPORT PrinterEnum
4006+{
4007+ Q_GADGET
4008+
4009+public:
4010+ enum class AccessControl
4011+ {
4012+ AccessAllow = 0,
4013+ AccessDeny,
4014+ };
4015+ Q_ENUM(AccessControl)
4016+
4017+ enum class ColorModelType
4018+ {
4019+ GrayType = 0,
4020+ ColorType,
4021+ UnknownType,
4022+ };
4023+ Q_ENUM(ColorModelType)
4024+
4025+ enum class ColorSpace
4026+ {
4027+ NSpace = 0,
4028+ RGBSpace,
4029+ RGBKSpace,
4030+ GraySpace,
4031+ CMYSpace,
4032+ CMYKSpace,
4033+ UnknownSpace,
4034+ };
4035+ Q_ENUM(ColorSpace)
4036+
4037+ enum class ColorOrganization
4038+ {
4039+ ChunkyOrganization = 0,
4040+ BandedOrganization,
4041+ PlanarOrganization,
4042+ UnknownOrganization,
4043+ };
4044+ Q_ENUM(ColorOrganization)
4045+
4046+ enum class CartridgeType
4047+ {
4048+ BlackCartridge = 0,
4049+ CyanCartridge,
4050+ MagentaCartridge,
4051+ YellowCartridge,
4052+ RedCartridge,
4053+ GreenCartridge,
4054+ BlueCartridge,
4055+ UnknownCartridge,
4056+ WhiteCartridge,
4057+ };
4058+ Q_ENUM(CartridgeType)
4059+
4060+ enum class DuplexMode
4061+ {
4062+ DuplexNone = 0,
4063+ DuplexLongSide,
4064+ DuplexShortSide,
4065+ };
4066+ Q_ENUM(DuplexMode)
4067+
4068+ enum class ErrorPolicy
4069+ {
4070+ RetryOnError = 0,
4071+ AbortOnError,
4072+ StopPrinterOnError,
4073+ RetryCurrentOnError,
4074+ };
4075+ Q_ENUM(ErrorPolicy)
4076+
4077+ // Match enums from ipp_jstate_t
4078+ enum class JobState
4079+ {
4080+ Pending = 3,
4081+ Held,
4082+ Processing,
4083+ Stopped,
4084+ Canceled,
4085+ Aborted,
4086+ Complete,
4087+ };
4088+ Q_ENUM(JobState)
4089+
4090+ enum class OperationPolicy
4091+ {
4092+ DefaultOperation = 0,
4093+ AuthenticatedOperation,
4094+ };
4095+ Q_ENUM(OperationPolicy)
4096+
4097+ enum class PrintRange
4098+ {
4099+ AllPages = 0,
4100+ PageRange,
4101+ };
4102+ Q_ENUM(PrintRange)
4103+
4104+ enum class State
4105+ {
4106+ IdleState = 0,
4107+ ActiveState,
4108+ AbortedState,
4109+ ErrorState,
4110+ };
4111+ Q_ENUM(State)
4112+
4113+ enum class PrinterType
4114+ {
4115+ ProxyType = 0, // Represents a printer not yet loaded.
4116+ CupsType,
4117+ PdfType,
4118+ };
4119+ Q_ENUM(PrinterType)
4120+};
4121+
4122+#endif // USC_PRINTERS_ENUMS_H
4123
4124=== added file 'modules/Ubuntu/Components/Extras/Printers/i18n.cpp'
4125--- modules/Ubuntu/Components/Extras/Printers/i18n.cpp 1970-01-01 00:00:00 +0000
4126+++ modules/Ubuntu/Components/Extras/Printers/i18n.cpp 2017-02-22 12:44:26 +0000
4127@@ -0,0 +1,44 @@
4128+/*
4129+ * Copyright (C) 2014, 2017 Canonical, Ltd.
4130+ *
4131+ * This program is free software; you can redistribute it and/or modify
4132+ * it under the terms of the GNU Lesser General Public License as published by
4133+ * the Free Software Foundation; version 3.
4134+ *
4135+ * This program is distributed in the hope that it will be useful,
4136+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
4137+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4138+ * GNU Lesser General Public License for more details.
4139+ *
4140+ * You should have received a copy of the GNU Lesser General Public License
4141+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
4142+ *
4143+ * Authored by: Ken VanDine <ken.vandine@canonical.com>
4144+ * Andrew Hayzen <andrew.hayzen@canonical.com>
4145+ */
4146+
4147+#define NO_TR_OVERRIDE
4148+#include "i18n.h"
4149+
4150+#include <libintl.h>
4151+
4152+const char *thisDomain = "";
4153+
4154+void initTr(const char *domain, const char *localeDir)
4155+{
4156+ // Don't bind the domain or set textdomain as it changes the Apps domain
4157+ // as well. Instead store the domain and use it in the lookups
4158+// bindtextdomain(domain, localeDir);
4159+// textdomain(domain);
4160+ Q_UNUSED(localeDir);
4161+
4162+ thisDomain = domain;
4163+}
4164+
4165+QString __(const char *text, const char *domain)
4166+{
4167+ Q_UNUSED(domain);
4168+
4169+ // Use the stored domain
4170+ return QString::fromUtf8(dgettext(thisDomain, text));
4171+}
4172
4173=== added file 'modules/Ubuntu/Components/Extras/Printers/i18n.h'
4174--- modules/Ubuntu/Components/Extras/Printers/i18n.h 1970-01-01 00:00:00 +0000
4175+++ modules/Ubuntu/Components/Extras/Printers/i18n.h 2017-02-22 12:44:26 +0000
4176@@ -0,0 +1,29 @@
4177+/*
4178+ * Copyright (C) 2014, 2017 Canonical, Ltd.
4179+ *
4180+ * This program is free software; you can redistribute it and/or modify
4181+ * it under the terms of the GNU Lesser General Public License as published by
4182+ * the Free Software Foundation; version 3.
4183+ *
4184+ * This program is distributed in the hope that it will be useful,
4185+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
4186+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4187+ * GNU Lesser General Public License for more details.
4188+ *
4189+ * You should have received a copy of the GNU Lesser General Public License
4190+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
4191+ *
4192+ * Authored by: Ken VanDine <ken.vandine@canonical.com>
4193+ * Andrew Hayzen <andrew.hayzen@canonical.com>
4194+ */
4195+
4196+#ifndef I18N_H
4197+#define I18N_H
4198+
4199+#include <QtCore/QString>
4200+
4201+void initTr(const char *domain, const char *localeDir);
4202+QString __(const char *text, const char *domain = 0);
4203+
4204+#endif // I18N_H
4205+
4206
4207=== added directory 'modules/Ubuntu/Components/Extras/Printers/models'
4208=== added file 'modules/Ubuntu/Components/Extras/Printers/models/drivermodel.cpp'
4209--- modules/Ubuntu/Components/Extras/Printers/models/drivermodel.cpp 1970-01-01 00:00:00 +0000
4210+++ modules/Ubuntu/Components/Extras/Printers/models/drivermodel.cpp 2017-02-22 12:44:26 +0000
4211@@ -0,0 +1,175 @@
4212+/*
4213+ * Copyright (C) 2017 Canonical, Ltd.
4214+ *
4215+ * This program is free software; you can redistribute it and/or modify
4216+ * it under the terms of the GNU Lesser General Public License as published by
4217+ * the Free Software Foundation; version 3.
4218+ *
4219+ * This program is distributed in the hope that it will be useful,
4220+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
4221+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4222+ * GNU Lesser General Public License for more details.
4223+ *
4224+ * You should have received a copy of the GNU Lesser General Public License
4225+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
4226+ */
4227+
4228+#include "backend/backend_cups.h"
4229+#include "models/drivermodel.h"
4230+
4231+#include <QDebug>
4232+#include <QtConcurrent>
4233+
4234+DriverModel::DriverModel(PrinterBackend *backend, QObject *parent)
4235+ : QAbstractListModel(parent)
4236+ , m_backend(backend)
4237+{
4238+ connect(m_backend, SIGNAL(printerDriversLoaded(const QList<PrinterDriver>&)),
4239+ this, SLOT(printerDriversLoaded(const QList<PrinterDriver>&)));
4240+
4241+ QObject::connect(&m_watcher,
4242+ &QFutureWatcher<PrinterDriver>::finished,
4243+ this,
4244+ &DriverModel::filterFinished);
4245+
4246+}
4247+
4248+DriverModel::~DriverModel()
4249+{
4250+ cancel();
4251+}
4252+
4253+int DriverModel::rowCount(const QModelIndex &parent) const
4254+{
4255+ Q_UNUSED(parent);
4256+ return m_drivers.size();
4257+}
4258+
4259+int DriverModel::count() const
4260+{
4261+ return rowCount();
4262+}
4263+
4264+QVariant DriverModel::data(const QModelIndex &index, int role) const
4265+{
4266+ QVariant ret;
4267+
4268+ if ((0 <= index.row()) && (index.row() < m_drivers.size())) {
4269+
4270+ auto driver = m_drivers[index.row()];
4271+
4272+ switch (role) {
4273+ case Qt::DisplayRole:
4274+ ret = driver.toString();
4275+ break;
4276+ case NameRole:
4277+ ret = driver.name;
4278+ break;
4279+ case DeviceIdRole:
4280+ ret = driver.deviceId;
4281+ break;
4282+ case LanguageRole:
4283+ ret = driver.language;
4284+ break;
4285+ case MakeModelRole:
4286+ ret = driver.makeModel;
4287+ break;
4288+ }
4289+ }
4290+
4291+ return ret;
4292+}
4293+
4294+QHash<int, QByteArray> DriverModel::roleNames() const
4295+{
4296+ static QHash<int,QByteArray> names;
4297+
4298+ if (Q_UNLIKELY(names.empty())) {
4299+ names[Qt::DisplayRole] = "displayName";
4300+ names[NameRole] = "name";
4301+ names[DeviceIdRole] = "deviceId";
4302+ names[LanguageRole] = "language";
4303+ names[MakeModelRole] = "makeModel";
4304+ }
4305+
4306+ return names;
4307+}
4308+
4309+void DriverModel::setFilter(const QString& pattern)
4310+{
4311+ QList<QByteArray> needles;
4312+ Q_FOREACH(const QString patternPart, pattern.toLower().split(" ")) {
4313+ needles.append(patternPart.toUtf8());
4314+ }
4315+ QList<PrinterDriver> list;
4316+
4317+ if (m_watcher.isRunning())
4318+ m_watcher.cancel();
4319+
4320+ if (pattern.isEmpty()) {
4321+ setModel(m_originalDrivers);
4322+ m_filter = pattern;
4323+ return;
4324+ }
4325+
4326+ if (!m_filter.isEmpty() && !m_drivers.isEmpty() &&
4327+ pattern.startsWith(m_filter))
4328+ list = m_drivers; // search in the smaller list
4329+ else
4330+ list = m_originalDrivers; //search in the whole list
4331+
4332+ m_filter = pattern;
4333+
4334+ QFuture<PrinterDriver> future(QtConcurrent::filtered(list,
4335+ [needles] (const PrinterDriver &driver) {
4336+ QByteArray haystack = driver.makeModel.toLower();
4337+ Q_FOREACH(const QByteArray needle, needles) {
4338+ if (!haystack.contains(needle)) {
4339+ return false;
4340+ }
4341+ }
4342+ return true;
4343+ }
4344+ )
4345+ );
4346+
4347+ Q_EMIT filterBegin();
4348+
4349+ m_watcher.setFuture(future);
4350+}
4351+
4352+QString DriverModel::filter() const
4353+{
4354+ return m_filter;
4355+}
4356+
4357+void DriverModel::filterFinished()
4358+{
4359+ setModel(m_watcher.future().results());
4360+}
4361+
4362+void DriverModel::load()
4363+{
4364+ m_backend->requestPrinterDrivers();
4365+}
4366+
4367+void DriverModel::cancel()
4368+{
4369+ if (m_watcher.isRunning())
4370+ m_watcher.cancel();
4371+}
4372+
4373+void DriverModel::printerDriversLoaded(const QList<PrinterDriver> &drivers)
4374+{
4375+ m_originalDrivers = drivers;
4376+ setModel(m_originalDrivers);
4377+}
4378+
4379+void DriverModel::setModel(const QList<PrinterDriver> &drivers)
4380+{
4381+ beginResetModel();
4382+ m_drivers = drivers;
4383+ endResetModel();
4384+
4385+ Q_EMIT filterComplete();
4386+}
4387
4388=== added file 'modules/Ubuntu/Components/Extras/Printers/models/drivermodel.h'
4389--- modules/Ubuntu/Components/Extras/Printers/models/drivermodel.h 1970-01-01 00:00:00 +0000
4390+++ modules/Ubuntu/Components/Extras/Printers/models/drivermodel.h 2017-02-22 12:44:26 +0000
4391@@ -0,0 +1,82 @@
4392+/*
4393+ * Copyright (C) 2017 Canonical, Ltd.
4394+ *
4395+ * This program is free software; you can redistribute it and/or modify
4396+ * it under the terms of the GNU Lesser General Public License as published by
4397+ * the Free Software Foundation; version 3.
4398+ *
4399+ * This program is distributed in the hope that it will be useful,
4400+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
4401+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4402+ * GNU Lesser General Public License for more details.
4403+ *
4404+ * You should have received a copy of the GNU Lesser General Public License
4405+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
4406+ */
4407+
4408+#ifndef USC_PRINTER_DRIVERMODEL_H
4409+#define USC_PRINTER_DRIVERMODEL_H
4410+
4411+#include "printers_global.h"
4412+
4413+#include "structs.h"
4414+
4415+#include <QAbstractListModel>
4416+#include <QFutureWatcher>
4417+#include <QModelIndex>
4418+#include <QObject>
4419+#include <QVariant>
4420+
4421+class PRINTERS_DECL_EXPORT DriverModel : public QAbstractListModel
4422+{
4423+ Q_OBJECT
4424+ Q_PROPERTY(int count READ count NOTIFY countChanged)
4425+public:
4426+ explicit DriverModel(PrinterBackend *backend, QObject *parent = Q_NULLPTR);
4427+ ~DriverModel();
4428+
4429+ enum Roles
4430+ {
4431+ // Qt::DisplayRole holds driver name
4432+ NameRole = Qt::UserRole,
4433+ DeviceIdRole,
4434+ LanguageRole,
4435+ MakeModelRole,
4436+ LastRole = MakeModelRole,
4437+ };
4438+
4439+ virtual int rowCount(const QModelIndex &parent = QModelIndex()) const override;
4440+ virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
4441+ virtual QHash<int, QByteArray> roleNames() const override;
4442+
4443+ int count() const;
4444+
4445+ QString filter() const;
4446+ void setFilter(const QString& pattern);
4447+
4448+public Q_SLOTS:
4449+ // Start loading the model.
4450+ void load();
4451+
4452+ // Cancel loading of the model.
4453+ void cancel();
4454+
4455+private Q_SLOTS:
4456+ void printerDriversLoaded(const QList<PrinterDriver> &drivers);
4457+ void filterFinished();
4458+
4459+Q_SIGNALS:
4460+ void countChanged();
4461+ void filterBegin();
4462+ void filterComplete();
4463+
4464+private:
4465+ void setModel(const QList<PrinterDriver> &drivers);
4466+ PrinterBackend *m_backend;
4467+ QList<PrinterDriver> m_drivers;
4468+ QList<PrinterDriver> m_originalDrivers;
4469+ QString m_filter;
4470+ QFutureWatcher<PrinterDriver> m_watcher;
4471+};
4472+
4473+#endif // USC_PRINTER_DRIVERMODEL_H
4474
4475=== added file 'modules/Ubuntu/Components/Extras/Printers/models/jobmodel.cpp'
4476--- modules/Ubuntu/Components/Extras/Printers/models/jobmodel.cpp 1970-01-01 00:00:00 +0000
4477+++ modules/Ubuntu/Components/Extras/Printers/models/jobmodel.cpp 2017-02-22 12:44:26 +0000
4478@@ -0,0 +1,391 @@
4479+/*
4480+ * Copyright (C) 2017 Canonical, Ltd.
4481+ *
4482+ * This program is free software; you can redistribute it and/or modify
4483+ * it under the terms of the GNU Lesser General Public License as published by
4484+ * the Free Software Foundation; version 3.
4485+ *
4486+ * This program is distributed in the hope that it will be useful,
4487+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
4488+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4489+ * GNU Lesser General Public License for more details.
4490+ *
4491+ * You should have received a copy of the GNU Lesser General Public License
4492+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
4493+ */
4494+
4495+#include "utils.h"
4496+
4497+#include "backend/backend_cups.h"
4498+
4499+#include "models/jobmodel.h"
4500+
4501+#include <QDebug>
4502+
4503+JobModel::JobModel(QObject *parent) : QAbstractListModel(parent)
4504+{
4505+}
4506+
4507+JobModel::JobModel(PrinterBackend *backend,
4508+ QObject *parent)
4509+ : QAbstractListModel(parent)
4510+ , m_backend(backend)
4511+{
4512+ update();
4513+
4514+ QObject::connect(m_backend, &PrinterBackend::jobCreated,
4515+ this, &JobModel::jobSignalCatchAll);
4516+ QObject::connect(m_backend, &PrinterBackend::jobState,
4517+ this, &JobModel::jobSignalCatchAll);
4518+ QObject::connect(m_backend, &PrinterBackend::jobCompleted,
4519+ this, &JobModel::jobSignalCatchAll);
4520+}
4521+
4522+JobModel::~JobModel()
4523+{
4524+}
4525+
4526+void JobModel::jobSignalCatchAll(
4527+ const QString &text, const QString &printer_uri,
4528+ const QString &printer_name, uint printer_state,
4529+ const QString &printer_state_reasons, bool printer_is_accepting_jobs,
4530+ uint job_id, uint job_state, const QString &job_state_reasons,
4531+ const QString &job_name, uint job_impressions_completed)
4532+{
4533+ Q_UNUSED(text);
4534+ Q_UNUSED(printer_uri);
4535+ Q_UNUSED(printer_name);
4536+ Q_UNUSED(printer_state);
4537+ Q_UNUSED(printer_state_reasons);
4538+ Q_UNUSED(printer_is_accepting_jobs);
4539+ Q_UNUSED(job_id);
4540+ Q_UNUSED(job_state);
4541+ Q_UNUSED(job_state_reasons);
4542+ Q_UNUSED(job_name);
4543+ Q_UNUSED(job_impressions_completed);
4544+
4545+ auto job = getJobById(job_id);
4546+ if (job)
4547+ job->setImpressionsCompleted(job_impressions_completed);
4548+
4549+ update();
4550+}
4551+
4552+void JobModel::update()
4553+{
4554+ // Store the old count and get the new printers
4555+ int oldCount = m_jobs.size();
4556+ QList<QSharedPointer<PrinterJob>> newJobs = m_backend->printerGetJobs();
4557+
4558+ // Go through the old model
4559+ for (int i=0; i < m_jobs.count(); i++) {
4560+ // Determine if the old printer exists in the new model
4561+ bool exists = false;
4562+
4563+ Q_FOREACH(QSharedPointer<PrinterJob> p, newJobs) {
4564+ if (p->jobId() == m_jobs.at(i)->jobId()) {
4565+ exists = true;
4566+
4567+ // Ensure the other properties of the job are up to date
4568+ if (!m_jobs.at(i)->deepCompare(p)) {
4569+ m_jobs.at(i)->updateFrom(p);
4570+
4571+ Q_EMIT dataChanged(index(i), index(i));
4572+ }
4573+
4574+ break;
4575+ }
4576+ }
4577+
4578+ // If it doesn't exist then remove it from the old model
4579+ if (!exists) {
4580+ beginRemoveRows(QModelIndex(), i, i);
4581+ QSharedPointer<PrinterJob> p = m_jobs.takeAt(i);
4582+ endRemoveRows();
4583+
4584+ i--; // as we have removed an item decrement
4585+ }
4586+ }
4587+
4588+ // Go through the new model
4589+ for (int i=0; i < newJobs.count(); i++) {
4590+ // Determine if the new printer exists in the old model
4591+ bool exists = false;
4592+ int j;
4593+
4594+ for (j=0; j < m_jobs.count(); j++) {
4595+ if (m_jobs.at(j)->jobId() == newJobs.at(i)->jobId()) {
4596+ exists = true;
4597+ break;
4598+ }
4599+ }
4600+
4601+ if (exists) {
4602+ if (j == i) { // New printer exists and in correct position
4603+ continue;
4604+ } else {
4605+ // New printer does exist but needs to be moved in old model
4606+ beginMoveRows(QModelIndex(), j, 1, QModelIndex(), i);
4607+ m_jobs.move(j, i);
4608+ endMoveRows();
4609+ }
4610+ } else {
4611+ // New printer does not exist insert into model
4612+ beginInsertRows(QModelIndex(), i, i);
4613+ m_jobs.insert(i, newJobs.at(i));
4614+ endInsertRows();
4615+ }
4616+ }
4617+
4618+ if (oldCount != m_jobs.size()) {
4619+ Q_EMIT countChanged();
4620+ }
4621+}
4622+
4623+int JobModel::rowCount(const QModelIndex &parent) const
4624+{
4625+ Q_UNUSED(parent);
4626+ return m_jobs.size();
4627+}
4628+
4629+int JobModel::count() const
4630+{
4631+ return rowCount();
4632+}
4633+
4634+QVariant JobModel::data(const QModelIndex &index, int role) const
4635+{
4636+ QVariant ret;
4637+
4638+ if ((0<=index.row()) && (index.row()<m_jobs.size())) {
4639+
4640+ auto job = m_jobs[index.row()];
4641+
4642+ switch (role) {
4643+ case CollateRole:
4644+ ret = job->collate();
4645+ break;
4646+ case ColorModelRole: {
4647+ if (job->printer()) {
4648+ ret = job->printer()->supportedColorModels().at(job->colorModel()).text;
4649+ } else {
4650+ qWarning() << "Printer is undefined, no colorModel";
4651+ ret = "";
4652+ }
4653+ break;
4654+ }
4655+ case CompletedTimeRole:
4656+ ret = job->completedTime().toString(QLocale::system().dateTimeFormat());
4657+ break;
4658+ case CopiesRole:
4659+ ret = job->copies();
4660+ break;
4661+ case CreationTimeRole:
4662+ ret = job->creationTime().toString(QLocale::system().dateTimeFormat());
4663+ break;
4664+ case DuplexRole: {
4665+ if (job->printer()) {
4666+ ret = job->printer()->supportedDuplexStrings().at(job->duplexMode());
4667+ } else {
4668+ qWarning() << "Printer is undefined, no duplexMode";
4669+ ret = "";
4670+ }
4671+ break;
4672+ }
4673+ case IdRole:
4674+ ret = job->jobId();
4675+ break;
4676+ case ImpressionsCompletedRole:
4677+ ret = job->impressionsCompleted();
4678+ break;
4679+ case LandscapeRole:
4680+ ret = job->landscape();
4681+ break;
4682+ case MessagesRole:
4683+ ret = job->messages();
4684+ break;
4685+ case PrinterNameRole:
4686+ ret = job->printerName();
4687+ break;
4688+ case PrintRangeRole:
4689+ ret = job->printRange();
4690+ break;
4691+ case PrintRangeModeRole:
4692+ ret = QVariant::fromValue<PrinterEnum::PrintRange>(job->printRangeMode());
4693+ break;
4694+ case ProcessingTimeRole:
4695+ ret = job->processingTime().toString(QLocale::system().dateTimeFormat());
4696+ break;
4697+ case QualityRole: {
4698+ if (job->printer()) {
4699+ ret = job->printer()->supportedPrintQualities().at(job->quality()).text;
4700+ } else {
4701+ qWarning() << "Printer is undefined, no quality";
4702+ ret = "";
4703+ }
4704+ break;
4705+ }
4706+ case ReverseRole:
4707+ ret = job->reverse();
4708+ break;
4709+ case SizeRole:
4710+ ret = job->size();
4711+ break;
4712+ case StateRole:
4713+ // TODO: improve, for now have a switch
4714+ switch (job->state()) {
4715+ case PrinterEnum::JobState::Aborted:
4716+ ret = "Aborted";
4717+ break;
4718+ case PrinterEnum::JobState::Canceled:
4719+ ret = "Canceled";
4720+ break;
4721+ case PrinterEnum::JobState::Complete:
4722+ ret = "Compelete";
4723+ break;
4724+ case PrinterEnum::JobState::Held:
4725+ ret = "Held";
4726+ break;
4727+ case PrinterEnum::JobState::Pending:
4728+ ret = "Pending";
4729+ break;
4730+ case PrinterEnum::JobState::Processing:
4731+ ret = "Processing";
4732+ break;
4733+ case PrinterEnum::JobState::Stopped:
4734+ ret = "Stopped";
4735+ break;
4736+ }
4737+ break;
4738+ case Qt::DisplayRole:
4739+ case TitleRole:
4740+ ret = job->title();
4741+ break;
4742+ case UserRole:
4743+ ret = job->user();
4744+ break;
4745+ }
4746+ }
4747+
4748+ return ret;
4749+}
4750+
4751+QHash<int, QByteArray> JobModel::roleNames() const
4752+{
4753+ static QHash<int,QByteArray> names;
4754+
4755+ if (Q_UNLIKELY(names.empty())) {
4756+ names[Qt::DisplayRole] = "displayName";
4757+ names[IdRole] = "id";
4758+ names[CollateRole] = "collate";
4759+ names[ColorModelRole] = "colorModel";
4760+ names[CompletedTimeRole] = "completedTime";
4761+ names[CopiesRole] = "copies";
4762+ names[CreationTimeRole] = "creationTime";
4763+ names[DuplexRole] = "duplexMode";
4764+ names[ImpressionsCompletedRole] = "impressionsCompleted";
4765+ names[LandscapeRole] = "landscape";
4766+ names[MessagesRole] = "messages";
4767+ names[PrinterNameRole] = "printerName";
4768+ names[PrintRangeRole] = "printRange";
4769+ names[PrintRangeModeRole] = "printRangeMode";
4770+ names[ProcessingTimeRole] = "processingTime";
4771+ names[QualityRole] = "quality";
4772+ names[ReverseRole] = "reverse";
4773+ names[SizeRole] = "size";
4774+ names[StateRole] = "state";
4775+ names[TitleRole] = "title";
4776+ names[UserRole] = "user";
4777+ names[LastStateMessageRole] = "lastStateMessage";
4778+ }
4779+
4780+ return names;
4781+}
4782+
4783+QVariantMap JobModel::get(const int row) const
4784+{
4785+ QHashIterator<int, QByteArray> iterator(roleNames());
4786+ QVariantMap result;
4787+ QModelIndex modelIndex = index(row, 0);
4788+
4789+ while (iterator.hasNext()) {
4790+ iterator.next();
4791+ result[iterator.value()] = modelIndex.data(iterator.key());
4792+ }
4793+
4794+ return result;
4795+}
4796+
4797+QSharedPointer<PrinterJob> JobModel::getJobById(const int &id)
4798+{
4799+ Q_FOREACH(auto job, m_jobs) {
4800+ if (job->jobId() == id) {
4801+ return job;
4802+ }
4803+ }
4804+ return QSharedPointer<PrinterJob>(Q_NULLPTR);
4805+}
4806+
4807+
4808+JobFilter::JobFilter(QObject *parent) : QSortFilterProxyModel(parent)
4809+{
4810+ connect(this, SIGNAL(sourceModelChanged()), SLOT(onSourceModelChanged()));
4811+}
4812+
4813+JobFilter::~JobFilter()
4814+{
4815+}
4816+
4817+QVariantMap JobFilter::get(const int row) const
4818+{
4819+ QHashIterator<int, QByteArray> iterator(roleNames());
4820+ QVariantMap result;
4821+ QModelIndex modelIndex = index(row, 0);
4822+
4823+ while (iterator.hasNext()) {
4824+ iterator.next();
4825+ result[iterator.value()] = modelIndex.data(iterator.key());
4826+ }
4827+
4828+ return result;
4829+}
4830+
4831+void JobFilter::onSourceModelChanged()
4832+{
4833+ connect((JobModel*) sourceModel(),
4834+ SIGNAL(countChanged()),
4835+ this,
4836+ SIGNAL(countChanged()));
4837+}
4838+
4839+void JobFilter::onSourceModelCountChanged()
4840+{
4841+ Q_EMIT countChanged();
4842+}
4843+
4844+int JobFilter::count() const
4845+{
4846+ return rowCount();
4847+}
4848+
4849+void JobFilter::filterOnPrinterName(const QString &name)
4850+{
4851+ m_printerName = name;
4852+ m_printerNameFilterEnabled = true;
4853+ invalidate();
4854+}
4855+
4856+bool JobFilter::filterAcceptsRow(int sourceRow,
4857+ const QModelIndex &sourceParent) const
4858+{
4859+ bool accepts = true;
4860+ QModelIndex childIndex = sourceModel()->index(sourceRow, 0, sourceParent);
4861+
4862+ if (accepts && m_printerNameFilterEnabled) {
4863+ QString printerName = childIndex.model()->data(
4864+ childIndex, JobModel::PrinterNameRole).toString();
4865+ accepts = m_printerName == printerName;
4866+ }
4867+
4868+ return accepts;
4869+}
4870
4871=== added file 'modules/Ubuntu/Components/Extras/Printers/models/jobmodel.h'
4872--- modules/Ubuntu/Components/Extras/Printers/models/jobmodel.h 1970-01-01 00:00:00 +0000
4873+++ modules/Ubuntu/Components/Extras/Printers/models/jobmodel.h 2017-02-22 12:44:26 +0000
4874@@ -0,0 +1,125 @@
4875+/*
4876+ * Copyright (C) 2017 Canonical, Ltd.
4877+ *
4878+ * This program is free software; you can redistribute it and/or modify
4879+ * it under the terms of the GNU Lesser General Public License as published by
4880+ * the Free Software Foundation; version 3.
4881+ *
4882+ * This program is distributed in the hope that it will be useful,
4883+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
4884+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4885+ * GNU Lesser General Public License for more details.
4886+ *
4887+ * You should have received a copy of the GNU Lesser General Public License
4888+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
4889+ */
4890+
4891+#ifndef USC_JOB_MODEL_H
4892+#define USC_JOB_MODEL_H
4893+
4894+#include "printers_global.h"
4895+#include "backend/backend.h"
4896+#include "printer/printerjob.h"
4897+
4898+#include <QAbstractListModel>
4899+#include <QByteArray>
4900+#include <QModelIndex>
4901+#include <QObject>
4902+#include <QSharedPointer>
4903+#include <QSortFilterProxyModel>
4904+#include <QTimer>
4905+#include <QVariant>
4906+
4907+class PRINTERS_DECL_EXPORT JobModel : public QAbstractListModel
4908+{
4909+ Q_OBJECT
4910+
4911+ Q_PROPERTY(int count READ count NOTIFY countChanged)
4912+public:
4913+ explicit JobModel(QObject *parent = Q_NULLPTR);
4914+ explicit JobModel(PrinterBackend *backend,
4915+ QObject *parent = Q_NULLPTR);
4916+ ~JobModel();
4917+
4918+ enum Roles
4919+ {
4920+ // Qt::DisplayRole holds job title
4921+ IdRole = Qt::UserRole,
4922+ CollateRole,
4923+ ColorModelRole,
4924+ CompletedTimeRole,
4925+ CopiesRole,
4926+ CreationTimeRole,
4927+ DuplexRole,
4928+ ImpressionsCompletedRole,
4929+ LandscapeRole,
4930+ MessagesRole,
4931+ PrinterNameRole,
4932+ PrintRangeRole,
4933+ PrintRangeModeRole,
4934+ ProcessingTimeRole,
4935+ QualityRole,
4936+ ReverseRole,
4937+ SizeRole,
4938+ StateRole,
4939+ TitleRole,
4940+ UserRole,
4941+ LastStateMessageRole,
4942+ LastRole = LastStateMessageRole,
4943+ };
4944+
4945+ virtual int rowCount(const QModelIndex &parent = QModelIndex()) const override;
4946+ virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
4947+ virtual QHash<int, QByteArray> roleNames() const override;
4948+
4949+ int count() const;
4950+
4951+ Q_INVOKABLE QVariantMap get(const int row) const;
4952+ QSharedPointer<PrinterJob> getJobById(const int &id);
4953+private:
4954+ PrinterBackend *m_backend;
4955+
4956+ QList<QSharedPointer<PrinterJob>> m_jobs;
4957+private Q_SLOTS:
4958+ void update();
4959+ void jobSignalCatchAll(const QString &text, const QString &printer_uri,
4960+ const QString &printer_name, uint printer_state,
4961+ const QString &printer_state_reasons,
4962+ bool printer_is_accepting_jobs, uint job_id,
4963+ uint job_state, const QString &job_state_reasons,
4964+ const QString &job_name,
4965+ uint job_impressions_completed);
4966+
4967+Q_SIGNALS:
4968+ void countChanged();
4969+};
4970+
4971+class PRINTERS_DECL_EXPORT JobFilter : public QSortFilterProxyModel
4972+{
4973+ Q_OBJECT
4974+ Q_PROPERTY(int count READ count NOTIFY countChanged)
4975+public:
4976+ explicit JobFilter(QObject *parent = Q_NULLPTR);
4977+ ~JobFilter();
4978+
4979+ Q_INVOKABLE QVariantMap get(const int row) const;
4980+
4981+ void filterOnPrinterName(const QString &name);
4982+ int count() const;
4983+protected:
4984+ virtual bool filterAcceptsRow(
4985+ int sourceRow, const QModelIndex &sourceParent) const override;
4986+
4987+Q_SIGNALS:
4988+ void countChanged();
4989+
4990+private Q_SLOTS:
4991+ void onSourceModelChanged();
4992+ void onSourceModelCountChanged();
4993+
4994+private:
4995+ QString m_printerName = QString::null;
4996+ bool m_printerNameFilterEnabled = false;
4997+};
4998+
4999+#endif // USC_JOB_MODEL_H
5000
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches