Merge lp:~jonas-drange/ubuntu-settings-components/printers-async into lp:ubuntu-settings-components

Proposed by Jonas G. Drange
Status: Superseded
Proposed branch: lp:~jonas-drange/ubuntu-settings-components/printers-async
Merge into: lp:ubuntu-settings-components
Diff against target: 8783 lines (+8454/-1)
52 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 (+113/-0)
examples/Printers.qml (+452/-0)
plugins/Ubuntu/Settings/CMakeLists.txt (+1/-0)
plugins/Ubuntu/Settings/Printers/CMakeLists.txt (+47/-0)
plugins/Ubuntu/Settings/Printers/backend/backend.cpp (+361/-0)
plugins/Ubuntu/Settings/Printers/backend/backend.h (+219/-0)
plugins/Ubuntu/Settings/Printers/backend/backend_cups.cpp (+506/-0)
plugins/Ubuntu/Settings/Printers/backend/backend_cups.h (+166/-0)
plugins/Ubuntu/Settings/Printers/backend/backend_pdf.cpp (+124/-0)
plugins/Ubuntu/Settings/Printers/backend/backend_pdf.h (+51/-0)
plugins/Ubuntu/Settings/Printers/cups/cupsfacade.cpp (+555/-0)
plugins/Ubuntu/Settings/Printers/cups/cupsfacade.h (+164/-0)
plugins/Ubuntu/Settings/Printers/cups/cupspkhelper.cpp (+800/-0)
plugins/Ubuntu/Settings/Printers/cups/cupspkhelper.h (+120/-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 (+181/-0)
plugins/Ubuntu/Settings/Printers/models/drivermodel.h (+83/-0)
plugins/Ubuntu/Settings/Printers/models/jobmodel.cpp (+248/-0)
plugins/Ubuntu/Settings/Printers/models/jobmodel.h (+80/-0)
plugins/Ubuntu/Settings/Printers/models/printermodel.cpp (+555/-0)
plugins/Ubuntu/Settings/Printers/models/printermodel.h (+152/-0)
plugins/Ubuntu/Settings/Printers/org.cups.cupsd.Notifier.xml (+146/-0)
plugins/Ubuntu/Settings/Printers/plugin.cpp (+56/-0)
plugins/Ubuntu/Settings/Printers/plugin.h (+33/-0)
plugins/Ubuntu/Settings/Printers/printer/printer.cpp (+333/-0)
plugins/Ubuntu/Settings/Printers/printer/printer.h (+126/-0)
plugins/Ubuntu/Settings/Printers/printer/printerjob.cpp (+388/-0)
plugins/Ubuntu/Settings/Printers/printer/printerjob.h (+139/-0)
plugins/Ubuntu/Settings/Printers/printers/printers.cpp (+175/-0)
plugins/Ubuntu/Settings/Printers/printers/printers.h (+100/-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 (+34/-0)
tests/unittests/Printers/mockbackend.h (+433/-0)
tests/unittests/Printers/tst_drivermodel.cpp (+136/-0)
tests/unittests/Printers/tst_printer.cpp (+240/-0)
tests/unittests/Printers/tst_printerfilter.cpp (+86/-0)
tests/unittests/Printers/tst_printerjob.cpp (+205/-0)
tests/unittests/Printers/tst_printermodel.cpp (+233/-0)
tests/unittests/Printers/tst_printers.cpp (+115/-0)
To merge this branch: bzr merge lp:~jonas-drange/ubuntu-settings-components/printers-async
Reviewer Review Type Date Requested Status
Unity Team Pending
Review via email: mp+316781@code.launchpad.net
To post a comment you must log in.

Unmerged revisions

231. By Jonas G. Drange

more asyncness with proxies

230. By Jonas G. Drange

allows cancelling of workers

229. By Jonas G. Drange

makes tests pass with the more async approach

228. By Andrew Hayzen

* Resolve console warnings - add TODOs in methods that should be implemented where I've put a fake return value for now
* Set a default for switches that compiler thinks it can reach the end of the control without
* Add return values for methods that are not implemented yet
* Add Q_UNUSED for parameters that aren't used

227. By Andrew Hayzen

* Link notifications to JobModel - removing polling
* Fix compiler errors that appeared from the previous branch
* Fix logic change in last branch that was wrong way around

226. By Andrew Hayzen

* Add updateFrom to Printer and PrinterJob
* Call updateFrom in PrinterModel and JobModel when printers are the same
* Emit dataChanged signal in models when updateFrom returns a change
* Add deepCompare to Printer and PrinterJob

225. By Jonas G. Drange

* Deprecate QTimer based update()
* Replace with cups subscription based update()
* Fixes tests.

224. By Andrew Hayzen

* Add JobModel which lists the jobs for a certain printer
* Add JobRole to PrinterModel to access jobs
* Add JobState enum to track enums from cups
* Add example Queue which lists jobs for a printer with their name, id, status and allows you to cancel the job by clicking

223. By Andrew Hayzen

* Add cancel method to Printers

222. By Andrew Hayzen

* Always fit-to-page for now as printing app doesn't know about page sizes or scaling yet

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

Subscribers

People subscribed via source and target branches

to all changes: