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