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

Proposed by Jonas G. Drange on 2017-01-10
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 2017-01-10 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 on 2017-01-10

internalizes cph stuff

186. By Jonas G. Drange on 2017-01-11

inch closer to settings duplex

187. By Jonas G. Drange on 2017-01-12

latest changes

188. By Jonas G. Drange on 2017-01-12

makes string stuff in pkhelper saner

189. By Jonas G. Drange on 2017-01-12

enables you to set the duplex of a printer

190. By Jonas G. Drange on 2017-01-12

fix path errors

191. By Jonas G. Drange on 2017-01-12

adds metaobj to cupsfacade system, other various fixes

192. By Jonas G. Drange on 2017-01-12

allows read/write of pageSize

193. By Jonas G. Drange on 2017-01-12

adds pagesize read/write

194. By Jonas G. Drange on 2017-01-17

adds colormodels stuff

195. By Jonas G. Drange on 2017-01-17

merges andrews printerjob work, as well as solving any conflicts

196. By Jonas G. Drange on 2017-01-17

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

197. By Jonas G. Drange on 2017-01-17

implements setData for ColorModel

198. By Jonas G. Drange on 2017-01-17

implements setData for ColorModel

199. By Jonas G. Drange on 2017-01-17

fixes colormodel implementation for printers with models

200. By Andrew Hayzen on 2017-01-17

* 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 on 2017-01-17

* Create startTimer method in PrinterModel

202. By Andrew Hayzen on 2017-01-18

* 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 on 2017-01-18

adds implementation of a proxyfilter for allPrinters, sorted by default

204. By Jonas G. Drange on 2017-01-18

migrates to allPrinters and marks the default

205. By Jonas G. Drange on 2017-01-18

merges andrews latest and solves conflicts

206. By Jonas G. Drange on 2017-01-18

removes displayrole arg in test as it is not needed

207. By Jonas G. Drange on 2017-01-19

* 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 on 2017-01-19

* 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 on 2017-01-19

* 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 on 2017-01-20

* 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 on 2017-01-23

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

212. By Jonas G. Drange on 2017-01-23

* 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 on 2017-01-23

syncs with trunk

214. By Jonas G. Drange on 2017-01-23

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

215. By Jonas G. Drange on 2017-01-23

adds printer state

216. By Andrew Hayzen on 2017-01-24

* Add support for translations in the printer components

217. By Andrew Hayzen on 2017-01-25

* Add Collate and Reverse options to PrinterJob

218. By Andrew Hayzen on 2017-01-26

* 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 on 2017-01-27

resolves conflicts

220. By Andrew Hayzen on 2017-01-31

* 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 on 2017-01-31

  * 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 on 2017-01-31

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

223. By Andrew Hayzen on 2017-02-02

* Add cancel method to Printers

224. By Andrew Hayzen on 2017-02-02

* 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 on 2017-02-03

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

226. By Andrew Hayzen on 2017-02-03

* 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 on 2017-02-06

* 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 on 2017-02-06

* 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 on 2017-02-07

* 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 on 2017-02-17

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

231. By Jonas G. Drange on 2017-02-17

frees all the jobs which works fine

232. By Jonas G. Drange on 2017-02-17

* 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 on 2017-02-17

* 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 on 2017-02-17

frees all the jobs which works fine

230. By Jonas G. Drange on 2017-02-17

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

229. By Andrew Hayzen on 2017-02-07

* 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 on 2017-02-06

* 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 on 2017-02-06

* 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 on 2017-02-03

* 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 on 2017-02-03

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

224. By Andrew Hayzen on 2017-02-02

* 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 on 2017-02-02

* 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