Merge lp:~jonas-drange/ubuntu-ui-extras/consolidated-devices into lp:~phablet-team/ubuntu-ui-extras/printer-staging
- consolidated-devices
- Merge into printer-staging
Status: | Superseded |
---|---|
Proposed branch: | lp:~jonas-drange/ubuntu-ui-extras/consolidated-devices |
Merge into: | lp:~phablet-team/ubuntu-ui-extras/printer-staging |
Diff against target: |
2069 lines (+1492/-112) 26 files modified
modules/Ubuntu/Components/Extras/Example/Printers.qml (+73/-28) modules/Ubuntu/Components/Extras/Printers/CMakeLists.txt (+1/-0) modules/Ubuntu/Components/Extras/Printers/backend/backend_cups.cpp (+9/-8) modules/Ubuntu/Components/Extras/Printers/cups/devicesearcher.cpp (+36/-5) modules/Ubuntu/Components/Extras/Printers/cups/devicesearcher.h (+1/-0) modules/Ubuntu/Components/Extras/Printers/cups/ppdutils.cpp (+343/-0) modules/Ubuntu/Components/Extras/Printers/cups/ppdutils.h (+87/-0) modules/Ubuntu/Components/Extras/Printers/models/devicemodel.cpp (+216/-1) modules/Ubuntu/Components/Extras/Printers/models/devicemodel.h (+80/-1) modules/Ubuntu/Components/Extras/Printers/printer/printer.cpp (+2/-1) modules/Ubuntu/Components/Extras/Printers/printer/printerjob.cpp (+2/-2) modules/Ubuntu/Components/Extras/Printers/printers/printers.cpp (+9/-0) modules/Ubuntu/Components/Extras/Printers/printers/printers.h (+3/-0) modules/Ubuntu/Components/Extras/Printers/structs.h (+0/-2) modules/Ubuntu/Components/Extras/Printers/utils.h (+52/-53) po/ubuntu-ui-extras.pot (+5/-5) tests/unittests/Printers/CMakeLists.txt (+16/-0) tests/unittests/Printers/mockbackend.h (+3/-3) tests/unittests/Printers/tst_consolidateddevicemodel.cpp (+162/-0) tests/unittests/Printers/tst_devicefilter.cpp (+106/-0) tests/unittests/Printers/tst_jobmodel.cpp (+1/-0) tests/unittests/Printers/tst_ppdutils.cpp (+138/-0) tests/unittests/Printers/tst_printer.cpp (+2/-2) tests/unittests/Printers/tst_printerdevicemodel.cpp (+35/-0) tests/unittests/Printers/tst_printermodel.cpp (+3/-1) tests/unittests/Printers/tst_utils.cpp (+107/-0) |
To merge this branch: | bzr merge lp:~jonas-drange/ubuntu-ui-extras/consolidated-devices |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Andrew Hayzen (community) | Needs Information | ||
Review via email: mp+320637@code.launchpad.net |
This proposal has been superseded by a proposal from 2017-03-30.
Commit message
Adds ConsolidatedDev
Description of the change
Adds ConsolidatedDev
- 159. By Jonas G. Drange
-
add ppdutils
- 160. By Jonas G. Drange
-
matching now works well for hp devices
- 161. By Jonas G. Drange
-
adds ppdutils, a IEEE 1284 parser, and other fixes to consolidate devices
- 162. By Jonas G. Drange
-
make fixmakemodel static
Andrew Hayzen (ahayzen) wrote : | # |
This is looking much better than before (it doesn't crash :-) ).
Now we have Utils and PpdUtils, would it make sense to move some of the methods from Utils into PpdUtils, eg ppdChoiceToDupl
Jonas G. Drange (jonas-drange) wrote : | # |
Good point. Yeah, I think that should be doable.
- 163. By Jonas G. Drange
-
do RAAI via parent->child semantics, and be more aggressive when to clear out the consolidated model
- 164. By Jonas G. Drange
-
pushes ppd specific utils from Utils into PpdUtils
- 165. By Jonas G. Drange
-
smaller refactor making device makemodel fixes easily testable
- 166. By Jonas G. Drange
-
fixes broken test after changing the test data
- 167. By Jonas G. Drange
-
syncs with staging and fixes a test that broke
- 168. By Jonas G. Drange
-
uses makeLower
- 169. By Jonas G. Drange
-
syncs with staging
- 170. By Jonas G. Drange
-
creates hp devices for hplip capable printers
- 171. By Jonas G. Drange
-
sets the faxid on any fax
- 172. By Jonas G. Drange
-
actually start the process, suggest hplip
- 173. By Jonas G. Drange
-
drop the device id for fax
- 174. By Jonas G. Drange
-
uses info as a last resort
- 175. By Jonas G. Drange
-
make sure non-consolidateable devices are kept
- 176. By Jonas G. Drange
-
fixes test that now broke, but had to change, and add .get to drivermodel
Unmerged revisions
- 176. By Jonas G. Drange
-
fixes test that now broke, but had to change, and add .get to drivermodel
- 175. By Jonas G. Drange
-
make sure non-consolidateable devices are kept
- 174. By Jonas G. Drange
-
uses info as a last resort
- 173. By Jonas G. Drange
-
drop the device id for fax
- 172. By Jonas G. Drange
-
actually start the process, suggest hplip
- 171. By Jonas G. Drange
-
sets the faxid on any fax
- 170. By Jonas G. Drange
-
creates hp devices for hplip capable printers
- 169. By Jonas G. Drange
-
syncs with staging
- 168. By Jonas G. Drange
-
uses makeLower
- 167. By Jonas G. Drange
-
syncs with staging and fixes a test that broke
Preview Diff
1 | === modified file 'modules/Ubuntu/Components/Extras/Example/Printers.qml' |
2 | --- modules/Ubuntu/Components/Extras/Example/Printers.qml 2017-03-21 23:02:42 +0000 |
3 | +++ modules/Ubuntu/Components/Extras/Example/Printers.qml 2017-03-30 13:23:27 +0000 |
4 | @@ -685,7 +685,7 @@ |
5 | verticalCenter: parent.verticalCenter |
6 | } |
7 | property var target |
8 | - Component.onCompleted: target = Printers.devices |
9 | + Component.onCompleted: target = Printers.consolidatedDevices |
10 | running: target.searching |
11 | } |
12 | } |
13 | @@ -699,27 +699,12 @@ |
14 | topMargin: units.gu(2) |
15 | } |
16 | height: contentItem.childrenRect.height |
17 | - model: Printers.devices |
18 | + model: Printers.consolidatedDevices |
19 | delegate: ListItem { |
20 | height: modelLayout.height + (divider.visible ? divider.height : 0) |
21 | ListItemLayout { |
22 | id: modelLayout |
23 | title.text: displayName |
24 | - subtitle.text: { |
25 | - if (type == PrinterEnum.LPDType) return "LPD"; |
26 | - if (type == PrinterEnum.IppSType) return "IppS"; |
27 | - if (type == PrinterEnum.Ipp14Type) return "Ipp14"; |
28 | - if (type == PrinterEnum.HttpType) return "Http"; |
29 | - if (type == PrinterEnum.BehType) return "Beh"; |
30 | - if (type == PrinterEnum.SocketType) return "Socket"; |
31 | - if (type == PrinterEnum.HttpsType) return "Https"; |
32 | - if (type == PrinterEnum.IppType) return "Ipp"; |
33 | - if (type == PrinterEnum.HPType) return "HP"; |
34 | - if (type == PrinterEnum.USBType) return "USB"; |
35 | - if (type == PrinterEnum.HPFaxType) return "HPFax"; |
36 | - if (type == PrinterEnum.DNSSDType) return "DNSSD"; |
37 | - else return "Unknown protocol"; |
38 | - } |
39 | |
40 | Icon { |
41 | id: icon |
42 | @@ -729,17 +714,22 @@ |
43 | SlotsLayout.position: SlotsLayout.First |
44 | } |
45 | |
46 | - Button { |
47 | - text: "Select printer" |
48 | - onClicked: { |
49 | - var suggestedPrinterName = (" " + displayName).slice(1); |
50 | - suggestedPrinterName = suggestedPrinterName.replace(/\ /g, "\-"); |
51 | - printerUri.text = uri; |
52 | - printerName.text = suggestedPrinterName; |
53 | - printerDescription.text = info; |
54 | - printerLocation.text = location; |
55 | - } |
56 | - } |
57 | + ProgressionSlot {} |
58 | + } |
59 | + onClicked: { |
60 | + var p = pageStack.push(chooseConnectionPage, { |
61 | + consolidatedDevice: model |
62 | + }); |
63 | + p.onConnectionChosen.connect(function (conn) { |
64 | + pageStack.pop(); |
65 | + |
66 | + var suggestedPrinterName = (" " + conn.displayName).slice(1); |
67 | + suggestedPrinterName = suggestedPrinterName.replace(/\ /g, "\-"); |
68 | + printerUri.text = conn.uri; |
69 | + printerName.text = suggestedPrinterName; |
70 | + printerDescription.text = conn.info; |
71 | + printerLocation.text = conn.location; |
72 | + }); |
73 | } |
74 | } |
75 | } |
76 | @@ -757,4 +747,59 @@ |
77 | } |
78 | } |
79 | } |
80 | + |
81 | + Component { |
82 | + id: chooseConnectionPage |
83 | + |
84 | + Page { |
85 | + visible: false |
86 | + property var consolidatedDevice |
87 | + signal connectionChosen(var conn) |
88 | + header: PageHeader { |
89 | + id: chooseConnectionPageHeader |
90 | + title: "Choose connection" |
91 | + flickable: connectionsList |
92 | + } |
93 | + |
94 | + ListView { |
95 | + id: connectionsList |
96 | + anchors.fill: parent |
97 | + model: consolidatedDevice.devices |
98 | + delegate: ListItem { |
99 | + height: modelLayout.height + (divider.visible ? divider.height : 0) |
100 | + ListItemLayout { |
101 | + id: modelLayout |
102 | + title.text: info |
103 | + subtitle.text: { |
104 | + if (type == PrinterEnum.LPDType) return "LPD"; |
105 | + if (type == PrinterEnum.IppSType) return "IppS"; |
106 | + if (type == PrinterEnum.Ipp14Type) return "Ipp14"; |
107 | + if (type == PrinterEnum.HttpType) return "Http"; |
108 | + if (type == PrinterEnum.BehType) return "Beh"; |
109 | + if (type == PrinterEnum.SocketType) return "Socket"; |
110 | + if (type == PrinterEnum.HttpsType) return "Https"; |
111 | + if (type == PrinterEnum.IppType) return "Ipp"; |
112 | + if (type == PrinterEnum.HPType) return "HP"; |
113 | + if (type == PrinterEnum.USBType) return "USB"; |
114 | + if (type == PrinterEnum.HPFaxType) return "HPFax"; |
115 | + if (type == PrinterEnum.DNSSDType) return "DNSSD"; |
116 | + else return "Unknown protocol"; |
117 | + } |
118 | + summary.text: uri |
119 | + |
120 | + Icon { |
121 | + id: icon |
122 | + width: height |
123 | + height: units.gu(2.5) |
124 | + name: "network-printer-symbolic" |
125 | + SlotsLayout.position: SlotsLayout.First |
126 | + } |
127 | + |
128 | + ProgressionSlot {} |
129 | + } |
130 | + onClicked: connectionChosen(model) |
131 | + } |
132 | + } |
133 | + } |
134 | + } |
135 | } |
136 | |
137 | === modified file 'modules/Ubuntu/Components/Extras/Printers/CMakeLists.txt' |
138 | --- modules/Ubuntu/Components/Extras/Printers/CMakeLists.txt 2017-03-13 12:51:16 +0000 |
139 | +++ modules/Ubuntu/Components/Extras/Printers/CMakeLists.txt 2017-03-30 13:23:27 +0000 |
140 | @@ -34,6 +34,7 @@ |
141 | cups/jobloader.cpp |
142 | cups/printerdriverloader.cpp |
143 | cups/printerloader.cpp |
144 | + cups/ppdutils.cpp |
145 | |
146 | models/devicemodel.cpp |
147 | models/drivermodel.cpp |
148 | |
149 | === modified file 'modules/Ubuntu/Components/Extras/Printers/backend/backend_cups.cpp' |
150 | --- modules/Ubuntu/Components/Extras/Printers/backend/backend_cups.cpp 2017-03-15 17:42:21 +0000 |
151 | +++ modules/Ubuntu/Components/Extras/Printers/backend/backend_cups.cpp 2017-03-30 13:23:27 +0000 |
152 | @@ -17,6 +17,7 @@ |
153 | #include "backend/backend_cups.h" |
154 | #include "cups/devicesearcher.h" |
155 | #include "cups/jobloader.h" |
156 | +#include "cups/ppdutils.h" |
157 | #include "cups/printerdriverloader.h" |
158 | #include "cups/printerloader.h" |
159 | #include "utils.h" |
160 | @@ -264,9 +265,9 @@ |
161 | ppd_choice_t* def = ppdFindChoice(ppdColorModel, |
162 | ppdColorModel->defchoice); |
163 | if (def) { |
164 | - model = Utils::parsePpdColorModel(def->choice, |
165 | - def->text, |
166 | - "ColorModel"); |
167 | + model = PpdUtils::parsePpdColorModel(def->choice, |
168 | + def->text, |
169 | + "ColorModel"); |
170 | } |
171 | } |
172 | ret[option] = QVariant::fromValue(model); |
173 | @@ -278,8 +279,8 @@ |
174 | ppd_choice_t* def = ppdFindChoice(ppdQuality, |
175 | ppdQuality->defchoice); |
176 | if (def) { |
177 | - quality = Utils::parsePpdPrintQuality(def->choice, |
178 | - def->text, opt); |
179 | + quality = PpdUtils::parsePpdPrintQuality(def->choice, |
180 | + def->text, opt); |
181 | } |
182 | } |
183 | } |
184 | @@ -291,7 +292,7 @@ |
185 | if (qualityOpt) { |
186 | for (int i = 0; i < qualityOpt->num_choices; ++i) { |
187 | qualities.append( |
188 | - Utils::parsePpdPrintQuality( |
189 | + PpdUtils::parsePpdPrintQuality( |
190 | qualityOpt->choices[i].choice, |
191 | qualityOpt->choices[i].text, |
192 | opt |
193 | @@ -307,7 +308,7 @@ |
194 | if (colorModels) { |
195 | for (int i = 0; i < colorModels->num_choices; ++i) { |
196 | models.append( |
197 | - Utils::parsePpdColorModel( |
198 | + PpdUtils::parsePpdColorModel( |
199 | colorModels->choices[i].choice, |
200 | colorModels->choices[i].text, |
201 | QStringLiteral("ColorModel") |
202 | @@ -357,7 +358,7 @@ |
203 | |
204 | __CUPS_ADD_OPTION(dest, "copies", QString::number(options->copies()).toLocal8Bit()); |
205 | __CUPS_ADD_OPTION(dest, "ColorModel", options->getColorModel().name.toLocal8Bit()); |
206 | - __CUPS_ADD_OPTION(dest, "Duplex", Utils::duplexModeToPpdChoice(options->getDuplexMode()).toLocal8Bit()); |
207 | + __CUPS_ADD_OPTION(dest, "Duplex", PpdUtils::duplexModeToPpdChoice(options->getDuplexMode()).toLocal8Bit()); |
208 | |
209 | if (options->landscape()) { |
210 | __CUPS_ADD_OPTION(dest, "landscape", ""); |
211 | |
212 | === modified file 'modules/Ubuntu/Components/Extras/Printers/cups/devicesearcher.cpp' |
213 | --- modules/Ubuntu/Components/Extras/Printers/cups/devicesearcher.cpp 2017-03-08 14:47:16 +0000 |
214 | +++ modules/Ubuntu/Components/Extras/Printers/cups/devicesearcher.cpp 2017-03-30 13:23:27 +0000 |
215 | @@ -15,9 +15,9 @@ |
216 | */ |
217 | |
218 | #include "cups/ippclient.h" |
219 | +#include "cups/ppdutils.h" |
220 | #include "devicesearcher.h" |
221 | - |
222 | -#include <QUrl> |
223 | +#include "utils.h" |
224 | |
225 | DeviceSearcher::DeviceSearcher(IppClient *client, QObject *parent) |
226 | : QObject(parent) |
227 | @@ -54,14 +54,45 @@ |
228 | } |
229 | |
230 | Device d; |
231 | + |
232 | d.cls = deviceClass; |
233 | d.id = deviceId; |
234 | d.info = deviceInfo; |
235 | - d.makeModel = deviceMakeAndModel; |
236 | + |
237 | d.uri = deviceUri; |
238 | d.location = deviceLocation; |
239 | - |
240 | - searcher->deviceFound(d); |
241 | + d.makeModel = deviceMakeAndModel; |
242 | + |
243 | + DeviceSearcher::fixMakeModel(d); |
244 | + |
245 | + if (!d.makeModel.isEmpty()) { |
246 | + searcher->deviceFound(d); |
247 | + } |
248 | +} |
249 | + |
250 | +void DeviceSearcher::fixMakeModel(Device &device) |
251 | +{ |
252 | + // We can't set make-model on devices without enough info. |
253 | + if (device.id.isEmpty() && |
254 | + (device.makeModel.isEmpty() || |
255 | + device.makeModel == QStringLiteral("Unknown"))) { |
256 | + device.makeModel = QString::null; |
257 | + |
258 | + return; |
259 | + } |
260 | + |
261 | + QString canidateMakeModel; |
262 | + |
263 | + if (!device.id.isEmpty()) { |
264 | + auto idDict = Utils::parseDeviceId(device.id); |
265 | + canidateMakeModel = QString("%1 %2").arg(idDict["MFG"], idDict["MDL"]); |
266 | + } else if (!device.makeModel.isEmpty()) { |
267 | + canidateMakeModel = device.makeModel; |
268 | + } |
269 | + |
270 | + auto split = PpdUtils::splitMakeModel(canidateMakeModel); |
271 | + canidateMakeModel = QString("%1 %2").arg(split.first, split.second); |
272 | + device.makeModel = canidateMakeModel; |
273 | } |
274 | |
275 | void DeviceSearcher::deviceFound(const Device &device) |
276 | |
277 | === modified file 'modules/Ubuntu/Components/Extras/Printers/cups/devicesearcher.h' |
278 | --- modules/Ubuntu/Components/Extras/Printers/cups/devicesearcher.h 2017-03-08 11:29:01 +0000 |
279 | +++ modules/Ubuntu/Components/Extras/Printers/cups/devicesearcher.h 2017-03-30 13:23:27 +0000 |
280 | @@ -47,6 +47,7 @@ |
281 | const char *location, |
282 | void *context); |
283 | void deviceFound(const Device &device); |
284 | + static void fixMakeModel(Device &device); |
285 | |
286 | Q_SIGNALS: |
287 | void loaded(const Device &device); |
288 | |
289 | === added file 'modules/Ubuntu/Components/Extras/Printers/cups/ppdutils.cpp' |
290 | --- modules/Ubuntu/Components/Extras/Printers/cups/ppdutils.cpp 1970-01-01 00:00:00 +0000 |
291 | +++ modules/Ubuntu/Components/Extras/Printers/cups/ppdutils.cpp 2017-03-30 13:23:27 +0000 |
292 | @@ -0,0 +1,343 @@ |
293 | +/* |
294 | + * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2014, 2015 Red Hat, Inc. |
295 | + * Copyright (C) 2006 Florian Festi <ffesti@redhat.com> |
296 | + * Copyright (C) 2006, 2007, 2008, 2009 Tim Waugh <twaugh@redhat.com> |
297 | + * Copyright (C) 2017 Canonical, Ltd. |
298 | + * |
299 | + * This program is free software; you can redistribute it and/or modify |
300 | + * it under the terms of the GNU Lesser General Public License as published by |
301 | + * the Free Software Foundation; version 3. |
302 | + * |
303 | + * This program is distributed in the hope that it will be useful, |
304 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
305 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
306 | + * GNU Lesser General Public License for more details. |
307 | + * |
308 | + * You should have received a copy of the GNU Lesser General Public License |
309 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
310 | + */ |
311 | + |
312 | +#include "ppdutils.h" |
313 | + |
314 | +#include <QSet> |
315 | + |
316 | +const QRegularExpression PpdUtils::versionNumbers = QRegularExpression( |
317 | + " v(?:er\\.)?\\d(?:\\d*\\.\\d+)?(?: |$)" |
318 | +); |
319 | +const QRegularExpression PpdUtils::ignoredSuffixes = QRegularExpression( |
320 | + "," |
321 | + "| hpijs" |
322 | + "| foomatic/" |
323 | + "| - " |
324 | + "| w/" |
325 | + "| \\(" |
326 | + "| postscript" |
327 | + "| ps" |
328 | + "| pdf" |
329 | + "| pxl" |
330 | + "| zjs" |
331 | + "| zxs" |
332 | + "| pcl3" |
333 | + "| printer" |
334 | + "|_bt" |
335 | + "| pcl" |
336 | + "| ufr ii" |
337 | + "| br-script" |
338 | +); |
339 | +const QRegularExpression PpdUtils::ignoreSeries = QRegularExpression( |
340 | + " series| all-in-one", |
341 | + QRegularExpression::CaseInsensitiveOption |
342 | +); |
343 | +const QList<QPair<QString, QRegularExpression>> PpdUtils::manufacturerByModels |
344 | + = QList<QPair<QString, QRegularExpression>>({ |
345 | + QPair<QString, QRegularExpression>( |
346 | + QStringLiteral("HP"), QRegularExpression( |
347 | + "deskjet" |
348 | + "|dj[ 0-9]?" |
349 | + "|laserjet" |
350 | + "|lj" |
351 | + "|color laserjet" |
352 | + "|color lj" |
353 | + "|designjet" |
354 | + "|officejet" |
355 | + "|oj" |
356 | + "|photosmart" |
357 | + "|ps " |
358 | + "|psc" |
359 | + "|edgeline" |
360 | + ) |
361 | + ), |
362 | + QPair<QString, QRegularExpression>( |
363 | + QStringLiteral("Epson"), QRegularExpression("stylus|aculaser") |
364 | + ), |
365 | + QPair<QString, QRegularExpression>( |
366 | + QStringLiteral("Apple"), QRegularExpression( |
367 | + "stylewriter" |
368 | + "|imagewriter" |
369 | + "|deskwriter" |
370 | + "|laserwriter" |
371 | + ) |
372 | + ), |
373 | + QPair<QString, QRegularExpression>( |
374 | + QStringLiteral("Canon"), QRegularExpression( |
375 | + "pixus" |
376 | + "|pixma" |
377 | + "|selphy" |
378 | + "|imagerunner" |
379 | + "|bj" |
380 | + "|lbp" |
381 | + ) |
382 | + ), |
383 | + QPair<QString, QRegularExpression>( |
384 | + QStringLiteral("Brother"), QRegularExpression("hl|dcp|mfc") |
385 | + ), |
386 | + QPair<QString, QRegularExpression>( |
387 | + QStringLiteral("Xerox"), QRegularExpression( |
388 | + "docuprint" |
389 | + "|docupage" |
390 | + "|phaser" |
391 | + "|workcentre" |
392 | + "|homecentre" |
393 | + ) |
394 | + ), |
395 | + QPair<QString, QRegularExpression>( |
396 | + QStringLiteral("Lexmark"), |
397 | + QRegularExpression("optra|(:color )?jetprinter") |
398 | + ), |
399 | + QPair<QString, QRegularExpression>( |
400 | + QStringLiteral("KONICA MINOLTA"), QRegularExpression( |
401 | + "magicolor" |
402 | + "|pageworks" |
403 | + "|pagepro" |
404 | + ) |
405 | + ), |
406 | + QPair<QString, QRegularExpression>( |
407 | + QStringLiteral("Kyocera"), QRegularExpression( |
408 | + "fs-" |
409 | + "|km-" |
410 | + "|taskalfa" |
411 | + ) |
412 | + ), |
413 | + QPair<QString, QRegularExpression>( |
414 | + QStringLiteral("Ricoh"), QRegularExpression("aficio") |
415 | + ), |
416 | + QPair<QString, QRegularExpression>( |
417 | + QStringLiteral("Oce"), QRegularExpression("varioprint") |
418 | + ), |
419 | + QPair<QString, QRegularExpression>( |
420 | + QStringLiteral("Oki"), QRegularExpression("okipage|microline") |
421 | + ), |
422 | +}); |
423 | + |
424 | +const QMap<QString, QString> PpdUtils::hpByModel = QMap<QString, QString>{ |
425 | + {"dj", "DeskJet"}, |
426 | + {"lj", "LaserJet"}, |
427 | + {"laserjet", "LaserJet"}, |
428 | + {"oj", "OfficeJet"}, |
429 | + {"officejet", "OfficeJet"}, |
430 | + {"color lj", "Color LaserJet"}, |
431 | + {"ps ", "PhotoSmart"}, |
432 | + {"hp ", ""}, |
433 | +}; |
434 | + |
435 | +QString PpdUtils::dedupeMakeModel(const QString &str) |
436 | +{ |
437 | + QSet<QString> stringSet; |
438 | + QStringList deduped; |
439 | + Q_FOREACH(auto m, str.split(" ")) { |
440 | + if (!stringSet.contains(m)) { |
441 | + deduped << m; |
442 | + } |
443 | + stringSet << m; |
444 | + } |
445 | + return deduped.join(" "); |
446 | +} |
447 | + |
448 | +QPair<QString, QString> PpdUtils::splitMakeModel(const QString &makeModel) |
449 | +{ |
450 | + const QString mm = dedupeMakeModel(makeModel); |
451 | + const QString makeModelLower = mm.toLower(); |
452 | + |
453 | + QString make; |
454 | + QString model; |
455 | + |
456 | + /* Informs us whether or not the make part was produced in such a way that |
457 | + it might need cleaning. I.e. we've made a guess at what the make is. */ |
458 | + bool cleanupMake = false; |
459 | + |
460 | + /* If the string starts with a known model name (like "LaserJet") assume |
461 | + that the manufacturer name is missing and add the manufacturer name |
462 | + corresponding to the model name */ |
463 | + Q_FOREACH(auto manufacturerByModel, PpdUtils::manufacturerByModels) { |
464 | + if (manufacturerByModel.second.match(makeModelLower).hasMatch()) { |
465 | + make = manufacturerByModel.first; |
466 | + model = mm; |
467 | + } |
468 | + } |
469 | + |
470 | + /* Take the first word as the name of the manufacturer. |
471 | + |
472 | + Note that we have skipped a bunch of special cases where the first word |
473 | + isn't necessarily the manufacturer, hence the next TODO. |
474 | + |
475 | + TODO: Handle special cases for TurboPrint, lexmark international, |
476 | + fuji xerox, etc. */ |
477 | + int firstSpace = mm.indexOf(" "); |
478 | + if (make.isEmpty() && firstSpace > 0) { |
479 | + make = mm.left(firstSpace); |
480 | + model = mm.mid(make.size() + 1); |
481 | + |
482 | + /* We've picked the first part as the make, but it may be that we've |
483 | + split "hewlett packard" and are left with "hewlett". */ |
484 | + cleanupMake = true; |
485 | + } |
486 | + |
487 | + // Standardised names for manufacturers. |
488 | + const QString makeLower = make.toLower(); |
489 | + if (cleanupMake) { |
490 | + if (makeLower.startsWith("hewlett") && makeLower.endsWith("packard")) { |
491 | + make = "HP"; |
492 | + // makeLower = "hp" |
493 | + } else if (makeLower.startsWith("konica") && |
494 | + makeLower.endsWith("minolta")) { |
495 | + make = "KONICA MINOLTA"; |
496 | + // makeLower = "konica minolta" |
497 | + } else { |
498 | + // Fix case errors. |
499 | + Q_FOREACH(auto manufacturerByModel, manufacturerByModels) { |
500 | + if (makeLower == manufacturerByModel.first.toLower()) { |
501 | + make = manufacturerByModel.first; |
502 | + break; |
503 | + } |
504 | + } |
505 | + } |
506 | + } |
507 | + |
508 | + // Remove the word "Series" if present. |
509 | + model = model.remove(ignoreSeries); |
510 | + |
511 | + /* Find a version number and cut the model from there. Note that some |
512 | + models may legitimately look like version numbers. So we cut only if the |
513 | + version number has max one digit, or a dot with digits before and after. */ |
514 | + QString modelLower = model.toLower(); |
515 | + int versionAt = modelLower.indexOf(" v"); |
516 | + if (versionAt >= 0) { |
517 | + // Look for v or ver. followed by digits and dots. |
518 | + auto match = versionNumbers.match(modelLower); |
519 | + if (match.hasMatch()) { |
520 | + int matchAt = match.capturedStart(); |
521 | + model.truncate(matchAt); |
522 | + modelLower.truncate(matchAt); |
523 | + } |
524 | + } |
525 | + |
526 | + // Remove ignored suffixes. |
527 | + auto suffixMatch = ignoredSuffixes.match(modelLower); |
528 | + if (suffixMatch.hasMatch()) { |
529 | + int matchAt = suffixMatch.capturedStart(); |
530 | + model.truncate(matchAt); |
531 | + modelLower.truncate(matchAt); |
532 | + } |
533 | + |
534 | + // Replace lj or dj with LaserJet and DeskJet respectively, etc. |
535 | + if (makeLower == "hp") { |
536 | + Q_FOREACH(auto name, hpByModel.keys()) { |
537 | + if (modelLower.contains(name)) { |
538 | + int start = modelLower.indexOf(name); |
539 | + model.remove(start, name.size()); |
540 | + model.insert(start, hpByModel[name]); |
541 | + modelLower = model.toLower(); |
542 | + } |
543 | + } |
544 | + } |
545 | + |
546 | + return QPair<QString, QString>(make, model); |
547 | +} |
548 | + |
549 | +QString PpdUtils::normalize(const QString &str) |
550 | +{ |
551 | + QString strLower = str.trimmed().toLower(); |
552 | + QString normalized; |
553 | + |
554 | + const int BLANK = 0; |
555 | + const int ALPHA = 1; |
556 | + const int DIGIT = 2; |
557 | + int lastchar = BLANK; |
558 | + |
559 | + bool alnumfound = false; |
560 | + |
561 | + for (int i = 0; i < strLower.size(); i++) { |
562 | + if (strLower[i].isLetter()) { |
563 | + if (lastchar != ALPHA && alnumfound) { |
564 | + normalized += " "; |
565 | + } |
566 | + lastchar = ALPHA; |
567 | + } else if (strLower[i].isDigit()) { |
568 | + if (lastchar != DIGIT && alnumfound) { |
569 | + normalized += " "; |
570 | + } |
571 | + lastchar = DIGIT; |
572 | + } else { |
573 | + lastchar = BLANK; |
574 | + } |
575 | + |
576 | + if (strLower[i].isLetterOrNumber()) { |
577 | + normalized += strLower[i]; |
578 | + alnumfound = true; |
579 | + } |
580 | + } |
581 | + return normalized; |
582 | +} |
583 | + |
584 | +PrinterEnum::DuplexMode PpdUtils::ppdChoiceToDuplexMode(const QString &choice) |
585 | +{ |
586 | + if (choice == "DuplexTumble") |
587 | + return PrinterEnum::DuplexMode::DuplexShortSide; |
588 | + else if (choice == "DuplexNoTumble") |
589 | + return PrinterEnum::DuplexMode::DuplexLongSide; |
590 | + else |
591 | + return PrinterEnum::DuplexMode::DuplexNone; |
592 | +} |
593 | + |
594 | +const QString PpdUtils::duplexModeToPpdChoice( |
595 | + const PrinterEnum::DuplexMode &mode) |
596 | +{ |
597 | + switch (mode) { |
598 | + case PrinterEnum::DuplexMode::DuplexShortSide: |
599 | + return "DuplexTumble"; |
600 | + case PrinterEnum::DuplexMode::DuplexLongSide: |
601 | + return "DuplexNoTumble"; |
602 | + case PrinterEnum::DuplexMode::DuplexNone: |
603 | + default: |
604 | + return "None"; |
605 | + } |
606 | +} |
607 | + |
608 | + |
609 | +ColorModel PpdUtils::parsePpdColorModel(const QString &name, |
610 | + const QString &text, |
611 | + const QString &optionName) |
612 | +{ |
613 | + ColorModel ret; |
614 | + ret.name = name; |
615 | + ret.text = text; |
616 | + ret.originalOption = optionName; |
617 | + |
618 | + if (ret.name.contains("Gray") || ret.name.contains("Black")) { |
619 | + ret.colorType = PrinterEnum::ColorModelType::GrayType; |
620 | + } else { |
621 | + ret.colorType = PrinterEnum::ColorModelType::ColorType; |
622 | + } |
623 | + return ret; |
624 | +} |
625 | + |
626 | +PrintQuality PpdUtils::parsePpdPrintQuality(const QString &choice, |
627 | + const QString &text, |
628 | + const QString &optionName) |
629 | +{ |
630 | + PrintQuality quality; |
631 | + quality.name = choice; |
632 | + quality.text = text; |
633 | + quality.originalOption = optionName; |
634 | + return quality; |
635 | +} |
636 | |
637 | === added file 'modules/Ubuntu/Components/Extras/Printers/cups/ppdutils.h' |
638 | --- modules/Ubuntu/Components/Extras/Printers/cups/ppdutils.h 1970-01-01 00:00:00 +0000 |
639 | +++ modules/Ubuntu/Components/Extras/Printers/cups/ppdutils.h 2017-03-30 13:23:27 +0000 |
640 | @@ -0,0 +1,87 @@ |
641 | +/* |
642 | + * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2014, 2015 Red Hat, Inc. |
643 | + * Copyright (C) 2006 Florian Festi <ffesti@redhat.com> |
644 | + * Copyright (C) 2006, 2007, 2008, 2009 Tim Waugh <twaugh@redhat.com> |
645 | + * Copyright (C) 2017 Canonical, Ltd. |
646 | + * |
647 | + * This program is free software; you can redistribute it and/or modify |
648 | + * it under the terms of the GNU Lesser General Public License as published by |
649 | + * the Free Software Foundation; version 3. |
650 | + * |
651 | + * This program is distributed in the hope that it will be useful, |
652 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
653 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
654 | + * GNU Lesser General Public License for more details. |
655 | + * |
656 | + * You should have received a copy of the GNU Lesser General Public License |
657 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
658 | + */ |
659 | + |
660 | +#ifndef USC_PRINTERS_CUPS_PPDUTILS_H |
661 | +#define USC_PRINTERS_CUPS_PPDUTILS_H |
662 | + |
663 | +#include "enums.h" |
664 | +#include "i18n.h" |
665 | +#include "printers_global.h" |
666 | +#include "structs.h" |
667 | + |
668 | +#include <cups/ppd.h> |
669 | + |
670 | +#include <QList> |
671 | +#include <QMap> |
672 | +#include <QPair> |
673 | +#include <QRegularExpression> |
674 | +#include <QString> |
675 | + |
676 | +struct PRINTERS_DECL_EXPORT PpdUtils |
677 | +{ |
678 | +private: |
679 | + static const QList<QPair<QString, QRegularExpression>> manufacturerByModels; |
680 | + static const QRegularExpression ignoredSuffixes; |
681 | + static const QRegularExpression versionNumbers; |
682 | + static const QRegularExpression ignoreSeries; |
683 | + static const QMap<QString, QString> hpByModel; |
684 | + static QString dedupeMakeModel(const QString &str); |
685 | +public: |
686 | + |
687 | + /** |
688 | + Split a ppd-make-and-model string into a canonical make and model pair. |
689 | + Returns a string pair representing the make and the model. |
690 | + */ |
691 | + static QPair<QString, QString> splitMakeModel(const QString &makeModel); |
692 | + |
693 | + /** |
694 | + This function normalizes manufacturer and model names for comparing. |
695 | + The string is turned to lower case and leading and trailing white |
696 | + space is removed. After that each sequence of non-alphanumeric |
697 | + characters (including white space) is replaced by a single space and |
698 | + also at each change between letters and numbers a single space is added. |
699 | + This makes the comparison only done by alphanumeric characters and the |
700 | + words formed from them. So mostly two strings which sound the same when |
701 | + you pronounce them are considered equal. Printer manufacturers do not |
702 | + market two models whose names sound the same but differ only by |
703 | + upper/lower case, spaces, dashes, ..., but in printer drivers names can |
704 | + be easily supplied with these details of the name written in the wrong |
705 | + way, especially if the IEEE-1284 device ID of the printer is not known. |
706 | + This way we get a very reliable matching of printer model names. |
707 | + Examples: |
708 | + - Epson PM-A820 -> epson pm a 820 |
709 | + - Epson PM A820 -> epson pm a 820 |
710 | + - HP PhotoSmart C 8100 -> hp photosmart c 8100 |
711 | + - hp Photosmart C8100 -> hp photosmart c 8100 |
712 | + */ |
713 | + static QString normalize(const QString &str); |
714 | + |
715 | + static PrinterEnum::DuplexMode ppdChoiceToDuplexMode( |
716 | + const QString &choice); |
717 | + static const QString duplexModeToPpdChoice( |
718 | + const PrinterEnum::DuplexMode &mode); |
719 | + static ColorModel parsePpdColorModel(const QString &name, |
720 | + const QString &text, |
721 | + const QString &optionName); |
722 | + static PrintQuality parsePpdPrintQuality(const QString &choice, |
723 | + const QString &text, |
724 | + const QString &optionName); |
725 | +}; |
726 | + |
727 | +#endif // USC_PRINTERS_CUPS_PPDUTILS_H |
728 | |
729 | === modified file 'modules/Ubuntu/Components/Extras/Printers/models/devicemodel.cpp' |
730 | --- modules/Ubuntu/Components/Extras/Printers/models/devicemodel.cpp 2017-03-09 14:34:05 +0000 |
731 | +++ modules/Ubuntu/Components/Extras/Printers/models/devicemodel.cpp 2017-03-30 13:23:27 +0000 |
732 | @@ -15,9 +15,13 @@ |
733 | */ |
734 | |
735 | #include "backend/backend_cups.h" |
736 | +#include "cups/ppdutils.h" |
737 | #include "models/devicemodel.h" |
738 | |
739 | #include <QDebug> |
740 | +#include <QPair> |
741 | +#include <QQmlEngine> |
742 | +#include <QSet> |
743 | |
744 | DeviceModel::DeviceModel(PrinterBackend *backend, QObject *parent) |
745 | : QAbstractListModel(parent) |
746 | @@ -116,8 +120,16 @@ |
747 | |
748 | bool DeviceModel::deviceWanted(const Device &device) |
749 | { |
750 | + bool wanted = true; |
751 | + |
752 | + // Skip non-uris. |
753 | auto parts = device.uri.split(":", QString::SkipEmptyParts); |
754 | - return parts.size() > 1; |
755 | + wanted = wanted && parts.size() > 1; |
756 | + |
757 | + // Skip "Unknown" make-models. |
758 | + wanted = wanted && device.makeModel != QStringLiteral("Unknown"); |
759 | + |
760 | + return wanted; |
761 | } |
762 | |
763 | void DeviceModel::clear() |
764 | @@ -147,3 +159,206 @@ |
765 | m_isSearching = false; |
766 | Q_EMIT searchingChanged(); |
767 | } |
768 | + |
769 | +bool DeviceModel::searching() |
770 | +{ |
771 | + return m_isSearching; |
772 | +} |
773 | + |
774 | +DeviceFilter::DeviceFilter(QObject *parent) : QSortFilterProxyModel(parent) |
775 | +{ |
776 | + connect(this, SIGNAL(sourceModelChanged()), SLOT(onSourceModelChanged())); |
777 | +} |
778 | + |
779 | +DeviceFilter::~DeviceFilter() |
780 | +{ |
781 | +} |
782 | + |
783 | +void DeviceFilter::onSourceModelChanged() |
784 | +{ |
785 | + connect( |
786 | + (DeviceModel*) sourceModel(), SIGNAL(countChanged()), |
787 | + this, SIGNAL(countChanged()) |
788 | + ); |
789 | +} |
790 | + |
791 | +void DeviceFilter::onSourceModelCountChanged() |
792 | +{ |
793 | + Q_EMIT countChanged(); |
794 | +} |
795 | + |
796 | +int DeviceFilter::count() const |
797 | +{ |
798 | + return rowCount(); |
799 | +} |
800 | + |
801 | +void DeviceFilter::filterOnNormalizedMakeModel( |
802 | + const QString &normalizedMakeModel) |
803 | +{ |
804 | + m_normalizedMakeModel = normalizedMakeModel; |
805 | + m_normalizedMakeModelFilterEnabled = true; |
806 | + invalidate(); |
807 | +} |
808 | + |
809 | +void DeviceFilter::filterOnUri(const QString &uri) |
810 | +{ |
811 | + m_uri = uri; |
812 | + m_uriFilterEnabled = true; |
813 | + invalidate(); |
814 | +} |
815 | + |
816 | +bool DeviceFilter::filterAcceptsRow(int sourceRow, |
817 | + const QModelIndex &sourceParent) const |
818 | +{ |
819 | + bool accepts = true; |
820 | + QModelIndex childIndex = sourceModel()->index(sourceRow, 0, sourceParent); |
821 | + |
822 | + if (accepts && m_normalizedMakeModelFilterEnabled) { |
823 | + QString makeModel = childIndex.model()->data( |
824 | + childIndex, DeviceModel::MakeModelRole).toString(); |
825 | + accepts = m_normalizedMakeModel == PpdUtils::normalize(makeModel); |
826 | + } |
827 | + |
828 | + if (accepts && m_uriFilterEnabled) { |
829 | + QString uri = childIndex.model()->data( |
830 | + childIndex, DeviceModel::UriRole).toString(); |
831 | + accepts = m_uri == uri; |
832 | + } |
833 | + |
834 | + return accepts; |
835 | +} |
836 | + |
837 | +ConsolidatedDeviceModel::ConsolidatedDeviceModel(DeviceModel *deviceModel, |
838 | + QObject *parent) |
839 | + : QAbstractListModel(parent) |
840 | + , m_deviceModel(deviceModel) |
841 | +{ |
842 | + connect(m_deviceModel, SIGNAL(searchingChanged()), |
843 | + this, SLOT(deviceModelSearchChanged())); |
844 | +} |
845 | + |
846 | +ConsolidatedDeviceModel::~ConsolidatedDeviceModel() |
847 | +{ |
848 | +} |
849 | + |
850 | +int ConsolidatedDeviceModel::rowCount(const QModelIndex &parent) const |
851 | +{ |
852 | + Q_UNUSED(parent); |
853 | + return m_consolidatedDevices.size(); |
854 | +} |
855 | + |
856 | +int ConsolidatedDeviceModel::count() const |
857 | +{ |
858 | + return rowCount(); |
859 | +} |
860 | + |
861 | +bool ConsolidatedDeviceModel::searching() |
862 | +{ |
863 | + return m_deviceModel->searching(); |
864 | +} |
865 | + |
866 | +bool ConsolidatedDeviceModel::makeModelIsConsolidateable( |
867 | + const QString &makeModel) const |
868 | +{ |
869 | + return !makeModel.isEmpty() && makeModel != QStringLiteral("Unknown"); |
870 | +} |
871 | + |
872 | +void ConsolidatedDeviceModel::deviceModelSearchChanged() |
873 | +{ |
874 | + int oldCount = rowCount(); |
875 | + |
876 | + beginResetModel(); |
877 | + m_consolidatedDevices.clear(); |
878 | + |
879 | + /* Device model has finished searching, so let's sync our model with it. |
880 | + Note that if the deviceModel is searching, we simply dump our model. */ |
881 | + if (!m_deviceModel->searching()) { |
882 | + // A set of normalized make-models. |
883 | + QSet<QString> normalizedMakeModels; |
884 | + |
885 | + // Used to preserve makeModels after it has been normalized. |
886 | + QMap<QString, QString> normalizedToMakeModel; |
887 | + for (int i = 0; i < m_deviceModel->rowCount(); i++) { |
888 | + QModelIndex idx = m_deviceModel->index(i, 0); |
889 | + QString makeModel = m_deviceModel->data( |
890 | + idx, DeviceModel::MakeModelRole |
891 | + ).toString(); |
892 | + |
893 | + // We can't do anything about a make-model-less device. |
894 | + if (makeModel.isEmpty()) { |
895 | + continue; |
896 | + } |
897 | + |
898 | + if (makeModelIsConsolidateable(makeModel)) { |
899 | + QString normalized = PpdUtils::normalize(makeModel); |
900 | + normalizedMakeModels << normalized; |
901 | + normalizedToMakeModel[normalized] = makeModel; |
902 | + } |
903 | + } |
904 | + |
905 | + /* For every make-and-model, create a proxy model, and install a filter |
906 | + on it. Then do the same for those consolidated under URI (i.e. not |
907 | + consolidated). |
908 | + Note that a parent is set on each filter so that it is cleaned up when |
909 | + we the consolidated model is destroyed. */ |
910 | + Q_FOREACH(auto mm, normalizedMakeModels) { |
911 | + auto filter = new DeviceFilter(this); |
912 | + filter->setSourceModel(m_deviceModel); |
913 | + filter->filterOnNormalizedMakeModel(mm); |
914 | + |
915 | + QPair<QString, DeviceFilter*> p(mm, filter); |
916 | + m_consolidatedDevices << p; |
917 | + } |
918 | + } |
919 | + |
920 | + endResetModel(); |
921 | + |
922 | + if (oldCount != rowCount()) { |
923 | + Q_EMIT countChanged(); |
924 | + } |
925 | + Q_EMIT searchingChanged(); |
926 | +} |
927 | + |
928 | +QVariant ConsolidatedDeviceModel::data(const QModelIndex &index, int role) const |
929 | +{ |
930 | + QVariant ret; |
931 | + |
932 | + if ((0 <= index.row()) && (index.row() < m_consolidatedDevices.size())) { |
933 | + |
934 | + auto consolidated = m_consolidatedDevices[index.row()]; |
935 | + auto mm = consolidated.first; |
936 | + auto filter = consolidated.second; |
937 | + |
938 | + switch (role) { |
939 | + case Qt::DisplayRole: |
940 | + case ConsolidatedNameRole: |
941 | + ret = filter->data( |
942 | + filter->index(0, 0), DeviceModel::Roles::MakeModelRole |
943 | + ).toString(); |
944 | + break; |
945 | + case DevicesRole: { |
946 | + ret = QVariant::fromValue(filter); |
947 | + break; |
948 | + case ConnectionsCountRole: |
949 | + ret = filter->count(); |
950 | + break; |
951 | + } |
952 | + } |
953 | + } |
954 | + |
955 | + return ret; |
956 | +} |
957 | + |
958 | +QHash<int, QByteArray> ConsolidatedDeviceModel::roleNames() const |
959 | +{ |
960 | + static QHash<int,QByteArray> names; |
961 | + |
962 | + if (Q_UNLIKELY(names.empty())) { |
963 | + names[Qt::DisplayRole] = "displayName"; |
964 | + names[ConsolidatedNameRole] = "consolidatedName"; |
965 | + names[DevicesRole] = "devices"; |
966 | + names[ConnectionsCountRole] = "connectionsCount"; |
967 | + } |
968 | + |
969 | + return names; |
970 | +} |
971 | |
972 | === modified file 'modules/Ubuntu/Components/Extras/Printers/models/devicemodel.h' |
973 | --- modules/Ubuntu/Components/Extras/Printers/models/devicemodel.h 2017-03-09 14:34:05 +0000 |
974 | +++ modules/Ubuntu/Components/Extras/Printers/models/devicemodel.h 2017-03-30 13:23:27 +0000 |
975 | @@ -31,7 +31,7 @@ |
976 | { |
977 | Q_OBJECT |
978 | Q_PROPERTY(int count READ count NOTIFY countChanged) |
979 | - Q_PROPERTY(bool searching MEMBER m_isSearching NOTIFY searchingChanged) |
980 | + Q_PROPERTY(bool searching READ searching NOTIFY searchingChanged) |
981 | public: |
982 | explicit DeviceModel(PrinterBackend *backend, QObject *parent = Q_NULLPTR); |
983 | ~DeviceModel(); |
984 | @@ -56,6 +56,7 @@ |
985 | int count() const; |
986 | void load(); |
987 | void clear(); |
988 | + bool searching(); |
989 | |
990 | private Q_SLOTS: |
991 | void deviceLoaded(const Device &device); |
992 | @@ -75,4 +76,82 @@ |
993 | bool m_isSearching; |
994 | }; |
995 | |
996 | + |
997 | +class PRINTERS_DECL_EXPORT DeviceFilter : public QSortFilterProxyModel |
998 | +{ |
999 | + Q_OBJECT |
1000 | + Q_PROPERTY(int count READ count NOTIFY countChanged) |
1001 | +public: |
1002 | + explicit DeviceFilter(QObject *parent = Q_NULLPTR); |
1003 | + ~DeviceFilter(); |
1004 | + |
1005 | + void filterOnNormalizedMakeModel(const QString &normalizedMakeModel); |
1006 | + void filterOnUri(const QString &uri); |
1007 | + int count() const; |
1008 | +protected: |
1009 | + virtual bool filterAcceptsRow( |
1010 | + int sourceRow, const QModelIndex &sourceParent) const override; |
1011 | + |
1012 | +Q_SIGNALS: |
1013 | + void countChanged(); |
1014 | + |
1015 | +private Q_SLOTS: |
1016 | + void onSourceModelChanged(); |
1017 | + void onSourceModelCountChanged(); |
1018 | + |
1019 | +private: |
1020 | + QString m_normalizedMakeModel = QString::null; |
1021 | + bool m_normalizedMakeModelFilterEnabled = false; |
1022 | + |
1023 | + QString m_uri = QString::null; |
1024 | + bool m_uriFilterEnabled = false; |
1025 | +}; |
1026 | + |
1027 | +/* The ConsolidatedDeviceModel is a model of ConsolidatedDevices. A |
1028 | +ConsolidatedDevice is a set of Devices that share a printer name (assuming |
1029 | +unique printer names). |
1030 | + |
1031 | +TODO: The DeviceModel should implement the consolidation by use of e.g. a |
1032 | +table model, or tree model. This model can then be deleted. */ |
1033 | +class PRINTERS_DECL_EXPORT ConsolidatedDeviceModel : public QAbstractListModel |
1034 | +{ |
1035 | + Q_OBJECT |
1036 | + Q_PROPERTY(int count READ count NOTIFY countChanged) |
1037 | + Q_PROPERTY(bool searching READ searching NOTIFY searchingChanged) |
1038 | +public: |
1039 | + explicit ConsolidatedDeviceModel(DeviceModel *deviceModel, |
1040 | + QObject *parent = Q_NULLPTR); |
1041 | + ~ConsolidatedDeviceModel(); |
1042 | + |
1043 | + enum Roles |
1044 | + { |
1045 | + // Qt::DisplayRole holds consolidated device name |
1046 | + DevicesRole = Qt::UserRole, |
1047 | + ConsolidatedNameRole, |
1048 | + ConnectionsCountRole, |
1049 | + LastRole = ConnectionsCountRole, |
1050 | + }; |
1051 | + |
1052 | + virtual int rowCount(const QModelIndex &parent = QModelIndex()) const override; |
1053 | + virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; |
1054 | + virtual QHash<int, QByteArray> roleNames() const override; |
1055 | + |
1056 | + int count() const; |
1057 | + void load(); |
1058 | + bool searching(); |
1059 | + bool makeModelIsConsolidateable(const QString &makeModel) const; |
1060 | + |
1061 | +private Q_SLOTS: |
1062 | + void deviceModelSearchChanged(); |
1063 | + |
1064 | +Q_SIGNALS: |
1065 | + void countChanged(); |
1066 | + void searchingChanged(); |
1067 | + |
1068 | +private: |
1069 | + DeviceModel *m_deviceModel; |
1070 | + |
1071 | + QList<QPair<QString, DeviceFilter*>> m_consolidatedDevices; |
1072 | +}; |
1073 | + |
1074 | #endif // USC_PRINTER_DEVICEMODEL_H |
1075 | |
1076 | === modified file 'modules/Ubuntu/Components/Extras/Printers/printer/printer.cpp' |
1077 | --- modules/Ubuntu/Components/Extras/Printers/printer/printer.cpp 2017-03-21 13:01:42 +0000 |
1078 | +++ modules/Ubuntu/Components/Extras/Printers/printer/printer.cpp 2017-03-30 13:23:27 +0000 |
1079 | @@ -14,6 +14,7 @@ |
1080 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1081 | */ |
1082 | |
1083 | +#include "cups/ppdutils.h" |
1084 | #include "i18n.h" |
1085 | #include "utils.h" |
1086 | |
1087 | @@ -291,7 +292,7 @@ |
1088 | return; |
1089 | } |
1090 | |
1091 | - QStringList vals({Utils::duplexModeToPpdChoice(duplexMode)}); |
1092 | + QStringList vals({PpdUtils::duplexModeToPpdChoice(duplexMode)}); |
1093 | m_backend->printerAddOption(name(), "Duplex", vals); |
1094 | } |
1095 | |
1096 | |
1097 | === modified file 'modules/Ubuntu/Components/Extras/Printers/printer/printerjob.cpp' |
1098 | --- modules/Ubuntu/Components/Extras/Printers/printer/printerjob.cpp 2017-03-16 15:47:11 +0000 |
1099 | +++ modules/Ubuntu/Components/Extras/Printers/printer/printerjob.cpp 2017-03-30 13:23:27 +0000 |
1100 | @@ -18,9 +18,9 @@ |
1101 | #include <QUrl> |
1102 | |
1103 | #include "backend/backend_cups.h" |
1104 | +#include "cups/ppdutils.h" |
1105 | #include "models/printermodel.h" |
1106 | #include "printer/printerjob.h" |
1107 | -#include "utils.h" |
1108 | |
1109 | PrinterJob::PrinterJob(QString printerName, |
1110 | PrinterBackend *backend, |
1111 | @@ -166,7 +166,7 @@ |
1112 | |
1113 | // No duplexMode will result in PrinterJob using defaultDuplexMode |
1114 | QString duplex = attributes.value("Duplex").toString(); |
1115 | - PrinterEnum::DuplexMode duplexMode = Utils::ppdChoiceToDuplexMode(duplex); |
1116 | + PrinterEnum::DuplexMode duplexMode = PpdUtils::ppdChoiceToDuplexMode(duplex); |
1117 | for (int i=0; i < m_printer->supportedDuplexModes().length(); i++) { |
1118 | if (m_printer->supportedDuplexModes().at(i) == duplexMode) { |
1119 | setDuplexMode(i); |
1120 | |
1121 | === modified file 'modules/Ubuntu/Components/Extras/Printers/printers/printers.cpp' |
1122 | --- modules/Ubuntu/Components/Extras/Printers/printers/printers.cpp 2017-03-21 14:51:38 +0000 |
1123 | +++ modules/Ubuntu/Components/Extras/Printers/printers/printers.cpp 2017-03-30 13:23:27 +0000 |
1124 | @@ -35,6 +35,7 @@ |
1125 | : QObject(parent) |
1126 | , m_backend(backend) |
1127 | , m_devices(backend) |
1128 | + , m_consolidatedDevices(&m_devices) |
1129 | , m_drivers(backend) |
1130 | , m_model(backend) |
1131 | , m_jobs(backend) |
1132 | @@ -259,6 +260,14 @@ |
1133 | m_devices.load(); |
1134 | } |
1135 | |
1136 | +QAbstractItemModel* Printers::consolidatedDevices() |
1137 | +{ |
1138 | + m_devices.load(); |
1139 | + auto ret = &m_consolidatedDevices; |
1140 | + QQmlEngine::setObjectOwnership(ret, QQmlEngine::CppOwnership); |
1141 | + return ret; |
1142 | +} |
1143 | + |
1144 | bool Printers::addPrinter(const QString &name, const QString &ppd, |
1145 | const QString &device, const QString &description, |
1146 | const QString &location) |
1147 | |
1148 | === modified file 'modules/Ubuntu/Components/Extras/Printers/printers/printers.h' |
1149 | --- modules/Ubuntu/Components/Extras/Printers/printers/printers.h 2017-03-21 14:51:38 +0000 |
1150 | +++ modules/Ubuntu/Components/Extras/Printers/printers/printers.h 2017-03-30 13:23:27 +0000 |
1151 | @@ -42,6 +42,7 @@ |
1152 | Q_PROPERTY(QAbstractItemModel* printJobs READ printJobs CONSTANT) |
1153 | Q_PROPERTY(QAbstractItemModel* drivers READ drivers CONSTANT) |
1154 | Q_PROPERTY(QAbstractItemModel* devices READ devices CONSTANT) |
1155 | + Q_PROPERTY(QAbstractItemModel* consolidatedDevices READ consolidatedDevices CONSTANT) |
1156 | Q_PROPERTY(QString driverFilter READ driverFilter WRITE setDriverFilter NOTIFY driverFilterChanged) |
1157 | Q_PROPERTY(QString defaultPrinterName READ defaultPrinterName WRITE setDefaultPrinterName NOTIFY defaultPrinterNameChanged) |
1158 | Q_PROPERTY(QString lastMessage READ lastMessage CONSTANT) |
1159 | @@ -60,6 +61,7 @@ |
1160 | QAbstractItemModel* printJobs(); |
1161 | QAbstractItemModel* drivers(); |
1162 | QAbstractItemModel* devices(); |
1163 | + QAbstractItemModel* consolidatedDevices(); |
1164 | QString driverFilter() const; |
1165 | QString defaultPrinterName() const; |
1166 | QString lastMessage() const; |
1167 | @@ -111,6 +113,7 @@ |
1168 | void provisionPrinter(const QString &name); |
1169 | PrinterBackend *m_backend; |
1170 | DeviceModel m_devices; |
1171 | + ConsolidatedDeviceModel m_consolidatedDevices; |
1172 | DriverModel m_drivers; |
1173 | PrinterModel m_model; |
1174 | JobModel m_jobs; |
1175 | |
1176 | === modified file 'modules/Ubuntu/Components/Extras/Printers/structs.h' |
1177 | --- modules/Ubuntu/Components/Extras/Printers/structs.h 2017-03-16 15:34:02 +0000 |
1178 | +++ modules/Ubuntu/Components/Extras/Printers/structs.h 2017-03-30 13:23:27 +0000 |
1179 | @@ -163,8 +163,6 @@ |
1180 | } |
1181 | }; |
1182 | |
1183 | - |
1184 | - |
1185 | Q_DECLARE_TYPEINFO(ColorModel, Q_PRIMITIVE_TYPE); |
1186 | Q_DECLARE_METATYPE(ColorModel) |
1187 | |
1188 | |
1189 | === modified file 'modules/Ubuntu/Components/Extras/Printers/utils.h' |
1190 | --- modules/Ubuntu/Components/Extras/Printers/utils.h 2017-02-21 10:46:29 +0000 |
1191 | +++ modules/Ubuntu/Components/Extras/Printers/utils.h 2017-03-30 13:23:27 +0000 |
1192 | @@ -19,39 +19,14 @@ |
1193 | |
1194 | #include "enums.h" |
1195 | #include "i18n.h" |
1196 | -#include "structs.h" |
1197 | - |
1198 | -#include <cups/ppd.h> |
1199 | - |
1200 | + |
1201 | +#include <QMap> |
1202 | +#include <QPrinter> |
1203 | #include <QString> |
1204 | -#include <QPrinter> |
1205 | |
1206 | class Utils |
1207 | { |
1208 | public: |
1209 | - static PrinterEnum::DuplexMode ppdChoiceToDuplexMode(const QString &choice) |
1210 | - { |
1211 | - if (choice == "DuplexTumble") |
1212 | - return PrinterEnum::DuplexMode::DuplexShortSide; |
1213 | - else if (choice == "DuplexNoTumble") |
1214 | - return PrinterEnum::DuplexMode::DuplexLongSide; |
1215 | - else |
1216 | - return PrinterEnum::DuplexMode::DuplexNone; |
1217 | - } |
1218 | - |
1219 | - static const QString duplexModeToPpdChoice(const PrinterEnum::DuplexMode &mode) |
1220 | - { |
1221 | - switch (mode) { |
1222 | - case PrinterEnum::DuplexMode::DuplexShortSide: |
1223 | - return "DuplexTumble"; |
1224 | - case PrinterEnum::DuplexMode::DuplexLongSide: |
1225 | - return "DuplexNoTumble"; |
1226 | - case PrinterEnum::DuplexMode::DuplexNone: |
1227 | - default: |
1228 | - return "None"; |
1229 | - } |
1230 | - } |
1231 | - |
1232 | static const QString duplexModeToUIString(const PrinterEnum::DuplexMode &mode) |
1233 | { |
1234 | switch (mode) { |
1235 | @@ -79,31 +54,55 @@ |
1236 | } |
1237 | } |
1238 | |
1239 | - static ColorModel parsePpdColorModel(const QString &name, const QString &text, |
1240 | - const QString &optionName) |
1241 | - { |
1242 | - ColorModel ret; |
1243 | - ret.name = name; |
1244 | - ret.text = text; |
1245 | - ret.originalOption = optionName; |
1246 | - |
1247 | - if (ret.name.contains("Gray") || ret.name.contains("Black")) { |
1248 | - ret.colorType = PrinterEnum::ColorModelType::GrayType; |
1249 | - } else { |
1250 | - ret.colorType = PrinterEnum::ColorModelType::ColorType; |
1251 | - } |
1252 | - return ret; |
1253 | - } |
1254 | - |
1255 | - static PrintQuality parsePpdPrintQuality(const QString &choice, |
1256 | - const QString &text, |
1257 | - const QString &optionName) |
1258 | - { |
1259 | - PrintQuality quality; |
1260 | - quality.name = choice; |
1261 | - quality.text = text; |
1262 | - quality.originalOption = optionName; |
1263 | - return quality; |
1264 | + // Parse an IEEE 1284 Device ID, so that it may be indexed by field name. |
1265 | + static QMap<QString, QString> parseDeviceId(const QString &did) |
1266 | + { |
1267 | + QMap<QString, QString> idDict; |
1268 | + const auto parts = did.split(";"); |
1269 | + const auto col = QStringLiteral(":"); |
1270 | + |
1271 | + Q_FOREACH(auto part, parts) { |
1272 | + if (!part.contains(col)) { |
1273 | + continue; |
1274 | + } |
1275 | + auto nameValue = part.split(col); |
1276 | + idDict[nameValue[0].trimmed()] = nameValue[1].trimmed(); |
1277 | + } |
1278 | + |
1279 | + const auto man = QStringLiteral("MANUFACTURER"); |
1280 | + const auto mfg = QStringLiteral("MFG"); |
1281 | + if (idDict.contains(man)) { |
1282 | + idDict.insert(mfg, idDict.value(mfg, idDict.value(man))); |
1283 | + } |
1284 | + |
1285 | + const auto mod = QStringLiteral("MODEL"); |
1286 | + const auto mdl = QStringLiteral("MDL"); |
1287 | + if (idDict.contains(mod)) { |
1288 | + idDict.insert(mdl, idDict.value(mdl, idDict.value(mod))); |
1289 | + } |
1290 | + |
1291 | + const auto com = QStringLiteral("COMMAND SET"); |
1292 | + const auto cmd = QStringLiteral("CMD"); |
1293 | + if (idDict.contains(com)) { |
1294 | + idDict.insert(cmd, idDict.value(cmd, idDict.value(com))); |
1295 | + } |
1296 | + |
1297 | + const auto defaultNames = QStringList({ |
1298 | + mfg, mdl, cmd, |
1299 | + QStringLiteral("CLS"), |
1300 | + QStringLiteral("DES"), |
1301 | + QStringLiteral("SN"), |
1302 | + QStringLiteral("S"), |
1303 | + QStringLiteral("P"), |
1304 | + QStringLiteral("J"), |
1305 | + }); |
1306 | + Q_FOREACH(auto name, defaultNames) { |
1307 | + idDict.insert(name, idDict.value(name, QString::null)); |
1308 | + } |
1309 | + |
1310 | + // TODO: We'd make an array of CMD, but we don't currently need it. |
1311 | + |
1312 | + return idDict; |
1313 | } |
1314 | }; |
1315 | |
1316 | |
1317 | === modified file 'po/ubuntu-ui-extras.pot' |
1318 | --- po/ubuntu-ui-extras.pot 2017-03-21 14:51:38 +0000 |
1319 | +++ po/ubuntu-ui-extras.pot 2017-03-30 13:23:27 +0000 |
1320 | @@ -8,7 +8,7 @@ |
1321 | msgstr "" |
1322 | "Project-Id-Version: ubuntu-ui-extras\n" |
1323 | "Report-Msgid-Bugs-To: \n" |
1324 | -"POT-Creation-Date: 2017-03-21 15:33+0100\n" |
1325 | +"POT-Creation-Date: 2017-03-30 15:15+0200\n" |
1326 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" |
1327 | "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" |
1328 | "Language-Team: LANGUAGE <LL@li.org>\n" |
1329 | @@ -55,7 +55,7 @@ |
1330 | msgid "Idle" |
1331 | msgstr "" |
1332 | |
1333 | -#: modules/Ubuntu/Components/Extras/Printers/utils.h:61 |
1334 | +#: modules/Ubuntu/Components/Extras/Printers/utils.h:36 |
1335 | msgid "Long Edge (Standard)" |
1336 | msgstr "" |
1337 | |
1338 | @@ -67,7 +67,7 @@ |
1339 | msgid "Normal" |
1340 | msgstr "" |
1341 | |
1342 | -#: modules/Ubuntu/Components/Extras/Printers/utils.h:64 |
1343 | +#: modules/Ubuntu/Components/Extras/Printers/utils.h:39 |
1344 | msgid "One Sided" |
1345 | msgstr "" |
1346 | |
1347 | @@ -91,7 +91,7 @@ |
1348 | msgid "Rotate" |
1349 | msgstr "" |
1350 | |
1351 | -#: modules/Ubuntu/Components/Extras/Printers/utils.h:59 |
1352 | +#: modules/Ubuntu/Components/Extras/Printers/utils.h:34 |
1353 | msgid "Short Edge (Flip)" |
1354 | msgstr "" |
1355 | |
1356 | @@ -99,7 +99,7 @@ |
1357 | msgid "Stopped" |
1358 | msgstr "" |
1359 | |
1360 | -#: modules/Ubuntu/Components/Extras/Printers/printers/printers.cpp:387 |
1361 | +#: modules/Ubuntu/Components/Extras/Printers/printers/printers.cpp:396 |
1362 | msgid "Test page" |
1363 | msgstr "" |
1364 | |
1365 | |
1366 | === modified file 'tests/unittests/Printers/CMakeLists.txt' |
1367 | --- tests/unittests/Printers/CMakeLists.txt 2017-03-10 10:28:20 +0000 |
1368 | +++ tests/unittests/Printers/CMakeLists.txt 2017-03-30 13:23:27 +0000 |
1369 | @@ -56,3 +56,19 @@ |
1370 | add_executable(testPrintersDeviceModel tst_printerdevicemodel.cpp ${MOCK_SOURCES}) |
1371 | target_link_libraries(testPrintersDeviceModel UbuntuComponentsExtrasPrintersQml Qt5::Test Qt5::Gui) |
1372 | add_test(tst_printerdevicemodel testPrintersDeviceModel) |
1373 | + |
1374 | +add_executable(testPrintersPpdUtils tst_ppdutils.cpp) |
1375 | +target_link_libraries(testPrintersPpdUtils UbuntuComponentsExtrasPrintersQml Qt5::Test Qt5::Gui) |
1376 | +add_test(tst_ppdutils testPrintersPpdUtils) |
1377 | + |
1378 | +add_executable(testPrintersUtils tst_utils.cpp) |
1379 | +target_link_libraries(testPrintersUtils UbuntuComponentsExtrasPrintersQml Qt5::Test Qt5::Gui) |
1380 | +add_test(tst_utils testPrintersUtils) |
1381 | + |
1382 | +add_executable(testPrintersDeviceFilter tst_devicefilter.cpp ${MOCK_SOURCES}) |
1383 | +target_link_libraries(testPrintersDeviceFilter UbuntuComponentsExtrasPrintersQml Qt5::Test Qt5::Gui) |
1384 | +add_test(tst_devicefilter testPrintersDeviceFilter) |
1385 | + |
1386 | +add_executable(testPrintersConsolidatedDeviceModel tst_consolidateddevicemodel.cpp ${MOCK_SOURCES}) |
1387 | +target_link_libraries(testPrintersConsolidatedDeviceModel UbuntuComponentsExtrasPrintersQml Qt5::Test Qt5::Gui) |
1388 | +add_test(tst_consolidateddevicemodel testPrintersConsolidatedDeviceModel) |
1389 | |
1390 | === modified file 'tests/unittests/Printers/mockbackend.h' |
1391 | --- tests/unittests/Printers/mockbackend.h 2017-03-15 17:42:21 +0000 |
1392 | +++ tests/unittests/Printers/mockbackend.h 2017-03-30 13:23:27 +0000 |
1393 | @@ -18,7 +18,7 @@ |
1394 | #define USC_PRINTERS_MOCK_BACKEND_H |
1395 | |
1396 | #include "backend/backend.h" |
1397 | -#include "utils.h" |
1398 | +#include "cups/ppdutils.h" |
1399 | |
1400 | class MockPrinterBackend : public PrinterBackend |
1401 | { |
1402 | @@ -235,7 +235,7 @@ |
1403 | attributes.insert("ColorModel", job->getColorModel().name); |
1404 | attributes.insert("CompletedTime", job->completedTime()); |
1405 | attributes.insert("CreationTime", job->creationTime()); |
1406 | - attributes.insert("Duplex", Utils::duplexModeToPpdChoice(job->getDuplexMode())); |
1407 | + attributes.insert("Duplex", PpdUtils::duplexModeToPpdChoice(job->getDuplexMode())); |
1408 | attributes.insert("impressionsCompleted", job->impressionsCompleted()); |
1409 | attributes.insert("landscape", job->landscape()); |
1410 | attributes.insert("messages", job->messages()); |
1411 | @@ -322,7 +322,7 @@ |
1412 | virtual PrinterEnum::DuplexMode defaultDuplexMode() const override |
1413 | { |
1414 | auto ppdMode = printerGetOption(printerName(), "Duplex").toString(); |
1415 | - return Utils::ppdChoiceToDuplexMode(ppdMode); |
1416 | + return PpdUtils::ppdChoiceToDuplexMode(ppdMode); |
1417 | } |
1418 | |
1419 | virtual QList<PrinterEnum::DuplexMode> supportedDuplexModes() const override |
1420 | |
1421 | === added file 'tests/unittests/Printers/tst_consolidateddevicemodel.cpp' |
1422 | --- tests/unittests/Printers/tst_consolidateddevicemodel.cpp 1970-01-01 00:00:00 +0000 |
1423 | +++ tests/unittests/Printers/tst_consolidateddevicemodel.cpp 2017-03-30 13:23:27 +0000 |
1424 | @@ -0,0 +1,162 @@ |
1425 | +/* |
1426 | + * Copyright (C) 2017 Canonical, Ltd. |
1427 | + * |
1428 | + * This program is free software; you can redistribute it and/or modify |
1429 | + * it under the terms of the GNU Lesser General Public License as published by |
1430 | + * the Free Software Foundation; version 3. |
1431 | + * |
1432 | + * This program is distributed in the hope that it will be useful, |
1433 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1434 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1435 | + * GNU Lesser General Public License for more details. |
1436 | + * |
1437 | + * You should have received a copy of the GNU Lesser General Public License |
1438 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1439 | + */ |
1440 | + |
1441 | +#include "mockbackend.h" |
1442 | + |
1443 | +#include "backend/backend.h" |
1444 | +#include "models/devicemodel.h" |
1445 | +#include "structs.h" |
1446 | + |
1447 | +#include <QDebug> |
1448 | +#include <QObject> |
1449 | +#include <QSignalSpy> |
1450 | +#include <QTest> |
1451 | + |
1452 | +Q_DECLARE_METATYPE(ConsolidatedDeviceModel::Roles) |
1453 | + |
1454 | +class TestConsolidatedDeviceModel : public QObject |
1455 | +{ |
1456 | + Q_OBJECT |
1457 | +private Q_SLOTS: |
1458 | + void init() |
1459 | + { |
1460 | + m_backend = new MockPrinterBackend(); |
1461 | + m_devicesModel = new DeviceModel(m_backend); |
1462 | + } |
1463 | + void cleanup() |
1464 | + { |
1465 | + QSignalSpy destroyedSpy(m_devicesModel, SIGNAL(destroyed(QObject*))); |
1466 | + m_devicesModel->deleteLater(); |
1467 | + QTRY_COMPARE(destroyedSpy.count(), 1); |
1468 | + delete m_backend; |
1469 | + } |
1470 | + void testConsolidation_data() |
1471 | + { |
1472 | + QTest::addColumn<QList<Device>>("devices"); |
1473 | + QTest::addColumn<int>("targetConsolidatedDeviceCount"); |
1474 | + |
1475 | + { |
1476 | + Device a; a.uri = "dnssd://foo/bar"; a.makeModel = "foo"; |
1477 | + Device b; b.uri = "ipps://bar/qux"; b.makeModel = "bar"; |
1478 | + |
1479 | + QList<Device> devs({a, b}); |
1480 | + QTest::newRow("no consolidation") << devs << 2; |
1481 | + } |
1482 | + { |
1483 | + Device a; a.uri = "dnssd://foo/bar"; a.makeModel = "foo"; |
1484 | + Device b; b.uri = "ipps://foo/bar"; b.makeModel = "foo"; |
1485 | + |
1486 | + QList<Device> devs({a, b}); |
1487 | + QTest::newRow("consolidation") << devs << 1; |
1488 | + } |
1489 | + { |
1490 | + Device a; a.uri = "dnssd://foo/bar"; a.makeModel = "foo"; |
1491 | + Device b; b.uri = "ipps://foo/bar"; b.makeModel = "foo"; |
1492 | + Device c; c.uri = "socket://baz/qux"; c.makeModel = "baz"; |
1493 | + |
1494 | + QList<Device> devs({a, b, c}); |
1495 | + QTest::newRow("mixed") << devs << 2; |
1496 | + } |
1497 | + { |
1498 | + Device a; a.uri = "dnssd://foo/bar"; a.makeModel = "foo"; |
1499 | + Device b; b.uri = "ipps://foo/bar"; b.makeModel = ""; |
1500 | + Device c; c.uri = "socket://baz/qux"; c.makeModel = ""; |
1501 | + |
1502 | + QList<Device> devs({a, b, c}); |
1503 | + QTest::newRow("don't consolidate make-and-model-less devices") << devs << 1; |
1504 | + } |
1505 | + { |
1506 | + Device a; a.uri = "dnssd://foo/bar"; a.makeModel = "Unknown"; |
1507 | + Device b; b.uri = "ipps://foo/bar"; b.makeModel = "Unknown"; |
1508 | + Device c; c.uri = "socket://baz/qux"; c.makeModel = "Unknown"; |
1509 | + |
1510 | + QList<Device> devs({a, b, c}); |
1511 | + QTest::newRow("Unknown devices aren't even considered.") << devs << 0; |
1512 | + } |
1513 | + } |
1514 | + void testConsolidation() |
1515 | + { |
1516 | + QFETCH(QList<Device>, devices); |
1517 | + QFETCH(int, targetConsolidatedDeviceCount); |
1518 | + |
1519 | + ConsolidatedDeviceModel model(m_devicesModel); |
1520 | + |
1521 | + Q_FOREACH(auto device, devices) { |
1522 | + m_backend->mockDeviceFound(device); |
1523 | + } |
1524 | + m_backend->deviceSearchFinished(); |
1525 | + |
1526 | + QCOMPARE(model.rowCount(), targetConsolidatedDeviceCount); |
1527 | + } |
1528 | + void testRoles_data() |
1529 | + { |
1530 | + QTest::addColumn<ConsolidatedDeviceModel::Roles>("role"); |
1531 | + QTest::addColumn<QVariant>("value"); |
1532 | + QTest::addColumn<QList<Device>>("devices"); |
1533 | + |
1534 | + { |
1535 | + Device a; a.uri = "ipp://foo/bar"; a.makeModel = "HP LaserFjert"; |
1536 | + Device b; a.uri = "ipp://foo/bar"; b.makeModel = "HP LaserFjert"; |
1537 | + |
1538 | + QList<Device> devs({a, b}); |
1539 | + |
1540 | + ConsolidatedDeviceModel::Roles role( |
1541 | + ConsolidatedDeviceModel::ConsolidatedNameRole |
1542 | + ); |
1543 | + QVariant value(QString("HP LaserFjert")); |
1544 | + QTest::newRow("ConsolidatedNameRole") << role << value << devs; |
1545 | + } |
1546 | + } |
1547 | + void testRoles() |
1548 | + { |
1549 | + QFETCH(ConsolidatedDeviceModel::Roles, role); |
1550 | + QFETCH(QVariant, value); |
1551 | + QFETCH(QList<Device>, devices); |
1552 | + |
1553 | + ConsolidatedDeviceModel model(m_devicesModel); |
1554 | + |
1555 | + Q_FOREACH(auto device, devices) { |
1556 | + m_backend->mockDeviceFound(device); |
1557 | + } |
1558 | + m_backend->deviceSearchFinished(); |
1559 | + |
1560 | + QCOMPARE(model.data(model.index(0), role), value); |
1561 | + } |
1562 | + void testDevicesRole() |
1563 | + { |
1564 | + Device a; a.uri = "ipp://foo/bar"; a.makeModel = "foo"; |
1565 | + Device b; b.uri = "dnssd://foo/bar"; b.makeModel = "foo"; |
1566 | + |
1567 | + ConsolidatedDeviceModel model(m_devicesModel); |
1568 | + |
1569 | + m_backend->mockDeviceFound(a); |
1570 | + m_backend->mockDeviceFound(b); |
1571 | + m_backend->deviceSearchFinished(); |
1572 | + |
1573 | + auto filter = model.data( |
1574 | + model.index(0), ConsolidatedDeviceModel::Roles::DevicesRole |
1575 | + ).value<DeviceFilter*>(); |
1576 | + |
1577 | + QCOMPARE(filter->count(), 2); |
1578 | + } |
1579 | +private: |
1580 | + MockPrinterBackend *m_backend; |
1581 | + DeviceModel *m_devicesModel; |
1582 | +}; |
1583 | + |
1584 | +QTEST_GUILESS_MAIN(TestConsolidatedDeviceModel) |
1585 | +#include "tst_consolidateddevicemodel.moc" |
1586 | + |
1587 | |
1588 | === added file 'tests/unittests/Printers/tst_devicefilter.cpp' |
1589 | --- tests/unittests/Printers/tst_devicefilter.cpp 1970-01-01 00:00:00 +0000 |
1590 | +++ tests/unittests/Printers/tst_devicefilter.cpp 2017-03-30 13:23:27 +0000 |
1591 | @@ -0,0 +1,106 @@ |
1592 | +/* |
1593 | + * Copyright (C) 2017 Canonical, Ltd. |
1594 | + * |
1595 | + * This program is free software; you can redistribute it and/or modify |
1596 | + * it under the terms of the GNU Lesser General Public License as published by |
1597 | + * the Free Software Foundation; version 3. |
1598 | + * |
1599 | + * This program is distributed in the hope that it will be useful, |
1600 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1601 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1602 | + * GNU Lesser General Public License for more details. |
1603 | + * |
1604 | + * You should have received a copy of the GNU Lesser General Public License |
1605 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1606 | + */ |
1607 | + |
1608 | +#include "mockbackend.h" |
1609 | + |
1610 | +#include "cups/ppdutils.h" |
1611 | +#include "models/devicemodel.h" |
1612 | + |
1613 | +#include <QObject> |
1614 | +#include <QSignalSpy> |
1615 | +#include <QScopedPointer> |
1616 | +#include <QTest> |
1617 | + |
1618 | +class TestDeviceFilter : public QObject |
1619 | +{ |
1620 | + Q_OBJECT |
1621 | +private Q_SLOTS: |
1622 | + void testFilterOnMakeModel() |
1623 | + { |
1624 | + QScopedPointer<MockPrinterBackend> backend(new MockPrinterBackend); |
1625 | + DeviceModel model(backend.data()); |
1626 | + |
1627 | + Device dev1; dev1.uri = "ipp://foo/bar"; |
1628 | + dev1.makeModel = "foo"; |
1629 | + Device dev2; dev2.uri = "dnssd://bar/qux"; |
1630 | + dev2.makeModel = "bar"; |
1631 | + |
1632 | + backend->mockDeviceFound(dev1); |
1633 | + backend->mockDeviceFound(dev2); |
1634 | + |
1635 | + DeviceFilter filter; |
1636 | + filter.setSourceModel(&model); |
1637 | + |
1638 | + QCOMPARE(filter.count(), 2); |
1639 | + |
1640 | + // Install filter. |
1641 | + filter.filterOnNormalizedMakeModel("foo"); |
1642 | + QCOMPARE(filter.count(), 1); |
1643 | + } |
1644 | + |
1645 | + /* Test that "Foo BazJet-5000" and "Foo BazJet 5000" are accepted in same |
1646 | + filter. */ |
1647 | + void testNormalizedFiltering() |
1648 | + { |
1649 | + QScopedPointer<MockPrinterBackend> backend(new MockPrinterBackend); |
1650 | + DeviceModel model(backend.data()); |
1651 | + |
1652 | + Device dev1; dev1.uri = "ipp://foo/bar"; |
1653 | + dev1.makeModel = "Foo BazJet 5000"; |
1654 | + Device dev2; dev2.uri = "dnssd://bar/qux"; |
1655 | + dev2.makeModel = "Foo BazJet-5000"; |
1656 | + Device dev3; dev3.uri = "dnssd://bar/baz"; |
1657 | + dev3.makeModel = "Foo DeskQux 350"; |
1658 | + |
1659 | + backend->mockDeviceFound(dev1); |
1660 | + backend->mockDeviceFound(dev2); |
1661 | + backend->mockDeviceFound(dev3); |
1662 | + |
1663 | + DeviceFilter filter; |
1664 | + filter.setSourceModel(&model); |
1665 | + |
1666 | + // Install filter. |
1667 | + filter.filterOnNormalizedMakeModel( |
1668 | + PpdUtils::normalize("Foo BazJet 5000")); |
1669 | + QCOMPARE(filter.count(), 2); |
1670 | + } |
1671 | + void testFilterOnUri() |
1672 | + { |
1673 | + QScopedPointer<MockPrinterBackend> backend(new MockPrinterBackend); |
1674 | + DeviceModel model(backend.data()); |
1675 | + |
1676 | + Device dev1; dev1.uri = "ipp://foo/bar"; |
1677 | + dev1.id = "foo"; |
1678 | + Device dev2; dev2.uri = "dnssd://bar/qux"; |
1679 | + dev2.id = "bar"; |
1680 | + |
1681 | + backend->mockDeviceFound(dev1); |
1682 | + backend->mockDeviceFound(dev2); |
1683 | + |
1684 | + DeviceFilter filter; |
1685 | + filter.setSourceModel(&model); |
1686 | + |
1687 | + QCOMPARE(filter.count(), 2); |
1688 | + |
1689 | + // Install filter. |
1690 | + filter.filterOnUri("ipp://foo/bar"); |
1691 | + QCOMPARE(filter.count(), 1); |
1692 | + } |
1693 | +}; |
1694 | + |
1695 | +QTEST_GUILESS_MAIN(TestDeviceFilter) |
1696 | +#include "tst_devicefilter.moc" |
1697 | + |
1698 | |
1699 | === modified file 'tests/unittests/Printers/tst_jobmodel.cpp' |
1700 | --- tests/unittests/Printers/tst_jobmodel.cpp 2017-03-15 17:30:21 +0000 |
1701 | +++ tests/unittests/Printers/tst_jobmodel.cpp 2017-03-30 13:23:27 +0000 |
1702 | @@ -19,6 +19,7 @@ |
1703 | #include "backend/backend.h" |
1704 | #include "models/jobmodel.h" |
1705 | #include "printers/printers.h" |
1706 | +#include "utils.h" |
1707 | |
1708 | #include <QDebug> |
1709 | #include <QObject> |
1710 | |
1711 | === added file 'tests/unittests/Printers/tst_ppdutils.cpp' |
1712 | --- tests/unittests/Printers/tst_ppdutils.cpp 1970-01-01 00:00:00 +0000 |
1713 | +++ tests/unittests/Printers/tst_ppdutils.cpp 2017-03-30 13:23:27 +0000 |
1714 | @@ -0,0 +1,138 @@ |
1715 | +/* |
1716 | + * Copyright (C) 2017 Canonical, Ltd. |
1717 | + * |
1718 | + * This program is free software; you can redistribute it and/or modify |
1719 | + * it under the terms of the GNU Lesser General Public License as published by |
1720 | + * the Free Software Foundation; version 3. |
1721 | + * |
1722 | + * This program is distributed in the hope that it will be useful, |
1723 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1724 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1725 | + * GNU Lesser General Public License for more details. |
1726 | + * |
1727 | + * You should have received a copy of the GNU Lesser General Public License |
1728 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1729 | + */ |
1730 | + |
1731 | +#include "cups/ppdutils.h" |
1732 | + |
1733 | +#include <QObject> |
1734 | +#include <QTest> |
1735 | + |
1736 | +typedef QPair<QString, QString> MakeModel; |
1737 | +Q_DECLARE_METATYPE(MakeModel) |
1738 | + |
1739 | +class TstPpdUtils : public QObject |
1740 | +{ |
1741 | + Q_OBJECT |
1742 | +private Q_SLOTS: |
1743 | + void testSplitMakeModel_data() |
1744 | + { |
1745 | + QTest::addColumn<QString>("makeModel"); |
1746 | + QTest::addColumn<MakeModel>("expected"); |
1747 | + |
1748 | + { |
1749 | + QString actual("HP HP Officejet Pro 8610"); |
1750 | + auto expected = MakeModel("HP", "OfficeJet Pro 8610"); |
1751 | + QTest::newRow("duplicate hp") << actual << expected; |
1752 | + } |
1753 | + { |
1754 | + QString actual("Officejet Officejet Pro 8610"); |
1755 | + auto expected = MakeModel("HP", "OfficeJet Pro 8610"); |
1756 | + QTest::newRow("officejet officejet (yes it actually happens)") << actual << expected; |
1757 | + } |
1758 | + { |
1759 | + QString actual("laserjet 4500"); |
1760 | + auto expected = MakeModel("HP", "LaserJet 4500"); |
1761 | + QTest::newRow("just a hp model") << actual << expected; |
1762 | + } |
1763 | + { |
1764 | + QString actual("HP OfficeJet Pro 8600"); |
1765 | + auto expected = MakeModel("HP", "OfficeJet Pro 8600"); |
1766 | + QTest::newRow("a sane hp officejet") << actual << expected; |
1767 | + } |
1768 | + { |
1769 | + QString actual("HP Foo All-In-One"); |
1770 | + auto expected = MakeModel("HP", "Foo"); |
1771 | + QTest::newRow("remove all-in-one") << actual << expected; |
1772 | + } |
1773 | + { |
1774 | + QString actual("Hewlett-Packard SmartJet 50 v0.10"); |
1775 | + auto expected = MakeModel("HP", "SmartJet 50"); |
1776 | + QTest::newRow("Hewlett-Packard SmartJet 50 v0.10") << actual << expected; |
1777 | + } |
1778 | + { |
1779 | + QString actual("Foobar Bazjet 9000"); |
1780 | + auto expected = MakeModel("Foobar", "Bazjet 9000"); |
1781 | + QTest::newRow("the excellent foobar bazjet 9000") << actual << expected; |
1782 | + } |
1783 | + { |
1784 | + QString actual("HP LaserJet 4 Plus v2013.111 Postscript (recommended)"); |
1785 | + auto expected = MakeModel("HP", "LaserJet 4 Plus"); |
1786 | + QTest::newRow("example #1") << actual << expected; |
1787 | + } |
1788 | + { |
1789 | + QString actual("Canon MG4100 series Ver.3.90"); |
1790 | + auto expected = MakeModel("Canon", "MG4100"); |
1791 | + QTest::newRow("example #2") << actual << expected; |
1792 | + } |
1793 | + { |
1794 | + QString actual("Xerox Foo Printer"); |
1795 | + auto expected = MakeModel("Xerox", "Foo"); |
1796 | + QTest::newRow("remove ignored suffix 'printer'") << actual << expected; |
1797 | + } |
1798 | + { |
1799 | + QString actual("hp lj 4500"); |
1800 | + auto expected = MakeModel("HP", "LaserJet 4500"); |
1801 | + QTest::newRow("shortened hp model name") << actual << expected; |
1802 | + } |
1803 | + { |
1804 | + QString actual("okipage foobar"); |
1805 | + auto expected = MakeModel("Oki", "okipage foobar"); |
1806 | + QTest::newRow("some okipage printer") << actual << expected; |
1807 | + } |
1808 | + } |
1809 | + void testSplitMakeModel() |
1810 | + { |
1811 | + QFETCH(QString, makeModel); |
1812 | + QFETCH(MakeModel, expected); |
1813 | + auto actual = PpdUtils::splitMakeModel(makeModel); |
1814 | + QCOMPARE(actual, expected); |
1815 | + } |
1816 | + void testNormalize_data() |
1817 | + { |
1818 | + QTest::addColumn<QString>("actual"); |
1819 | + QTest::addColumn<QString>("expected"); |
1820 | + |
1821 | + { |
1822 | + QString actual("Epson PM-A820"); |
1823 | + QString expected("epson pm a 820"); |
1824 | + QTest::newRow("epson with dash") << actual << expected; |
1825 | + } |
1826 | + { |
1827 | + QString actual("Epson PM A820"); |
1828 | + QString expected("epson pm a 820"); |
1829 | + QTest::newRow("epson without dash") << actual << expected; |
1830 | + } |
1831 | + { |
1832 | + QString actual("HP PhotoSmart C 8100"); |
1833 | + QString expected("hp photosmart c 8100"); |
1834 | + QTest::newRow("C 8100") << actual << expected; |
1835 | + } |
1836 | + { |
1837 | + QString actual("HP PhotoSmart C8100"); |
1838 | + QString expected("hp photosmart c 8100"); |
1839 | + QTest::newRow("C8100") << actual << expected; |
1840 | + } |
1841 | + } |
1842 | + void testNormalize() |
1843 | + { |
1844 | + QFETCH(QString, actual); |
1845 | + QFETCH(QString, expected); |
1846 | + actual = PpdUtils::normalize(actual); |
1847 | + QCOMPARE(actual, expected); |
1848 | + } |
1849 | +}; |
1850 | + |
1851 | +QTEST_GUILESS_MAIN(TstPpdUtils) |
1852 | +#include "tst_ppdutils.moc" |
1853 | |
1854 | === modified file 'tests/unittests/Printers/tst_printer.cpp' |
1855 | --- tests/unittests/Printers/tst_printer.cpp 2017-03-20 13:31:55 +0000 |
1856 | +++ tests/unittests/Printers/tst_printer.cpp 2017-03-30 13:23:27 +0000 |
1857 | @@ -14,12 +14,12 @@ |
1858 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1859 | */ |
1860 | |
1861 | -#include "utils.h" |
1862 | |
1863 | #include "mockbackend.h" |
1864 | |
1865 | #include "backend/backend.h" |
1866 | #include "backend/backend_pdf.h" |
1867 | +#include "cups/ppdutils.h" |
1868 | #include "printer/printer.h" |
1869 | |
1870 | #include <QDebug> |
1871 | @@ -158,7 +158,7 @@ |
1872 | QStringList duplexVals = duplexVar.toStringList(); |
1873 | QCOMPARE( |
1874 | duplexVals.at(0), |
1875 | - (QString) Utils::duplexModeToPpdChoice(PrinterEnum::DuplexMode::DuplexLongSide) |
1876 | + (QString) PpdUtils::duplexModeToPpdChoice(PrinterEnum::DuplexMode::DuplexLongSide) |
1877 | ); |
1878 | } |
1879 | void testSetDefaultPageSize_data() |
1880 | |
1881 | === modified file 'tests/unittests/Printers/tst_printerdevicemodel.cpp' |
1882 | --- tests/unittests/Printers/tst_printerdevicemodel.cpp 2017-03-08 16:13:02 +0000 |
1883 | +++ tests/unittests/Printers/tst_printerdevicemodel.cpp 2017-03-30 13:23:27 +0000 |
1884 | @@ -60,6 +60,11 @@ |
1885 | Device d; d.uri = "dnssd:"; |
1886 | QTest::newRow("non-empty uri, malformed") << d << false; |
1887 | } |
1888 | + { |
1889 | + Device d; d.uri = "dnssd://foo/bar"; |
1890 | + d.makeModel = "Unknown"; |
1891 | + QTest::newRow("non-empty uri, but Unknown make/model") << d << false; |
1892 | + } |
1893 | } |
1894 | void testAcceptedDevices() |
1895 | { |
1896 | @@ -152,6 +157,36 @@ |
1897 | m_backend->mockDeviceFound(device); |
1898 | QCOMPARE(m_model->data(m_model->index(0), role), value); |
1899 | } |
1900 | + void testFiltering_data() |
1901 | + { |
1902 | + QTest::addColumn<QList<Device>>("devices"); |
1903 | + QTest::addColumn<QString>("filterId"); |
1904 | + QTest::addColumn<int>("targetCount"); |
1905 | + QTest::addColumn<QStringList>("targetIds"); |
1906 | + |
1907 | + { |
1908 | + Device a; a.uri = "dnssd://foo/bar"; a.id = "foo"; |
1909 | + Device b; b.uri = "ipps://bar/qux"; b.id = "bar"; |
1910 | + |
1911 | + QList<Device> devs({a, b}); |
1912 | + QTest::newRow("no filtering") << devs << "" << 2 << QStringList({"foo", "bar"}); |
1913 | + } |
1914 | + } |
1915 | + void testFiltering() |
1916 | + { |
1917 | + QFETCH(QList<Device>, devices); |
1918 | + QFETCH(QString, filterId); |
1919 | + QFETCH(int, targetCount); |
1920 | + QFETCH(QStringList, targetIds); |
1921 | + |
1922 | + Q_FOREACH(auto dev, devices) { |
1923 | + m_backend->mockDeviceFound(dev); |
1924 | + } |
1925 | + |
1926 | + DeviceFilter filter; |
1927 | + filter.setSourceModel(m_model); |
1928 | + QCOMPARE(filter.count(), targetCount); |
1929 | + } |
1930 | private: |
1931 | MockPrinterBackend *m_backend; |
1932 | DeviceModel *m_model; |
1933 | |
1934 | === modified file 'tests/unittests/Printers/tst_printermodel.cpp' |
1935 | --- tests/unittests/Printers/tst_printermodel.cpp 2017-03-10 13:15:27 +0000 |
1936 | +++ tests/unittests/Printers/tst_printermodel.cpp 2017-03-30 13:23:27 +0000 |
1937 | @@ -17,9 +17,11 @@ |
1938 | #include "mockbackend.h" |
1939 | |
1940 | #include "backend/backend.h" |
1941 | +#include "cups/ppdutils.h" |
1942 | #include "models/printermodel.h" |
1943 | #include "printer/printer.h" |
1944 | #include "printer/printerjob.h" |
1945 | +#include "utils.h" |
1946 | |
1947 | #include <QDebug> |
1948 | #include <QObject> |
1949 | @@ -231,7 +233,7 @@ |
1950 | PrinterBackend* backend = new MockPrinterBackend("a-printer"); |
1951 | ((MockPrinterBackend*) backend)->m_supportedDuplexModes = modes; |
1952 | ((MockPrinterBackend*) backend)->printerOptions["a-printer"].insert( |
1953 | - "Duplex", QVariant::fromValue(Utils::duplexModeToPpdChoice(PrinterEnum::DuplexMode::DuplexLongSide)) |
1954 | + "Duplex", QVariant::fromValue(PpdUtils::duplexModeToPpdChoice(PrinterEnum::DuplexMode::DuplexLongSide)) |
1955 | ); |
1956 | |
1957 | auto printerA = QSharedPointer<Printer>(new Printer(backend)); |
1958 | |
1959 | === added file 'tests/unittests/Printers/tst_utils.cpp' |
1960 | --- tests/unittests/Printers/tst_utils.cpp 1970-01-01 00:00:00 +0000 |
1961 | +++ tests/unittests/Printers/tst_utils.cpp 2017-03-30 13:23:27 +0000 |
1962 | @@ -0,0 +1,107 @@ |
1963 | +/* |
1964 | + * Copyright (C) 2017 Canonical, Ltd. |
1965 | + * |
1966 | + * This program is free software; you can redistribute it and/or modify |
1967 | + * it under the terms of the GNU Lesser General Public License as published by |
1968 | + * the Free Software Foundation; version 3. |
1969 | + * |
1970 | + * This program is distributed in the hope that it will be useful, |
1971 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1972 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1973 | + * GNU Lesser General Public License for more details. |
1974 | + * |
1975 | + * You should have received a copy of the GNU Lesser General Public License |
1976 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1977 | + */ |
1978 | + |
1979 | +#include "utils.h" |
1980 | + |
1981 | +#include <QObject> |
1982 | +#include <QTest> |
1983 | + |
1984 | +typedef QMap<QString, QString> IdDict; |
1985 | +Q_DECLARE_METATYPE(IdDict) |
1986 | + |
1987 | +class TstPrinterUtils : public QObject |
1988 | +{ |
1989 | + Q_OBJECT |
1990 | +private Q_SLOTS: |
1991 | + void tstParseDeviceId_data() |
1992 | + { |
1993 | + QTest::addColumn<QString>("deviceId"); |
1994 | + QTest::addColumn<IdDict>("expected"); |
1995 | + |
1996 | + { |
1997 | + QString actual("MFG:Hewlett-Packard;CMD:PJL,BIDI-ECP,PCLXL,PCL,PDF,PJL,POSTSCRIPT;MDL:HP Color LaserJet CM3530 MFP;CLS:PRINTER;DES:Hewlett-Packard Color LaserJet CM3530 MFP;"); |
1998 | + auto expected = QMap<QString, QString>{ |
1999 | + {"MFG", "Hewlett-Packard"}, |
2000 | + {"MDL", "HP Color LaserJet CM3530 MFP"}, |
2001 | + {"CMD", "PJL,BIDI-ECP,PCLXL,PCL,PDF,PJL,POSTSCRIPT"}, |
2002 | + {"CLS", "PRINTER"}, |
2003 | + {"DES", "Hewlett-Packard Color LaserJet CM3530 MFP"}, |
2004 | + {"SN", QString::null}, |
2005 | + {"S", QString::null}, |
2006 | + {"P", QString::null}, |
2007 | + {"J", QString::null}, |
2008 | + }; |
2009 | + QTest::newRow("duplicate hp") << actual << expected; |
2010 | + } |
2011 | + { |
2012 | + QString actual("MFG:HP;MDL:HP Officejet Pro 8610;CMD:PCL,JPEG,URF;"); |
2013 | + auto expected = QMap<QString, QString>{ |
2014 | + {"MFG", "HP"}, |
2015 | + {"MDL", "HP Officejet Pro 8610"}, |
2016 | + {"CMD", "PCL,JPEG,URF"}, |
2017 | + {"CLS", QString::null}, |
2018 | + {"DES", QString::null}, |
2019 | + {"SN", QString::null}, |
2020 | + {"S", QString::null}, |
2021 | + {"P", QString::null}, |
2022 | + {"J", QString::null}, |
2023 | + }; |
2024 | + QTest::newRow("actual hp device id") << actual << expected; |
2025 | + } |
2026 | + { |
2027 | + QString actual("MFG:HP;MDL:Officejet Pro 8600;CMD:PCL3GUI,PCL3,PJL,JPEG,PCLM,URF,DW-PCL,802.11,802.3,DESKJET,DYN;CLS:PRINTER;DES:CM749A;CID:HPIJVIPAV2;LEDMDIS:USB#FF#CC#00,USB#07#01#02;SN:CN371DWH1V05KC;S:038080C484201021005a00800004518005a4418003c4618005a4118005a;Z:0102,05000009000008000008000008000008,0600,0700000000000000000000,0b0000000000000000000099ba0000000099b50000000099b90000000099b6,0c0,0e00000000000000000000,0f00000000000000000000,10000002000008000008000008000008,110,12002,150,17000000000000000000000000000000,181;"); |
2028 | + auto expected = QMap<QString, QString>{ |
2029 | + {"MFG", "HP"}, |
2030 | + {"MDL", "Officejet Pro 8600"}, |
2031 | + {"CMD", "PCL3GUI,PCL3,PJL,JPEG,PCLM,URF,DW-PCL,802.11,802.3,DESKJET,DYN"}, |
2032 | + {"CLS", "PRINTER"}, |
2033 | + {"DES", "CM749A"}, |
2034 | + {"SN", "CN371DWH1V05KC"}, |
2035 | + {"S", "038080C484201021005a00800004518005a4418003c4618005a4118005a"}, |
2036 | + {"P", QString::null}, |
2037 | + {"J", QString::null}, |
2038 | + {"Z", "0102,05000009000008000008000008000008,0600,0700000000000000000000,0b0000000000000000000099ba0000000099b50000000099b90000000099b6,0c0,0e00000000000000000000,0f00000000000000000000,10000002000008000008000008000008,110,12002,150,17000000000000000000000000000000,181"}, |
2039 | + {"CID", "HPIJVIPAV2"}, |
2040 | + {"LEDMDIS", "USB#FF#CC#00,USB#07#01#02"} |
2041 | + }; |
2042 | + QTest::newRow("actual, but crazy, hp device id") << actual << expected; |
2043 | + } |
2044 | + { |
2045 | + QString actual("MFG:EPSON;CMD:ESCPL2,BDC,D4;MDL:Stylus Photo RX420;CLS:PRINTER;DES:EPSON Stylus Photo RX420;"); |
2046 | + auto expected = QMap<QString, QString>{ |
2047 | + {"MFG", "EPSON"}, |
2048 | + {"MDL", "Stylus Photo RX420"}, |
2049 | + {"CMD", "ESCPL2,BDC,D4"}, |
2050 | + {"CLS", "PRINTER"}, |
2051 | + {"DES", "EPSON Stylus Photo RX420"}, |
2052 | + {"SN", QString::null}, |
2053 | + {"S", QString::null}, |
2054 | + {"P", QString::null}, |
2055 | + {"J", QString::null}, |
2056 | + }; |
2057 | + QTest::newRow("some epson stylus") << actual << expected; |
2058 | + } |
2059 | + } |
2060 | + void tstParseDeviceId() |
2061 | + { |
2062 | + QFETCH(QString, deviceId); |
2063 | + QFETCH(IdDict, expected); |
2064 | + QCOMPARE(Utils::parseDeviceId(deviceId), expected); |
2065 | + } |
2066 | +}; |
2067 | + |
2068 | +QTEST_GUILESS_MAIN(TstPrinterUtils) |
2069 | +#include "tst_utils.moc" |
Getting a crash when using this http:// pastebin. ubuntu. com/24228896/