Merge lp:~phablet-team/ubuntu-settings-components/printer-components into lp:ubuntu-settings-components

Proposed by Jonas G. Drange
Status: Work in progress
Proposed branch: lp:~phablet-team/ubuntu-settings-components/printer-components
Merge into: lp:ubuntu-settings-components
Diff against target: 9826 lines (+9462/-1)
59 files modified
CMakeLists.txt (+7/-0)
debian/changelog (+7/-0)
debian/control (+3/-0)
debian/qml-module-ubuntu-settings-components.install (+1/-0)
examples/PrinterQueue.qml (+118/-0)
examples/Printers.qml (+604/-0)
plugins/Ubuntu/Settings/CMakeLists.txt (+1/-0)
plugins/Ubuntu/Settings/Printers/CMakeLists.txt (+53/-0)
plugins/Ubuntu/Settings/Printers/backend/backend.cpp (+350/-0)
plugins/Ubuntu/Settings/Printers/backend/backend.h (+223/-0)
plugins/Ubuntu/Settings/Printers/backend/backend_cups.cpp (+787/-0)
plugins/Ubuntu/Settings/Printers/backend/backend_cups.h (+152/-0)
plugins/Ubuntu/Settings/Printers/backend/backend_pdf.cpp (+119/-0)
plugins/Ubuntu/Settings/Printers/backend/backend_pdf.h (+47/-0)
plugins/Ubuntu/Settings/Printers/cups/ippclient.cpp (+988/-0)
plugins/Ubuntu/Settings/Printers/cups/ippclient.h (+121/-0)
plugins/Ubuntu/Settings/Printers/cups/printerdriverloader.cpp (+128/-0)
plugins/Ubuntu/Settings/Printers/cups/printerdriverloader.h (+61/-0)
plugins/Ubuntu/Settings/Printers/cups/printerloader.cpp (+53/-0)
plugins/Ubuntu/Settings/Printers/cups/printerloader.h (+49/-0)
plugins/Ubuntu/Settings/Printers/enums.h (+141/-0)
plugins/Ubuntu/Settings/Printers/i18n.cpp (+44/-0)
plugins/Ubuntu/Settings/Printers/i18n.h (+29/-0)
plugins/Ubuntu/Settings/Printers/models/drivermodel.cpp (+175/-0)
plugins/Ubuntu/Settings/Printers/models/drivermodel.h (+82/-0)
plugins/Ubuntu/Settings/Printers/models/jobmodel.cpp (+391/-0)
plugins/Ubuntu/Settings/Printers/models/jobmodel.h (+125/-0)
plugins/Ubuntu/Settings/Printers/models/printermodel.cpp (+533/-0)
plugins/Ubuntu/Settings/Printers/models/printermodel.h (+161/-0)
plugins/Ubuntu/Settings/Printers/org.cups.cupsd.Notifier.xml (+146/-0)
plugins/Ubuntu/Settings/Printers/plugin.cpp (+58/-0)
plugins/Ubuntu/Settings/Printers/plugin.h (+33/-0)
plugins/Ubuntu/Settings/Printers/printer/printer.cpp (+358/-0)
plugins/Ubuntu/Settings/Printers/printer/printer.h (+109/-0)
plugins/Ubuntu/Settings/Printers/printer/printerjob.cpp (+514/-0)
plugins/Ubuntu/Settings/Printers/printer/printerjob.h (+174/-0)
plugins/Ubuntu/Settings/Printers/printer/printersignalhandler.cpp (+69/-0)
plugins/Ubuntu/Settings/Printers/printer/printersignalhandler.h (+55/-0)
plugins/Ubuntu/Settings/Printers/printers/printers.cpp (+251/-0)
plugins/Ubuntu/Settings/Printers/printers/printers.h (+107/-0)
plugins/Ubuntu/Settings/Printers/printers_global.h (+23/-0)
plugins/Ubuntu/Settings/Printers/qmldir (+2/-0)
plugins/Ubuntu/Settings/Printers/structs.h (+103/-0)
plugins/Ubuntu/Settings/Printers/utils.h (+110/-0)
po/ubuntu-settings-components.pot (+25/-1)
po/update-usc-pot (+1/-0)
tests/CMakeLists.txt (+1/-0)
tests/unittests/CMakeLists.txt (+1/-0)
tests/unittests/Printers/CMakeLists.txt (+46/-0)
tests/unittests/Printers/mockbackend.h (+443/-0)
tests/unittests/Printers/tst_drivermodel.cpp (+136/-0)
tests/unittests/Printers/tst_jobfilter.cpp (+55/-0)
tests/unittests/Printers/tst_jobmodel.cpp (+120/-0)
tests/unittests/Printers/tst_printer.cpp (+284/-0)
tests/unittests/Printers/tst_printerfilter.cpp (+117/-0)
tests/unittests/Printers/tst_printerjob.cpp (+207/-0)
tests/unittests/Printers/tst_printermodel.cpp (+161/-0)
tests/unittests/Printers/tst_printers.cpp (+156/-0)
tests/unittests/Printers/tst_signalhandler.cpp (+44/-0)
To merge this branch: bzr merge lp:~phablet-team/ubuntu-settings-components/printer-components
Reviewer Review Type Date Requested Status
Unity Team Pending
Review via email: mp+314435@code.launchpad.net

Commit message

* packaging: suggest cups, depend on libcups2-dev
* adds cups bindings for printer/job management

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

internalizes cph stuff

186. By Jonas G. Drange

inch closer to settings duplex

187. By Jonas G. Drange

latest changes

188. By Jonas G. Drange

