Merge lp:~jonas-drange/ubuntu-settings-components/buffer-printer-state-events into lp:ubuntu-settings-components

Proposed by Jonas G. Drange
Status: Superseded
Proposed branch: lp:~jonas-drange/ubuntu-settings-components/buffer-printer-state-events
Merge into: lp:ubuntu-settings-components
Diff against target: 9821 lines (+9457/-1)
59 files modified
CMakeLists.txt (+7/-0)
debian/changelog (+7/-0)
debian/control (+3/-0)
debian/qml-module-ubuntu-settings-components.install (+1/-0)
examples/PrinterQueue.qml (+118/-0)
examples/Printers.qml (+604/-0)
plugins/Ubuntu/Settings/CMakeLists.txt (+1/-0)
plugins/Ubuntu/Settings/Printers/CMakeLists.txt (+53/-0)
plugins/Ubuntu/Settings/Printers/backend/backend.cpp (+350/-0)
plugins/Ubuntu/Settings/Printers/backend/backend.h (+223/-0)
plugins/Ubuntu/Settings/Printers/backend/backend_cups.cpp (+782/-0)
plugins/Ubuntu/Settings/Printers/backend/backend_cups.h (+152/-0)
plugins/Ubuntu/Settings/Printers/backend/backend_pdf.cpp (+119/-0)
plugins/Ubuntu/Settings/Printers/backend/backend_pdf.h (+47/-0)
plugins/Ubuntu/Settings/Printers/cups/ippclient.cpp (+988/-0)
plugins/Ubuntu/Settings/Printers/cups/ippclient.h (+121/-0)
plugins/Ubuntu/Settings/Printers/cups/printerdriverloader.cpp (+128/-0)
plugins/Ubuntu/Settings/Printers/cups/printerdriverloader.h (+61/-0)
plugins/Ubuntu/Settings/Printers/cups/printerloader.cpp (+53/-0)
plugins/Ubuntu/Settings/Printers/cups/printerloader.h (+49/-0)
plugins/Ubuntu/Settings/Printers/enums.h (+141/-0)
plugins/Ubuntu/Settings/Printers/i18n.cpp (+44/-0)
plugins/Ubuntu/Settings/Printers/i18n.h (+29/-0)
plugins/Ubuntu/Settings/Printers/models/drivermodel.cpp (+175/-0)
plugins/Ubuntu/Settings/Printers/models/drivermodel.h (+82/-0)
plugins/Ubuntu/Settings/Printers/models/jobmodel.cpp (+391/-0)
plugins/Ubuntu/Settings/Printers/models/jobmodel.h (+125/-0)
plugins/Ubuntu/Settings/Printers/models/printermodel.cpp (+533/-0)
plugins/Ubuntu/Settings/Printers/models/printermodel.h (+161/-0)
plugins/Ubuntu/Settings/Printers/org.cups.cupsd.Notifier.xml (+146/-0)
plugins/Ubuntu/Settings/Printers/plugin.cpp (+58/-0)
plugins/Ubuntu/Settings/Printers/plugin.h (+33/-0)
plugins/Ubuntu/Settings/Printers/printer/printer.cpp (+358/-0)
plugins/Ubuntu/Settings/Printers/printer/printer.h (+109/-0)
plugins/Ubuntu/Settings/Printers/printer/printerjob.cpp (+514/-0)
plugins/Ubuntu/Settings/Printers/printer/printerjob.h (+174/-0)
plugins/Ubuntu/Settings/Printers/printer/printersignalhandler.cpp (+69/-0)
plugins/Ubuntu/Settings/Printers/printer/printersignalhandler.h (+55/-0)
plugins/Ubuntu/Settings/Printers/printers/printers.cpp (+251/-0)
plugins/Ubuntu/Settings/Printers/printers/printers.h (+107/-0)
plugins/Ubuntu/Settings/Printers/printers_global.h (+23/-0)
plugins/Ubuntu/Settings/Printers/qmldir (+2/-0)
plugins/Ubuntu/Settings/Printers/structs.h (+103/-0)
plugins/Ubuntu/Settings/Printers/utils.h (+110/-0)
po/ubuntu-settings-components.pot (+25/-1)
po/update-usc-pot (+1/-0)
tests/CMakeLists.txt (+1/-0)
tests/unittests/CMakeLists.txt (+1/-0)
tests/unittests/Printers/CMakeLists.txt (+46/-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-settings-components/buffer-printer-state-events
Reviewer Review Type Date Requested Status
Unity Team Pending
Review via email: mp+317481@code.launchpad.net

Commit message

* adds PrinterSignalHandler that is responsible for handling (some) printer signals.
* uses PrinterSignalHandler in PrinterModel so that i only will update printers a minimum number of times

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

undoes spurious change

Unmerged revisions

254. By Jonas G. Drange

undoes spurious change

253. By Jonas G. Drange

adds signalhandler to buffer printer change signals

252. By Jonas G. Drange

reverts changes to instance, and removes ownerrole

251. By Jonas G. Drange

fix printers falling through

250. By Jonas G. Drange

addresses andrews comments

249. By Jonas G. Drange

fixes jobs on each printer, removes setname, but emits signal when it changes

248. By Jonas G. Drange

do some debugging

247. By Jonas G. Drange

adds some bells and whistles to jobmodel

246. By Jonas G. Drange

adds jobmodel and jobfilter tests

245. By Jonas G. Drange

updates printer tests

Preview Diff

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

Subscribers

People subscribed via source and target branches

to all changes: