Merge lp:~jonas-drange/ubuntu-ui-extras/consolidated-devices into lp:ubuntu-ui-extras/staging
- consolidated-devices
- Merge into staging
Status: | Needs review |
---|---|
Proposed branch: | lp:~jonas-drange/ubuntu-ui-extras/consolidated-devices |
Merge into: | lp:ubuntu-ui-extras/staging |
Diff against target: |
2545 lines (+1738/-170) 30 files modified
debian/control (+1/-0) 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 (+85/-2) modules/Ubuntu/Components/Extras/Printers/cups/devicesearcher.h (+15/-0) modules/Ubuntu/Components/Extras/Printers/cups/ppdutils.cpp (+345/-0) modules/Ubuntu/Components/Extras/Printers/cups/ppdutils.h (+87/-0) modules/Ubuntu/Components/Extras/Printers/models/devicemodel.cpp (+266/-2) modules/Ubuntu/Components/Extras/Printers/models/devicemodel.h (+82/-1) modules/Ubuntu/Components/Extras/Printers/models/drivermodel.cpp (+14/-0) modules/Ubuntu/Components/Extras/Printers/models/drivermodel.h (+1/-0) 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 (+34/-29) modules/Ubuntu/Components/Extras/Printers/utils.h (+60/-53) po/ubuntu-ui-extras.pot (+9/-5) tests/unittests/Printers/CMakeLists.txt (+16/-0) tests/unittests/Printers/mockbackend.h (+3/-3) tests/unittests/Printers/tst_consolidateddevicemodel.cpp (+186/-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 (+3/-3) tests/unittests/Printers/tst_printerdevice.cpp (+19/-22) tests/unittests/Printers/tst_printerdevicemodel.cpp (+58/-10) 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 |
---|---|---|---|
system-apps-ci-bot | continuous-integration | Approve | |
Andrew Hayzen (community) | Approve | ||
Review via email: mp+321497@code.launchpad.net |
This proposal supersedes a proposal from 2017-03-22.
Commit message
Adds ConsolidatedDev
Description of the change
Adds ConsolidatedDev
Andrew Hayzen (ahayzen) wrote : Posted in a previous version of this proposal | # |
Andrew Hayzen (ahayzen) wrote : Posted in a previous version of this proposal | # |
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 : Posted in a previous version of this proposal | # |
Good point. Yeah, I think that should be doable.
Jonas G. Drange (jonas-drange) wrote : | # |
I'm proposing it against staging, as we want to release this before a printer-staging -> staging dance.
system-apps-ci-bot (system-apps-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:164
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Andrew Hayzen (ahayzen) wrote : | # |
From the silo building...
/<<BUILDDIR>
/<<BUILDDIR>
Guess this needs pulling of staging to include my changes, and then change to use PpdUtils.
Andrew Hayzen (ahayzen) wrote : | # |
Looks good so far, just 3 inline comments.
1) It seems like this should be set?
Otherwise further down this if will be false
if (makeLower == "hp") {
...
}
2) Same here
3) Should this then do
makeLower = make.toLower();
Andrew Hayzen (ahayzen) wrote : | # |
All tests pass, feedback fixed. LGTM :-)
system-apps-ci-bot (system-apps-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:169
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
system-apps-ci-bot (system-apps-ci-bot) wrote : | # |
FAILED: Autolanding.
More details in the following jenkins job:
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Andrew Hayzen (ahayzen) wrote : | # |
The changes look good.
One inline comment:
1) Looks like this is missing
proc.start(program, arguments);
?
Also I think ubuntu-ui-extras should now depends or at least recommend the package hplip.
- 173. By Jonas G. Drange
-
drop the device id for fax
system-apps-ci-bot (system-apps-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:172
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Andrew Hayzen (ahayzen) wrote : | # |
This LGTM now, all it misses is usb fax's. We can improve that later if required.
system-apps-ci-bot (system-apps-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:173
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
- 174. By Jonas G. Drange
-
uses info as a last resort
system-apps-ci-bot (system-apps-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:174
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Andrew Hayzen (ahayzen) wrote : | # |
Changes look good, and there is a test :-)
Only thing is if you want todo what we discussed about having a way of adding printers that are "unknown". So that you can pick the name/uri and it fills the fields it knows but lets the user pick the driver etc.
- 175. By Jonas G. Drange
-
make sure non-consolidateable devices are kept
system-apps-ci-bot (system-apps-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:175
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
- 176. By Jonas G. Drange
-
fixes test that now broke, but had to change, and add .get to drivermodel
system-apps-ci-bot (system-apps-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:176
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
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 'debian/control' | |||
2 | --- debian/control 2017-02-24 20:38:24 +0000 | |||
3 | +++ debian/control 2017-04-07 12:51:46 +0000 | |||
4 | @@ -37,6 +37,7 @@ | |||
5 | 37 | qml-module-qtquick2, | 37 | qml-module-qtquick2, |
6 | 38 | qml-module-ubuntu-components, | 38 | qml-module-ubuntu-components, |
7 | 39 | qml-module-qtquick-window2, | 39 | qml-module-qtquick-window2, |
8 | 40 | Suggests: hplip, | ||
9 | 40 | Provides: qtdeclarative5-ubuntu-ui-extras0.1 | 41 | Provides: qtdeclarative5-ubuntu-ui-extras0.1 |
10 | 41 | Conflicts: qtdeclarative5-ubuntu-ui-extras0.1 | 42 | Conflicts: qtdeclarative5-ubuntu-ui-extras0.1 |
11 | 42 | Replaces: qtdeclarative5-ubuntu-ui-extras0.1 | 43 | Replaces: qtdeclarative5-ubuntu-ui-extras0.1 |
12 | 43 | 44 | ||
13 | === modified file 'modules/Ubuntu/Components/Extras/Example/Printers.qml' | |||
14 | --- modules/Ubuntu/Components/Extras/Example/Printers.qml 2017-03-21 23:02:42 +0000 | |||
15 | +++ modules/Ubuntu/Components/Extras/Example/Printers.qml 2017-04-07 12:51:46 +0000 | |||
16 | @@ -685,7 +685,7 @@ | |||
17 | 685 | verticalCenter: parent.verticalCenter | 685 | verticalCenter: parent.verticalCenter |
18 | 686 | } | 686 | } |
19 | 687 | property var target | 687 | property var target |
21 | 688 | Component.onCompleted: target = Printers.devices | 688 | Component.onCompleted: target = Printers.consolidatedDevices |
22 | 689 | running: target.searching | 689 | running: target.searching |
23 | 690 | } | 690 | } |
24 | 691 | } | 691 | } |
25 | @@ -699,27 +699,12 @@ | |||
26 | 699 | topMargin: units.gu(2) | 699 | topMargin: units.gu(2) |
27 | 700 | } | 700 | } |
28 | 701 | height: contentItem.childrenRect.height | 701 | height: contentItem.childrenRect.height |
30 | 702 | model: Printers.devices | 702 | model: Printers.consolidatedDevices |
31 | 703 | delegate: ListItem { | 703 | delegate: ListItem { |
32 | 704 | height: modelLayout.height + (divider.visible ? divider.height : 0) | 704 | height: modelLayout.height + (divider.visible ? divider.height : 0) |
33 | 705 | ListItemLayout { | 705 | ListItemLayout { |
34 | 706 | id: modelLayout | 706 | id: modelLayout |
35 | 707 | title.text: displayName | 707 | title.text: displayName |
36 | 708 | subtitle.text: { | ||
37 | 709 | if (type == PrinterEnum.LPDType) return "LPD"; | ||
38 | 710 | if (type == PrinterEnum.IppSType) return "IppS"; | ||
39 | 711 | if (type == PrinterEnum.Ipp14Type) return "Ipp14"; | ||
40 | 712 | if (type == PrinterEnum.HttpType) return "Http"; | ||
41 | 713 | if (type == PrinterEnum.BehType) return "Beh"; | ||
42 | 714 | if (type == PrinterEnum.SocketType) return "Socket"; | ||
43 | 715 | if (type == PrinterEnum.HttpsType) return "Https"; | ||
44 | 716 | if (type == PrinterEnum.IppType) return "Ipp"; | ||
45 | 717 | if (type == PrinterEnum.HPType) return "HP"; | ||
46 | 718 | if (type == PrinterEnum.USBType) return "USB"; | ||
47 | 719 | if (type == PrinterEnum.HPFaxType) return "HPFax"; | ||
48 | 720 | if (type == PrinterEnum.DNSSDType) return "DNSSD"; | ||
49 | 721 | else return "Unknown protocol"; | ||
50 | 722 | } | ||
51 | 723 | 708 | ||
52 | 724 | Icon { | 709 | Icon { |
53 | 725 | id: icon | 710 | id: icon |
54 | @@ -729,17 +714,22 @@ | |||
55 | 729 | SlotsLayout.position: SlotsLayout.First | 714 | SlotsLayout.position: SlotsLayout.First |
56 | 730 | } | 715 | } |
57 | 731 | 716 | ||
69 | 732 | Button { | 717 | ProgressionSlot {} |
70 | 733 | text: "Select printer" | 718 | } |
71 | 734 | onClicked: { | 719 | onClicked: { |
72 | 735 | var suggestedPrinterName = (" " + displayName).slice(1); | 720 | var p = pageStack.push(chooseConnectionPage, { |
73 | 736 | suggestedPrinterName = suggestedPrinterName.replace(/\ /g, "\-"); | 721 | consolidatedDevice: model |
74 | 737 | printerUri.text = uri; | 722 | }); |
75 | 738 | printerName.text = suggestedPrinterName; | 723 | p.onConnectionChosen.connect(function (conn) { |
76 | 739 | printerDescription.text = info; | 724 | pageStack.pop(); |
77 | 740 | printerLocation.text = location; | 725 | |
78 | 741 | } | 726 | var suggestedPrinterName = (" " + conn.displayName).slice(1); |
79 | 742 | } | 727 | suggestedPrinterName = suggestedPrinterName.replace(/\ /g, "\-"); |
80 | 728 | printerUri.text = conn.uri; | ||
81 | 729 | printerName.text = suggestedPrinterName; | ||
82 | 730 | printerDescription.text = conn.info; | ||
83 | 731 | printerLocation.text = conn.location; | ||
84 | 732 | }); | ||
85 | 743 | } | 733 | } |
86 | 744 | } | 734 | } |
87 | 745 | } | 735 | } |
88 | @@ -757,4 +747,59 @@ | |||
89 | 757 | } | 747 | } |
90 | 758 | } | 748 | } |
91 | 759 | } | 749 | } |
92 | 750 | |||
93 | 751 | Component { | ||
94 | 752 | id: chooseConnectionPage | ||
95 | 753 | |||
96 | 754 | Page { | ||
97 | 755 | visible: false | ||
98 | 756 | property var consolidatedDevice | ||
99 | 757 | signal connectionChosen(var conn) | ||
100 | 758 | header: PageHeader { | ||
101 | 759 | id: chooseConnectionPageHeader | ||
102 | 760 | title: "Choose connection" | ||
103 | 761 | flickable: connectionsList | ||
104 | 762 | } | ||
105 | 763 | |||
106 | 764 | ListView { | ||
107 | 765 | id: connectionsList | ||
108 | 766 | anchors.fill: parent | ||
109 | 767 | model: consolidatedDevice.devices | ||
110 | 768 | delegate: ListItem { | ||
111 | 769 | height: modelLayout.height + (divider.visible ? divider.height : 0) | ||
112 | 770 | ListItemLayout { | ||
113 | 771 | id: modelLayout | ||
114 | 772 | title.text: info | ||
115 | 773 | subtitle.text: { | ||
116 | 774 | if (type == PrinterEnum.LPDType) return "LPD"; | ||
117 | 775 | if (type == PrinterEnum.IppSType) return "IppS"; | ||
118 | 776 | if (type == PrinterEnum.Ipp14Type) return "Ipp14"; | ||
119 | 777 | if (type == PrinterEnum.HttpType) return "Http"; | ||
120 | 778 | if (type == PrinterEnum.BehType) return "Beh"; | ||
121 | 779 | if (type == PrinterEnum.SocketType) return "Socket"; | ||
122 | 780 | if (type == PrinterEnum.HttpsType) return "Https"; | ||
123 | 781 | if (type == PrinterEnum.IppType) return "Ipp"; | ||
124 | 782 | if (type == PrinterEnum.HPType) return "HP"; | ||
125 | 783 | if (type == PrinterEnum.USBType) return "USB"; | ||
126 | 784 | if (type == PrinterEnum.HPFaxType) return "HPFax"; | ||
127 | 785 | if (type == PrinterEnum.DNSSDType) return "DNSSD"; | ||
128 | 786 | else return "Unknown protocol"; | ||
129 | 787 | } | ||
130 | 788 | summary.text: uri | ||
131 | 789 | |||
132 | 790 | Icon { | ||
133 | 791 | id: icon | ||
134 | 792 | width: height | ||
135 | 793 | height: units.gu(2.5) | ||
136 | 794 | name: "network-printer-symbolic" | ||
137 | 795 | SlotsLayout.position: SlotsLayout.First | ||
138 | 796 | } | ||
139 | 797 | |||
140 | 798 | ProgressionSlot {} | ||
141 | 799 | } | ||
142 | 800 | onClicked: connectionChosen(model) | ||
143 | 801 | } | ||
144 | 802 | } | ||
145 | 803 | } | ||
146 | 804 | } | ||
147 | 760 | } | 805 | } |
148 | 761 | 806 | ||
149 | === modified file 'modules/Ubuntu/Components/Extras/Printers/CMakeLists.txt' | |||
150 | --- modules/Ubuntu/Components/Extras/Printers/CMakeLists.txt 2017-03-13 12:51:16 +0000 | |||
151 | +++ modules/Ubuntu/Components/Extras/Printers/CMakeLists.txt 2017-04-07 12:51:46 +0000 | |||
152 | @@ -34,6 +34,7 @@ | |||
153 | 34 | cups/jobloader.cpp | 34 | cups/jobloader.cpp |
154 | 35 | cups/printerdriverloader.cpp | 35 | cups/printerdriverloader.cpp |
155 | 36 | cups/printerloader.cpp | 36 | cups/printerloader.cpp |
156 | 37 | cups/ppdutils.cpp | ||
157 | 37 | 38 | ||
158 | 38 | models/devicemodel.cpp | 39 | models/devicemodel.cpp |
159 | 39 | models/drivermodel.cpp | 40 | models/drivermodel.cpp |
160 | 40 | 41 | ||
161 | === modified file 'modules/Ubuntu/Components/Extras/Printers/backend/backend_cups.cpp' | |||
162 | --- modules/Ubuntu/Components/Extras/Printers/backend/backend_cups.cpp 2017-03-15 17:42:21 +0000 | |||
163 | +++ modules/Ubuntu/Components/Extras/Printers/backend/backend_cups.cpp 2017-04-07 12:51:46 +0000 | |||
164 | @@ -17,6 +17,7 @@ | |||
165 | 17 | #include "backend/backend_cups.h" | 17 | #include "backend/backend_cups.h" |
166 | 18 | #include "cups/devicesearcher.h" | 18 | #include "cups/devicesearcher.h" |
167 | 19 | #include "cups/jobloader.h" | 19 | #include "cups/jobloader.h" |
168 | 20 | #include "cups/ppdutils.h" | ||
169 | 20 | #include "cups/printerdriverloader.h" | 21 | #include "cups/printerdriverloader.h" |
170 | 21 | #include "cups/printerloader.h" | 22 | #include "cups/printerloader.h" |
171 | 22 | #include "utils.h" | 23 | #include "utils.h" |
172 | @@ -264,9 +265,9 @@ | |||
173 | 264 | ppd_choice_t* def = ppdFindChoice(ppdColorModel, | 265 | ppd_choice_t* def = ppdFindChoice(ppdColorModel, |
174 | 265 | ppdColorModel->defchoice); | 266 | ppdColorModel->defchoice); |
175 | 266 | if (def) { | 267 | if (def) { |
179 | 267 | model = Utils::parsePpdColorModel(def->choice, | 268 | model = PpdUtils::parsePpdColorModel(def->choice, |
180 | 268 | def->text, | 269 | def->text, |
181 | 269 | "ColorModel"); | 270 | "ColorModel"); |
182 | 270 | } | 271 | } |
183 | 271 | } | 272 | } |
184 | 272 | ret[option] = QVariant::fromValue(model); | 273 | ret[option] = QVariant::fromValue(model); |
185 | @@ -278,8 +279,8 @@ | |||
186 | 278 | ppd_choice_t* def = ppdFindChoice(ppdQuality, | 279 | ppd_choice_t* def = ppdFindChoice(ppdQuality, |
187 | 279 | ppdQuality->defchoice); | 280 | ppdQuality->defchoice); |
188 | 280 | if (def) { | 281 | if (def) { |
191 | 281 | quality = Utils::parsePpdPrintQuality(def->choice, | 282 | quality = PpdUtils::parsePpdPrintQuality(def->choice, |
192 | 282 | def->text, opt); | 283 | def->text, opt); |
193 | 283 | } | 284 | } |
194 | 284 | } | 285 | } |
195 | 285 | } | 286 | } |
196 | @@ -291,7 +292,7 @@ | |||
197 | 291 | if (qualityOpt) { | 292 | if (qualityOpt) { |
198 | 292 | for (int i = 0; i < qualityOpt->num_choices; ++i) { | 293 | for (int i = 0; i < qualityOpt->num_choices; ++i) { |
199 | 293 | qualities.append( | 294 | qualities.append( |
201 | 294 | Utils::parsePpdPrintQuality( | 295 | PpdUtils::parsePpdPrintQuality( |
202 | 295 | qualityOpt->choices[i].choice, | 296 | qualityOpt->choices[i].choice, |
203 | 296 | qualityOpt->choices[i].text, | 297 | qualityOpt->choices[i].text, |
204 | 297 | opt | 298 | opt |
205 | @@ -307,7 +308,7 @@ | |||
206 | 307 | if (colorModels) { | 308 | if (colorModels) { |
207 | 308 | for (int i = 0; i < colorModels->num_choices; ++i) { | 309 | for (int i = 0; i < colorModels->num_choices; ++i) { |
208 | 309 | models.append( | 310 | models.append( |
210 | 310 | Utils::parsePpdColorModel( | 311 | PpdUtils::parsePpdColorModel( |
211 | 311 | colorModels->choices[i].choice, | 312 | colorModels->choices[i].choice, |
212 | 312 | colorModels->choices[i].text, | 313 | colorModels->choices[i].text, |
213 | 313 | QStringLiteral("ColorModel") | 314 | QStringLiteral("ColorModel") |
214 | @@ -357,7 +358,7 @@ | |||
215 | 357 | 358 | ||
216 | 358 | __CUPS_ADD_OPTION(dest, "copies", QString::number(options->copies()).toLocal8Bit()); | 359 | __CUPS_ADD_OPTION(dest, "copies", QString::number(options->copies()).toLocal8Bit()); |
217 | 359 | __CUPS_ADD_OPTION(dest, "ColorModel", options->getColorModel().name.toLocal8Bit()); | 360 | __CUPS_ADD_OPTION(dest, "ColorModel", options->getColorModel().name.toLocal8Bit()); |
219 | 360 | __CUPS_ADD_OPTION(dest, "Duplex", Utils::duplexModeToPpdChoice(options->getDuplexMode()).toLocal8Bit()); | 361 | __CUPS_ADD_OPTION(dest, "Duplex", PpdUtils::duplexModeToPpdChoice(options->getDuplexMode()).toLocal8Bit()); |
220 | 361 | 362 | ||
221 | 362 | if (options->landscape()) { | 363 | if (options->landscape()) { |
222 | 363 | __CUPS_ADD_OPTION(dest, "landscape", ""); | 364 | __CUPS_ADD_OPTION(dest, "landscape", ""); |
223 | 364 | 365 | ||
224 | === modified file 'modules/Ubuntu/Components/Extras/Printers/cups/devicesearcher.cpp' | |||
225 | --- modules/Ubuntu/Components/Extras/Printers/cups/devicesearcher.cpp 2017-03-08 14:47:16 +0000 | |||
226 | +++ modules/Ubuntu/Components/Extras/Printers/cups/devicesearcher.cpp 2017-04-07 12:51:46 +0000 | |||
227 | @@ -1,5 +1,10 @@ | |||
228 | 1 | /* | 1 | /* |
229 | 2 | * Copyright (C) 2017 Canonical, Ltd. | 2 | * Copyright (C) 2017 Canonical, Ltd. |
230 | 3 | * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Red Hat, Inc. | ||
231 | 4 | * Authors: | ||
232 | 5 | * Tim Waugh <twaugh@redhat.com> | ||
233 | 6 | * Florian Festi <ffesti@redhat.com> | ||
234 | 7 | * Jonas G. Drange <jonas.drange@canonical.com> | ||
235 | 3 | * | 8 | * |
236 | 4 | * This program is free software; you can redistribute it and/or modify | 9 | * This program is free software; you can redistribute it and/or modify |
237 | 5 | * it under the terms of the GNU Lesser General Public License as published by | 10 | * it under the terms of the GNU Lesser General Public License as published by |
238 | @@ -16,8 +21,10 @@ | |||
239 | 16 | 21 | ||
240 | 17 | #include "cups/ippclient.h" | 22 | #include "cups/ippclient.h" |
241 | 18 | #include "devicesearcher.h" | 23 | #include "devicesearcher.h" |
242 | 24 | #include "i18n.h" | ||
243 | 19 | 25 | ||
245 | 20 | #include <QUrl> | 26 | #include <QProcess> |
246 | 27 | #include <QRegularExpression> | ||
247 | 21 | 28 | ||
248 | 22 | DeviceSearcher::DeviceSearcher(IppClient *client, QObject *parent) | 29 | DeviceSearcher::DeviceSearcher(IppClient *client, QObject *parent) |
249 | 23 | : QObject(parent) | 30 | : QObject(parent) |
250 | @@ -54,12 +61,19 @@ | |||
251 | 54 | } | 61 | } |
252 | 55 | 62 | ||
253 | 56 | Device d; | 63 | Device d; |
254 | 64 | |||
255 | 57 | d.cls = deviceClass; | 65 | d.cls = deviceClass; |
256 | 58 | d.id = deviceId; | 66 | d.id = deviceId; |
257 | 59 | d.info = deviceInfo; | 67 | d.info = deviceInfo; |
259 | 60 | d.makeModel = deviceMakeAndModel; | 68 | |
260 | 61 | d.uri = deviceUri; | 69 | d.uri = deviceUri; |
261 | 62 | d.location = deviceLocation; | 70 | d.location = deviceLocation; |
262 | 71 | d.makeModel = deviceMakeAndModel; | ||
263 | 72 | |||
264 | 73 | // If this is a HP device, create hp devices from it. | ||
265 | 74 | Q_FOREACH(auto hpDevice, DeviceSearcher::createHpDevices(d)) { | ||
266 | 75 | searcher->deviceFound(hpDevice); | ||
267 | 76 | } | ||
268 | 63 | 77 | ||
269 | 64 | searcher->deviceFound(d); | 78 | searcher->deviceFound(d); |
270 | 65 | } | 79 | } |
271 | @@ -68,3 +82,72 @@ | |||
272 | 68 | { | 82 | { |
273 | 69 | Q_EMIT loaded(device); | 83 | Q_EMIT loaded(device); |
274 | 70 | } | 84 | } |
275 | 85 | |||
276 | 86 | QList<Device> DeviceSearcher::createHpDevices(const Device &device) | ||
277 | 87 | { | ||
278 | 88 | QList<Device> ret; | ||
279 | 89 | |||
280 | 90 | auto deviceHost = device.host(); | ||
281 | 91 | auto makeModelLower = device.makeModel.toLower(); | ||
282 | 92 | bool isHpType = device.type() == PrinterEnum::DeviceType::HPType; | ||
283 | 93 | bool isNetwork = device.cls == QStringLiteral("network"); | ||
284 | 94 | bool haveMakeModel = !device.makeModel.isEmpty(); | ||
285 | 95 | bool unknownMake = device.makeModel == QStringLiteral("Unknown"); | ||
286 | 96 | bool startsWithHp = (makeModelLower.startsWith(QStringLiteral("hp")) || | ||
287 | 97 | makeModelLower.startsWith(QStringLiteral("hewlett"))); | ||
288 | 98 | |||
289 | 99 | if (!isHpType && isNetwork && (!haveMakeModel || unknownMake || | ||
290 | 100 | startsWithHp)) { | ||
291 | 101 | auto uri = hplipUri(deviceHost, HpPrinterMode::HpPrinter); | ||
292 | 102 | |||
293 | 103 | // We couldn't get anything useful from the hplip tools. | ||
294 | 104 | if (uri.isEmpty()) { | ||
295 | 105 | return ret; | ||
296 | 106 | } | ||
297 | 107 | |||
298 | 108 | // We now have a hp printer device we want to add to the list. | ||
299 | 109 | Device printerDev(device); | ||
300 | 110 | printerDev.uri = uri; | ||
301 | 111 | ret << printerDev; | ||
302 | 112 | |||
303 | 113 | // TODO: Check if it's able to scan. | ||
304 | 114 | |||
305 | 115 | auto faxUri = hplipUri(deviceHost, HpPrinterMode::HpFax); | ||
306 | 116 | if (!faxUri.isEmpty()) { | ||
307 | 117 | Device faxDev(device); | ||
308 | 118 | faxDev.uri = faxUri; | ||
309 | 119 | faxDev.info = __("Fax"); | ||
310 | 120 | ret << faxDev; | ||
311 | 121 | } | ||
312 | 122 | } | ||
313 | 123 | |||
314 | 124 | return ret; | ||
315 | 125 | } | ||
316 | 126 | |||
317 | 127 | QString DeviceSearcher::hplipUri(const QString &host, | ||
318 | 128 | const HpPrinterMode &mode) | ||
319 | 129 | { | ||
320 | 130 | QString program = "hp-makeuri"; | ||
321 | 131 | QStringList arguments; | ||
322 | 132 | QString ret; | ||
323 | 133 | |||
324 | 134 | switch (mode) { | ||
325 | 135 | default: | ||
326 | 136 | case HpPrinterMode::HpPrinter: | ||
327 | 137 | arguments << "-c"; | ||
328 | 138 | break; | ||
329 | 139 | case HpPrinterMode::HpFax: | ||
330 | 140 | arguments << "-f"; | ||
331 | 141 | break; | ||
332 | 142 | } | ||
333 | 143 | |||
334 | 144 | arguments << host; | ||
335 | 145 | |||
336 | 146 | QProcess proc; | ||
337 | 147 | proc.start(program, arguments); | ||
338 | 148 | |||
339 | 149 | if (proc.waitForFinished() && proc.exitStatus() == QProcess::NormalExit) { | ||
340 | 150 | ret = QString(proc.readAllStandardOutput()).trimmed(); | ||
341 | 151 | } | ||
342 | 152 | return ret; | ||
343 | 153 | } | ||
344 | 71 | 154 | ||
345 | === modified file 'modules/Ubuntu/Components/Extras/Printers/cups/devicesearcher.h' | |||
346 | --- modules/Ubuntu/Components/Extras/Printers/cups/devicesearcher.h 2017-03-08 11:29:01 +0000 | |||
347 | +++ modules/Ubuntu/Components/Extras/Printers/cups/devicesearcher.h 2017-04-07 12:51:46 +0000 | |||
348 | @@ -1,5 +1,10 @@ | |||
349 | 1 | /* | 1 | /* |
350 | 2 | * Copyright (C) 2017 Canonical, Ltd. | 2 | * Copyright (C) 2017 Canonical, Ltd. |
351 | 3 | * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Red Hat, Inc. | ||
352 | 4 | * Authors: | ||
353 | 5 | * Tim Waugh <twaugh@redhat.com> | ||
354 | 6 | * Florian Festi <ffesti@redhat.com> | ||
355 | 7 | * Jonas G. Drange <jonas.drange@canonical.com> | ||
356 | 3 | * | 8 | * |
357 | 4 | * This program is free software; you can redistribute it and/or modify | 9 | * This program is free software; you can redistribute it and/or modify |
358 | 5 | * it under the terms of the GNU Lesser General Public License as published by | 10 | * it under the terms of the GNU Lesser General Public License as published by |
359 | @@ -38,6 +43,12 @@ | |||
360 | 38 | void load(); | 43 | void load(); |
361 | 39 | 44 | ||
362 | 40 | private: | 45 | private: |
363 | 46 | enum class HpPrinterMode | ||
364 | 47 | { | ||
365 | 48 | HpPrinter, | ||
366 | 49 | HpFax, | ||
367 | 50 | HpScanner, | ||
368 | 51 | }; | ||
369 | 41 | static void deviceCallBack( | 52 | static void deviceCallBack( |
370 | 42 | const char *cls, | 53 | const char *cls, |
371 | 43 | const char *id, | 54 | const char *id, |
372 | @@ -48,6 +59,10 @@ | |||
373 | 48 | void *context); | 59 | void *context); |
374 | 49 | void deviceFound(const Device &device); | 60 | void deviceFound(const Device &device); |
375 | 50 | 61 | ||
376 | 62 | static QList<Device> createHpDevices(const Device &device); | ||
377 | 63 | static QString hplipUri(const QString &host, | ||
378 | 64 | const HpPrinterMode &mode); | ||
379 | 65 | |||
380 | 51 | Q_SIGNALS: | 66 | Q_SIGNALS: |
381 | 52 | void loaded(const Device &device); | 67 | void loaded(const Device &device); |
382 | 53 | void failed(const QString &errorMessage); | 68 | void failed(const QString &errorMessage); |
383 | 54 | 69 | ||
384 | === added file 'modules/Ubuntu/Components/Extras/Printers/cups/ppdutils.cpp' | |||
385 | --- modules/Ubuntu/Components/Extras/Printers/cups/ppdutils.cpp 1970-01-01 00:00:00 +0000 | |||
386 | +++ modules/Ubuntu/Components/Extras/Printers/cups/ppdutils.cpp 2017-04-07 12:51:46 +0000 | |||
387 | @@ -0,0 +1,345 @@ | |||
388 | 1 | /* | ||
389 | 2 | * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2014, 2015 Red Hat, Inc. | ||
390 | 3 | * Copyright (C) 2006 Florian Festi <ffesti@redhat.com> | ||
391 | 4 | * Copyright (C) 2006, 2007, 2008, 2009 Tim Waugh <twaugh@redhat.com> | ||
392 | 5 | * Copyright (C) 2017 Canonical, Ltd. | ||
393 | 6 | * | ||
394 | 7 | * This program is free software; you can redistribute it and/or modify | ||
395 | 8 | * it under the terms of the GNU Lesser General Public License as published by | ||
396 | 9 | * the Free Software Foundation; version 3. | ||
397 | 10 | * | ||
398 | 11 | * This program is distributed in the hope that it will be useful, | ||
399 | 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
400 | 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
401 | 14 | * GNU Lesser General Public License for more details. | ||
402 | 15 | * | ||
403 | 16 | * You should have received a copy of the GNU Lesser General Public License | ||
404 | 17 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
405 | 18 | */ | ||
406 | 19 | |||
407 | 20 | #include "ppdutils.h" | ||
408 | 21 | |||
409 | 22 | #include <QSet> | ||
410 | 23 | |||
411 | 24 | const QRegularExpression PpdUtils::versionNumbers = QRegularExpression( | ||
412 | 25 | " v(?:er\\.)?\\d(?:\\d*\\.\\d+)?(?: |$)" | ||
413 | 26 | ); | ||
414 | 27 | const QRegularExpression PpdUtils::ignoredSuffixes = QRegularExpression( | ||
415 | 28 | "," | ||
416 | 29 | "| hpijs" | ||
417 | 30 | "| foomatic/" | ||
418 | 31 | "| - " | ||
419 | 32 | "| w/" | ||
420 | 33 | "| \\(" | ||
421 | 34 | "| postscript" | ||
422 | 35 | "| ps" | ||
423 | 36 | "| ps3" | ||
424 | 37 | "| pdf" | ||
425 | 38 | "| pxl" | ||
426 | 39 | "| zjs" | ||
427 | 40 | "| zxs" | ||
428 | 41 | "| pcl3" | ||
429 | 42 | "| printer" | ||
430 | 43 | "|_bt" | ||
431 | 44 | "| pcl" | ||
432 | 45 | "| ufr ii" | ||
433 | 46 | "| br-script" | ||
434 | 47 | ); | ||
435 | 48 | const QRegularExpression PpdUtils::ignoreSeries = QRegularExpression( | ||
436 | 49 | " series| all-in-one", | ||
437 | 50 | QRegularExpression::CaseInsensitiveOption | ||
438 | 51 | ); | ||
439 | 52 | const QList<QPair<QString, QRegularExpression>> PpdUtils::manufacturerByModels | ||
440 | 53 | = QList<QPair<QString, QRegularExpression>>({ | ||
441 | 54 | QPair<QString, QRegularExpression>( | ||
442 | 55 | QStringLiteral("HP"), QRegularExpression( | ||
443 | 56 | "deskjet" | ||
444 | 57 | "|dj[ 0-9]?" | ||
445 | 58 | "|laserjet" | ||
446 | 59 | "|lj" | ||
447 | 60 | "|color laserjet" | ||
448 | 61 | "|color lj" | ||
449 | 62 | "|designjet" | ||
450 | 63 | "|officejet" | ||
451 | 64 | "|oj" | ||
452 | 65 | "|photosmart" | ||
453 | 66 | "|ps " | ||
454 | 67 | "|psc" | ||
455 | 68 | "|edgeline" | ||
456 | 69 | ) | ||
457 | 70 | ), | ||
458 | 71 | QPair<QString, QRegularExpression>( | ||
459 | 72 | QStringLiteral("Epson"), QRegularExpression("stylus|aculaser") | ||
460 | 73 | ), | ||
461 | 74 | QPair<QString, QRegularExpression>( | ||
462 | 75 | QStringLiteral("Apple"), QRegularExpression( | ||
463 | 76 | "stylewriter" | ||
464 | 77 | "|imagewriter" | ||
465 | 78 | "|deskwriter" | ||
466 | 79 | "|laserwriter" | ||
467 | 80 | ) | ||
468 | 81 | ), | ||
469 | 82 | QPair<QString, QRegularExpression>( | ||
470 | 83 | QStringLiteral("Canon"), QRegularExpression( | ||
471 | 84 | "pixus" | ||
472 | 85 | "|pixma" | ||
473 | 86 | "|selphy" | ||
474 | 87 | "|imagerunner" | ||
475 | 88 | "|bj" | ||
476 | 89 | "|lbp" | ||
477 | 90 | ) | ||
478 | 91 | ), | ||
479 | 92 | QPair<QString, QRegularExpression>( | ||
480 | 93 | QStringLiteral("Brother"), QRegularExpression("hl|dcp|mfc") | ||
481 | 94 | ), | ||
482 | 95 | QPair<QString, QRegularExpression>( | ||
483 | 96 | QStringLiteral("Xerox"), QRegularExpression( | ||
484 | 97 | "docuprint" | ||
485 | 98 | "|docupage" | ||
486 | 99 | "|phaser" | ||
487 | 100 | "|workcentre" | ||
488 | 101 | "|homecentre" | ||
489 | 102 | ) | ||
490 | 103 | ), | ||
491 | 104 | QPair<QString, QRegularExpression>( | ||
492 | 105 | QStringLiteral("Lexmark"), | ||
493 | 106 | QRegularExpression("optra|(:color )?jetprinter") | ||
494 | 107 | ), | ||
495 | 108 | QPair<QString, QRegularExpression>( | ||
496 | 109 | QStringLiteral("KONICA MINOLTA"), QRegularExpression( | ||
497 | 110 | "magicolor" | ||
498 | 111 | "|pageworks" | ||
499 | 112 | "|pagepro" | ||
500 | 113 | ) | ||
501 | 114 | ), | ||
502 | 115 | QPair<QString, QRegularExpression>( | ||
503 | 116 | QStringLiteral("Kyocera"), QRegularExpression( | ||
504 | 117 | "fs-" | ||
505 | 118 | "|km-" | ||
506 | 119 | "|taskalfa" | ||
507 | 120 | ) | ||
508 | 121 | ), | ||
509 | 122 | QPair<QString, QRegularExpression>( | ||
510 | 123 | QStringLiteral("Ricoh"), QRegularExpression("aficio") | ||
511 | 124 | ), | ||
512 | 125 | QPair<QString, QRegularExpression>( | ||
513 | 126 | QStringLiteral("Oce"), QRegularExpression("varioprint") | ||
514 | 127 | ), | ||
515 | 128 | QPair<QString, QRegularExpression>( | ||
516 | 129 | QStringLiteral("Oki"), QRegularExpression("okipage|microline") | ||
517 | 130 | ), | ||
518 | 131 | }); | ||
519 | 132 | |||
520 | 133 | const QMap<QString, QString> PpdUtils::hpByModel = QMap<QString, QString>{ | ||
521 | 134 | {"dj", "DeskJet"}, | ||
522 | 135 | {"lj", "LaserJet"}, | ||
523 | 136 | {"laserjet", "LaserJet"}, | ||
524 | 137 | {"oj", "OfficeJet"}, | ||
525 | 138 | {"officejet", "OfficeJet"}, | ||
526 | 139 | {"color lj", "Color LaserJet"}, | ||
527 | 140 | {"ps ", "PhotoSmart"}, | ||
528 | 141 | {"hp ", ""}, | ||
529 | 142 | }; | ||
530 | 143 | |||
531 | 144 | QString PpdUtils::dedupeMakeModel(const QString &str) | ||
532 | 145 | { | ||
533 | 146 | QSet<QString> stringSet; | ||
534 | 147 | QStringList deduped; | ||
535 | 148 | Q_FOREACH(auto m, str.split(" ")) { | ||
536 | 149 | if (!stringSet.contains(m)) { | ||
537 | 150 | deduped << m; | ||
538 | 151 | } | ||
539 | 152 | stringSet << m; | ||
540 | 153 | } | ||
541 | 154 | return deduped.join(" "); | ||
542 | 155 | } | ||
543 | 156 | |||
544 | 157 | QPair<QString, QString> PpdUtils::splitMakeModel(const QString &makeModel) | ||
545 | 158 | { | ||
546 | 159 | const QString mm = dedupeMakeModel(makeModel); | ||
547 | 160 | const QString makeModelLower = mm.toLower(); | ||
548 | 161 | |||
549 | 162 | QString make; | ||
550 | 163 | QString model; | ||
551 | 164 | |||
552 | 165 | /* Informs us whether or not the make part was produced in such a way that | ||
553 | 166 | it might need cleaning. I.e. we've made a guess at what the make is. */ | ||
554 | 167 | bool cleanupMake = false; | ||
555 | 168 | |||
556 | 169 | /* If the string starts with a known model name (like "LaserJet") assume | ||
557 | 170 | that the manufacturer name is missing and add the manufacturer name | ||
558 | 171 | corresponding to the model name */ | ||
559 | 172 | Q_FOREACH(auto manufacturerByModel, PpdUtils::manufacturerByModels) { | ||
560 | 173 | if (manufacturerByModel.second.match(makeModelLower).hasMatch()) { | ||
561 | 174 | make = manufacturerByModel.first; | ||
562 | 175 | model = mm; | ||
563 | 176 | } | ||
564 | 177 | } | ||
565 | 178 | |||
566 | 179 | /* Take the first word as the name of the manufacturer. | ||
567 | 180 | |||
568 | 181 | Note that we have skipped a bunch of special cases where the first word | ||
569 | 182 | isn't necessarily the manufacturer, hence the next TODO. | ||
570 | 183 | |||
571 | 184 | TODO: Handle special cases for TurboPrint, lexmark international, | ||
572 | 185 | fuji xerox, etc. */ | ||
573 | 186 | int firstSpace = mm.indexOf(" "); | ||
574 | 187 | if (make.isEmpty() && firstSpace > 0) { | ||
575 | 188 | make = mm.left(firstSpace); | ||
576 | 189 | model = mm.mid(make.size() + 1); | ||
577 | 190 | |||
578 | 191 | /* We've picked the first part as the make, but it may be that we've | ||
579 | 192 | split "hewlett packard" and are left with "hewlett". */ | ||
580 | 193 | cleanupMake = true; | ||
581 | 194 | } | ||
582 | 195 | |||
583 | 196 | // Standardised names for manufacturers. | ||
584 | 197 | QString makeLower = make.toLower(); | ||
585 | 198 | if (cleanupMake) { | ||
586 | 199 | if (makeLower.startsWith("hewlett") && makeLower.endsWith("packard")) { | ||
587 | 200 | make = "HP"; | ||
588 | 201 | makeLower = "hp"; | ||
589 | 202 | } else if (makeLower.startsWith("konica") && | ||
590 | 203 | makeLower.endsWith("minolta")) { | ||
591 | 204 | make = "KONICA MINOLTA"; | ||
592 | 205 | makeLower = "konica minolta"; | ||
593 | 206 | } else { | ||
594 | 207 | // Fix case errors. | ||
595 | 208 | Q_FOREACH(auto manufacturerByModel, manufacturerByModels) { | ||
596 | 209 | if (makeLower == manufacturerByModel.first.toLower()) { | ||
597 | 210 | make = manufacturerByModel.first; | ||
598 | 211 | makeLower = make.toLower(); | ||
599 | 212 | break; | ||
600 | 213 | } | ||
601 | 214 | } | ||
602 | 215 | } | ||
603 | 216 | } | ||
604 | 217 | |||
605 | 218 | // Remove the word "Series" if present. | ||
606 | 219 | model = model.remove(ignoreSeries); | ||
607 | 220 | |||
608 | 221 | /* Find a version number and cut the model from there. Note that some | ||
609 | 222 | models may legitimately look like version numbers. So we cut only if the | ||
610 | 223 | version number has max one digit, or a dot with digits before and after. */ | ||
611 | 224 | QString modelLower = model.toLower(); | ||
612 | 225 | int versionAt = modelLower.indexOf(" v"); | ||
613 | 226 | if (versionAt >= 0) { | ||
614 | 227 | // Look for v or ver. followed by digits and dots. | ||
615 | 228 | auto match = versionNumbers.match(modelLower); | ||
616 | 229 | if (match.hasMatch()) { | ||
617 | 230 | int matchAt = match.capturedStart(); | ||
618 | 231 | model.truncate(matchAt); | ||
619 | 232 | modelLower.truncate(matchAt); | ||
620 | 233 | } | ||
621 | 234 | } | ||
622 | 235 | |||
623 | 236 | // Remove ignored suffixes. | ||
624 | 237 | auto suffixMatch = ignoredSuffixes.match(modelLower); | ||
625 | 238 | if (suffixMatch.hasMatch()) { | ||
626 | 239 | int matchAt = suffixMatch.capturedStart(); | ||
627 | 240 | model.truncate(matchAt); | ||
628 | 241 | modelLower.truncate(matchAt); | ||
629 | 242 | } | ||
630 | 243 | |||
631 | 244 | // Replace lj or dj with LaserJet and DeskJet respectively, etc. | ||
632 | 245 | if (makeLower == "hp") { | ||
633 | 246 | Q_FOREACH(auto name, hpByModel.keys()) { | ||
634 | 247 | if (modelLower.contains(name)) { | ||
635 | 248 | int start = modelLower.indexOf(name); | ||
636 | 249 | model.remove(start, name.size()); | ||
637 | 250 | model.insert(start, hpByModel[name]); | ||
638 | 251 | modelLower = model.toLower(); | ||
639 | 252 | } | ||
640 | 253 | } | ||
641 | 254 | } | ||
642 | 255 | |||
643 | 256 | return QPair<QString, QString>(make, model); | ||
644 | 257 | } | ||
645 | 258 | |||
646 | 259 | QString PpdUtils::normalize(const QString &str) | ||
647 | 260 | { | ||
648 | 261 | QString strLower = str.trimmed().toLower(); | ||
649 | 262 | QString normalized; | ||
650 | 263 | |||
651 | 264 | const int BLANK = 0; | ||
652 | 265 | const int ALPHA = 1; | ||
653 | 266 | const int DIGIT = 2; | ||
654 | 267 | int lastchar = BLANK; | ||
655 | 268 | |||
656 | 269 | bool alnumfound = false; | ||
657 | 270 | |||
658 | 271 | for (int i = 0; i < strLower.size(); i++) { | ||
659 | 272 | if (strLower[i].isLetter()) { | ||
660 | 273 | if (lastchar != ALPHA && alnumfound) { | ||
661 | 274 | normalized += " "; | ||
662 | 275 | } | ||
663 | 276 | lastchar = ALPHA; | ||
664 | 277 | } else if (strLower[i].isDigit()) { | ||
665 | 278 | if (lastchar != DIGIT && alnumfound) { | ||
666 | 279 | normalized += " "; | ||
667 | 280 | } | ||
668 | 281 | lastchar = DIGIT; | ||
669 | 282 | } else { | ||
670 | 283 | lastchar = BLANK; | ||
671 | 284 | } | ||
672 | 285 | |||
673 | 286 | if (strLower[i].isLetterOrNumber()) { | ||
674 | 287 | normalized += strLower[i]; | ||
675 | 288 | alnumfound = true; | ||
676 | 289 | } | ||
677 | 290 | } | ||
678 | 291 | return normalized; | ||
679 | 292 | } | ||
680 | 293 | |||
681 | 294 | PrinterEnum::DuplexMode PpdUtils::ppdChoiceToDuplexMode(const QString &choice) | ||
682 | 295 | { | ||
683 | 296 | if (choice == "DuplexTumble") | ||
684 | 297 | return PrinterEnum::DuplexMode::DuplexShortSide; | ||
685 | 298 | else if (choice == "DuplexNoTumble") | ||
686 | 299 | return PrinterEnum::DuplexMode::DuplexLongSide; | ||
687 | 300 | else | ||
688 | 301 | return PrinterEnum::DuplexMode::DuplexNone; | ||
689 | 302 | } | ||
690 | 303 | |||
691 | 304 | const QString PpdUtils::duplexModeToPpdChoice( | ||
692 | 305 | const PrinterEnum::DuplexMode &mode) | ||
693 | 306 | { | ||
694 | 307 | switch (mode) { | ||
695 | 308 | case PrinterEnum::DuplexMode::DuplexShortSide: | ||
696 | 309 | return "DuplexTumble"; | ||
697 | 310 | case PrinterEnum::DuplexMode::DuplexLongSide: | ||
698 | 311 | return "DuplexNoTumble"; | ||
699 | 312 | case PrinterEnum::DuplexMode::DuplexNone: | ||
700 | 313 | default: | ||
701 | 314 | return "None"; | ||
702 | 315 | } | ||
703 | 316 | } | ||
704 | 317 | |||
705 | 318 | |||
706 | 319 | ColorModel PpdUtils::parsePpdColorModel(const QString &name, | ||
707 | 320 | const QString &text, | ||
708 | 321 | const QString &optionName) | ||
709 | 322 | { | ||
710 | 323 | ColorModel ret; | ||
711 | 324 | ret.name = name; | ||
712 | 325 | ret.text = text; | ||
713 | 326 | ret.originalOption = optionName; | ||
714 | 327 | |||
715 | 328 | if (ret.name.contains("Gray") || ret.name.contains("Black")) { | ||
716 | 329 | ret.colorType = PrinterEnum::ColorModelType::GrayType; | ||
717 | 330 | } else { | ||
718 | 331 | ret.colorType = PrinterEnum::ColorModelType::ColorType; | ||
719 | 332 | } | ||
720 | 333 | return ret; | ||
721 | 334 | } | ||
722 | 335 | |||
723 | 336 | PrintQuality PpdUtils::parsePpdPrintQuality(const QString &choice, | ||
724 | 337 | const QString &text, | ||
725 | 338 | const QString &optionName) | ||
726 | 339 | { | ||
727 | 340 | PrintQuality quality; | ||
728 | 341 | quality.name = choice; | ||
729 | 342 | quality.text = text; | ||
730 | 343 | quality.originalOption = optionName; | ||
731 | 344 | return quality; | ||
732 | 345 | } | ||
733 | 0 | 346 | ||
734 | === added file 'modules/Ubuntu/Components/Extras/Printers/cups/ppdutils.h' | |||
735 | --- modules/Ubuntu/Components/Extras/Printers/cups/ppdutils.h 1970-01-01 00:00:00 +0000 | |||
736 | +++ modules/Ubuntu/Components/Extras/Printers/cups/ppdutils.h 2017-04-07 12:51:46 +0000 | |||
737 | @@ -0,0 +1,87 @@ | |||
738 | 1 | /* | ||
739 | 2 | * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2014, 2015 Red Hat, Inc. | ||
740 | 3 | * Copyright (C) 2006 Florian Festi <ffesti@redhat.com> | ||
741 | 4 | * Copyright (C) 2006, 2007, 2008, 2009 Tim Waugh <twaugh@redhat.com> | ||
742 | 5 | * Copyright (C) 2017 Canonical, Ltd. | ||
743 | 6 | * | ||
744 | 7 | * This program is free software; you can redistribute it and/or modify | ||
745 | 8 | * it under the terms of the GNU Lesser General Public License as published by | ||
746 | 9 | * the Free Software Foundation; version 3. | ||
747 | 10 | * | ||
748 | 11 | * This program is distributed in the hope that it will be useful, | ||
749 | 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
750 | 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
751 | 14 | * GNU Lesser General Public License for more details. | ||
752 | 15 | * | ||
753 | 16 | * You should have received a copy of the GNU Lesser General Public License | ||
754 | 17 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
755 | 18 | */ | ||
756 | 19 | |||
757 | 20 | #ifndef USC_PRINTERS_CUPS_PPDUTILS_H | ||
758 | 21 | #define USC_PRINTERS_CUPS_PPDUTILS_H | ||
759 | 22 | |||
760 | 23 | #include "enums.h" | ||
761 | 24 | #include "i18n.h" | ||
762 | 25 | #include "printers_global.h" | ||
763 | 26 | #include "structs.h" | ||
764 | 27 | |||
765 | 28 | #include <cups/ppd.h> | ||
766 | 29 | |||
767 | 30 | #include <QList> | ||
768 | 31 | #include <QMap> | ||
769 | 32 | #include <QPair> | ||
770 | 33 | #include <QRegularExpression> | ||
771 | 34 | #include <QString> | ||
772 | 35 | |||
773 | 36 | struct PRINTERS_DECL_EXPORT PpdUtils | ||
774 | 37 | { | ||
775 | 38 | private: | ||
776 | 39 | static const QList<QPair<QString, QRegularExpression>> manufacturerByModels; | ||
777 | 40 | static const QRegularExpression ignoredSuffixes; | ||
778 | 41 | static const QRegularExpression versionNumbers; | ||
779 | 42 | static const QRegularExpression ignoreSeries; | ||
780 | 43 | static const QMap<QString, QString> hpByModel; | ||
781 | 44 | static QString dedupeMakeModel(const QString &str); | ||
782 | 45 | public: | ||
783 | 46 | |||
784 | 47 | /** | ||
785 | 48 | Split a ppd-make-and-model string into a canonical make and model pair. | ||
786 | 49 | Returns a string pair representing the make and the model. | ||
787 | 50 | */ | ||
788 | 51 | static QPair<QString, QString> splitMakeModel(const QString &makeModel); | ||
789 | 52 | |||
790 | 53 | /** | ||
791 | 54 | This function normalizes manufacturer and model names for comparing. | ||
792 | 55 | The string is turned to lower case and leading and trailing white | ||
793 | 56 | space is removed. After that each sequence of non-alphanumeric | ||
794 | 57 | characters (including white space) is replaced by a single space and | ||
795 | 58 | also at each change between letters and numbers a single space is added. | ||
796 | 59 | This makes the comparison only done by alphanumeric characters and the | ||
797 | 60 | words formed from them. So mostly two strings which sound the same when | ||
798 | 61 | you pronounce them are considered equal. Printer manufacturers do not | ||
799 | 62 | market two models whose names sound the same but differ only by | ||
800 | 63 | upper/lower case, spaces, dashes, ..., but in printer drivers names can | ||
801 | 64 | be easily supplied with these details of the name written in the wrong | ||
802 | 65 | way, especially if the IEEE-1284 device ID of the printer is not known. | ||
803 | 66 | This way we get a very reliable matching of printer model names. | ||
804 | 67 | Examples: | ||
805 | 68 | - Epson PM-A820 -> epson pm a 820 | ||
806 | 69 | - Epson PM A820 -> epson pm a 820 | ||
807 | 70 | - HP PhotoSmart C 8100 -> hp photosmart c 8100 | ||
808 | 71 | - hp Photosmart C8100 -> hp photosmart c 8100 | ||
809 | 72 | */ | ||
810 | 73 | static QString normalize(const QString &str); | ||
811 | 74 | |||
812 | 75 | static PrinterEnum::DuplexMode ppdChoiceToDuplexMode( | ||
813 | 76 | const QString &choice); | ||
814 | 77 | static const QString duplexModeToPpdChoice( | ||
815 | 78 | const PrinterEnum::DuplexMode &mode); | ||
816 | 79 | static ColorModel parsePpdColorModel(const QString &name, | ||
817 | 80 | const QString &text, | ||
818 | 81 | const QString &optionName); | ||
819 | 82 | static PrintQuality parsePpdPrintQuality(const QString &choice, | ||
820 | 83 | const QString &text, | ||
821 | 84 | const QString &optionName); | ||
822 | 85 | }; | ||
823 | 86 | |||
824 | 87 | #endif // USC_PRINTERS_CUPS_PPDUTILS_H | ||
825 | 0 | 88 | ||
826 | === modified file 'modules/Ubuntu/Components/Extras/Printers/models/devicemodel.cpp' | |||
827 | --- modules/Ubuntu/Components/Extras/Printers/models/devicemodel.cpp 2017-03-09 14:34:05 +0000 | |||
828 | +++ modules/Ubuntu/Components/Extras/Printers/models/devicemodel.cpp 2017-04-07 12:51:46 +0000 | |||
829 | @@ -15,9 +15,14 @@ | |||
830 | 15 | */ | 15 | */ |
831 | 16 | 16 | ||
832 | 17 | #include "backend/backend_cups.h" | 17 | #include "backend/backend_cups.h" |
833 | 18 | #include "cups/ppdutils.h" | ||
834 | 18 | #include "models/devicemodel.h" | 19 | #include "models/devicemodel.h" |
835 | 20 | #include "utils.h" | ||
836 | 19 | 21 | ||
837 | 20 | #include <QDebug> | 22 | #include <QDebug> |
838 | 23 | #include <QPair> | ||
839 | 24 | #include <QQmlEngine> | ||
840 | 25 | #include <QSet> | ||
841 | 21 | 26 | ||
842 | 22 | DeviceModel::DeviceModel(PrinterBackend *backend, QObject *parent) | 27 | DeviceModel::DeviceModel(PrinterBackend *backend, QObject *parent) |
843 | 23 | : QAbstractListModel(parent) | 28 | : QAbstractListModel(parent) |
844 | @@ -104,10 +109,35 @@ | |||
845 | 104 | return; | 109 | return; |
846 | 105 | } | 110 | } |
847 | 106 | 111 | ||
849 | 107 | if (!m_devices.contains(device)) { | 112 | // Need to make a copy as this device came from some thread. |
850 | 113 | Device dev(device); | ||
851 | 114 | |||
852 | 115 | // Try to "fix" makeModel as much as possible before inserting it. | ||
853 | 116 | QString canidateMakeModel; | ||
854 | 117 | if (dev.id.isEmpty()) { | ||
855 | 118 | canidateMakeModel = dev.makeModel; | ||
856 | 119 | } else { | ||
857 | 120 | auto idDict = Utils::parseDeviceId(dev.id); | ||
858 | 121 | canidateMakeModel = QString("%1 %2").arg(idDict["MFG"], idDict["MDL"]); | ||
859 | 122 | } | ||
860 | 123 | |||
861 | 124 | // Candidate is unknown, so try to use info as a last resort. | ||
862 | 125 | if (canidateMakeModel.toLower() == QStringLiteral("unknown")) { | ||
863 | 126 | canidateMakeModel = dev.info; | ||
864 | 127 | } | ||
865 | 128 | |||
866 | 129 | auto split = PpdUtils::splitMakeModel(canidateMakeModel); | ||
867 | 130 | |||
868 | 131 | // Check if the pair is non-empty to avoid producing " ". | ||
869 | 132 | if (!split.first.isEmpty() && !split.second.isEmpty()) { | ||
870 | 133 | canidateMakeModel = QString("%1 %2").arg(split.first, split.second); | ||
871 | 134 | } | ||
872 | 135 | dev.makeModel = canidateMakeModel; | ||
873 | 136 | |||
874 | 137 | if (!m_devices.contains(dev)) { | ||
875 | 108 | int i = m_devices.size(); | 138 | int i = m_devices.size(); |
876 | 109 | beginInsertRows(QModelIndex(), i, i); | 139 | beginInsertRows(QModelIndex(), i, i); |
878 | 110 | m_devices.append(device); | 140 | m_devices.append(dev); |
879 | 111 | endInsertRows(); | 141 | endInsertRows(); |
880 | 112 | 142 | ||
881 | 113 | Q_EMIT countChanged(); | 143 | Q_EMIT countChanged(); |
882 | @@ -116,6 +146,7 @@ | |||
883 | 116 | 146 | ||
884 | 117 | bool DeviceModel::deviceWanted(const Device &device) | 147 | bool DeviceModel::deviceWanted(const Device &device) |
885 | 118 | { | 148 | { |
886 | 149 | // We'll drop devices without URIs because they have no worth. | ||
887 | 119 | auto parts = device.uri.split(":", QString::SkipEmptyParts); | 150 | auto parts = device.uri.split(":", QString::SkipEmptyParts); |
888 | 120 | return parts.size() > 1; | 151 | return parts.size() > 1; |
889 | 121 | } | 152 | } |
890 | @@ -147,3 +178,236 @@ | |||
891 | 147 | m_isSearching = false; | 178 | m_isSearching = false; |
892 | 148 | Q_EMIT searchingChanged(); | 179 | Q_EMIT searchingChanged(); |
893 | 149 | } | 180 | } |
894 | 181 | |||
895 | 182 | bool DeviceModel::searching() | ||
896 | 183 | { | ||
897 | 184 | return m_isSearching; | ||
898 | 185 | } | ||
899 | 186 | |||
900 | 187 | DeviceFilter::DeviceFilter(QObject *parent) : QSortFilterProxyModel(parent) | ||
901 | 188 | { | ||
902 | 189 | connect(this, SIGNAL(sourceModelChanged()), SLOT(onSourceModelChanged())); | ||
903 | 190 | } | ||
904 | 191 | |||
905 | 192 | DeviceFilter::~DeviceFilter() | ||
906 | 193 | { | ||
907 | 194 | } | ||
908 | 195 | |||
909 | 196 | void DeviceFilter::onSourceModelChanged() | ||
910 | 197 | { | ||
911 | 198 | connect( | ||
912 | 199 | (DeviceModel*) sourceModel(), SIGNAL(countChanged()), | ||
913 | 200 | this, SIGNAL(countChanged()) | ||
914 | 201 | ); | ||
915 | 202 | } | ||
916 | 203 | |||
917 | 204 | void DeviceFilter::onSourceModelCountChanged() | ||
918 | 205 | { | ||
919 | 206 | Q_EMIT countChanged(); | ||
920 | 207 | } | ||
921 | 208 | |||
922 | 209 | int DeviceFilter::count() const | ||
923 | 210 | { | ||
924 | 211 | return rowCount(); | ||
925 | 212 | } | ||
926 | 213 | |||
927 | 214 | void DeviceFilter::filterOnNormalizedMakeModel( | ||
928 | 215 | const QString &normalizedMakeModel) | ||
929 | 216 | { | ||
930 | 217 | m_normalizedMakeModel = normalizedMakeModel; | ||
931 | 218 | m_normalizedMakeModelFilterEnabled = true; | ||
932 | 219 | invalidate(); | ||
933 | 220 | } | ||
934 | 221 | |||
935 | 222 | void DeviceFilter::filterOnUri(const QString &uri) | ||
936 | 223 | { | ||
937 | 224 | m_uri = uri; | ||
938 | 225 | m_uriFilterEnabled = true; | ||
939 | 226 | invalidate(); | ||
940 | 227 | } | ||
941 | 228 | |||
942 | 229 | bool DeviceFilter::filterAcceptsRow(int sourceRow, | ||
943 | 230 | const QModelIndex &sourceParent) const | ||
944 | 231 | { | ||
945 | 232 | bool accepts = true; | ||
946 | 233 | QModelIndex childIndex = sourceModel()->index(sourceRow, 0, sourceParent); | ||
947 | 234 | |||
948 | 235 | if (accepts && m_normalizedMakeModelFilterEnabled) { | ||
949 | 236 | QString makeModel = childIndex.model()->data( | ||
950 | 237 | childIndex, DeviceModel::MakeModelRole).toString(); | ||
951 | 238 | accepts = m_normalizedMakeModel == PpdUtils::normalize(makeModel); | ||
952 | 239 | } | ||
953 | 240 | |||
954 | 241 | if (accepts && m_uriFilterEnabled) { | ||
955 | 242 | QString uri = childIndex.model()->data( | ||
956 | 243 | childIndex, DeviceModel::UriRole).toString(); | ||
957 | 244 | accepts = m_uri == uri; | ||
958 | 245 | } | ||
959 | 246 | |||
960 | 247 | return accepts; | ||
961 | 248 | } | ||
962 | 249 | |||
963 | 250 | QVariantMap DeviceFilter::get(const int row) const | ||
964 | 251 | { | ||
965 | 252 | QHashIterator<int, QByteArray> iterator(roleNames()); | ||
966 | 253 | QVariantMap result; | ||
967 | 254 | QModelIndex modelIndex = index(row, 0); | ||
968 | 255 | |||
969 | 256 | while (iterator.hasNext()) { | ||
970 | 257 | iterator.next(); | ||
971 | 258 | result[iterator.value()] = modelIndex.data(iterator.key()); | ||
972 | 259 | } | ||
973 | 260 | |||
974 | 261 | return result; | ||
975 | 262 | } | ||
976 | 263 | |||
977 | 264 | ConsolidatedDeviceModel::ConsolidatedDeviceModel(DeviceModel *deviceModel, | ||
978 | 265 | QObject *parent) | ||
979 | 266 | : QAbstractListModel(parent) | ||
980 | 267 | , m_deviceModel(deviceModel) | ||
981 | 268 | { | ||
982 | 269 | connect(m_deviceModel, SIGNAL(searchingChanged()), | ||
983 | 270 | this, SLOT(deviceModelSearchChanged())); | ||
984 | 271 | } | ||
985 | 272 | |||
986 | 273 | ConsolidatedDeviceModel::~ConsolidatedDeviceModel() | ||
987 | 274 | { | ||
988 | 275 | } | ||
989 | 276 | |||
990 | 277 | int ConsolidatedDeviceModel::rowCount(const QModelIndex &parent) const | ||
991 | 278 | { | ||
992 | 279 | Q_UNUSED(parent); | ||
993 | 280 | return m_consolidatedDevices.size(); | ||
994 | 281 | } | ||
995 | 282 | |||
996 | 283 | int ConsolidatedDeviceModel::count() const | ||
997 | 284 | { | ||
998 | 285 | return rowCount(); | ||
999 | 286 | } | ||
1000 | 287 | |||
1001 | 288 | bool ConsolidatedDeviceModel::searching() | ||
1002 | 289 | { | ||
1003 | 290 | return m_deviceModel->searching(); | ||
1004 | 291 | } | ||
1005 | 292 | |||
1006 | 293 | bool ConsolidatedDeviceModel::makeModelIsConsolidateable( | ||
1007 | 294 | const QString &makeModel) const | ||
1008 | 295 | { | ||
1009 | 296 | return !makeModel.isEmpty() && makeModel.toLower() != QStringLiteral("unknown"); | ||
1010 | 297 | } | ||
1011 | 298 | |||
1012 | 299 | void ConsolidatedDeviceModel::deviceModelSearchChanged() | ||
1013 | 300 | { | ||
1014 | 301 | int oldCount = rowCount(); | ||
1015 | 302 | |||
1016 | 303 | beginResetModel(); | ||
1017 | 304 | m_consolidatedDevices.clear(); | ||
1018 | 305 | |||
1019 | 306 | /* Device model has finished searching, so let's sync our model with it. | ||
1020 | 307 | Note that if the deviceModel is searching, we simply dump our model. */ | ||
1021 | 308 | if (!m_deviceModel->searching()) { | ||
1022 | 309 | // A set of normalized make-models. | ||
1023 | 310 | QSet<QString> normalizedMakeModels; | ||
1024 | 311 | |||
1025 | 312 | // A list of devices that cannot be consolidated. | ||
1026 | 313 | QList<QString> unconsolidatableDeviceUris; | ||
1027 | 314 | |||
1028 | 315 | // Used to preserve makeModels after it has been normalized. | ||
1029 | 316 | QMap<QString, QString> normalizedToMakeModel; | ||
1030 | 317 | for (int i = 0; i < m_deviceModel->rowCount(); i++) { | ||
1031 | 318 | QModelIndex idx = m_deviceModel->index(i, 0); | ||
1032 | 319 | |||
1033 | 320 | QString makeModel = m_deviceModel->data( | ||
1034 | 321 | idx, DeviceModel::MakeModelRole | ||
1035 | 322 | ).toString(); | ||
1036 | 323 | |||
1037 | 324 | QString uri = m_deviceModel->data( | ||
1038 | 325 | idx, DeviceModel::UriRole | ||
1039 | 326 | ).toString(); | ||
1040 | 327 | |||
1041 | 328 | if (makeModelIsConsolidateable(makeModel)) { | ||
1042 | 329 | QString normalized = PpdUtils::normalize(makeModel); | ||
1043 | 330 | normalizedMakeModels << normalized; | ||
1044 | 331 | normalizedToMakeModel[normalized] = makeModel; | ||
1045 | 332 | |||
1046 | 333 | } else { | ||
1047 | 334 | /* We can't consolidate this device, but we keep the URI to | ||
1048 | 335 | create a singular consolidated device. */ | ||
1049 | 336 | unconsolidatableDeviceUris << uri; | ||
1050 | 337 | } | ||
1051 | 338 | } | ||
1052 | 339 | |||
1053 | 340 | /* For every make-and-model, create a proxy model, and install a filter | ||
1054 | 341 | on it. Then do the same for those consolidated under URI (i.e. not | ||
1055 | 342 | consolidated). | ||
1056 | 343 | Note that a parent is set on each filter so that it is cleaned up when | ||
1057 | 344 | we the consolidated model is destroyed. */ | ||
1058 | 345 | Q_FOREACH(auto mm, normalizedMakeModels) { | ||
1059 | 346 | auto filter = new DeviceFilter(this); | ||
1060 | 347 | filter->setSourceModel(m_deviceModel); | ||
1061 | 348 | filter->filterOnNormalizedMakeModel(mm); | ||
1062 | 349 | |||
1063 | 350 | QPair<QString, DeviceFilter*> p(mm, filter); | ||
1064 | 351 | m_consolidatedDevices << p; | ||
1065 | 352 | } | ||
1066 | 353 | Q_FOREACH(auto uri, unconsolidatableDeviceUris) { | ||
1067 | 354 | auto filter = new DeviceFilter(this); | ||
1068 | 355 | filter->setSourceModel(m_deviceModel); | ||
1069 | 356 | filter->filterOnUri(uri); | ||
1070 | 357 | |||
1071 | 358 | QPair<QString, DeviceFilter*> p(uri, filter); | ||
1072 | 359 | m_consolidatedDevices << p; | ||
1073 | 360 | } | ||
1074 | 361 | } | ||
1075 | 362 | |||
1076 | 363 | endResetModel(); | ||
1077 | 364 | |||
1078 | 365 | if (oldCount != rowCount()) { | ||
1079 | 366 | Q_EMIT countChanged(); | ||
1080 | 367 | } | ||
1081 | 368 | Q_EMIT searchingChanged(); | ||
1082 | 369 | } | ||
1083 | 370 | |||
1084 | 371 | QVariant ConsolidatedDeviceModel::data(const QModelIndex &index, int role) const | ||
1085 | 372 | { | ||
1086 | 373 | QVariant ret; | ||
1087 | 374 | |||
1088 | 375 | if ((0 <= index.row()) && (index.row() < m_consolidatedDevices.size())) { | ||
1089 | 376 | |||
1090 | 377 | auto consolidated = m_consolidatedDevices[index.row()]; | ||
1091 | 378 | auto mm = consolidated.first; | ||
1092 | 379 | auto filter = consolidated.second; | ||
1093 | 380 | |||
1094 | 381 | switch (role) { | ||
1095 | 382 | case Qt::DisplayRole: | ||
1096 | 383 | case ConsolidatedNameRole: | ||
1097 | 384 | ret = filter->data( | ||
1098 | 385 | filter->index(0, 0), DeviceModel::Roles::MakeModelRole | ||
1099 | 386 | ).toString(); | ||
1100 | 387 | break; | ||
1101 | 388 | case DevicesRole: { | ||
1102 | 389 | ret = QVariant::fromValue(filter); | ||
1103 | 390 | break; | ||
1104 | 391 | case ConnectionsCountRole: | ||
1105 | 392 | ret = filter->count(); | ||
1106 | 393 | break; | ||
1107 | 394 | } | ||
1108 | 395 | } | ||
1109 | 396 | } | ||
1110 | 397 | |||
1111 | 398 | return ret; | ||
1112 | 399 | } | ||
1113 | 400 | |||
1114 | 401 | QHash<int, QByteArray> ConsolidatedDeviceModel::roleNames() const | ||
1115 | 402 | { | ||
1116 | 403 | static QHash<int,QByteArray> names; | ||
1117 | 404 | |||
1118 | 405 | if (Q_UNLIKELY(names.empty())) { | ||
1119 | 406 | names[Qt::DisplayRole] = "displayName"; | ||
1120 | 407 | names[ConsolidatedNameRole] = "consolidatedName"; | ||
1121 | 408 | names[DevicesRole] = "devices"; | ||
1122 | 409 | names[ConnectionsCountRole] = "connectionsCount"; | ||
1123 | 410 | } | ||
1124 | 411 | |||
1125 | 412 | return names; | ||
1126 | 413 | } | ||
1127 | 150 | 414 | ||
1128 | === modified file 'modules/Ubuntu/Components/Extras/Printers/models/devicemodel.h' | |||
1129 | --- modules/Ubuntu/Components/Extras/Printers/models/devicemodel.h 2017-03-09 14:34:05 +0000 | |||
1130 | +++ modules/Ubuntu/Components/Extras/Printers/models/devicemodel.h 2017-04-07 12:51:46 +0000 | |||
1131 | @@ -31,7 +31,7 @@ | |||
1132 | 31 | { | 31 | { |
1133 | 32 | Q_OBJECT | 32 | Q_OBJECT |
1134 | 33 | Q_PROPERTY(int count READ count NOTIFY countChanged) | 33 | Q_PROPERTY(int count READ count NOTIFY countChanged) |
1136 | 34 | Q_PROPERTY(bool searching MEMBER m_isSearching NOTIFY searchingChanged) | 34 | Q_PROPERTY(bool searching READ searching NOTIFY searchingChanged) |
1137 | 35 | public: | 35 | public: |
1138 | 36 | explicit DeviceModel(PrinterBackend *backend, QObject *parent = Q_NULLPTR); | 36 | explicit DeviceModel(PrinterBackend *backend, QObject *parent = Q_NULLPTR); |
1139 | 37 | ~DeviceModel(); | 37 | ~DeviceModel(); |
1140 | @@ -56,6 +56,7 @@ | |||
1141 | 56 | int count() const; | 56 | int count() const; |
1142 | 57 | void load(); | 57 | void load(); |
1143 | 58 | void clear(); | 58 | void clear(); |
1144 | 59 | bool searching(); | ||
1145 | 59 | 60 | ||
1146 | 60 | private Q_SLOTS: | 61 | private Q_SLOTS: |
1147 | 61 | void deviceLoaded(const Device &device); | 62 | void deviceLoaded(const Device &device); |
1148 | @@ -75,4 +76,84 @@ | |||
1149 | 75 | bool m_isSearching; | 76 | bool m_isSearching; |
1150 | 76 | }; | 77 | }; |
1151 | 77 | 78 | ||
1152 | 79 | |||
1153 | 80 | class PRINTERS_DECL_EXPORT DeviceFilter : public QSortFilterProxyModel | ||
1154 | 81 | { | ||
1155 | 82 | Q_OBJECT | ||
1156 | 83 | Q_PROPERTY(int count READ count NOTIFY countChanged) | ||
1157 | 84 | public: | ||
1158 | 85 | explicit DeviceFilter(QObject *parent = Q_NULLPTR); | ||
1159 | 86 | ~DeviceFilter(); | ||
1160 | 87 | |||
1161 | 88 | void filterOnNormalizedMakeModel(const QString &normalizedMakeModel); | ||
1162 | 89 | void filterOnUri(const QString &uri); | ||
1163 | 90 | int count() const; | ||
1164 | 91 | Q_INVOKABLE QVariantMap get(const int row) const; | ||
1165 | 92 | |||
1166 | 93 | protected: | ||
1167 | 94 | virtual bool filterAcceptsRow( | ||
1168 | 95 | int sourceRow, const QModelIndex &sourceParent) const override; | ||
1169 | 96 | |||
1170 | 97 | Q_SIGNALS: | ||
1171 | 98 | void countChanged(); | ||
1172 | 99 | |||
1173 | 100 | private Q_SLOTS: | ||
1174 | 101 | void onSourceModelChanged(); | ||
1175 | 102 | void onSourceModelCountChanged(); | ||
1176 | 103 | |||
1177 | 104 | private: | ||
1178 | 105 | QString m_normalizedMakeModel = QString::null; | ||
1179 | 106 | bool m_normalizedMakeModelFilterEnabled = false; | ||
1180 | 107 | |||
1181 | 108 | QString m_uri = QString::null; | ||
1182 | 109 | bool m_uriFilterEnabled = false; | ||
1183 | 110 | }; | ||
1184 | 111 | |||
1185 | 112 | /* The ConsolidatedDeviceModel is a model of ConsolidatedDevices. A | ||
1186 | 113 | ConsolidatedDevice is a set of Devices that share a printer name (assuming | ||
1187 | 114 | unique printer names). | ||
1188 | 115 | |||
1189 | 116 | TODO: The DeviceModel should implement the consolidation by use of e.g. a | ||
1190 | 117 | table model, or tree model. This model can then be deleted. */ | ||
1191 | 118 | class PRINTERS_DECL_EXPORT ConsolidatedDeviceModel : public QAbstractListModel | ||
1192 | 119 | { | ||
1193 | 120 | Q_OBJECT | ||
1194 | 121 | Q_PROPERTY(int count READ count NOTIFY countChanged) | ||
1195 | 122 | Q_PROPERTY(bool searching READ searching NOTIFY searchingChanged) | ||
1196 | 123 | public: | ||
1197 | 124 | explicit ConsolidatedDeviceModel(DeviceModel *deviceModel, | ||
1198 | 125 | QObject *parent = Q_NULLPTR); | ||
1199 | 126 | ~ConsolidatedDeviceModel(); | ||
1200 | 127 | |||
1201 | 128 | enum Roles | ||
1202 | 129 | { | ||
1203 | 130 | // Qt::DisplayRole holds consolidated device name | ||
1204 | 131 | DevicesRole = Qt::UserRole, | ||
1205 | 132 | ConsolidatedNameRole, | ||
1206 | 133 | ConnectionsCountRole, | ||
1207 | 134 | LastRole = ConnectionsCountRole, | ||
1208 | 135 | }; | ||
1209 | 136 | |||
1210 | 137 | virtual int rowCount(const QModelIndex &parent = QModelIndex()) const override; | ||
1211 | 138 | virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; | ||
1212 | 139 | virtual QHash<int, QByteArray> roleNames() const override; | ||
1213 | 140 | |||
1214 | 141 | int count() const; | ||
1215 | 142 | void load(); | ||
1216 | 143 | bool searching(); | ||
1217 | 144 | bool makeModelIsConsolidateable(const QString &makeModel) const; | ||
1218 | 145 | |||
1219 | 146 | private Q_SLOTS: | ||
1220 | 147 | void deviceModelSearchChanged(); | ||
1221 | 148 | |||
1222 | 149 | Q_SIGNALS: | ||
1223 | 150 | void countChanged(); | ||
1224 | 151 | void searchingChanged(); | ||
1225 | 152 | |||
1226 | 153 | private: | ||
1227 | 154 | DeviceModel *m_deviceModel; | ||
1228 | 155 | |||
1229 | 156 | QList<QPair<QString, DeviceFilter*>> m_consolidatedDevices; | ||
1230 | 157 | }; | ||
1231 | 158 | |||
1232 | 78 | #endif // USC_PRINTER_DEVICEMODEL_H | 159 | #endif // USC_PRINTER_DEVICEMODEL_H |
1233 | 79 | 160 | ||
1234 | === modified file 'modules/Ubuntu/Components/Extras/Printers/models/drivermodel.cpp' | |||
1235 | --- modules/Ubuntu/Components/Extras/Printers/models/drivermodel.cpp 2017-02-21 10:46:29 +0000 | |||
1236 | +++ modules/Ubuntu/Components/Extras/Printers/models/drivermodel.cpp 2017-04-07 12:51:46 +0000 | |||
1237 | @@ -159,6 +159,20 @@ | |||
1238 | 159 | m_watcher.cancel(); | 159 | m_watcher.cancel(); |
1239 | 160 | } | 160 | } |
1240 | 161 | 161 | ||
1241 | 162 | QVariantMap DriverModel::get(const int row) const | ||
1242 | 163 | { | ||
1243 | 164 | QHashIterator<int, QByteArray> iterator(roleNames()); | ||
1244 | 165 | QVariantMap result; | ||
1245 | 166 | QModelIndex modelIndex = index(row, 0); | ||
1246 | 167 | |||
1247 | 168 | while (iterator.hasNext()) { | ||
1248 | 169 | iterator.next(); | ||
1249 | 170 | result[iterator.value()] = modelIndex.data(iterator.key()); | ||
1250 | 171 | } | ||
1251 | 172 | |||
1252 | 173 | return result; | ||
1253 | 174 | } | ||
1254 | 175 | |||
1255 | 162 | void DriverModel::printerDriversLoaded(const QList<PrinterDriver> &drivers) | 176 | void DriverModel::printerDriversLoaded(const QList<PrinterDriver> &drivers) |
1256 | 163 | { | 177 | { |
1257 | 164 | m_originalDrivers = drivers; | 178 | m_originalDrivers = drivers; |
1258 | 165 | 179 | ||
1259 | === modified file 'modules/Ubuntu/Components/Extras/Printers/models/drivermodel.h' | |||
1260 | --- modules/Ubuntu/Components/Extras/Printers/models/drivermodel.h 2017-02-21 10:46:29 +0000 | |||
1261 | +++ modules/Ubuntu/Components/Extras/Printers/models/drivermodel.h 2017-04-07 12:51:46 +0000 | |||
1262 | @@ -60,6 +60,7 @@ | |||
1263 | 60 | 60 | ||
1264 | 61 | // Cancel loading of the model. | 61 | // Cancel loading of the model. |
1265 | 62 | void cancel(); | 62 | void cancel(); |
1266 | 63 | QVariantMap get(const int row) const; | ||
1267 | 63 | 64 | ||
1268 | 64 | private Q_SLOTS: | 65 | private Q_SLOTS: |
1269 | 65 | void printerDriversLoaded(const QList<PrinterDriver> &drivers); | 66 | void printerDriversLoaded(const QList<PrinterDriver> &drivers); |
1270 | 66 | 67 | ||
1271 | === modified file 'modules/Ubuntu/Components/Extras/Printers/printer/printer.cpp' | |||
1272 | --- modules/Ubuntu/Components/Extras/Printers/printer/printer.cpp 2017-03-31 10:09:01 +0000 | |||
1273 | +++ modules/Ubuntu/Components/Extras/Printers/printer/printer.cpp 2017-04-07 12:51:46 +0000 | |||
1274 | @@ -14,6 +14,7 @@ | |||
1275 | 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/>. |
1276 | 15 | */ | 15 | */ |
1277 | 16 | 16 | ||
1278 | 17 | #include "cups/ppdutils.h" | ||
1279 | 17 | #include "i18n.h" | 18 | #include "i18n.h" |
1280 | 18 | #include "utils.h" | 19 | #include "utils.h" |
1281 | 19 | 20 | ||
1282 | @@ -291,7 +292,7 @@ | |||
1283 | 291 | return; | 292 | return; |
1284 | 292 | } | 293 | } |
1285 | 293 | 294 | ||
1287 | 294 | QStringList vals({Utils::duplexModeToPpdChoice(duplexMode)}); | 295 | QStringList vals({PpdUtils::duplexModeToPpdChoice(duplexMode)}); |
1288 | 295 | m_backend->printerAddOption(name(), "Duplex", vals); | 296 | m_backend->printerAddOption(name(), "Duplex", vals); |
1289 | 296 | } | 297 | } |
1290 | 297 | 298 | ||
1291 | 298 | 299 | ||
1292 | === modified file 'modules/Ubuntu/Components/Extras/Printers/printer/printerjob.cpp' | |||
1293 | --- modules/Ubuntu/Components/Extras/Printers/printer/printerjob.cpp 2017-04-03 11:56:59 +0000 | |||
1294 | +++ modules/Ubuntu/Components/Extras/Printers/printer/printerjob.cpp 2017-04-07 12:51:46 +0000 | |||
1295 | @@ -18,9 +18,9 @@ | |||
1296 | 18 | #include <QUrl> | 18 | #include <QUrl> |
1297 | 19 | 19 | ||
1298 | 20 | #include "backend/backend_cups.h" | 20 | #include "backend/backend_cups.h" |
1299 | 21 | #include "cups/ppdutils.h" | ||
1300 | 21 | #include "models/printermodel.h" | 22 | #include "models/printermodel.h" |
1301 | 22 | #include "printer/printerjob.h" | 23 | #include "printer/printerjob.h" |
1302 | 23 | #include "utils.h" | ||
1303 | 24 | 24 | ||
1304 | 25 | PrinterJob::PrinterJob(QString printerName, | 25 | PrinterJob::PrinterJob(QString printerName, |
1305 | 26 | PrinterBackend *backend, | 26 | PrinterBackend *backend, |
1306 | @@ -166,7 +166,7 @@ | |||
1307 | 166 | 166 | ||
1308 | 167 | // No duplexMode will result in PrinterJob using defaultDuplexMode | 167 | // No duplexMode will result in PrinterJob using defaultDuplexMode |
1309 | 168 | QString duplex = attributes.value("Duplex").toString(); | 168 | QString duplex = attributes.value("Duplex").toString(); |
1311 | 169 | PrinterEnum::DuplexMode duplexMode = Utils::ppdChoiceToDuplexMode(duplex); | 169 | PrinterEnum::DuplexMode duplexMode = PpdUtils::ppdChoiceToDuplexMode(duplex); |
1312 | 170 | for (int i=0; i < m_printer->supportedDuplexModes().length(); i++) { | 170 | for (int i=0; i < m_printer->supportedDuplexModes().length(); i++) { |
1313 | 171 | if (m_printer->supportedDuplexModes().at(i) == duplexMode) { | 171 | if (m_printer->supportedDuplexModes().at(i) == duplexMode) { |
1314 | 172 | setDuplexMode(i); | 172 | setDuplexMode(i); |
1315 | 173 | 173 | ||
1316 | === modified file 'modules/Ubuntu/Components/Extras/Printers/printers/printers.cpp' | |||
1317 | --- modules/Ubuntu/Components/Extras/Printers/printers/printers.cpp 2017-04-04 10:57:41 +0000 | |||
1318 | +++ modules/Ubuntu/Components/Extras/Printers/printers/printers.cpp 2017-04-07 12:51:46 +0000 | |||
1319 | @@ -35,6 +35,7 @@ | |||
1320 | 35 | : QObject(parent) | 35 | : QObject(parent) |
1321 | 36 | , m_backend(backend) | 36 | , m_backend(backend) |
1322 | 37 | , m_devices(backend) | 37 | , m_devices(backend) |
1323 | 38 | , m_consolidatedDevices(&m_devices) | ||
1324 | 38 | , m_drivers(backend) | 39 | , m_drivers(backend) |
1325 | 39 | , m_model(backend) | 40 | , m_model(backend) |
1326 | 40 | , m_jobs(backend) | 41 | , m_jobs(backend) |
1327 | @@ -259,6 +260,14 @@ | |||
1328 | 259 | m_devices.load(); | 260 | m_devices.load(); |
1329 | 260 | } | 261 | } |
1330 | 261 | 262 | ||
1331 | 263 | QAbstractItemModel* Printers::consolidatedDevices() | ||
1332 | 264 | { | ||
1333 | 265 | m_devices.load(); | ||
1334 | 266 | auto ret = &m_consolidatedDevices; | ||
1335 | 267 | QQmlEngine::setObjectOwnership(ret, QQmlEngine::CppOwnership); | ||
1336 | 268 | return ret; | ||
1337 | 269 | } | ||
1338 | 270 | |||
1339 | 262 | bool Printers::addPrinter(const QString &name, const QString &ppd, | 271 | bool Printers::addPrinter(const QString &name, const QString &ppd, |
1340 | 263 | const QString &device, const QString &description, | 272 | const QString &device, const QString &description, |
1341 | 264 | const QString &location) | 273 | const QString &location) |
1342 | 265 | 274 | ||
1343 | === modified file 'modules/Ubuntu/Components/Extras/Printers/printers/printers.h' | |||
1344 | --- modules/Ubuntu/Components/Extras/Printers/printers/printers.h 2017-04-04 10:57:41 +0000 | |||
1345 | +++ modules/Ubuntu/Components/Extras/Printers/printers/printers.h 2017-04-07 12:51:46 +0000 | |||
1346 | @@ -42,6 +42,7 @@ | |||
1347 | 42 | Q_PROPERTY(QAbstractItemModel* printJobs READ printJobs CONSTANT) | 42 | Q_PROPERTY(QAbstractItemModel* printJobs READ printJobs CONSTANT) |
1348 | 43 | Q_PROPERTY(QAbstractItemModel* drivers READ drivers CONSTANT) | 43 | Q_PROPERTY(QAbstractItemModel* drivers READ drivers CONSTANT) |
1349 | 44 | Q_PROPERTY(QAbstractItemModel* devices READ devices CONSTANT) | 44 | Q_PROPERTY(QAbstractItemModel* devices READ devices CONSTANT) |
1350 | 45 | Q_PROPERTY(QAbstractItemModel* consolidatedDevices READ consolidatedDevices CONSTANT) | ||
1351 | 45 | Q_PROPERTY(QString driverFilter READ driverFilter WRITE setDriverFilter NOTIFY driverFilterChanged) | 46 | Q_PROPERTY(QString driverFilter READ driverFilter WRITE setDriverFilter NOTIFY driverFilterChanged) |
1352 | 46 | Q_PROPERTY(QString defaultPrinterName READ defaultPrinterName WRITE setDefaultPrinterName NOTIFY defaultPrinterNameChanged) | 47 | Q_PROPERTY(QString defaultPrinterName READ defaultPrinterName WRITE setDefaultPrinterName NOTIFY defaultPrinterNameChanged) |
1353 | 47 | Q_PROPERTY(QString lastMessage READ lastMessage CONSTANT) | 48 | Q_PROPERTY(QString lastMessage READ lastMessage CONSTANT) |
1354 | @@ -60,6 +61,7 @@ | |||
1355 | 60 | QAbstractItemModel* printJobs(); | 61 | QAbstractItemModel* printJobs(); |
1356 | 61 | QAbstractItemModel* drivers(); | 62 | QAbstractItemModel* drivers(); |
1357 | 62 | QAbstractItemModel* devices(); | 63 | QAbstractItemModel* devices(); |
1358 | 64 | QAbstractItemModel* consolidatedDevices(); | ||
1359 | 63 | QString driverFilter() const; | 65 | QString driverFilter() const; |
1360 | 64 | QString defaultPrinterName() const; | 66 | QString defaultPrinterName() const; |
1361 | 65 | QString lastMessage() const; | 67 | QString lastMessage() const; |
1362 | @@ -111,6 +113,7 @@ | |||
1363 | 111 | void provisionPrinter(const QString &name, const bool setAsDefault); | 113 | void provisionPrinter(const QString &name, const bool setAsDefault); |
1364 | 112 | PrinterBackend *m_backend; | 114 | PrinterBackend *m_backend; |
1365 | 113 | DeviceModel m_devices; | 115 | DeviceModel m_devices; |
1366 | 116 | ConsolidatedDeviceModel m_consolidatedDevices; | ||
1367 | 114 | DriverModel m_drivers; | 117 | DriverModel m_drivers; |
1368 | 115 | PrinterModel m_model; | 118 | PrinterModel m_model; |
1369 | 116 | JobModel m_jobs; | 119 | JobModel m_jobs; |
1370 | 117 | 120 | ||
1371 | === modified file 'modules/Ubuntu/Components/Extras/Printers/structs.h' | |||
1372 | --- modules/Ubuntu/Components/Extras/Printers/structs.h 2017-04-03 11:43:30 +0000 | |||
1373 | +++ modules/Ubuntu/Components/Extras/Printers/structs.h 2017-04-07 12:51:46 +0000 | |||
1374 | @@ -21,6 +21,7 @@ | |||
1375 | 21 | #include "i18n.h" | 21 | #include "i18n.h" |
1376 | 22 | 22 | ||
1377 | 23 | #include <QtCore/QMap> | 23 | #include <QtCore/QMap> |
1378 | 24 | #include <QtCore/QUrl> | ||
1379 | 24 | #include <QDebug> | 25 | #include <QDebug> |
1380 | 25 | #include <QMetaType> | 26 | #include <QMetaType> |
1381 | 26 | 27 | ||
1382 | @@ -95,7 +96,18 @@ | |||
1383 | 95 | QString makeModel; | 96 | QString makeModel; |
1384 | 96 | QString uri; | 97 | QString uri; |
1385 | 97 | QString location; | 98 | QString location; |
1387 | 98 | PrinterEnum::DeviceType type() | 99 | |
1388 | 100 | Device() {} | ||
1389 | 101 | Device(const Device &other) { | ||
1390 | 102 | this->cls = QString::fromStdString(other.cls.toStdString()); | ||
1391 | 103 | this->id = QString::fromStdString(other.id.toStdString()); | ||
1392 | 104 | this->info = QString::fromStdString(other.info.toStdString()); | ||
1393 | 105 | this->makeModel = QString::fromStdString(other.makeModel.toStdString()); | ||
1394 | 106 | this->uri = QString::fromStdString(other.uri.toStdString()); | ||
1395 | 107 | this->location = QString::fromStdString(other.location.toStdString()); | ||
1396 | 108 | } | ||
1397 | 109 | |||
1398 | 110 | PrinterEnum::DeviceType type() const | ||
1399 | 99 | { | 111 | { |
1400 | 100 | auto parts = uri.split(":", QString::SkipEmptyParts); | 112 | auto parts = uri.split(":", QString::SkipEmptyParts); |
1401 | 101 | QString scheme = parts.size() > 0 ? parts[0] : QStringLiteral(""); | 113 | QString scheme = parts.size() > 0 ? parts[0] : QStringLiteral(""); |
1402 | @@ -127,34 +139,29 @@ | |||
1403 | 127 | return PrinterEnum::DeviceType::UnknownType; | 139 | return PrinterEnum::DeviceType::UnknownType; |
1404 | 128 | } | 140 | } |
1405 | 129 | 141 | ||
1415 | 130 | QString toString() const { | 142 | QString toString() const |
1416 | 131 | /* 1. Split the id, which is of format "KEY:VAL; … KEYN:VALN;" into | 143 | { |
1417 | 132 | ["KEY:VAL", …, "KEYN:VALN"] | 144 | return makeModel; |
1418 | 133 | 2. Split each pair into | 145 | } |
1410 | 134 | ["KEY", "VAL"] … ["KEYN", "VALN"]*/ | ||
1411 | 135 | QMap<QString, QString> idMap; | ||
1412 | 136 | auto pairs = id.split(";"); | ||
1413 | 137 | Q_FOREACH(const QString &pair, pairs) { | ||
1414 | 138 | auto keyValue = pair.split(":"); | ||
1419 | 139 | 146 | ||
1426 | 140 | /* Sometimes key,val pairs are not terminated by ";". We just | 147 | QString host() const |
1427 | 141 | use the first value in that case. E.g.: | 148 | { |
1428 | 142 | "MFG:HP MDL:Laserfjert;" | 149 | auto ret = QString(); |
1429 | 143 | Will give "HP" as MFG, and "" as MDL. */ | 150 | switch (type()) { |
1430 | 144 | if (keyValue.size() >= 2) { | 151 | case PrinterEnum::DeviceType::SocketType: |
1431 | 145 | idMap[keyValue[0]] = keyValue[1]; | 152 | case PrinterEnum::DeviceType::LPDType: |
1432 | 153 | case PrinterEnum::DeviceType::IppSType: | ||
1433 | 154 | case PrinterEnum::DeviceType::Ipp14Type: | ||
1434 | 155 | case PrinterEnum::DeviceType::IppType: | ||
1435 | 156 | { | ||
1436 | 157 | auto url = QUrl(uri); | ||
1437 | 158 | ret = url.host(); | ||
1438 | 146 | } | 159 | } |
1450 | 147 | } | 160 | break; |
1451 | 148 | auto mfg = idMap.value("MFG", ""); | 161 | default: |
1452 | 149 | auto mdl = idMap.value("MDL", ""); | 162 | break; |
1453 | 150 | 163 | } | |
1454 | 151 | /* If the MDL field contains CMD, somebody forgot to terminate, and we | 164 | return ret; |
1444 | 152 | remove it. */ | ||
1445 | 153 | if (mdl.contains("CMD")) { | ||
1446 | 154 | mdl = mdl.split("CMD")[0]; | ||
1447 | 155 | } | ||
1448 | 156 | |||
1449 | 157 | return QString("%1 %2").arg(mfg).arg(mdl); | ||
1455 | 158 | } | 165 | } |
1456 | 159 | 166 | ||
1457 | 160 | bool operator==(const Device &other) | 167 | bool operator==(const Device &other) |
1458 | @@ -165,8 +172,6 @@ | |||
1459 | 165 | } | 172 | } |
1460 | 166 | }; | 173 | }; |
1461 | 167 | 174 | ||
1462 | 168 | |||
1463 | 169 | |||
1464 | 170 | Q_DECLARE_TYPEINFO(ColorModel, Q_PRIMITIVE_TYPE); | 175 | Q_DECLARE_TYPEINFO(ColorModel, Q_PRIMITIVE_TYPE); |
1465 | 171 | Q_DECLARE_METATYPE(ColorModel) | 176 | Q_DECLARE_METATYPE(ColorModel) |
1466 | 172 | 177 | ||
1467 | 173 | 178 | ||
1468 | === modified file 'modules/Ubuntu/Components/Extras/Printers/utils.h' | |||
1469 | --- modules/Ubuntu/Components/Extras/Printers/utils.h 2017-02-21 10:46:29 +0000 | |||
1470 | +++ modules/Ubuntu/Components/Extras/Printers/utils.h 2017-04-07 12:51:46 +0000 | |||
1471 | @@ -19,39 +19,14 @@ | |||
1472 | 19 | 19 | ||
1473 | 20 | #include "enums.h" | 20 | #include "enums.h" |
1474 | 21 | #include "i18n.h" | 21 | #include "i18n.h" |
1479 | 22 | #include "structs.h" | 22 | |
1480 | 23 | 23 | #include <QMap> | |
1481 | 24 | #include <cups/ppd.h> | 24 | #include <QPrinter> |
1478 | 25 | |||
1482 | 26 | #include <QString> | 25 | #include <QString> |
1483 | 27 | #include <QPrinter> | ||
1484 | 28 | 26 | ||
1485 | 29 | class Utils | 27 | class Utils |
1486 | 30 | { | 28 | { |
1487 | 31 | public: | 29 | public: |
1488 | 32 | static PrinterEnum::DuplexMode ppdChoiceToDuplexMode(const QString &choice) | ||
1489 | 33 | { | ||
1490 | 34 | if (choice == "DuplexTumble") | ||
1491 | 35 | return PrinterEnum::DuplexMode::DuplexShortSide; | ||
1492 | 36 | else if (choice == "DuplexNoTumble") | ||
1493 | 37 | return PrinterEnum::DuplexMode::DuplexLongSide; | ||
1494 | 38 | else | ||
1495 | 39 | return PrinterEnum::DuplexMode::DuplexNone; | ||
1496 | 40 | } | ||
1497 | 41 | |||
1498 | 42 | static const QString duplexModeToPpdChoice(const PrinterEnum::DuplexMode &mode) | ||
1499 | 43 | { | ||
1500 | 44 | switch (mode) { | ||
1501 | 45 | case PrinterEnum::DuplexMode::DuplexShortSide: | ||
1502 | 46 | return "DuplexTumble"; | ||
1503 | 47 | case PrinterEnum::DuplexMode::DuplexLongSide: | ||
1504 | 48 | return "DuplexNoTumble"; | ||
1505 | 49 | case PrinterEnum::DuplexMode::DuplexNone: | ||
1506 | 50 | default: | ||
1507 | 51 | return "None"; | ||
1508 | 52 | } | ||
1509 | 53 | } | ||
1510 | 54 | |||
1511 | 55 | static const QString duplexModeToUIString(const PrinterEnum::DuplexMode &mode) | 30 | static const QString duplexModeToUIString(const PrinterEnum::DuplexMode &mode) |
1512 | 56 | { | 31 | { |
1513 | 57 | switch (mode) { | 32 | switch (mode) { |
1514 | @@ -79,31 +54,63 @@ | |||
1515 | 79 | } | 54 | } |
1516 | 80 | } | 55 | } |
1517 | 81 | 56 | ||
1543 | 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. |
1544 | 83 | const QString &optionName) | 58 | static QMap<QString, QString> parseDeviceId(const QString &did) |
1545 | 84 | { | 59 | { |
1546 | 85 | ColorModel ret; | 60 | QMap<QString, QString> idDict; |
1547 | 86 | ret.name = name; | 61 | const auto parts = did.split(";"); |
1548 | 87 | ret.text = text; | 62 | const auto col = QStringLiteral(":"); |
1549 | 88 | ret.originalOption = optionName; | 63 | |
1550 | 89 | 64 | Q_FOREACH(auto part, parts) { | |
1551 | 90 | if (ret.name.contains("Gray") || ret.name.contains("Black")) { | 65 | if (!part.contains(col)) { |
1552 | 91 | ret.colorType = PrinterEnum::ColorModelType::GrayType; | 66 | continue; |
1553 | 92 | } else { | 67 | } |
1554 | 93 | ret.colorType = PrinterEnum::ColorModelType::ColorType; | 68 | auto nameValue = part.split(col); |
1555 | 94 | } | 69 | idDict[nameValue[0].trimmed()] = nameValue[1].trimmed(); |
1556 | 95 | return ret; | 70 | } |
1557 | 96 | } | 71 | |
1558 | 97 | 72 | const auto man = QStringLiteral("MANUFACTURER"); | |
1559 | 98 | static PrintQuality parsePpdPrintQuality(const QString &choice, | 73 | const auto mfg = QStringLiteral("MFG"); |
1560 | 99 | const QString &text, | 74 | if (idDict.contains(man)) { |
1561 | 100 | const QString &optionName) | 75 | idDict.insert(mfg, idDict.value(mfg, idDict.value(man))); |
1562 | 101 | { | 76 | } |
1563 | 102 | PrintQuality quality; | 77 | |
1564 | 103 | quality.name = choice; | 78 | const auto mod = QStringLiteral("MODEL"); |
1565 | 104 | quality.text = text; | 79 | const auto mdl = QStringLiteral("MDL"); |
1566 | 105 | quality.originalOption = optionName; | 80 | if (idDict.contains(mod)) { |
1567 | 106 | return quality; | 81 | idDict.insert(mdl, idDict.value(mdl, idDict.value(mod))); |
1568 | 82 | } | ||
1569 | 83 | |||
1570 | 84 | const auto com = QStringLiteral("COMMAND SET"); | ||
1571 | 85 | const auto cmd = QStringLiteral("CMD"); | ||
1572 | 86 | if (idDict.contains(com)) { | ||
1573 | 87 | idDict.insert(cmd, idDict.value(cmd, idDict.value(com))); | ||
1574 | 88 | } | ||
1575 | 89 | |||
1576 | 90 | const auto defaultNames = QStringList({ | ||
1577 | 91 | mfg, mdl, cmd, | ||
1578 | 92 | QStringLiteral("CLS"), | ||
1579 | 93 | QStringLiteral("DES"), | ||
1580 | 94 | QStringLiteral("SN"), | ||
1581 | 95 | QStringLiteral("S"), | ||
1582 | 96 | QStringLiteral("P"), | ||
1583 | 97 | QStringLiteral("J"), | ||
1584 | 98 | }); | ||
1585 | 99 | Q_FOREACH(auto name, defaultNames) { | ||
1586 | 100 | idDict.insert(name, idDict.value(name, QString::null)); | ||
1587 | 101 | } | ||
1588 | 102 | |||
1589 | 103 | /* Fix a seemingly very common mistake. Maybe make this general, i.e.: | ||
1590 | 104 | TODO: for every defaultName that appears in another default name, | ||
1591 | 105 | remove it. */ | ||
1592 | 106 | if (idDict.contains(mfg) && idDict.value(mfg).contains(cmd)) { | ||
1593 | 107 | QString dropCmd = idDict.value(mfg); | ||
1594 | 108 | idDict.insert(mfg, dropCmd.replace(cmd, "")); | ||
1595 | 109 | } | ||
1596 | 110 | |||
1597 | 111 | // TODO: We'd make an array of CMD, but we don't currently need it. | ||
1598 | 112 | |||
1599 | 113 | return idDict; | ||
1600 | 107 | } | 114 | } |
1601 | 108 | }; | 115 | }; |
1602 | 109 | 116 | ||
1603 | 110 | 117 | ||
1604 | === modified file 'po/ubuntu-ui-extras.pot' | |||
1605 | --- po/ubuntu-ui-extras.pot 2017-04-04 10:57:41 +0000 | |||
1606 | +++ po/ubuntu-ui-extras.pot 2017-04-07 12:51:46 +0000 | |||
1607 | @@ -8,7 +8,7 @@ | |||
1608 | 8 | msgstr "" | 8 | msgstr "" |
1609 | 9 | "Project-Id-Version: ubuntu-ui-extras\n" | 9 | "Project-Id-Version: ubuntu-ui-extras\n" |
1610 | 10 | "Report-Msgid-Bugs-To: \n" | 10 | "Report-Msgid-Bugs-To: \n" |
1612 | 11 | "POT-Creation-Date: 2017-04-04 12:56+0200\n" | 11 | "POT-Creation-Date: 2017-04-07 14:43+0200\n" |
1613 | 12 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" | 12 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" |
1614 | 13 | "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" | 13 | "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" |
1615 | 14 | "Language-Team: LANGUAGE <LL@li.org>\n" | 14 | "Language-Team: LANGUAGE <LL@li.org>\n" |
1616 | @@ -51,11 +51,15 @@ | |||
1617 | 51 | msgid "Enhancing photo..." | 51 | msgid "Enhancing photo..." |
1618 | 52 | msgstr "" | 52 | msgstr "" |
1619 | 53 | 53 | ||
1620 | 54 | #: modules/Ubuntu/Components/Extras/Printers/cups/devicesearcher.cpp:119 | ||
1621 | 55 | msgid "Fax" | ||
1622 | 56 | msgstr "" | ||
1623 | 57 | |||
1624 | 54 | #: modules/Ubuntu/Components/Extras/Example/Printers.qml:104 | 58 | #: modules/Ubuntu/Components/Extras/Example/Printers.qml:104 |
1625 | 55 | msgid "Idle" | 59 | msgid "Idle" |
1626 | 56 | msgstr "" | 60 | msgstr "" |
1627 | 57 | 61 | ||
1629 | 58 | #: modules/Ubuntu/Components/Extras/Printers/utils.h:61 | 62 | #: modules/Ubuntu/Components/Extras/Printers/utils.h:36 |
1630 | 59 | msgid "Long Edge (Standard)" | 63 | msgid "Long Edge (Standard)" |
1631 | 60 | msgstr "" | 64 | msgstr "" |
1632 | 61 | 65 | ||
1633 | @@ -67,7 +71,7 @@ | |||
1634 | 67 | msgid "Normal" | 71 | msgid "Normal" |
1635 | 68 | msgstr "" | 72 | msgstr "" |
1636 | 69 | 73 | ||
1638 | 70 | #: modules/Ubuntu/Components/Extras/Printers/utils.h:64 | 74 | #: modules/Ubuntu/Components/Extras/Printers/utils.h:39 |
1639 | 71 | msgid "One Sided" | 75 | msgid "One Sided" |
1640 | 72 | msgstr "" | 76 | msgstr "" |
1641 | 73 | 77 | ||
1642 | @@ -91,7 +95,7 @@ | |||
1643 | 91 | msgid "Rotate" | 95 | msgid "Rotate" |
1644 | 92 | msgstr "" | 96 | msgstr "" |
1645 | 93 | 97 | ||
1647 | 94 | #: modules/Ubuntu/Components/Extras/Printers/utils.h:59 | 98 | #: modules/Ubuntu/Components/Extras/Printers/utils.h:34 |
1648 | 95 | msgid "Short Edge (Flip)" | 99 | msgid "Short Edge (Flip)" |
1649 | 96 | msgstr "" | 100 | msgstr "" |
1650 | 97 | 101 | ||
1651 | @@ -99,7 +103,7 @@ | |||
1652 | 99 | msgid "Stopped" | 103 | msgid "Stopped" |
1653 | 100 | msgstr "" | 104 | msgstr "" |
1654 | 101 | 105 | ||
1656 | 102 | #: modules/Ubuntu/Components/Extras/Printers/printers/printers.cpp:395 | 106 | #: modules/Ubuntu/Components/Extras/Printers/printers/printers.cpp:404 |
1657 | 103 | msgid "Test page" | 107 | msgid "Test page" |
1658 | 104 | msgstr "" | 108 | msgstr "" |
1659 | 105 | 109 | ||
1660 | 106 | 110 | ||
1661 | === modified file 'tests/unittests/Printers/CMakeLists.txt' | |||
1662 | --- tests/unittests/Printers/CMakeLists.txt 2017-03-10 10:28:20 +0000 | |||
1663 | +++ tests/unittests/Printers/CMakeLists.txt 2017-04-07 12:51:46 +0000 | |||
1664 | @@ -56,3 +56,19 @@ | |||
1665 | 56 | add_executable(testPrintersDeviceModel tst_printerdevicemodel.cpp ${MOCK_SOURCES}) | 56 | add_executable(testPrintersDeviceModel tst_printerdevicemodel.cpp ${MOCK_SOURCES}) |
1666 | 57 | target_link_libraries(testPrintersDeviceModel UbuntuComponentsExtrasPrintersQml Qt5::Test Qt5::Gui) | 57 | target_link_libraries(testPrintersDeviceModel UbuntuComponentsExtrasPrintersQml Qt5::Test Qt5::Gui) |
1667 | 58 | add_test(tst_printerdevicemodel testPrintersDeviceModel) | 58 | add_test(tst_printerdevicemodel testPrintersDeviceModel) |
1668 | 59 | |||
1669 | 60 | add_executable(testPrintersPpdUtils tst_ppdutils.cpp) | ||
1670 | 61 | target_link_libraries(testPrintersPpdUtils UbuntuComponentsExtrasPrintersQml Qt5::Test Qt5::Gui) | ||
1671 | 62 | add_test(tst_ppdutils testPrintersPpdUtils) | ||
1672 | 63 | |||
1673 | 64 | add_executable(testPrintersUtils tst_utils.cpp) | ||
1674 | 65 | target_link_libraries(testPrintersUtils UbuntuComponentsExtrasPrintersQml Qt5::Test Qt5::Gui) | ||
1675 | 66 | add_test(tst_utils testPrintersUtils) | ||
1676 | 67 | |||
1677 | 68 | add_executable(testPrintersDeviceFilter tst_devicefilter.cpp ${MOCK_SOURCES}) | ||
1678 | 69 | target_link_libraries(testPrintersDeviceFilter UbuntuComponentsExtrasPrintersQml Qt5::Test Qt5::Gui) | ||
1679 | 70 | add_test(tst_devicefilter testPrintersDeviceFilter) | ||
1680 | 71 | |||
1681 | 72 | add_executable(testPrintersConsolidatedDeviceModel tst_consolidateddevicemodel.cpp ${MOCK_SOURCES}) | ||
1682 | 73 | target_link_libraries(testPrintersConsolidatedDeviceModel UbuntuComponentsExtrasPrintersQml Qt5::Test Qt5::Gui) | ||
1683 | 74 | add_test(tst_consolidateddevicemodel testPrintersConsolidatedDeviceModel) | ||
1684 | 59 | 75 | ||
1685 | === modified file 'tests/unittests/Printers/mockbackend.h' | |||
1686 | --- tests/unittests/Printers/mockbackend.h 2017-03-15 17:42:21 +0000 | |||
1687 | +++ tests/unittests/Printers/mockbackend.h 2017-04-07 12:51:46 +0000 | |||
1688 | @@ -18,7 +18,7 @@ | |||
1689 | 18 | #define USC_PRINTERS_MOCK_BACKEND_H | 18 | #define USC_PRINTERS_MOCK_BACKEND_H |
1690 | 19 | 19 | ||
1691 | 20 | #include "backend/backend.h" | 20 | #include "backend/backend.h" |
1693 | 21 | #include "utils.h" | 21 | #include "cups/ppdutils.h" |
1694 | 22 | 22 | ||
1695 | 23 | class MockPrinterBackend : public PrinterBackend | 23 | class MockPrinterBackend : public PrinterBackend |
1696 | 24 | { | 24 | { |
1697 | @@ -235,7 +235,7 @@ | |||
1698 | 235 | attributes.insert("ColorModel", job->getColorModel().name); | 235 | attributes.insert("ColorModel", job->getColorModel().name); |
1699 | 236 | attributes.insert("CompletedTime", job->completedTime()); | 236 | attributes.insert("CompletedTime", job->completedTime()); |
1700 | 237 | attributes.insert("CreationTime", job->creationTime()); | 237 | attributes.insert("CreationTime", job->creationTime()); |
1702 | 238 | attributes.insert("Duplex", Utils::duplexModeToPpdChoice(job->getDuplexMode())); | 238 | attributes.insert("Duplex", PpdUtils::duplexModeToPpdChoice(job->getDuplexMode())); |
1703 | 239 | attributes.insert("impressionsCompleted", job->impressionsCompleted()); | 239 | attributes.insert("impressionsCompleted", job->impressionsCompleted()); |
1704 | 240 | attributes.insert("landscape", job->landscape()); | 240 | attributes.insert("landscape", job->landscape()); |
1705 | 241 | attributes.insert("messages", job->messages()); | 241 | attributes.insert("messages", job->messages()); |
1706 | @@ -322,7 +322,7 @@ | |||
1707 | 322 | virtual PrinterEnum::DuplexMode defaultDuplexMode() const override | 322 | virtual PrinterEnum::DuplexMode defaultDuplexMode() const override |
1708 | 323 | { | 323 | { |
1709 | 324 | auto ppdMode = printerGetOption(printerName(), "Duplex").toString(); | 324 | auto ppdMode = printerGetOption(printerName(), "Duplex").toString(); |
1711 | 325 | return Utils::ppdChoiceToDuplexMode(ppdMode); | 325 | return PpdUtils::ppdChoiceToDuplexMode(ppdMode); |
1712 | 326 | } | 326 | } |
1713 | 327 | 327 | ||
1714 | 328 | virtual QList<PrinterEnum::DuplexMode> supportedDuplexModes() const override | 328 | virtual QList<PrinterEnum::DuplexMode> supportedDuplexModes() const override |
1715 | 329 | 329 | ||
1716 | === added file 'tests/unittests/Printers/tst_consolidateddevicemodel.cpp' | |||
1717 | --- tests/unittests/Printers/tst_consolidateddevicemodel.cpp 1970-01-01 00:00:00 +0000 | |||
1718 | +++ tests/unittests/Printers/tst_consolidateddevicemodel.cpp 2017-04-07 12:51:46 +0000 | |||
1719 | @@ -0,0 +1,186 @@ | |||
1720 | 1 | /* | ||
1721 | 2 | * Copyright (C) 2017 Canonical, Ltd. | ||
1722 | 3 | * | ||
1723 | 4 | * This program is free software; you can redistribute it and/or modify | ||
1724 | 5 | * it under the terms of the GNU Lesser General Public License as published by | ||
1725 | 6 | * the Free Software Foundation; version 3. | ||
1726 | 7 | * | ||
1727 | 8 | * This program is distributed in the hope that it will be useful, | ||
1728 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
1729 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
1730 | 11 | * GNU Lesser General Public License for more details. | ||
1731 | 12 | * | ||
1732 | 13 | * You should have received a copy of the GNU Lesser General Public License | ||
1733 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
1734 | 15 | */ | ||
1735 | 16 | |||
1736 | 17 | #include "mockbackend.h" | ||
1737 | 18 | |||
1738 | 19 | #include "backend/backend.h" | ||
1739 | 20 | #include "models/devicemodel.h" | ||
1740 | 21 | #include "structs.h" | ||
1741 | 22 | |||
1742 | 23 | #include <QDebug> | ||
1743 | 24 | #include <QObject> | ||
1744 | 25 | #include <QSignalSpy> | ||
1745 | 26 | #include <QTest> | ||
1746 | 27 | |||
1747 | 28 | Q_DECLARE_METATYPE(ConsolidatedDeviceModel::Roles) | ||
1748 | 29 | |||
1749 | 30 | class TestConsolidatedDeviceModel : public QObject | ||
1750 | 31 | { | ||
1751 | 32 | Q_OBJECT | ||
1752 | 33 | private Q_SLOTS: | ||
1753 | 34 | void init() | ||
1754 | 35 | { | ||
1755 | 36 | m_backend = new MockPrinterBackend(); | ||
1756 | 37 | m_devicesModel = new DeviceModel(m_backend); | ||
1757 | 38 | } | ||
1758 | 39 | void cleanup() | ||
1759 | 40 | { | ||
1760 | 41 | QSignalSpy destroyedSpy(m_devicesModel, SIGNAL(destroyed(QObject*))); | ||
1761 | 42 | m_devicesModel->deleteLater(); | ||
1762 | 43 | QTRY_COMPARE(destroyedSpy.count(), 1); | ||
1763 | 44 | delete m_backend; | ||
1764 | 45 | } | ||
1765 | 46 | void testConsolidation_data() | ||
1766 | 47 | { | ||
1767 | 48 | QTest::addColumn<QList<Device>>("devices"); | ||
1768 | 49 | QTest::addColumn<int>("targetConsolidatedDeviceCount"); | ||
1769 | 50 | |||
1770 | 51 | { | ||
1771 | 52 | Device a; a.uri = "dnssd://foo/bar"; a.makeModel = "Hp DeskJet"; | ||
1772 | 53 | Device b; b.uri = "ipps://bar/qux"; b.makeModel = "Foo BazJet"; | ||
1773 | 54 | |||
1774 | 55 | QList<Device> devs({a, b}); | ||
1775 | 56 | QTest::newRow("no consolidation") << devs << 2; | ||
1776 | 57 | } | ||
1777 | 58 | { | ||
1778 | 59 | Device a; a.uri = "dnssd://foo/bar"; a.makeModel = "Foo BazJet"; | ||
1779 | 60 | Device b; b.uri = "ipps://foo/bar"; b.makeModel = "Foo BazJet"; | ||
1780 | 61 | |||
1781 | 62 | QList<Device> devs({a, b}); | ||
1782 | 63 | QTest::newRow("consolidation") << devs << 1; | ||
1783 | 64 | } | ||
1784 | 65 | { | ||
1785 | 66 | Device a; a.uri = "dnssd://foo/bar"; a.makeModel = "Foo BazJet"; | ||
1786 | 67 | Device b; b.uri = "ipps://foo/bar"; b.makeModel = "Foo BazJet"; | ||
1787 | 68 | Device c; c.uri = "socket://baz/qux"; c.makeModel = "Bar LaserFjert"; | ||
1788 | 69 | |||
1789 | 70 | QList<Device> devs({a, b, c}); | ||
1790 | 71 | QTest::newRow("mixed") << devs << 2; | ||
1791 | 72 | } | ||
1792 | 73 | { | ||
1793 | 74 | Device a; a.uri = "dnssd://foo/bar"; a.makeModel = "foo"; | ||
1794 | 75 | Device b; b.uri = "ipps://foo/bar"; b.makeModel = ""; | ||
1795 | 76 | Device c; c.uri = "socket://baz/qux"; c.makeModel = ""; | ||
1796 | 77 | |||
1797 | 78 | QList<Device> devs({a, b, c}); | ||
1798 | 79 | QTest::newRow("don't consolidate make-and-model-less devices") << devs << 3; | ||
1799 | 80 | } | ||
1800 | 81 | { | ||
1801 | 82 | Device a; a.uri = "dnssd://foo/bar"; a.makeModel = "Unknown"; | ||
1802 | 83 | Device b; b.uri = "ipps://foo/bar"; b.makeModel = "Unknown"; | ||
1803 | 84 | Device c; c.uri = "socket://baz/qux"; c.makeModel = "Unknown"; | ||
1804 | 85 | |||
1805 | 86 | QList<Device> devs({a, b, c}); | ||
1806 | 87 | QTest::newRow("Unknown devices aren't consolidated.") << devs << 3; | ||
1807 | 88 | } | ||
1808 | 89 | { | ||
1809 | 90 | Device a; a.uri = "dnssd://a"; a.makeModel = "Ricoh Ricoh Aficio MP 4500"; | ||
1810 | 91 | a.id = "MFG:Ricoh;MDL:Aficio MP 4500CMD:PDF,PS,JPEG,PNG,PWG;"; | ||
1811 | 92 | |||
1812 | 93 | Device b; b.uri = "dnssd://b"; b.makeModel = "Ricoh Ricoh Aficio MP 4500"; | ||
1813 | 94 | b.id = "MFG:Ricoh;MDL:Aficio MP 4500CMD:PDF,PS,JPEG,PNG,PWG;"; | ||
1814 | 95 | |||
1815 | 96 | Device c; c.uri = "dnssd://c"; c.makeModel = "RICOH RICOH Aficio MP C4500 PS3"; | ||
1816 | 97 | c.id = "MFG:RICOH;MDL:Aficio MP C4500 PS3CMD:PDF,PS,JPEG,PNG,PWG;"; | ||
1817 | 98 | |||
1818 | 99 | QList<Device> devs({a, b, c}); | ||
1819 | 100 | QTest::newRow("Ricoh as it appears in uni of Bergen") << devs << 2; | ||
1820 | 101 | } | ||
1821 | 102 | { | ||
1822 | 103 | Device a; a.uri = "dnssd://a"; a.makeModel = "unknown"; | ||
1823 | 104 | a.id = ""; a.info = "HP Color LaserJet 2500 @ some-laptop"; | ||
1824 | 105 | |||
1825 | 106 | Device b; b.uri = "dnssd://b"; b.makeModel = "unknown"; | ||
1826 | 107 | b.id = ""; b.info = "HP Color LaserJet 2500 plz don't spam @ other-laptop"; | ||
1827 | 108 | |||
1828 | 109 | QList<Device> devs({a, b}); | ||
1829 | 110 | QTest::newRow("Buggy cups") << devs << 2; | ||
1830 | 111 | |||
1831 | 112 | } | ||
1832 | 113 | } | ||
1833 | 114 | void testConsolidation() | ||
1834 | 115 | { | ||
1835 | 116 | QFETCH(QList<Device>, devices); | ||
1836 | 117 | QFETCH(int, targetConsolidatedDeviceCount); | ||
1837 | 118 | |||
1838 | 119 | ConsolidatedDeviceModel model(m_devicesModel); | ||
1839 | 120 | |||
1840 | 121 | Q_FOREACH(auto device, devices) { | ||
1841 | 122 | m_backend->mockDeviceFound(device); | ||
1842 | 123 | } | ||
1843 | 124 | m_backend->deviceSearchFinished(); | ||
1844 | 125 | |||
1845 | 126 | QCOMPARE(model.rowCount(), targetConsolidatedDeviceCount); | ||
1846 | 127 | } | ||
1847 | 128 | void testRoles_data() | ||
1848 | 129 | { | ||
1849 | 130 | QTest::addColumn<ConsolidatedDeviceModel::Roles>("role"); | ||
1850 | 131 | QTest::addColumn<QVariant>("value"); | ||
1851 | 132 | QTest::addColumn<QList<Device>>("devices"); | ||
1852 | 133 | |||
1853 | 134 | { | ||
1854 | 135 | Device a; a.uri = "ipp://foo/bar"; a.makeModel = "HP LaserFjert 4500"; | ||
1855 | 136 | Device b; a.uri = "ipp://foo/bar"; b.makeModel = "HP LaserFjert 4500"; | ||
1856 | 137 | |||
1857 | 138 | QList<Device> devs({a, b}); | ||
1858 | 139 | |||
1859 | 140 | ConsolidatedDeviceModel::Roles role( | ||
1860 | 141 | ConsolidatedDeviceModel::ConsolidatedNameRole | ||
1861 | 142 | ); | ||
1862 | 143 | QVariant value(QString("HP LaserFjert 4500")); | ||
1863 | 144 | QTest::newRow("ConsolidatedNameRole") << role << value << devs; | ||
1864 | 145 | } | ||
1865 | 146 | } | ||
1866 | 147 | void testRoles() | ||
1867 | 148 | { | ||
1868 | 149 | QFETCH(ConsolidatedDeviceModel::Roles, role); | ||
1869 | 150 | QFETCH(QVariant, value); | ||
1870 | 151 | QFETCH(QList<Device>, devices); | ||
1871 | 152 | |||
1872 | 153 | ConsolidatedDeviceModel model(m_devicesModel); | ||
1873 | 154 | |||
1874 | 155 | Q_FOREACH(auto device, devices) { | ||
1875 | 156 | m_backend->mockDeviceFound(device); | ||
1876 | 157 | } | ||
1877 | 158 | m_backend->deviceSearchFinished(); | ||
1878 | 159 | |||
1879 | 160 | QCOMPARE(model.data(model.index(0), role), value); | ||
1880 | 161 | } | ||
1881 | 162 | void testDevicesRole() | ||
1882 | 163 | { | ||
1883 | 164 | Device a; a.uri = "ipp://foo/bar"; a.makeModel = "foo"; | ||
1884 | 165 | Device b; b.uri = "dnssd://foo/bar"; b.makeModel = "foo"; | ||
1885 | 166 | |||
1886 | 167 | ConsolidatedDeviceModel model(m_devicesModel); | ||
1887 | 168 | |||
1888 | 169 | m_backend->mockDeviceFound(a); | ||
1889 | 170 | m_backend->mockDeviceFound(b); | ||
1890 | 171 | m_backend->deviceSearchFinished(); | ||
1891 | 172 | |||
1892 | 173 | auto filter = model.data( | ||
1893 | 174 | model.index(0), ConsolidatedDeviceModel::Roles::DevicesRole | ||
1894 | 175 | ).value<DeviceFilter*>(); | ||
1895 | 176 | |||
1896 | 177 | QCOMPARE(filter->count(), 2); | ||
1897 | 178 | } | ||
1898 | 179 | private: | ||
1899 | 180 | MockPrinterBackend *m_backend; | ||
1900 | 181 | DeviceModel *m_devicesModel; | ||
1901 | 182 | }; | ||
1902 | 183 | |||
1903 | 184 | QTEST_GUILESS_MAIN(TestConsolidatedDeviceModel) | ||
1904 | 185 | #include "tst_consolidateddevicemodel.moc" | ||
1905 | 186 | |||
1906 | 0 | 187 | ||
1907 | === added file 'tests/unittests/Printers/tst_devicefilter.cpp' | |||
1908 | --- tests/unittests/Printers/tst_devicefilter.cpp 1970-01-01 00:00:00 +0000 | |||
1909 | +++ tests/unittests/Printers/tst_devicefilter.cpp 2017-04-07 12:51:46 +0000 | |||
1910 | @@ -0,0 +1,106 @@ | |||
1911 | 1 | /* | ||
1912 | 2 | * Copyright (C) 2017 Canonical, Ltd. | ||
1913 | 3 | * | ||
1914 | 4 | * This program is free software; you can redistribute it and/or modify | ||
1915 | 5 | * it under the terms of the GNU Lesser General Public License as published by | ||
1916 | 6 | * the Free Software Foundation; version 3. | ||
1917 | 7 | * | ||
1918 | 8 | * This program is distributed in the hope that it will be useful, | ||
1919 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
1920 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
1921 | 11 | * GNU Lesser General Public License for more details. | ||
1922 | 12 | * | ||
1923 | 13 | * You should have received a copy of the GNU Lesser General Public License | ||
1924 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
1925 | 15 | */ | ||
1926 | 16 | |||
1927 | 17 | #include "mockbackend.h" | ||
1928 | 18 | |||
1929 | 19 | #include "cups/ppdutils.h" | ||
1930 | 20 | #include "models/devicemodel.h" | ||
1931 | 21 | |||
1932 | 22 | #include <QObject> | ||
1933 | 23 | #include <QSignalSpy> | ||
1934 | 24 | #include <QScopedPointer> | ||
1935 | 25 | #include <QTest> | ||
1936 | 26 | |||
1937 | 27 | class TestDeviceFilter : public QObject | ||
1938 | 28 | { | ||
1939 | 29 | Q_OBJECT | ||
1940 | 30 | private Q_SLOTS: | ||
1941 | 31 | void testFilterOnMakeModel() | ||
1942 | 32 | { | ||
1943 | 33 | QScopedPointer<MockPrinterBackend> backend(new MockPrinterBackend); | ||
1944 | 34 | DeviceModel model(backend.data()); | ||
1945 | 35 | |||
1946 | 36 | Device dev1; dev1.uri = "ipp://foo/bar"; | ||
1947 | 37 | dev1.makeModel = "Foo BarJet"; | ||
1948 | 38 | Device dev2; dev2.uri = "dnssd://bar/qux"; | ||
1949 | 39 | dev2.makeModel = "Bar QuxJet"; | ||
1950 | 40 | |||
1951 | 41 | backend->mockDeviceFound(dev1); | ||
1952 | 42 | backend->mockDeviceFound(dev2); | ||
1953 | 43 | |||
1954 | 44 | DeviceFilter filter; | ||
1955 | 45 | filter.setSourceModel(&model); | ||
1956 | 46 | |||
1957 | 47 | QCOMPARE(filter.count(), 2); | ||
1958 | 48 | |||
1959 | 49 | // Install filter. | ||
1960 | 50 | filter.filterOnNormalizedMakeModel("foo barjet"); | ||
1961 | 51 | QCOMPARE(filter.count(), 1); | ||
1962 | 52 | } | ||
1963 | 53 | |||
1964 | 54 | /* Test that "Foo BazJet-5000" and "Foo BazJet 5000" are accepted in same | ||
1965 | 55 | filter. */ | ||
1966 | 56 | void testNormalizedFiltering() | ||
1967 | 57 | { | ||
1968 | 58 | QScopedPointer<MockPrinterBackend> backend(new MockPrinterBackend); | ||
1969 | 59 | DeviceModel model(backend.data()); | ||
1970 | 60 | |||
1971 | 61 | Device dev1; dev1.uri = "ipp://foo/bar"; | ||
1972 | 62 | dev1.makeModel = "Foo BazJet 5000"; | ||
1973 | 63 | Device dev2; dev2.uri = "dnssd://bar/qux"; | ||
1974 | 64 | dev2.makeModel = "Foo BazJet-5000"; | ||
1975 | 65 | Device dev3; dev3.uri = "dnssd://bar/baz"; | ||
1976 | 66 | dev3.makeModel = "Foo DeskQux 350"; | ||
1977 | 67 | |||
1978 | 68 | backend->mockDeviceFound(dev1); | ||
1979 | 69 | backend->mockDeviceFound(dev2); | ||
1980 | 70 | backend->mockDeviceFound(dev3); | ||
1981 | 71 | |||
1982 | 72 | DeviceFilter filter; | ||
1983 | 73 | filter.setSourceModel(&model); | ||
1984 | 74 | |||
1985 | 75 | // Install filter. | ||
1986 | 76 | filter.filterOnNormalizedMakeModel( | ||
1987 | 77 | PpdUtils::normalize("Foo BazJet 5000")); | ||
1988 | 78 | QCOMPARE(filter.count(), 2); | ||
1989 | 79 | } | ||
1990 | 80 | void testFilterOnUri() | ||
1991 | 81 | { | ||
1992 | 82 | QScopedPointer<MockPrinterBackend> backend(new MockPrinterBackend); | ||
1993 | 83 | DeviceModel model(backend.data()); | ||
1994 | 84 | |||
1995 | 85 | Device dev1; dev1.uri = "ipp://foo/bar"; | ||
1996 | 86 | dev1.id = "foo"; | ||
1997 | 87 | Device dev2; dev2.uri = "dnssd://bar/qux"; | ||
1998 | 88 | dev2.id = "bar"; | ||
1999 | 89 | |||
2000 | 90 | backend->mockDeviceFound(dev1); | ||
2001 | 91 | backend->mockDeviceFound(dev2); | ||
2002 | 92 | |||
2003 | 93 | DeviceFilter filter; | ||
2004 | 94 | filter.setSourceModel(&model); | ||
2005 | 95 | |||
2006 | 96 | QCOMPARE(filter.count(), 2); | ||
2007 | 97 | |||
2008 | 98 | // Install filter. | ||
2009 | 99 | filter.filterOnUri("ipp://foo/bar"); | ||
2010 | 100 | QCOMPARE(filter.count(), 1); | ||
2011 | 101 | } | ||
2012 | 102 | }; | ||
2013 | 103 | |||
2014 | 104 | QTEST_GUILESS_MAIN(TestDeviceFilter) | ||
2015 | 105 | #include "tst_devicefilter.moc" | ||
2016 | 106 | |||
2017 | 0 | 107 | ||
2018 | === modified file 'tests/unittests/Printers/tst_jobmodel.cpp' | |||
2019 | --- tests/unittests/Printers/tst_jobmodel.cpp 2017-03-15 17:30:21 +0000 | |||
2020 | +++ tests/unittests/Printers/tst_jobmodel.cpp 2017-04-07 12:51:46 +0000 | |||
2021 | @@ -19,6 +19,7 @@ | |||
2022 | 19 | #include "backend/backend.h" | 19 | #include "backend/backend.h" |
2023 | 20 | #include "models/jobmodel.h" | 20 | #include "models/jobmodel.h" |
2024 | 21 | #include "printers/printers.h" | 21 | #include "printers/printers.h" |
2025 | 22 | #include "utils.h" | ||
2026 | 22 | 23 | ||
2027 | 23 | #include <QDebug> | 24 | #include <QDebug> |
2028 | 24 | #include <QObject> | 25 | #include <QObject> |
2029 | 25 | 26 | ||
2030 | === added file 'tests/unittests/Printers/tst_ppdutils.cpp' | |||
2031 | --- tests/unittests/Printers/tst_ppdutils.cpp 1970-01-01 00:00:00 +0000 | |||
2032 | +++ tests/unittests/Printers/tst_ppdutils.cpp 2017-04-07 12:51:46 +0000 | |||
2033 | @@ -0,0 +1,138 @@ | |||
2034 | 1 | /* | ||
2035 | 2 | * Copyright (C) 2017 Canonical, Ltd. | ||
2036 | 3 | * | ||
2037 | 4 | * This program is free software; you can redistribute it and/or modify | ||
2038 | 5 | * it under the terms of the GNU Lesser General Public License as published by | ||
2039 | 6 | * the Free Software Foundation; version 3. | ||
2040 | 7 | * | ||
2041 | 8 | * This program is distributed in the hope that it will be useful, | ||
2042 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
2043 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
2044 | 11 | * GNU Lesser General Public License for more details. | ||
2045 | 12 | * | ||
2046 | 13 | * You should have received a copy of the GNU Lesser General Public License | ||
2047 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
2048 | 15 | */ | ||
2049 | 16 | |||
2050 | 17 | #include "cups/ppdutils.h" | ||
2051 | 18 | |||
2052 | 19 | #include <QObject> | ||
2053 | 20 | #include <QTest> | ||
2054 | 21 | |||
2055 | 22 | typedef QPair<QString, QString> MakeModel; | ||
2056 | 23 | Q_DECLARE_METATYPE(MakeModel) | ||
2057 | 24 | |||
2058 | 25 | class TstPpdUtils : public QObject | ||
2059 | 26 | { | ||
2060 | 27 | Q_OBJECT | ||
2061 | 28 | private Q_SLOTS: | ||
2062 | 29 | void testSplitMakeModel_data() | ||
2063 | 30 | { | ||
2064 | 31 | QTest::addColumn<QString>("makeModel"); | ||
2065 | 32 | QTest::addColumn<MakeModel>("expected"); | ||
2066 | 33 | |||
2067 | 34 | { | ||
2068 | 35 | QString actual("HP HP Officejet Pro 8610"); | ||
2069 | 36 | auto expected = MakeModel("HP", "OfficeJet Pro 8610"); | ||
2070 | 37 | QTest::newRow("duplicate hp") << actual << expected; | ||
2071 | 38 | } | ||
2072 | 39 | { | ||
2073 | 40 | QString actual("Officejet Officejet Pro 8610"); | ||
2074 | 41 | auto expected = MakeModel("HP", "OfficeJet Pro 8610"); | ||
2075 | 42 | QTest::newRow("officejet officejet (yes it actually happens)") << actual << expected; | ||
2076 | 43 | } | ||
2077 | 44 | { | ||
2078 | 45 | QString actual("laserjet 4500"); | ||
2079 | 46 | auto expected = MakeModel("HP", "LaserJet 4500"); | ||
2080 | 47 | QTest::newRow("just a hp model") << actual << expected; | ||
2081 | 48 | } | ||
2082 | 49 | { | ||
2083 | 50 | QString actual("HP OfficeJet Pro 8600"); | ||
2084 | 51 | auto expected = MakeModel("HP", "OfficeJet Pro 8600"); | ||
2085 | 52 | QTest::newRow("a sane hp officejet") << actual << expected; | ||
2086 | 53 | } | ||
2087 | 54 | { | ||
2088 | 55 | QString actual("HP Foo All-In-One"); | ||
2089 | 56 | auto expected = MakeModel("HP", "Foo"); | ||
2090 | 57 | QTest::newRow("remove all-in-one") << actual << expected; | ||
2091 | 58 | } | ||
2092 | 59 | { | ||
2093 | 60 | QString actual("Hewlett-Packard SmartJet 50 v0.10"); | ||
2094 | 61 | auto expected = MakeModel("HP", "SmartJet 50"); | ||
2095 | 62 | QTest::newRow("Hewlett-Packard SmartJet 50 v0.10") << actual << expected; | ||
2096 | 63 | } | ||
2097 | 64 | { | ||
2098 | 65 | QString actual("Foobar Bazjet 9000"); | ||
2099 | 66 | auto expected = MakeModel("Foobar", "Bazjet 9000"); | ||
2100 | 67 | QTest::newRow("the excellent foobar bazjet 9000") << actual << expected; | ||
2101 | 68 | } | ||
2102 | 69 | { | ||
2103 | 70 | QString actual("HP LaserJet 4 Plus v2013.111 Postscript (recommended)"); | ||
2104 | 71 | auto expected = MakeModel("HP", "LaserJet 4 Plus"); | ||
2105 | 72 | QTest::newRow("example #1") << actual << expected; | ||
2106 | 73 | } | ||
2107 | 74 | { | ||
2108 | 75 | QString actual("Canon MG4100 series Ver.3.90"); | ||
2109 | 76 | auto expected = MakeModel("Canon", "MG4100"); | ||
2110 | 77 | QTest::newRow("example #2") << actual << expected; | ||
2111 | 78 | } | ||
2112 | 79 | { | ||
2113 | 80 | QString actual("Xerox Foo Printer"); | ||
2114 | 81 | auto expected = MakeModel("Xerox", "Foo"); | ||
2115 | 82 | QTest::newRow("remove ignored suffix 'printer'") << actual << expected; | ||
2116 | 83 | } | ||
2117 | 84 | { | ||
2118 | 85 | QString actual("hp lj 4500"); | ||
2119 | 86 | auto expected = MakeModel("HP", "LaserJet 4500"); | ||
2120 | 87 | QTest::newRow("shortened hp model name") << actual << expected; | ||
2121 | 88 | } | ||
2122 | 89 | { | ||
2123 | 90 | QString actual("okipage foobar"); | ||
2124 | 91 | auto expected = MakeModel("Oki", "okipage foobar"); | ||
2125 | 92 | QTest::newRow("some okipage printer") << actual << expected; | ||
2126 | 93 | } | ||
2127 | 94 | } | ||
2128 | 95 | void testSplitMakeModel() | ||
2129 | 96 | { | ||
2130 | 97 | QFETCH(QString, makeModel); | ||
2131 | 98 | QFETCH(MakeModel, expected); | ||
2132 | 99 | auto actual = PpdUtils::splitMakeModel(makeModel); | ||
2133 | 100 | QCOMPARE(actual, expected); | ||
2134 | 101 | } | ||
2135 | 102 | void testNormalize_data() | ||
2136 | 103 | { | ||
2137 | 104 | QTest::addColumn<QString>("actual"); | ||
2138 | 105 | QTest::addColumn<QString>("expected"); | ||
2139 | 106 | |||
2140 | 107 | { | ||
2141 | 108 | QString actual("Epson PM-A820"); | ||
2142 | 109 | QString expected("epson pm a 820"); | ||
2143 | 110 | QTest::newRow("epson with dash") << actual << expected; | ||
2144 | 111 | } | ||
2145 | 112 | { | ||
2146 | 113 | QString actual("Epson PM A820"); | ||
2147 | 114 | QString expected("epson pm a 820"); | ||
2148 | 115 | QTest::newRow("epson without dash") << actual << expected; | ||
2149 | 116 | } | ||
2150 | 117 | { | ||
2151 | 118 | QString actual("HP PhotoSmart C 8100"); | ||
2152 | 119 | QString expected("hp photosmart c 8100"); | ||
2153 | 120 | QTest::newRow("C 8100") << actual << expected; | ||
2154 | 121 | } | ||
2155 | 122 | { | ||
2156 | 123 | QString actual("HP PhotoSmart C8100"); | ||
2157 | 124 | QString expected("hp photosmart c 8100"); | ||
2158 | 125 | QTest::newRow("C8100") << actual << expected; | ||
2159 | 126 | } | ||
2160 | 127 | } | ||
2161 | 128 | void testNormalize() | ||
2162 | 129 | { | ||
2163 | 130 | QFETCH(QString, actual); | ||
2164 | 131 | QFETCH(QString, expected); | ||
2165 | 132 | actual = PpdUtils::normalize(actual); | ||
2166 | 133 | QCOMPARE(actual, expected); | ||
2167 | 134 | } | ||
2168 | 135 | }; | ||
2169 | 136 | |||
2170 | 137 | QTEST_GUILESS_MAIN(TstPpdUtils) | ||
2171 | 138 | #include "tst_ppdutils.moc" | ||
2172 | 0 | 139 | ||
2173 | === modified file 'tests/unittests/Printers/tst_printer.cpp' | |||
2174 | --- tests/unittests/Printers/tst_printer.cpp 2017-03-31 11:03:15 +0000 | |||
2175 | +++ tests/unittests/Printers/tst_printer.cpp 2017-04-07 12:51:46 +0000 | |||
2176 | @@ -14,12 +14,12 @@ | |||
2177 | 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/>. |
2178 | 15 | */ | 15 | */ |
2179 | 16 | 16 | ||
2180 | 17 | #include "utils.h" | ||
2181 | 18 | 17 | ||
2182 | 19 | #include "mockbackend.h" | 18 | #include "mockbackend.h" |
2183 | 20 | 19 | ||
2184 | 21 | #include "backend/backend.h" | 20 | #include "backend/backend.h" |
2185 | 22 | #include "backend/backend_pdf.h" | 21 | #include "backend/backend_pdf.h" |
2186 | 22 | #include "cups/ppdutils.h" | ||
2187 | 23 | #include "printer/printer.h" | 23 | #include "printer/printer.h" |
2188 | 24 | 24 | ||
2189 | 25 | #include <QDebug> | 25 | #include <QDebug> |
2190 | @@ -161,7 +161,7 @@ | |||
2191 | 161 | QStringList duplexVals = duplexVar.toStringList(); | 161 | QStringList duplexVals = duplexVar.toStringList(); |
2192 | 162 | QCOMPARE( | 162 | QCOMPARE( |
2193 | 163 | duplexVals.at(0), | 163 | duplexVals.at(0), |
2195 | 164 | (QString) Utils::duplexModeToPpdChoice(PrinterEnum::DuplexMode::DuplexLongSide) | 164 | (QString) PpdUtils::duplexModeToPpdChoice(PrinterEnum::DuplexMode::DuplexLongSide) |
2196 | 165 | ); | 165 | ); |
2197 | 166 | } | 166 | } |
2198 | 167 | void testSetDefaultPageSize_data() | 167 | void testSetDefaultPageSize_data() |
2199 | @@ -429,7 +429,7 @@ | |||
2200 | 429 | 429 | ||
2201 | 430 | backend->m_supportedDuplexModes = duplexModes; | 430 | backend->m_supportedDuplexModes = duplexModes; |
2202 | 431 | backend->printerOptions[printerName].insert( | 431 | backend->printerOptions[printerName].insert( |
2204 | 432 | "Duplex", Utils::duplexModeToPpdChoice(newDefaultDuplexMode)); | 432 | "Duplex", PpdUtils::duplexModeToPpdChoice(newDefaultDuplexMode)); |
2205 | 433 | 433 | ||
2206 | 434 | QSharedPointer<Printer> p = QSharedPointer<Printer>(new Printer(backend)); | 434 | QSharedPointer<Printer> p = QSharedPointer<Printer>(new Printer(backend)); |
2207 | 435 | 435 | ||
2208 | 436 | 436 | ||
2209 | === modified file 'tests/unittests/Printers/tst_printerdevice.cpp' | |||
2210 | --- tests/unittests/Printers/tst_printerdevice.cpp 2017-03-16 15:34:02 +0000 | |||
2211 | +++ tests/unittests/Printers/tst_printerdevice.cpp 2017-04-07 12:51:46 +0000 | |||
2212 | @@ -93,37 +93,34 @@ | |||
2213 | 93 | 93 | ||
2214 | 94 | QCOMPARE(device.type(), expected); | 94 | QCOMPARE(device.type(), expected); |
2215 | 95 | } | 95 | } |
2217 | 96 | void testToString_data() | 96 | void testHost_data() |
2218 | 97 | { | 97 | { |
2220 | 98 | QTest::addColumn<QString>("id"); | 98 | QTest::addColumn<Device>("device"); |
2221 | 99 | QTest::addColumn<QString>("expected"); | 99 | QTest::addColumn<QString>("expected"); |
2222 | 100 | 100 | ||
2223 | 101 | { | 101 | { |
2240 | 102 | Device d; | 102 | Device d; d.uri = "usb"; |
2241 | 103 | QTest::newRow("a hp printer (remove CMD)") | 103 | QTest::newRow("local type, no host") << d << ""; |
2242 | 104 | << "MFG:HP;MDL:Color LaserJet 4500CMD:PDF,PS,JPEG,PNG,PWG,URF" | 104 | } |
2243 | 105 | << "HP Color LaserJet 4500"; | 105 | { |
2244 | 106 | } | 106 | Device d; d.uri = "dnssd://foo/bar"; |
2245 | 107 | { | 107 | QTest::newRow("dnssd urls have no hostname") << d << ""; |
2246 | 108 | Device d; ; | 108 | } |
2247 | 109 | QTest::newRow("Andrew's hp printer") | 109 | { |
2248 | 110 | << "MFG:HP;MDL:Officejet 5740 series;CMD:PCL,JPEG,URF,PWG;" | 110 | Device d; d.uri = "socket://192.168.1.7:9100"; |
2249 | 111 | << "HP Officejet 5740 series"; | 111 | QTest::newRow("a socket") << d << "192.168.1.7"; |
2250 | 112 | } | 112 | } |
2251 | 113 | { | 113 | { |
2252 | 114 | Device d; ; | 114 | Device d; d.uri = "ipp://my-domain.com/printer"; |
2253 | 115 | QTest::newRow("Base case with A as manufacturer, B as model.") | 115 | QTest::newRow("ipp") << d << "my-domain.com"; |
2238 | 116 | << "MFG:A;MDL:B" | ||
2239 | 117 | << "A B"; | ||
2254 | 118 | } | 116 | } |
2255 | 119 | } | 117 | } |
2257 | 120 | void testToString() | 118 | void testHost() |
2258 | 121 | { | 119 | { |
2260 | 122 | QFETCH(QString, id); | 120 | QFETCH(Device, device); |
2261 | 123 | QFETCH(QString, expected); | 121 | QFETCH(QString, expected); |
2262 | 124 | 122 | ||
2265 | 125 | Device d; d.id = id; | 123 | QCOMPARE(device.host(), expected); |
2264 | 126 | QCOMPARE(d.toString(), expected); | ||
2266 | 127 | } | 124 | } |
2267 | 128 | }; | 125 | }; |
2268 | 129 | 126 | ||
2269 | 130 | 127 | ||
2270 | === modified file 'tests/unittests/Printers/tst_printerdevicemodel.cpp' | |||
2271 | --- tests/unittests/Printers/tst_printerdevicemodel.cpp 2017-03-08 16:13:02 +0000 | |||
2272 | +++ tests/unittests/Printers/tst_printerdevicemodel.cpp 2017-04-07 12:51:46 +0000 | |||
2273 | @@ -54,12 +54,18 @@ | |||
2274 | 54 | } | 54 | } |
2275 | 55 | { | 55 | { |
2276 | 56 | Device d; d.uri = "dnssd://foo-bar"; | 56 | Device d; d.uri = "dnssd://foo-bar"; |
2277 | 57 | d.makeModel = "BazJet"; | ||
2278 | 57 | QTest::newRow("non-empty uri") << d << true; | 58 | QTest::newRow("non-empty uri") << d << true; |
2279 | 58 | } | 59 | } |
2280 | 59 | { | 60 | { |
2281 | 60 | Device d; d.uri = "dnssd:"; | 61 | Device d; d.uri = "dnssd:"; |
2282 | 61 | QTest::newRow("non-empty uri, malformed") << d << false; | 62 | QTest::newRow("non-empty uri, malformed") << d << false; |
2283 | 62 | } | 63 | } |
2284 | 64 | { | ||
2285 | 65 | Device d; d.uri = "dnssd://foo/bar"; | ||
2286 | 66 | d.makeModel = "Unknown"; | ||
2287 | 67 | QTest::newRow("non-empty uri, but Unknown make/model") << d << true; | ||
2288 | 68 | } | ||
2289 | 63 | } | 69 | } |
2290 | 64 | void testAcceptedDevices() | 70 | void testAcceptedDevices() |
2291 | 65 | { | 71 | { |
2292 | @@ -75,6 +81,7 @@ | |||
2293 | 75 | QSignalSpy insertSpy(m_model, SIGNAL(rowsInserted(const QModelIndex&, int, int))); | 81 | QSignalSpy insertSpy(m_model, SIGNAL(rowsInserted(const QModelIndex&, int, int))); |
2294 | 76 | 82 | ||
2295 | 77 | Device d; d.uri = "dnssd://foo-bar"; | 83 | Device d; d.uri = "dnssd://foo-bar"; |
2296 | 84 | d.makeModel = "BazJet"; | ||
2297 | 78 | m_backend->mockDeviceFound(d); | 85 | m_backend->mockDeviceFound(d); |
2298 | 79 | 86 | ||
2299 | 80 | QCOMPARE(m_model->count(), 1); | 87 | QCOMPARE(m_model->count(), 1); |
2300 | @@ -87,9 +94,11 @@ | |||
2301 | 87 | QSignalSpy insertSpy(m_model, SIGNAL(rowsInserted(const QModelIndex&, int, int))); | 94 | QSignalSpy insertSpy(m_model, SIGNAL(rowsInserted(const QModelIndex&, int, int))); |
2302 | 88 | 95 | ||
2303 | 89 | Device d; d.uri = "dnssd://foo-bar"; | 96 | Device d; d.uri = "dnssd://foo-bar"; |
2304 | 97 | d.makeModel = "BazJet"; | ||
2305 | 90 | m_backend->mockDeviceFound(d); | 98 | m_backend->mockDeviceFound(d); |
2306 | 91 | 99 | ||
2307 | 92 | Device d1; d1.uri = "dnssd://foo-bar"; | 100 | Device d1; d1.uri = "dnssd://foo-bar"; |
2308 | 101 | d1.makeModel = "BazJet"; | ||
2309 | 93 | m_backend->mockDeviceFound(d1); | 102 | m_backend->mockDeviceFound(d1); |
2310 | 94 | 103 | ||
2311 | 95 | QCOMPARE(m_model->count(), 1); | 104 | QCOMPARE(m_model->count(), 1); |
2312 | @@ -104,40 +113,49 @@ | |||
2313 | 104 | 113 | ||
2314 | 105 | { | 114 | { |
2315 | 106 | Device d; d.uri = "ipp://foo/bar"; | 115 | Device d; d.uri = "ipp://foo/bar"; |
2317 | 107 | d.id = "foo"; | 116 | d.id = "foo"; d.makeModel = "BazJet"; |
2318 | 108 | DeviceModel::Roles role(DeviceModel::IdRole); | 117 | DeviceModel::Roles role(DeviceModel::IdRole); |
2319 | 109 | QVariant value(d.id); | 118 | QVariant value(d.id); |
2320 | 110 | QTest::newRow("DisplayRole") << role << value << d; | 119 | QTest::newRow("DisplayRole") << role << value << d; |
2321 | 111 | } | 120 | } |
2322 | 112 | { | 121 | { |
2323 | 113 | Device d; d.uri = "ipp://foo/bar"; | 122 | Device d; d.uri = "ipp://foo/bar"; |
2325 | 114 | d.info = "foo"; | 123 | d.info = "foo"; d.makeModel = "BazJet"; |
2326 | 115 | DeviceModel::Roles role(DeviceModel::InfoRole); | 124 | DeviceModel::Roles role(DeviceModel::InfoRole); |
2327 | 116 | QVariant value(d.info); | 125 | QVariant value(d.info); |
2328 | 117 | QTest::newRow("InfoRole") << role << value << d; | 126 | QTest::newRow("InfoRole") << role << value << d; |
2329 | 118 | } | 127 | } |
2330 | 119 | { | 128 | { |
2331 | 120 | Device d; d.uri = "ipp://foo/bar"; | 129 | Device d; d.uri = "ipp://foo/bar"; |
2332 | 130 | d.makeModel = "BazJet"; | ||
2333 | 121 | DeviceModel::Roles role(DeviceModel::UriRole); | 131 | DeviceModel::Roles role(DeviceModel::UriRole); |
2334 | 122 | QVariant value(d.uri); | 132 | QVariant value(d.uri); |
2335 | 123 | QTest::newRow("UriRole") << role << value << d; | 133 | QTest::newRow("UriRole") << role << value << d; |
2336 | 124 | } | 134 | } |
2337 | 125 | { | 135 | { |
2338 | 126 | Device d; d.uri = "ipp://foo/bar"; | 136 | Device d; d.uri = "ipp://foo/bar"; |
2340 | 127 | d.location = "home"; | 137 | d.location = "home"; d.makeModel = "BazJet"; |
2341 | 128 | DeviceModel::Roles role(DeviceModel::LocationRole); | 138 | DeviceModel::Roles role(DeviceModel::LocationRole); |
2342 | 129 | QVariant value(d.location); | 139 | QVariant value(d.location); |
2343 | 130 | QTest::newRow("LocationRole") << role << value << d; | 140 | QTest::newRow("LocationRole") << role << value << d; |
2344 | 131 | } | 141 | } |
2345 | 132 | { | 142 | { |
2346 | 133 | Device d; d.uri = "ipp://foo/bar"; | 143 | Device d; d.uri = "ipp://foo/bar"; |
2354 | 134 | d.makeModel = "hp"; | 144 | d.makeModel = "Bar BazJet"; |
2355 | 135 | DeviceModel::Roles role(DeviceModel::MakeModelRole); | 145 | DeviceModel::Roles role(DeviceModel::MakeModelRole); |
2356 | 136 | QVariant value(d.makeModel); | 146 | QVariant value("Bar BazJet"); |
2357 | 137 | QTest::newRow("MakeModelRole") << role << value << d; | 147 | QTest::newRow("MakeModelRole") << role << value << d; |
2358 | 138 | } | 148 | } |
2359 | 139 | { | 149 | { |
2360 | 140 | Device d; d.uri = "ipp://foo/bar"; | 150 | Device d; d.uri = "ipp://foo/bar"; |
2361 | 151 | d.makeModel = "HP LaserJet 4500"; | ||
2362 | 152 | DeviceModel::Roles role(DeviceModel::MakeModelRole); | ||
2363 | 153 | QVariant value("HP LaserJet 4500"); | ||
2364 | 154 | QTest::newRow("MakeModelRole") << role << value << d; | ||
2365 | 155 | } | ||
2366 | 156 | { | ||
2367 | 157 | Device d; d.uri = "ipp://foo/bar"; | ||
2368 | 158 | d.makeModel = "BazJet"; | ||
2369 | 141 | DeviceModel::Roles role(DeviceModel::TypeRole); | 159 | DeviceModel::Roles role(DeviceModel::TypeRole); |
2370 | 142 | QVariant value = QVariant::fromValue(d.type()); | 160 | QVariant value = QVariant::fromValue(d.type()); |
2371 | 143 | QTest::newRow("TypeRole") << role << value << d; | 161 | QTest::newRow("TypeRole") << role << value << d; |
2372 | @@ -152,6 +170,36 @@ | |||
2373 | 152 | m_backend->mockDeviceFound(device); | 170 | m_backend->mockDeviceFound(device); |
2374 | 153 | QCOMPARE(m_model->data(m_model->index(0), role), value); | 171 | QCOMPARE(m_model->data(m_model->index(0), role), value); |
2375 | 154 | } | 172 | } |
2376 | 173 | void testFiltering_data() | ||
2377 | 174 | { | ||
2378 | 175 | QTest::addColumn<QList<Device>>("devices"); | ||
2379 | 176 | QTest::addColumn<QString>("filterId"); | ||
2380 | 177 | QTest::addColumn<int>("targetCount"); | ||
2381 | 178 | QTest::addColumn<QStringList>("targetIds"); | ||
2382 | 179 | |||
2383 | 180 | { | ||
2384 | 181 | Device a; a.uri = "dnssd://foo/bar"; a.id = "foo"; | ||
2385 | 182 | Device b; b.uri = "ipps://bar/qux"; b.id = "bar"; | ||
2386 | 183 | |||
2387 | 184 | QList<Device> devs({a, b}); | ||
2388 | 185 | QTest::newRow("no filtering") << devs << "" << 2 << QStringList({"foo", "bar"}); | ||
2389 | 186 | } | ||
2390 | 187 | } | ||
2391 | 188 | void testFiltering() | ||
2392 | 189 | { | ||
2393 | 190 | QFETCH(QList<Device>, devices); | ||
2394 | 191 | QFETCH(QString, filterId); | ||
2395 | 192 | QFETCH(int, targetCount); | ||
2396 | 193 | QFETCH(QStringList, targetIds); | ||
2397 | 194 | |||
2398 | 195 | Q_FOREACH(auto dev, devices) { | ||
2399 | 196 | m_backend->mockDeviceFound(dev); | ||
2400 | 197 | } | ||
2401 | 198 | |||
2402 | 199 | DeviceFilter filter; | ||
2403 | 200 | filter.setSourceModel(m_model); | ||
2404 | 201 | QCOMPARE(filter.count(), targetCount); | ||
2405 | 202 | } | ||
2406 | 155 | private: | 203 | private: |
2407 | 156 | MockPrinterBackend *m_backend; | 204 | MockPrinterBackend *m_backend; |
2408 | 157 | DeviceModel *m_model; | 205 | DeviceModel *m_model; |
2409 | 158 | 206 | ||
2410 | === modified file 'tests/unittests/Printers/tst_printermodel.cpp' | |||
2411 | --- tests/unittests/Printers/tst_printermodel.cpp 2017-03-10 13:15:27 +0000 | |||
2412 | +++ tests/unittests/Printers/tst_printermodel.cpp 2017-04-07 12:51:46 +0000 | |||
2413 | @@ -17,9 +17,11 @@ | |||
2414 | 17 | #include "mockbackend.h" | 17 | #include "mockbackend.h" |
2415 | 18 | 18 | ||
2416 | 19 | #include "backend/backend.h" | 19 | #include "backend/backend.h" |
2417 | 20 | #include "cups/ppdutils.h" | ||
2418 | 20 | #include "models/printermodel.h" | 21 | #include "models/printermodel.h" |
2419 | 21 | #include "printer/printer.h" | 22 | #include "printer/printer.h" |
2420 | 22 | #include "printer/printerjob.h" | 23 | #include "printer/printerjob.h" |
2421 | 24 | #include "utils.h" | ||
2422 | 23 | 25 | ||
2423 | 24 | #include <QDebug> | 26 | #include <QDebug> |
2424 | 25 | #include <QObject> | 27 | #include <QObject> |
2425 | @@ -231,7 +233,7 @@ | |||
2426 | 231 | PrinterBackend* backend = new MockPrinterBackend("a-printer"); | 233 | PrinterBackend* backend = new MockPrinterBackend("a-printer"); |
2427 | 232 | ((MockPrinterBackend*) backend)->m_supportedDuplexModes = modes; | 234 | ((MockPrinterBackend*) backend)->m_supportedDuplexModes = modes; |
2428 | 233 | ((MockPrinterBackend*) backend)->printerOptions["a-printer"].insert( | 235 | ((MockPrinterBackend*) backend)->printerOptions["a-printer"].insert( |
2430 | 234 | "Duplex", QVariant::fromValue(Utils::duplexModeToPpdChoice(PrinterEnum::DuplexMode::DuplexLongSide)) | 236 | "Duplex", QVariant::fromValue(PpdUtils::duplexModeToPpdChoice(PrinterEnum::DuplexMode::DuplexLongSide)) |
2431 | 235 | ); | 237 | ); |
2432 | 236 | 238 | ||
2433 | 237 | auto printerA = QSharedPointer<Printer>(new Printer(backend)); | 239 | auto printerA = QSharedPointer<Printer>(new Printer(backend)); |
2434 | 238 | 240 | ||
2435 | === added file 'tests/unittests/Printers/tst_utils.cpp' | |||
2436 | --- tests/unittests/Printers/tst_utils.cpp 1970-01-01 00:00:00 +0000 | |||
2437 | +++ tests/unittests/Printers/tst_utils.cpp 2017-04-07 12:51:46 +0000 | |||
2438 | @@ -0,0 +1,107 @@ | |||
2439 | 1 | /* | ||
2440 | 2 | * Copyright (C) 2017 Canonical, Ltd. | ||
2441 | 3 | * | ||
2442 | 4 | * This program is free software; you can redistribute it and/or modify | ||
2443 | 5 | * it under the terms of the GNU Lesser General Public License as published by | ||
2444 | 6 | * the Free Software Foundation; version 3. | ||
2445 | 7 | * | ||
2446 | 8 | * This program is distributed in the hope that it will be useful, | ||
2447 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
2448 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
2449 | 11 | * GNU Lesser General Public License for more details. | ||
2450 | 12 | * | ||
2451 | 13 | * You should have received a copy of the GNU Lesser General Public License | ||
2452 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
2453 | 15 | */ | ||
2454 | 16 | |||
2455 | 17 | #include "utils.h" | ||
2456 | 18 | |||
2457 | 19 | #include <QObject> | ||
2458 | 20 | #include <QTest> | ||
2459 | 21 | |||
2460 | 22 | typedef QMap<QString, QString> IdDict; | ||
2461 | 23 | Q_DECLARE_METATYPE(IdDict) | ||
2462 | 24 | |||
2463 | 25 | class TstPrinterUtils : public QObject | ||
2464 | 26 | { | ||
2465 | 27 | Q_OBJECT | ||
2466 | 28 | private Q_SLOTS: | ||
2467 | 29 | void tstParseDeviceId_data() | ||
2468 | 30 | { | ||
2469 | 31 | QTest::addColumn<QString>("deviceId"); | ||
2470 | 32 | QTest::addColumn<IdDict>("expected"); | ||
2471 | 33 | |||
2472 | 34 | { | ||
2473 | 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;"); | ||
2474 | 36 | auto expected = QMap<QString, QString>{ | ||
2475 | 37 | {"MFG", "Hewlett-Packard"}, | ||
2476 | 38 | {"MDL", "HP Color LaserJet CM3530 MFP"}, | ||
2477 | 39 | {"CMD", "PJL,BIDI-ECP,PCLXL,PCL,PDF,PJL,POSTSCRIPT"}, | ||
2478 | 40 | {"CLS", "PRINTER"}, | ||
2479 | 41 | {"DES", "Hewlett-Packard Color LaserJet CM3530 MFP"}, | ||
2480 | 42 | {"SN", QString::null}, | ||
2481 | 43 | {"S", QString::null}, | ||
2482 | 44 | {"P", QString::null}, | ||
2483 | 45 | {"J", QString::null}, | ||
2484 | 46 | }; | ||
2485 | 47 | QTest::newRow("duplicate hp") << actual << expected; | ||
2486 | 48 | } | ||
2487 | 49 | { | ||
2488 | 50 | QString actual("MFG:HP;MDL:HP Officejet Pro 8610;CMD:PCL,JPEG,URF;"); | ||
2489 | 51 | auto expected = QMap<QString, QString>{ | ||
2490 | 52 | {"MFG", "HP"}, | ||
2491 | 53 | {"MDL", "HP Officejet Pro 8610"}, | ||
2492 | 54 | {"CMD", "PCL,JPEG,URF"}, | ||
2493 | 55 | {"CLS", QString::null}, | ||
2494 | 56 | {"DES", QString::null}, | ||
2495 | 57 | {"SN", QString::null}, | ||
2496 | 58 | {"S", QString::null}, | ||
2497 | 59 | {"P", QString::null}, | ||
2498 | 60 | {"J", QString::null}, | ||
2499 | 61 | }; | ||
2500 | 62 | QTest::newRow("actual hp device id") << actual << expected; | ||
2501 | 63 | } | ||
2502 | 64 | { | ||
2503 | 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;"); | ||
2504 | 66 | auto expected = QMap<QString, QString>{ | ||
2505 | 67 | {"MFG", "HP"}, | ||
2506 | 68 | {"MDL", "Officejet Pro 8600"}, | ||
2507 | 69 | {"CMD", "PCL3GUI,PCL3,PJL,JPEG,PCLM,URF,DW-PCL,802.11,802.3,DESKJET,DYN"}, | ||
2508 | 70 | {"CLS", "PRINTER"}, | ||
2509 | 71 | {"DES", "CM749A"}, | ||
2510 | 72 | {"SN", "CN371DWH1V05KC"}, | ||
2511 | 73 | {"S", "038080C484201021005a00800004518005a4418003c4618005a4118005a"}, | ||
2512 | 74 | {"P", QString::null}, | ||
2513 | 75 | {"J", QString::null}, | ||
2514 | 76 | {"Z", "0102,05000009000008000008000008000008,0600,0700000000000000000000,0b0000000000000000000099ba0000000099b50000000099b90000000099b6,0c0,0e00000000000000000000,0f00000000000000000000,10000002000008000008000008000008,110,12002,150,17000000000000000000000000000000,181"}, | ||
2515 | 77 | {"CID", "HPIJVIPAV2"}, | ||
2516 | 78 | {"LEDMDIS", "USB#FF#CC#00,USB#07#01#02"} | ||
2517 | 79 | }; | ||
2518 | 80 | QTest::newRow("actual, but crazy, hp device id") << actual << expected; | ||
2519 | 81 | } | ||
2520 | 82 | { | ||
2521 | 83 | QString actual("MFG:EPSON;CMD:ESCPL2,BDC,D4;MDL:Stylus Photo RX420;CLS:PRINTER;DES:EPSON Stylus Photo RX420;"); | ||
2522 | 84 | auto expected = QMap<QString, QString>{ | ||
2523 | 85 | {"MFG", "EPSON"}, | ||
2524 | 86 | {"MDL", "Stylus Photo RX420"}, | ||
2525 | 87 | {"CMD", "ESCPL2,BDC,D4"}, | ||
2526 | 88 | {"CLS", "PRINTER"}, | ||
2527 | 89 | {"DES", "EPSON Stylus Photo RX420"}, | ||
2528 | 90 | {"SN", QString::null}, | ||
2529 | 91 | {"S", QString::null}, | ||
2530 | 92 | {"P", QString::null}, | ||
2531 | 93 | {"J", QString::null}, | ||
2532 | 94 | }; | ||
2533 | 95 | QTest::newRow("some epson stylus") << actual << expected; | ||
2534 | 96 | } | ||
2535 | 97 | } | ||
2536 | 98 | void tstParseDeviceId() | ||
2537 | 99 | { | ||
2538 | 100 | QFETCH(QString, deviceId); | ||
2539 | 101 | QFETCH(IdDict, expected); | ||
2540 | 102 | QCOMPARE(Utils::parseDeviceId(deviceId), expected); | ||
2541 | 103 | } | ||
2542 | 104 | }; | ||
2543 | 105 | |||
2544 | 106 | QTEST_GUILESS_MAIN(TstPrinterUtils) | ||
2545 | 107 | #include "tst_utils.moc" |
Getting a crash when using this http:// pastebin. ubuntu. com/24228896/