makes string stuff in pkhelper saner

189. By Jonas G. Drange

enables you to set the duplex of a printer

190. By Jonas G. Drange

fix path errors

191. By Jonas G. Drange

adds metaobj to cupsfacade system, other various fixes

192. By Jonas G. Drange

allows read/write of pageSize

193. By Jonas G. Drange

adds pagesize read/write

194. By Jonas G. Drange

adds colormodels stuff

195. By Jonas G. Drange

merges andrews printerjob work, as well as solving any conflicts

196. By Jonas G. Drange

corrects case where having colormodels gave you a dud as well, and fixes some warnings

197. By Jonas G. Drange

implements setData for ColorModel

198. By Jonas G. Drange

implements setData for ColorModel

199. By Jonas G. Drange

fixes colormodel implementation for printers with models

200. By Andrew Hayzen

* Add QTimer to poll availablePrinters for changes
* Improve update in PrinterModel so that it uses insert/move/remove instead of clear
* Add tests to ensure that insert/move/remove of printers works

201. By Andrew Hayzen

* Create startTimer method in PrinterModel

202. By Andrew Hayzen

* Expose colorModelType to QML
* Fix utils detection of colorModelType
* Change startTimer to not be a pointer
* Use new helpers in makeDest

203. By Jonas G. Drange

adds implementation of a proxyfilter for allPrinters, sorted by default

204. By Jonas G. Drange

migrates to allPrinters and marks the default

205. By Jonas G. Drange

merges andrews latest and solves conflicts

206. By Jonas G. Drange

removes displayrole arg in test as it is not needed

207. By Jonas G. Drange

* adds count and countChanged on the printer filter proxy by redirect of signal
* tests count and countChanged on PrinterFilter in tst_printerfilter

208. By Jonas G. Drange

* Add getter method to PrinterModel
* Fix issue in update when removing multiple rows (also added regression test)
* Add refresh command to PrinterInfo
* Add ability for setting defaults to emit signals, so when a default is set it remains set on the QML side

209. By Andrew Hayzen

* Add a Printers::allPrintersWithPdf option which includes a fake pdf at the bottom
* Add a PrinterInfoAllImpl which combines PrinterInfoPdfImpl and PrinterInfoImpl for listing
* Add a PrinterInfoPdfImpl which implements a fake pdf printer (just a name in the list)
* Add isPdf role to PrinterModel
* Add filterOnPdf to PrinterModel to allow for only showing pdf printers
* Force pdf printers to bottom of the PrinterModel
* Inject a fake ColorModel in PrinterPrivate::loadColorModel for pdf printers
* Change PrinterJob::setPrinterName to use PrinterInfoAllImpl for retreiving the PrinterInfo
* Improve the UI names for the Duplex modes
* Improve detection of color models so that "black" is a greyscale type
* In the example disable duplex mode when there is only None
* Fix colorModelType not emitting when it may have changed, causing QML to get out of sync

210. By Jonas G. Drange

* Implements read/write of select Quality ppd settings
* Updates PrinterJob to use the correct quality setting
* Fixes broken ColorModel system, now reflects most ppds, and leaves the ppd implementation details in the ppd (colorspace, organization, etc)

211. By Jonas G. Drange

* packaging: suggest cups, depend on libcups2-dev
* adds cups bindings for printer/job management

212. By Jonas G. Drange

* Rewrites Printer, PrinterJob, PrinterModel to all use a new backend.
* Adds new backend that bridges PDF and Cups implementations.
* Drops PIMPL idiom since we're not going to export C++ headers anyway.
* Drops most bridge patterns in classes that are now implementation details (QPrinterInfo, CupsFacade).
* Tries to deal with some RAII issues.

213. By Jonas G. Drange

syncs with trunk

214. By Jonas G. Drange

Jonas G. Drange 2017-01-23 deletes old, unused mocks

215. By Jonas G. Drange

adds printer state

216. By Andrew Hayzen

* Add support for translations in the printer components

217. By Andrew Hayzen

* Add Collate and Reverse options to PrinterJob

218. By Andrew Hayzen

* Add isTwoSided read-only property to PrinterJob which allows the QML to know if the selected duplexMode is actually going to print double sided

219. By Jonas G. Drange

resolves conflicts

220. By Andrew Hayzen

* Skip null printers for now as they result in errors due to missing ppd definitions and segfaults when trying to print

221. By Jonas G. Drange

  * allows creation of printers in example qml, and by extension the API
  * create printers by either providing a PPD file, or select a PPD from the database
  * adds a DriverModel that holds printer drivers, which can be filtered
  * adds testing of DriverModel

222. By Andrew Hayzen

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

223. By Andrew Hayzen

* Add cancel method to Printers

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

225. By Jonas G. Drange

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

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

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

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

229. By Andrew Hayzen

* Expose many more properties of PrinterJob to the JobModel
* Load PrinterJob properties from the ipp job attributes when loading for the JobModel

230. By Jonas G. Drange

merges ~jonas-drange/ubuntu-settings-components/asyncness/

231. By Jonas G. Drange

frees all the jobs which works fine

232. By Jonas G. Drange

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

Unmerged revisions

232. By Jonas G. Drange

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

231. By Jonas G. Drange

frees all the jobs which works fine

230. By Jonas G. Drange

merges ~jonas-drange/ubuntu-settings-components/asyncness/

229. By Andrew Hayzen

* Expose many more properties of PrinterJob to the JobModel
* Load PrinterJob properties from the ipp job attributes when loading for the JobModel

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

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

Subscribers

People subscribed via source and target branches