Merge lp:~stolowski/unity-scopes-shell/social-attributes into lp:unity-scopes-shell

Proposed by Paweł Stołowski on 2016-03-02
Status: Superseded
Proposed branch: lp:~stolowski/unity-scopes-shell/social-attributes
Merge into: lp:unity-scopes-shell
Diff against target: 4545 lines (+3122/-273)
73 files modified
CMakeLists.txt (+2/-2)
debian/changelog (+6/-4)
debian/control.in (+3/-2)
debian/libscope-harness.symbols.in (+1/-0)
po/POTFILES.in (+11/-0)
po/am.po (+3/-2)
po/ar.po (+3/-2)
po/br.po (+3/-2)
po/ca.po (+3/-2)
po/cs.po (+3/-2)
po/da.po (+3/-2)
po/de.po (+3/-2)
po/el.po (+3/-2)
po/en_GB.po (+3/-2)
po/eo.po (+3/-2)
po/es.po (+3/-2)
po/eu.po (+3/-2)
po/fa.po (+3/-2)
po/fi.po (+3/-2)
po/fr.po (+3/-2)
po/gd.po (+3/-2)
po/gl.po (+3/-2)
po/hu.po (+3/-2)
po/it.po (+3/-2)
po/ja.po (+3/-2)
po/nb.po (+3/-2)
po/nl.po (+3/-2)
po/pl.po (+3/-2)
po/pt.po (+3/-2)
po/pt_BR.po (+3/-2)
po/ru.po (+3/-2)
po/sl.po (+3/-2)
po/sv.po (+3/-2)
po/ta.po (+3/-2)
po/uk.po (+3/-2)
po/unity-plugin-scopes.pot (+1/-1)
po/zh_TW.po (+3/-2)
src/Unity/CMakeLists.txt (+13/-0)
src/Unity/collectors.cpp (+14/-30)
src/Unity/collectors.h (+2/-1)
src/Unity/department.cpp (+12/-0)
src/Unity/department.h (+3/-0)
src/Unity/filters.cpp (+281/-0)
src/Unity/filters.h (+94/-0)
src/Unity/modelupdate.h (+143/-0)
src/Unity/optionselectorfilter.cpp (+173/-0)
src/Unity/optionselectorfilter.h (+69/-0)
src/Unity/optionselectoroptions.cpp (+164/-0)
src/Unity/optionselectoroptions.h (+64/-0)
src/Unity/plugin.cpp (+12/-0)
src/Unity/rangeinputfilter.cpp (+298/-0)
src/Unity/rangeinputfilter.h (+93/-0)
src/Unity/resultsmodel.cpp (+2/-0)
src/Unity/scope.cpp (+139/-122)
src/Unity/scope.h (+18/-12)
src/Unity/utils.cpp (+2/-0)
src/Unity/valuesliderfilter.cpp (+155/-0)
src/Unity/valuesliderfilter.h (+70/-0)
src/Unity/valueslidervalues.cpp (+82/-0)
src/Unity/valueslidervalues.h (+56/-0)
src/python/CMakeLists.txt (+1/-1)
src/scope-harness/test-utils.cpp (+7/-0)
src/scope-harness/test-utils.h (+3/-0)
src/scope-harness/view/results-view.cpp (+12/-30)
tests/CMakeLists.txt (+7/-2)
tests/data/CMakeLists.txt (+1/-0)
tests/data/mock-scope-filters/CMakeLists.txt (+15/-0)
tests/data/mock-scope-filters/mock-scope-filters.cpp (+132/-0)
tests/data/mock-scope-filters/mock-scope-filters.ini.in (+8/-0)
tests/departmentstest.cpp (+0/-4)
tests/filtersendtoendtest.cpp (+367/-0)
tests/filterstest.cpp (+230/-0)
tests/optionselectorfiltertest.cpp (+263/-0)
To merge this branch: bzr merge lp:~stolowski/unity-scopes-shell/social-attributes
Reviewer Review Type Date Requested Status
Andrea Cimitan 2016-03-02 Pending
Unity Team 2016-03-02 Pending
Review via email: mp+287808@code.launchpad.net

This proposal has been superseded by a proposal from 2016-03-16.

Commit Message

Handle social-actions.

Description of the Change

To post a comment you must log in.
293. By Paweł Stołowski on 2016-03-03

Renamed to social-actions

294. By Paweł Stołowski on 2016-03-04

Provide unity-scopes-impl-10

295. By Paweł Stołowski on 2016-03-07

Merged trunk

296. By Paweł Stołowski on 2016-03-16

Merged filters

297. By Paweł Stołowski on 2016-03-24

Merged trunk

298. By Paweł Stołowski on 2016-03-24

Bump dependencies

299. By Paweł Stołowski on 2016-03-29

Debug

300. By Paweł Stołowski on 2016-03-29

Updated CATEGORY_JSON_DEFAULTS

301. By Paweł Stołowski on 2016-03-29

Fix updated CATEGORY_JSON_DEFAULTS

302. By Paweł Stołowski on 2016-03-29

Fix test failure

303. By Paweł Stołowski on 2016-03-29

Removed extra debug

304. By Paweł Stołowski on 2016-03-29

Debug updateResult

305. By Paweł Stołowski on 2016-03-29

Debug

306. By Paweł Stołowski on 2016-04-14

Merged trunk

307. By Paweł Stołowski on 2016-04-15

Bump

308. By Paweł Stołowski on 2016-04-26

Bump

309. By Paweł Stołowski on 2016-04-26

Fix version

310. By Paweł Stołowski on 2016-04-26

Fix version

311. By Paweł Stołowski on 2016-05-05

Merged trunk

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'CMakeLists.txt'
2--- CMakeLists.txt 2016-02-18 15:46:26 +0000
3+++ CMakeLists.txt 2016-03-16 11:17:35 +0000
4@@ -50,8 +50,8 @@
5 find_package(Qt5Test)
6 find_package(Boost COMPONENTS regex REQUIRED)
7
8-pkg_check_modules(SCOPESLIB REQUIRED libunity-scopes>=1.0.1)
9-pkg_check_modules(SCOPES_API REQUIRED unity-shell-scopes=10)
10+pkg_check_modules(SCOPESLIB REQUIRED libunity-scopes>=1.0.4)
11+pkg_check_modules(SCOPES_API REQUIRED unity-shell-scopes=11)
12
13 pkg_check_modules(GSETTINGSQT REQUIRED gsettings-qt)
14 pkg_check_modules(UBUNTU_LOCATION_SERVICE REQUIRED ubuntu-location-service)
15
16=== modified file 'debian/changelog'
17--- debian/changelog 2016-02-26 12:38:34 +0000
18+++ debian/changelog 2016-03-16 11:17:35 +0000
19@@ -1,3 +1,9 @@
20+unity-scopes-shell (0.5.7-0ubuntu1) UNRELEASED; urgency=medium
21+
22+ * Preliminary support for filters.
23+
24+ -- Pawel Stolowski <pawel.stolowski@canonical.com> Fri, 30 Oct 2015 16:17:55 +0100
25+
26 unity-scopes-shell (0.5.6+16.04.20160226.1-0ubuntu1) xenial; urgency=medium
27
28 [ CI Train Bot ]
29@@ -104,10 +110,6 @@
30 * Call parent class ::event (LP: #1495467)
31 * Improvements from running clazy over the code
32
33- [ CI Train Bot ]
34- * Improvements from running clazy over the code
35- * New rebuild forced.
36-
37 -- Pawel Stolowski <ci-train-bot@canonical.com> Thu, 24 Sep 2015 12:44:30 +0000
38
39 unity-scopes-shell (0.5.5+15.10.20150922-0ubuntu1) wily; urgency=medium
40
41=== modified file 'debian/control.in'
42--- debian/control.in 2016-02-18 15:46:26 +0000
43+++ debian/control.in 2016-03-16 11:17:35 +0000
44@@ -8,8 +8,8 @@
45 dh-python,
46 libboost-python-dev,
47 libboost-regex-dev,
48- libunity-api-dev (>= 7.107),
49- libunity-scopes-dev (>= 1.0.1~),
50+ libunity-api-dev (>= 7.108),
51+ libunity-scopes-dev (>= 1.0.4~),
52 libgsettings-qt-dev (>= 0.1),
53 libqtdbustest1-dev (>= 0.2),
54 libqtdbusmock1-dev (>= 0.2),
55@@ -51,6 +51,7 @@
56 unity-scopes-impl-8,
57 unity-scopes-impl-9,
58 unity-scopes-impl-10,
59+ unity-scopes-impl-11,
60 Breaks: unity8-private (<< 7.84),
61 unity8 (<< 8.11)
62 Replaces: unity8-private (<< 7.84)
63
64=== modified file 'debian/libscope-harness.symbols.in'
65--- debian/libscope-harness.symbols.in 2016-02-26 12:38:34 +0000
66+++ debian/libscope-harness.symbols.in 2016-03-16 11:17:35 +0000
67@@ -271,6 +271,7 @@
68 (c++)"unity::scopeharness::TestUtils::throwIfNot(bool, std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)@Base" 0.5.4+15.04.20150311.3
69 (c++)"unity::scopeharness::TestUtils::waitForResultsChange(QSharedPointer<unity::shell::scopes::ScopeInterface>)@Base" 0.5.4+15.04.20150311.3
70 (c++)"unity::scopeharness::TestUtils::waitForSearchFinish(QSharedPointer<unity::shell::scopes::ScopeInterface>)@Base" 0.5.4+15.04.20150311.3
71+ (c++)"unity::scopeharness::TestUtils::waitForFilterStateChange(QSharedPointer<unity::shell::scopes::ScopeInterface>)@Base" 0replaceme
72 (c++)"unity::scopeharness::view::AbstractView::~AbstractView()@Base" 0.5.4+15.04.20150311.3
73 (c++)"unity::scopeharness::view::PreviewView::columnCount() const@Base" 0.5.4+15.04.20150311.3
74 (c++)"unity::scopeharness::view::PreviewView::~PreviewView()@Base" 0.5.4+15.04.20150311.3
75
76=== modified file 'po/POTFILES.in'
77--- po/POTFILES.in 2016-02-11 15:35:47 +0000
78+++ po/POTFILES.in 2016-03-16 11:17:35 +0000
79@@ -1,14 +1,18 @@
80 tools/location/main.cpp
81 tests/resultstest.cpp
82+tests/filterstest.cpp
83 tests/settingsendtoendtest.cpp
84 tests/utilstest.cpp
85 tests/previewtest.cpp
86 tests/overviewtest.cpp
87 tests/departmentstest.cpp
88 tests/locationtest.cpp
89+tests/filtersendtoendtest.cpp
90 tests/favoritestest.cpp
91+tests/optionselectorfiltertest.cpp
92 tests/data/scopes/scopes.cpp
93 tests/data/mock-scope-departments/mock-scope-departments.cpp
94+tests/data/mock-scope-filters/mock-scope-filters.cpp
95 tests/data/mock-scope-ttl/mock-scope-ttl.cpp
96 tests/data/mock-scope-info/mock-scope-info.cpp
97 tests/data/mock-scope-departments-flipflop/mock-scope-departments-flipflop.cpp
98@@ -71,8 +75,11 @@
99 src/Unity/geoip.cpp
100 src/Unity/plugin.cpp
101 src/Unity/scopes.cpp
102+src/Unity/filters.cpp
103+src/Unity/optionselectoroptions.cpp
104 src/Unity/settingsmodel.cpp
105 src/Unity/utils.cpp
106+src/Unity/optionselectorfilter.cpp
107 src/Unity/resultsmap.cpp
108 src/Unity/locationservice.cpp
109 src/Unity/ubuntulocationservice.cpp
110@@ -117,7 +124,9 @@
111 src/scope-harness/view/results-view.h
112 src/scope-harness/view/preview-view.h
113 src/scope-harness/view/abstract-view.h
114+src/Unity/optionselectoroptions.h
115 src/Unity/iconutils.h
116+src/Unity/optionselectorfilter.h
117 src/Unity/ubuntulocationservice.h
118 src/Unity/localization.h
119 src/Unity/previewmodel.h
120@@ -126,8 +135,10 @@
121 src/Unity/plugin.h
122 src/Unity/department.h
123 src/Unity/scope.h
124+src/Unity/filters.h
125 src/Unity/logintoaccount.h
126 src/Unity/overviewcategories.h
127+src/Unity/modelupdate.h
128 src/Unity/locationservice.h
129 src/Unity/overviewscope.h
130 src/Unity/settingsmodel.h
131
132=== modified file 'po/am.po'
133--- po/am.po 2016-03-05 06:20:25 +0000
134+++ po/am.po 2016-03-16 11:17:35 +0000
135@@ -6,11 +6,12 @@
136 msgid ""
137 msgstr ""
138 "Project-Id-Version: unity-scopes-shell\n"
139-"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
140-"POT-Creation-Date: 2015-09-28 08:40+0000\n"
141+"Report-Msgid-Bugs-To: \n"
142+"POT-Creation-Date: 2015-09-01 10:42+0000\n"
143 "PO-Revision-Date: 2015-07-28 19:12+0000\n"
144 "Last-Translator: samson <Unknown>\n"
145 "Language-Team: Amharic <am@li.org>\n"
146+"Language: am\n"
147 "MIME-Version: 1.0\n"
148 "Content-Type: text/plain; charset=UTF-8\n"
149 "Content-Transfer-Encoding: 8bit\n"
150
151=== modified file 'po/ar.po'
152--- po/ar.po 2016-03-05 06:20:25 +0000
153+++ po/ar.po 2016-03-16 11:17:35 +0000
154@@ -6,11 +6,12 @@
155 msgid ""
156 msgstr ""
157 "Project-Id-Version: unity-scopes-shell\n"
158-"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
159-"POT-Creation-Date: 2015-09-28 08:40+0000\n"
160+"Report-Msgid-Bugs-To: \n"
161+"POT-Creation-Date: 2015-09-01 10:42+0000\n"
162 "PO-Revision-Date: 2015-07-18 12:07+0000\n"
163 "Last-Translator: Ibrahim Saed <ibraheem5000@gmail.com>\n"
164 "Language-Team: Arabic <ar@li.org>\n"
165+"Language: ar\n"
166 "MIME-Version: 1.0\n"
167 "Content-Type: text/plain; charset=UTF-8\n"
168 "Content-Transfer-Encoding: 8bit\n"
169
170=== modified file 'po/br.po'
171--- po/br.po 2016-03-05 06:20:25 +0000
172+++ po/br.po 2016-03-16 11:17:35 +0000
173@@ -6,11 +6,12 @@
174 msgid ""
175 msgstr ""
176 "Project-Id-Version: unity-scopes-shell\n"
177-"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
178-"POT-Creation-Date: 2015-09-28 08:40+0000\n"
179+"Report-Msgid-Bugs-To: \n"
180+"POT-Creation-Date: 2015-09-01 10:42+0000\n"
181 "PO-Revision-Date: 2015-07-16 12:51+0000\n"
182 "Last-Translator: Rouxel <vincent.rouxel@mailoo.org>\n"
183 "Language-Team: Breton <br@li.org>\n"
184+"Language: br\n"
185 "MIME-Version: 1.0\n"
186 "Content-Type: text/plain; charset=UTF-8\n"
187 "Content-Transfer-Encoding: 8bit\n"
188
189=== modified file 'po/ca.po'
190--- po/ca.po 2016-03-05 06:20:25 +0000
191+++ po/ca.po 2016-03-16 11:17:35 +0000
192@@ -6,11 +6,12 @@
193 msgid ""
194 msgstr ""
195 "Project-Id-Version: unity-scopes-shell\n"
196-"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
197-"POT-Creation-Date: 2015-09-28 08:40+0000\n"
198+"Report-Msgid-Bugs-To: \n"
199+"POT-Creation-Date: 2015-09-01 10:42+0000\n"
200 "PO-Revision-Date: 2015-07-16 05:42+0000\n"
201 "Last-Translator: David Planella <david.planella@ubuntu.com>\n"
202 "Language-Team: Catalan <ca@li.org>\n"
203+"Language: ca\n"
204 "MIME-Version: 1.0\n"
205 "Content-Type: text/plain; charset=UTF-8\n"
206 "Content-Transfer-Encoding: 8bit\n"
207
208=== modified file 'po/cs.po'
209--- po/cs.po 2016-03-05 06:20:25 +0000
210+++ po/cs.po 2016-03-16 11:17:35 +0000
211@@ -6,11 +6,12 @@
212 msgid ""
213 msgstr ""
214 "Project-Id-Version: unity-scopes-shell\n"
215-"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
216-"POT-Creation-Date: 2015-09-28 08:40+0000\n"
217+"Report-Msgid-Bugs-To: \n"
218+"POT-Creation-Date: 2015-09-01 10:42+0000\n"
219 "PO-Revision-Date: 2016-02-09 08:56+0000\n"
220 "Last-Translator: Vojtěch Daněk <vdanek@outlook.com>\n"
221 "Language-Team: Czech <cs@li.org>\n"
222+"Language: cs\n"
223 "MIME-Version: 1.0\n"
224 "Content-Type: text/plain; charset=UTF-8\n"
225 "Content-Transfer-Encoding: 8bit\n"
226
227=== modified file 'po/da.po'
228--- po/da.po 2016-03-05 06:20:25 +0000
229+++ po/da.po 2016-03-16 11:17:35 +0000
230@@ -6,11 +6,12 @@
231 msgid ""
232 msgstr ""
233 "Project-Id-Version: unity-scopes-shell\n"
234-"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
235-"POT-Creation-Date: 2015-09-28 08:40+0000\n"
236+"Report-Msgid-Bugs-To: \n"
237+"POT-Creation-Date: 2015-09-01 10:42+0000\n"
238 "PO-Revision-Date: 2015-06-03 19:27+0000\n"
239 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
240 "Language-Team: Danish <da@li.org>\n"
241+"Language: da\n"
242 "MIME-Version: 1.0\n"
243 "Content-Type: text/plain; charset=UTF-8\n"
244 "Content-Transfer-Encoding: 8bit\n"
245
246=== modified file 'po/de.po'
247--- po/de.po 2016-03-05 06:20:25 +0000
248+++ po/de.po 2016-03-16 11:17:35 +0000
249@@ -6,11 +6,12 @@
250 msgid ""
251 msgstr ""
252 "Project-Id-Version: unity-scopes-shell\n"
253-"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
254-"POT-Creation-Date: 2015-09-28 08:40+0000\n"
255+"Report-Msgid-Bugs-To: \n"
256+"POT-Creation-Date: 2015-09-01 10:42+0000\n"
257 "PO-Revision-Date: 2015-07-19 19:11+0000\n"
258 "Last-Translator: Phillip Sz <Unknown>\n"
259 "Language-Team: German <de@li.org>\n"
260+"Language: de\n"
261 "MIME-Version: 1.0\n"
262 "Content-Type: text/plain; charset=UTF-8\n"
263 "Content-Transfer-Encoding: 8bit\n"
264
265=== modified file 'po/el.po'
266--- po/el.po 2016-03-05 06:20:25 +0000
267+++ po/el.po 2016-03-16 11:17:35 +0000
268@@ -6,11 +6,12 @@
269 msgid ""
270 msgstr ""
271 "Project-Id-Version: unity-scopes-shell\n"
272-"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
273-"POT-Creation-Date: 2015-09-28 08:40+0000\n"
274+"Report-Msgid-Bugs-To: \n"
275+"POT-Creation-Date: 2015-09-01 10:42+0000\n"
276 "PO-Revision-Date: 2015-07-16 09:19+0000\n"
277 "Last-Translator: Simos Xenitellis <Unknown>\n"
278 "Language-Team: Greek <el@li.org>\n"
279+"Language: el\n"
280 "MIME-Version: 1.0\n"
281 "Content-Type: text/plain; charset=UTF-8\n"
282 "Content-Transfer-Encoding: 8bit\n"
283
284=== modified file 'po/en_GB.po'
285--- po/en_GB.po 2016-03-05 06:20:25 +0000
286+++ po/en_GB.po 2016-03-16 11:17:35 +0000
287@@ -6,11 +6,12 @@
288 msgid ""
289 msgstr ""
290 "Project-Id-Version: unity-scopes-shell\n"
291-"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
292-"POT-Creation-Date: 2015-09-28 08:40+0000\n"
293+"Report-Msgid-Bugs-To: \n"
294+"POT-Creation-Date: 2015-09-01 10:42+0000\n"
295 "PO-Revision-Date: 2016-02-03 11:21+0000\n"
296 "Last-Translator: James Tait <Unknown>\n"
297 "Language-Team: English (United Kingdom) <en_GB@li.org>\n"
298+"Language: \n"
299 "MIME-Version: 1.0\n"
300 "Content-Type: text/plain; charset=UTF-8\n"
301 "Content-Transfer-Encoding: 8bit\n"
302
303=== modified file 'po/eo.po'
304--- po/eo.po 2016-03-05 06:20:25 +0000
305+++ po/eo.po 2016-03-16 11:17:35 +0000
306@@ -6,11 +6,12 @@
307 msgid ""
308 msgstr ""
309 "Project-Id-Version: unity-scopes-shell\n"
310-"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
311-"POT-Creation-Date: 2015-09-28 08:40+0000\n"
312+"Report-Msgid-Bugs-To: \n"
313+"POT-Creation-Date: 2015-09-01 10:42+0000\n"
314 "PO-Revision-Date: 2015-07-16 09:53+0000\n"
315 "Last-Translator: Adolfo Jayme <fitoschido@gmail.com>\n"
316 "Language-Team: Esperanto <eo@li.org>\n"
317+"Language: eo\n"
318 "MIME-Version: 1.0\n"
319 "Content-Type: text/plain; charset=UTF-8\n"
320 "Content-Transfer-Encoding: 8bit\n"
321
322=== modified file 'po/es.po'
323--- po/es.po 2016-03-05 06:20:25 +0000
324+++ po/es.po 2016-03-16 11:17:35 +0000
325@@ -6,11 +6,12 @@
326 msgid ""
327 msgstr ""
328 "Project-Id-Version: unity-scopes-shell\n"
329-"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
330-"POT-Creation-Date: 2015-09-28 08:40+0000\n"
331+"Report-Msgid-Bugs-To: \n"
332+"POT-Creation-Date: 2015-09-01 10:42+0000\n"
333 "PO-Revision-Date: 2015-07-16 09:48+0000\n"
334 "Last-Translator: Adolfo Jayme <fitoschido@gmail.com>\n"
335 "Language-Team: Spanish <es@li.org>\n"
336+"Language: es\n"
337 "MIME-Version: 1.0\n"
338 "Content-Type: text/plain; charset=UTF-8\n"
339 "Content-Transfer-Encoding: 8bit\n"
340
341=== modified file 'po/eu.po'
342--- po/eu.po 2016-03-05 06:20:25 +0000
343+++ po/eu.po 2016-03-16 11:17:35 +0000
344@@ -6,11 +6,12 @@
345 msgid ""
346 msgstr ""
347 "Project-Id-Version: unity-scopes-shell\n"
348-"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
349-"POT-Creation-Date: 2015-09-28 08:40+0000\n"
350+"Report-Msgid-Bugs-To: \n"
351+"POT-Creation-Date: 2015-09-01 10:42+0000\n"
352 "PO-Revision-Date: 2015-07-16 07:02+0000\n"
353 "Last-Translator: Ibai Oihanguren Sala <Unknown>\n"
354 "Language-Team: Basque <eu@li.org>\n"
355+"Language: eu\n"
356 "MIME-Version: 1.0\n"
357 "Content-Type: text/plain; charset=UTF-8\n"
358 "Content-Transfer-Encoding: 8bit\n"
359
360=== modified file 'po/fa.po'
361--- po/fa.po 2016-03-05 06:20:25 +0000
362+++ po/fa.po 2016-03-16 11:17:35 +0000
363@@ -6,11 +6,12 @@
364 msgid ""
365 msgstr ""
366 "Project-Id-Version: unity-scopes-shell\n"
367-"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
368-"POT-Creation-Date: 2015-09-28 08:40+0000\n"
369+"Report-Msgid-Bugs-To: \n"
370+"POT-Creation-Date: 2015-09-01 10:42+0000\n"
371 "PO-Revision-Date: 2015-07-16 12:38+0000\n"
372 "Last-Translator: Danial Behzadi <dani.behzi@gmail.com>\n"
373 "Language-Team: Persian <fa@li.org>\n"
374+"Language: fa\n"
375 "MIME-Version: 1.0\n"
376 "Content-Type: text/plain; charset=UTF-8\n"
377 "Content-Transfer-Encoding: 8bit\n"
378
379=== modified file 'po/fi.po'
380--- po/fi.po 2016-03-05 06:20:25 +0000
381+++ po/fi.po 2016-03-16 11:17:35 +0000
382@@ -6,11 +6,12 @@
383 msgid ""
384 msgstr ""
385 "Project-Id-Version: unity-scopes-shell\n"
386-"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
387-"POT-Creation-Date: 2015-09-28 08:40+0000\n"
388+"Report-Msgid-Bugs-To: \n"
389+"POT-Creation-Date: 2015-09-01 10:42+0000\n"
390 "PO-Revision-Date: 2015-07-16 07:13+0000\n"
391 "Last-Translator: Jiri Grönroos <Unknown>\n"
392 "Language-Team: Finnish <fi@li.org>\n"
393+"Language: fi\n"
394 "MIME-Version: 1.0\n"
395 "Content-Type: text/plain; charset=UTF-8\n"
396 "Content-Transfer-Encoding: 8bit\n"
397
398=== modified file 'po/fr.po'
399--- po/fr.po 2016-03-05 06:20:25 +0000
400+++ po/fr.po 2016-03-16 11:17:35 +0000
401@@ -6,11 +6,12 @@
402 msgid ""
403 msgstr ""
404 "Project-Id-Version: unity-scopes-shell\n"
405-"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
406-"POT-Creation-Date: 2015-09-28 08:40+0000\n"
407+"Report-Msgid-Bugs-To: \n"
408+"POT-Creation-Date: 2015-09-01 10:42+0000\n"
409 "PO-Revision-Date: 2015-07-17 11:19+0000\n"
410 "Last-Translator: Nicolas Delvaux <Unknown>\n"
411 "Language-Team: French <fr@li.org>\n"
412+"Language: fr\n"
413 "MIME-Version: 1.0\n"
414 "Content-Type: text/plain; charset=UTF-8\n"
415 "Content-Transfer-Encoding: 8bit\n"
416
417=== modified file 'po/gd.po'
418--- po/gd.po 2016-03-05 06:20:25 +0000
419+++ po/gd.po 2016-03-16 11:17:35 +0000
420@@ -6,11 +6,12 @@
421 msgid ""
422 msgstr ""
423 "Project-Id-Version: unity-scopes-shell\n"
424-"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
425-"POT-Creation-Date: 2015-09-28 08:40+0000\n"
426+"Report-Msgid-Bugs-To: \n"
427+"POT-Creation-Date: 2015-09-01 10:42+0000\n"
428 "PO-Revision-Date: 2015-06-17 14:57+0000\n"
429 "Last-Translator: GunChleoc <Unknown>\n"
430 "Language-Team: Gaelic; Scottish <gd@li.org>\n"
431+"Language: \n"
432 "MIME-Version: 1.0\n"
433 "Content-Type: text/plain; charset=UTF-8\n"
434 "Content-Transfer-Encoding: 8bit\n"
435
436=== modified file 'po/gl.po'
437--- po/gl.po 2016-03-05 06:20:25 +0000
438+++ po/gl.po 2016-03-16 11:17:35 +0000
439@@ -6,11 +6,12 @@
440 msgid ""
441 msgstr ""
442 "Project-Id-Version: unity-scopes-shell\n"
443-"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
444-"POT-Creation-Date: 2015-09-28 08:40+0000\n"
445+"Report-Msgid-Bugs-To: \n"
446+"POT-Creation-Date: 2015-09-01 10:42+0000\n"
447 "PO-Revision-Date: 2015-07-16 09:18+0000\n"
448 "Last-Translator: Marcos Lans <Unknown>\n"
449 "Language-Team: Galician <gl@li.org>\n"
450+"Language: gl\n"
451 "MIME-Version: 1.0\n"
452 "Content-Type: text/plain; charset=UTF-8\n"
453 "Content-Transfer-Encoding: 8bit\n"
454
455=== modified file 'po/hu.po'
456--- po/hu.po 2016-03-05 06:20:25 +0000
457+++ po/hu.po 2016-03-16 11:17:35 +0000
458@@ -6,11 +6,12 @@
459 msgid ""
460 msgstr ""
461 "Project-Id-Version: unity-scopes-shell\n"
462-"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
463-"POT-Creation-Date: 2015-09-28 08:40+0000\n"
464+"Report-Msgid-Bugs-To: \n"
465+"POT-Creation-Date: 2015-09-01 10:42+0000\n"
466 "PO-Revision-Date: 2015-07-16 06:19+0000\n"
467 "Last-Translator: Richard Somlói <ricsipontaz@gmail.com>\n"
468 "Language-Team: Hungarian <hu@li.org>\n"
469+"Language: hu\n"
470 "MIME-Version: 1.0\n"
471 "Content-Type: text/plain; charset=UTF-8\n"
472 "Content-Transfer-Encoding: 8bit\n"
473
474=== modified file 'po/it.po'
475--- po/it.po 2016-03-05 06:20:25 +0000
476+++ po/it.po 2016-03-16 11:17:35 +0000
477@@ -6,11 +6,12 @@
478 msgid ""
479 msgstr ""
480 "Project-Id-Version: unity-scopes-shell\n"
481-"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
482-"POT-Creation-Date: 2015-09-28 08:40+0000\n"
483+"Report-Msgid-Bugs-To: \n"
484+"POT-Creation-Date: 2015-09-01 10:42+0000\n"
485 "PO-Revision-Date: 2015-07-16 06:11+0000\n"
486 "Last-Translator: Milo Casagrande <milo.casagrande@gmail.com>\n"
487 "Language-Team: Italian <it@li.org>\n"
488+"Language: it\n"
489 "MIME-Version: 1.0\n"
490 "Content-Type: text/plain; charset=UTF-8\n"
491 "Content-Transfer-Encoding: 8bit\n"
492
493=== modified file 'po/ja.po'
494--- po/ja.po 2016-03-05 06:20:25 +0000
495+++ po/ja.po 2016-03-16 11:17:35 +0000
496@@ -6,11 +6,12 @@
497 msgid ""
498 msgstr ""
499 "Project-Id-Version: unity-scopes-shell\n"
500-"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
501-"POT-Creation-Date: 2015-09-28 08:40+0000\n"
502+"Report-Msgid-Bugs-To: \n"
503+"POT-Creation-Date: 2015-09-01 10:42+0000\n"
504 "PO-Revision-Date: 2015-07-16 07:23+0000\n"
505 "Last-Translator: Mitsuya Shibata <mty.shibata@gmail.com>\n"
506 "Language-Team: Japanese <ja@li.org>\n"
507+"Language: ja\n"
508 "MIME-Version: 1.0\n"
509 "Content-Type: text/plain; charset=UTF-8\n"
510 "Content-Transfer-Encoding: 8bit\n"
511
512=== modified file 'po/nb.po'
513--- po/nb.po 2016-03-05 06:20:25 +0000
514+++ po/nb.po 2016-03-16 11:17:35 +0000
515@@ -6,11 +6,12 @@
516 msgid ""
517 msgstr ""
518 "Project-Id-Version: unity-scopes-shell\n"
519-"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
520-"POT-Creation-Date: 2015-09-28 08:40+0000\n"
521+"Report-Msgid-Bugs-To: \n"
522+"POT-Creation-Date: 2015-09-01 10:42+0000\n"
523 "PO-Revision-Date: 2015-07-16 10:49+0000\n"
524 "Last-Translator: Åka Sikrom <Unknown>\n"
525 "Language-Team: Norwegian Bokmal <nb@li.org>\n"
526+"Language: nb\n"
527 "MIME-Version: 1.0\n"
528 "Content-Type: text/plain; charset=UTF-8\n"
529 "Content-Transfer-Encoding: 8bit\n"
530
531=== modified file 'po/nl.po'
532--- po/nl.po 2016-03-05 06:20:25 +0000
533+++ po/nl.po 2016-03-16 11:17:35 +0000
534@@ -6,11 +6,12 @@
535 msgid ""
536 msgstr ""
537 "Project-Id-Version: unity-scopes-shell\n"
538-"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
539-"POT-Creation-Date: 2015-09-28 08:40+0000\n"
540+"Report-Msgid-Bugs-To: \n"
541+"POT-Creation-Date: 2015-09-01 10:42+0000\n"
542 "PO-Revision-Date: 2015-07-16 14:53+0000\n"
543 "Last-Translator: Hannie Dumoleyn <Unknown>\n"
544 "Language-Team: Dutch <nl@li.org>\n"
545+"Language: nl\n"
546 "MIME-Version: 1.0\n"
547 "Content-Type: text/plain; charset=UTF-8\n"
548 "Content-Transfer-Encoding: 8bit\n"
549
550=== modified file 'po/pl.po'
551--- po/pl.po 2016-03-05 06:20:25 +0000
552+++ po/pl.po 2016-03-16 11:17:35 +0000
553@@ -6,11 +6,12 @@
554 msgid ""
555 msgstr ""
556 "Project-Id-Version: unity-scopes-shell\n"
557-"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
558-"POT-Creation-Date: 2015-09-28 08:40+0000\n"
559+"Report-Msgid-Bugs-To: \n"
560+"POT-Creation-Date: 2015-09-01 10:42+0000\n"
561 "PO-Revision-Date: 2015-07-30 15:26+0000\n"
562 "Last-Translator: Krzysztof Tataradziński <Unknown>\n"
563 "Language-Team: Polish <pl@li.org>\n"
564+"Language: pl\n"
565 "MIME-Version: 1.0\n"
566 "Content-Type: text/plain; charset=UTF-8\n"
567 "Content-Transfer-Encoding: 8bit\n"
568
569=== modified file 'po/pt.po'
570--- po/pt.po 2016-03-05 06:20:25 +0000
571+++ po/pt.po 2016-03-16 11:17:35 +0000
572@@ -6,11 +6,12 @@
573 msgid ""
574 msgstr ""
575 "Project-Id-Version: unity-scopes-shell\n"
576-"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
577-"POT-Creation-Date: 2015-09-28 08:40+0000\n"
578+"Report-Msgid-Bugs-To: \n"
579+"POT-Creation-Date: 2015-09-01 10:42+0000\n"
580 "PO-Revision-Date: 2015-07-29 10:01+0000\n"
581 "Last-Translator: Ivo Xavier <ivofernandes12@gmail.com>\n"
582 "Language-Team: Portuguese <pt@li.org>\n"
583+"Language: pt\n"
584 "MIME-Version: 1.0\n"
585 "Content-Type: text/plain; charset=UTF-8\n"
586 "Content-Transfer-Encoding: 8bit\n"
587
588=== modified file 'po/pt_BR.po'
589--- po/pt_BR.po 2016-03-05 06:20:25 +0000
590+++ po/pt_BR.po 2016-03-16 11:17:35 +0000
591@@ -6,11 +6,12 @@
592 msgid ""
593 msgstr ""
594 "Project-Id-Version: unity-scopes-shell\n"
595-"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
596-"POT-Creation-Date: 2015-09-28 08:40+0000\n"
597+"Report-Msgid-Bugs-To: \n"
598+"POT-Creation-Date: 2015-09-01 10:42+0000\n"
599 "PO-Revision-Date: 2015-08-05 04:40+0000\n"
600 "Last-Translator: Tiago Hillebrandt <tiagohillebrandt@gmail.com>\n"
601 "Language-Team: Brazilian Portuguese <pt_BR@li.org>\n"
602+"Language: pt_BR\n"
603 "MIME-Version: 1.0\n"
604 "Content-Type: text/plain; charset=UTF-8\n"
605 "Content-Transfer-Encoding: 8bit\n"
606
607=== modified file 'po/ru.po'
608--- po/ru.po 2016-03-05 06:20:25 +0000
609+++ po/ru.po 2016-03-16 11:17:35 +0000
610@@ -6,11 +6,12 @@
611 msgid ""
612 msgstr ""
613 "Project-Id-Version: unity-scopes-shell\n"
614-"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
615-"POT-Creation-Date: 2015-09-28 08:40+0000\n"
616+"Report-Msgid-Bugs-To: \n"
617+"POT-Creation-Date: 2015-09-01 10:42+0000\n"
618 "PO-Revision-Date: 2015-07-16 05:57+0000\n"
619 "Last-Translator: ☠Jay ZDLin☠ <Unknown>\n"
620 "Language-Team: Russian <ru@li.org>\n"
621+"Language: ru\n"
622 "MIME-Version: 1.0\n"
623 "Content-Type: text/plain; charset=UTF-8\n"
624 "Content-Transfer-Encoding: 8bit\n"
625
626=== modified file 'po/sl.po'
627--- po/sl.po 2016-03-05 06:20:25 +0000
628+++ po/sl.po 2016-03-16 11:17:35 +0000
629@@ -6,11 +6,12 @@
630 msgid ""
631 msgstr ""
632 "Project-Id-Version: unity-scopes-shell\n"
633-"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
634-"POT-Creation-Date: 2015-09-28 08:40+0000\n"
635+"Report-Msgid-Bugs-To: \n"
636+"POT-Creation-Date: 2015-09-01 10:42+0000\n"
637 "PO-Revision-Date: 2015-07-20 05:29+0000\n"
638 "Last-Translator: Sasa Batistic <Unknown>\n"
639 "Language-Team: Slovenian <sl@li.org>\n"
640+"Language: sl\n"
641 "MIME-Version: 1.0\n"
642 "Content-Type: text/plain; charset=UTF-8\n"
643 "Content-Transfer-Encoding: 8bit\n"
644
645=== modified file 'po/sv.po'
646--- po/sv.po 2016-03-05 06:20:25 +0000
647+++ po/sv.po 2016-03-16 11:17:35 +0000
648@@ -6,11 +6,12 @@
649 msgid ""
650 msgstr ""
651 "Project-Id-Version: unity-scopes-shell\n"
652-"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
653-"POT-Creation-Date: 2015-09-28 08:40+0000\n"
654+"Report-Msgid-Bugs-To: \n"
655+"POT-Creation-Date: 2015-09-01 10:42+0000\n"
656 "PO-Revision-Date: 2015-08-05 08:33+0000\n"
657 "Last-Translator: Josef Andersson <Unknown>\n"
658 "Language-Team: Swedish <sv@li.org>\n"
659+"Language: sv\n"
660 "MIME-Version: 1.0\n"
661 "Content-Type: text/plain; charset=UTF-8\n"
662 "Content-Transfer-Encoding: 8bit\n"
663
664=== modified file 'po/ta.po'
665--- po/ta.po 2016-03-05 06:20:25 +0000
666+++ po/ta.po 2016-03-16 11:17:35 +0000
667@@ -6,11 +6,12 @@
668 msgid ""
669 msgstr ""
670 "Project-Id-Version: unity-scopes-shell\n"
671-"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
672-"POT-Creation-Date: 2015-09-28 08:40+0000\n"
673+"Report-Msgid-Bugs-To: \n"
674+"POT-Creation-Date: 2015-09-01 10:42+0000\n"
675 "PO-Revision-Date: 2015-07-16 08:52+0000\n"
676 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
677 "Language-Team: Tamil <ta@li.org>\n"
678+"Language: ta\n"
679 "MIME-Version: 1.0\n"
680 "Content-Type: text/plain; charset=UTF-8\n"
681 "Content-Transfer-Encoding: 8bit\n"
682
683=== modified file 'po/uk.po'
684--- po/uk.po 2016-03-05 06:20:25 +0000
685+++ po/uk.po 2016-03-16 11:17:35 +0000
686@@ -6,11 +6,12 @@
687 msgid ""
688 msgstr ""
689 "Project-Id-Version: unity-scopes-shell\n"
690-"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
691-"POT-Creation-Date: 2015-09-28 08:40+0000\n"
692+"Report-Msgid-Bugs-To: \n"
693+"POT-Creation-Date: 2015-09-01 10:42+0000\n"
694 "PO-Revision-Date: 2016-02-08 18:50+0000\n"
695 "Last-Translator: Yuri Chornoivan <yurchor@gmail.com>\n"
696 "Language-Team: Ukrainian <uk@li.org>\n"
697+"Language: uk\n"
698 "MIME-Version: 1.0\n"
699 "Content-Type: text/plain; charset=UTF-8\n"
700 "Content-Transfer-Encoding: 8bit\n"
701
702=== modified file 'po/unity-plugin-scopes.pot'
703--- po/unity-plugin-scopes.pot 2015-09-28 08:41:08 +0000
704+++ po/unity-plugin-scopes.pot 2016-03-16 11:17:35 +0000
705@@ -8,7 +8,7 @@
706 msgstr ""
707 "Project-Id-Version: PACKAGE VERSION\n"
708 "Report-Msgid-Bugs-To: \n"
709-"POT-Creation-Date: 2015-09-28 08:40+0000\n"
710+"POT-Creation-Date: 2015-09-01 10:42+0000\n"
711 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
712 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
713 "Language-Team: LANGUAGE <LL@li.org>\n"
714
715=== modified file 'po/zh_TW.po'
716--- po/zh_TW.po 2016-03-05 06:20:25 +0000
717+++ po/zh_TW.po 2016-03-16 11:17:35 +0000
718@@ -6,11 +6,12 @@
719 msgid ""
720 msgstr ""
721 "Project-Id-Version: unity-scopes-shell\n"
722-"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
723-"POT-Creation-Date: 2015-09-28 08:40+0000\n"
724+"Report-Msgid-Bugs-To: \n"
725+"POT-Creation-Date: 2015-09-01 10:42+0000\n"
726 "PO-Revision-Date: 2015-10-21 05:47+0000\n"
727 "Last-Translator: Rex Tsai <rex.tsai@canonical.com>\n"
728 "Language-Team: Chinese (Traditional) <zh_TW@li.org>\n"
729+"Language: \n"
730 "MIME-Version: 1.0\n"
731 "Content-Type: text/plain; charset=UTF-8\n"
732 "Content-Transfer-Encoding: 8bit\n"
733
734=== modified file 'src/Unity/CMakeLists.txt'
735--- src/Unity/CMakeLists.txt 2016-02-11 15:35:47 +0000
736+++ src/Unity/CMakeLists.txt 2016-03-16 11:17:35 +0000
737@@ -20,6 +20,12 @@
738 collectors.cpp
739 department.cpp
740 departmentnode.cpp
741+ filters.cpp
742+ optionselectorfilter.cpp
743+ optionselectoroptions.cpp
744+ rangeinputfilter.cpp
745+ valuesliderfilter.cpp
746+ valueslidervalues.cpp
747 geoip.cpp
748 localization.h
749 locationservice.cpp
750@@ -39,6 +45,13 @@
751 logintoaccount.cpp
752 # We need these headers here so moc runs and we get the moc-stuff
753 # compiled in, otherwise we miss some symbols
754+ ${SCOPES_API_INCLUDEDIR}/unity/shell/scopes/FiltersInterface.h
755+ ${SCOPES_API_INCLUDEDIR}/unity/shell/scopes/FilterBaseInterface.h
756+ ${SCOPES_API_INCLUDEDIR}/unity/shell/scopes/OptionSelectorFilterInterface.h
757+ ${SCOPES_API_INCLUDEDIR}/unity/shell/scopes/OptionSelectorOptionsInterface.h
758+ ${SCOPES_API_INCLUDEDIR}/unity/shell/scopes/RangeInputFilterInterface.h
759+ ${SCOPES_API_INCLUDEDIR}/unity/shell/scopes/ValueSliderFilterInterface.h
760+ ${SCOPES_API_INCLUDEDIR}/unity/shell/scopes/ValueSliderValuesInterface.h
761 ${SCOPES_API_INCLUDEDIR}/unity/shell/scopes/CategoriesInterface.h
762 ${SCOPES_API_INCLUDEDIR}/unity/shell/scopes/NavigationInterface.h
763 ${SCOPES_API_INCLUDEDIR}/unity/shell/scopes/PreviewModelInterface.h
764
765=== modified file 'src/Unity/collectors.cpp'
766--- src/Unity/collectors.cpp 2015-10-05 12:29:55 +0000
767+++ src/Unity/collectors.cpp 2016-03-16 11:17:35 +0000
768@@ -142,19 +142,13 @@
769 m_rootDepartment = department;
770 }
771
772- void setSortOrder(scopes::OptionSelectorFilter::SCPtr const& sortOrder)
773- {
774- QMutexLocker locker(&m_mutex);
775- m_sortOrderFilter = sortOrder;
776- }
777-
778- void setFilterState(scopes::FilterState const& state)
779- {
780- QMutexLocker locker(&m_mutex);
781- m_filterState = state;
782- }
783-
784- Status collect(QList<scopes::CategorisedResult::SPtr>& out_results, scopes::Department::SCPtr& out_rootDepartment, scopes::OptionSelectorFilter::SCPtr& out_sortOrder, scopes::FilterState& out_filterState)
785+ void addFilter(scopes::FilterBase::SCPtr const& filter)
786+ {
787+ QMutexLocker locker(&m_mutex);
788+ m_filters.append(filter);
789+ }
790+
791+ Status collect(QList<scopes::CategorisedResult::SPtr>& out_results, scopes::Department::SCPtr& out_rootDepartment, QList<scopes::FilterBase::SCPtr>& out_filters)
792 {
793 Status status;
794
795@@ -167,8 +161,7 @@
796 m_results.swap(out_results);
797 out_rootDepartment = m_rootDepartment;
798
799- out_sortOrder = m_sortOrderFilter;
800- out_filterState = m_filterState;
801+ out_filters = m_filters;
802
803 return status;
804 }
805@@ -176,8 +169,7 @@
806 private:
807 QList<scopes::CategorisedResult::SPtr> m_results;
808 scopes::Department::SCPtr m_rootDepartment;
809- scopes::OptionSelectorFilter::SCPtr m_sortOrderFilter;
810- scopes::FilterState m_filterState;
811+ QList<scopes::FilterBase::SCPtr> m_filters;
812 };
813
814 class PreviewDataCollector: public CollectorBase
815@@ -292,10 +284,11 @@
816 return m_collector->msecsSinceStart();
817 }
818
819-CollectorBase::Status PushEvent::collectSearchResults(QList<scopes::CategorisedResult::SPtr>& out_results, scopes::Department::SCPtr& rootDepartment, scopes::OptionSelectorFilter::SCPtr& sortOrder, scopes::FilterState& filterState)
820+CollectorBase::Status PushEvent::collectSearchResults(QList<scopes::CategorisedResult::SPtr>& out_results, scopes::Department::SCPtr& rootDepartment,
821+ QList<scopes::FilterBase::SCPtr>& out_filters)
822 {
823 auto collector = std::dynamic_pointer_cast<SearchDataCollector>(m_collector);
824- return collector->collect(out_results, rootDepartment, sortOrder, filterState);
825+ return collector->collect(out_results, rootDepartment, out_filters);
826 }
827
828 CollectorBase::Status PushEvent::collectPreviewData(scopes::ColumnLayoutList& out_columns, scopes::PreviewWidgetList& out_widgets, QHash<QString, QVariant>& out_data)
829@@ -358,21 +351,12 @@
830 m_collector->setDepartment(department);
831 }
832
833-void SearchResultReceiver::push(scopes::Filters const& filters, scopes::FilterState const& state)
834+void SearchResultReceiver::push(scopes::Filters const& filters, scopes::FilterState const& /* state */)
835 {
836 for (auto it = filters.begin(); it != filters.end(); ++it) {
837 scopes::FilterBase::SCPtr filter = *it;
838- if (filter->display_hints() == scopes::FilterBase::DisplayHints::Primary) {
839- scopes::OptionSelectorFilter::SCPtr option_filter = std::dynamic_pointer_cast<const scopes::OptionSelectorFilter>(filter);
840- if (!option_filter) {
841- continue;
842- } else {
843- m_collector->setSortOrder(option_filter);
844- break;
845- }
846- }
847+ m_collector->addFilter(filter);
848 }
849- m_collector->setFilterState(state);
850 }
851
852 // this might be called from any thread (might be main, might be any other thread)
853
854=== modified file 'src/Unity/collectors.h'
855--- src/Unity/collectors.h 2016-02-17 16:32:49 +0000
856+++ src/Unity/collectors.h 2016-03-16 11:17:35 +0000
857@@ -76,7 +76,8 @@
858 PushEvent(Type event_type, const std::shared_ptr<CollectorBase>& collector);
859 Type type();
860
861- CollectorBase::Status collectSearchResults(QList<std::shared_ptr<unity::scopes::CategorisedResult>>& out_results, unity::scopes::Department::SCPtr& out_rootDepartment, unity::scopes::OptionSelectorFilter::SCPtr& out_sortOrder, unity::scopes::FilterState& out_filterState);
862+ CollectorBase::Status collectSearchResults(QList<std::shared_ptr<unity::scopes::CategorisedResult>>& out_results, unity::scopes::Department::SCPtr&
863+ out_rootDepartment, QList<unity::scopes::FilterBase::SCPtr>& out_filters);
864 CollectorBase::Status collectPreviewData(unity::scopes::ColumnLayoutList& out_columns, unity::scopes::PreviewWidgetList& out_widgets, QHash<QString, QVariant>& out_data);
865 CollectorBase::Status collectActivationResponse(std::shared_ptr<unity::scopes::ActivationResponse>& out_response, std::shared_ptr<unity::scopes::Result>&
866 out_result, QString& categoryId);
867
868=== modified file 'src/Unity/department.cpp'
869--- src/Unity/department.cpp 2015-09-08 12:32:05 +0000
870+++ src/Unity/department.cpp 2016-03-16 11:17:35 +0000
871@@ -65,6 +65,7 @@
872 QSharedPointer<SubdepartmentData> subdept(new SubdepartmentData);
873 subdept->id = node->id();
874 subdept->label = node->label();
875+ subdept->allLabel = node->allLabel();
876 subdept->hasChildren = node->hasSubdepartments();
877 subdept->isActive = false;
878 m_subdepartments.append(subdept);
879@@ -122,12 +123,23 @@
880 switch (role) {
881 case RoleNavigationId: return data->id;
882 case RoleLabel: return data->label;
883+ case RoleAllLabel: return data->allLabel;
884 case RoleHasChildren: return data->hasChildren;
885 case RoleIsActive: return data->isActive;
886 default: return QVariant();
887 }
888 }
889
890+QSharedPointer<SubdepartmentData> Department::findSubdepartment(const QString &id) const
891+{
892+ for (auto sub: m_subdepartments) {
893+ if (sub->id == id) {
894+ return sub;
895+ }
896+ }
897+ return QSharedPointer<SubdepartmentData>();
898+}
899+
900 int Department::rowCount(const QModelIndex& parent) const
901 {
902 Q_UNUSED(parent);
903
904=== modified file 'src/Unity/department.h'
905--- src/Unity/department.h 2014-08-04 15:06:55 +0000
906+++ src/Unity/department.h 2016-03-16 11:17:35 +0000
907@@ -40,6 +40,7 @@
908 {
909 QString id;
910 QString label;
911+ QString allLabel;
912 bool hasChildren;
913 bool isActive;
914 };
915@@ -67,6 +68,8 @@
916 bool hidden() const override;
917 int count() const override;
918
919+ QSharedPointer<SubdepartmentData> findSubdepartment(const QString &id) const;
920+
921 Q_SIGNALS:
922
923 private:
924
925=== added file 'src/Unity/filters.cpp'
926--- src/Unity/filters.cpp 1970-01-01 00:00:00 +0000
927+++ src/Unity/filters.cpp 2016-03-16 11:17:35 +0000
928@@ -0,0 +1,281 @@
929+/*
930+ * Copyright (C) 2015 Canonical, Ltd.
931+ *
932+ * Authors:
933+ * Pawel Stolowski <pawel.stolowski@canonical.com>
934+ *
935+ * This program is free software; you can redistribute it and/or modify
936+ * it under the terms of the GNU General Public License as published by
937+ * the Free Software Foundation; version 3.
938+ *
939+ * This program is distributed in the hope that it will be useful,
940+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
941+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
942+ * GNU General Public License for more details.
943+ *
944+ * You should have received a copy of the GNU General Public License
945+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
946+ */
947+
948+// Self
949+#include "filters.h"
950+#include "optionselectorfilter.h"
951+#include "rangeinputfilter.h"
952+#include "valuesliderfilter.h"
953+#include <QSet>
954+#include <QMap>
955+#include <QQmlEngine>
956+#include <QDebug>
957+
958+#include <unity/scopes/OptionSelectorFilter.h>
959+#include <unity/scopes/RangeInputFilter.h>
960+#include <unity/scopes/ValueSliderFilter.h>
961+
962+namespace scopes_ng
963+{
964+
965+const int FILTER_CHANGE_PROCESSING_DELAY = 300;
966+
967+Filters::Filters(unity::scopes::FilterState const& filterState,
968+ unity::shell::scopes::ScopeInterface *parent) : ModelUpdate(parent),
969+ m_filterState(new unity::scopes::FilterState(filterState))
970+{
971+ m_filterStateChangeTimer.setSingleShot(true);
972+ QObject::connect(&m_filterStateChangeTimer, &QTimer::timeout, this, &Filters::delayedFilterStateChange);
973+}
974+
975+int Filters::rowCount(const QModelIndex&) const
976+{
977+ return m_filters.count();
978+}
979+
980+QVariant Filters::data(const QModelIndex& index, int role) const
981+{
982+ if (index.row() >= m_filters.count())
983+ {
984+ return QVariant();
985+ }
986+
987+ switch (role)
988+ {
989+ case unity::shell::scopes::FiltersInterface::Roles::RoleFilterId:
990+ return m_filters.at(index.row())->filterId();
991+ case Qt::DisplayRole:
992+ case unity::shell::scopes::FiltersInterface::Roles::RoleFilterType:
993+ return m_filters.at(index.row())->filterType();
994+ case unity::shell::scopes::FiltersInterface::Roles::RoleFilter:
995+ return QVariant::fromValue(m_filters.at(index.row()).data());
996+ default:
997+ break;
998+ };
999+
1000+ return QVariant();
1001+}
1002+
1003+void Filters::clear()
1004+{
1005+ if (m_filters.size() > 0)
1006+ {
1007+ beginResetModel();
1008+ m_filters.clear();
1009+ m_filterState.reset(new unity::scopes::FilterState());
1010+ endResetModel();
1011+ }
1012+}
1013+
1014+void Filters::reset()
1015+{
1016+ // Resetting filters will emit filterStateChanged.
1017+ qDebug() << "Resetting filters to defaults";
1018+ for (auto f: m_filters) {
1019+ qDebug() << "Resetting filter:" << f->filterId();
1020+ auto shellFilter = dynamic_cast<FilterUpdateInterface*>(f.data());
1021+ Q_ASSERT(shellFilter);
1022+ shellFilter->reset();
1023+ }
1024+}
1025+
1026+void Filters::onFilterStateChanged()
1027+{
1028+ qDebug() << "Filter::onFilterStateChanged";
1029+ m_filterStateChangeTimer.start(FILTER_CHANGE_PROCESSING_DELAY);
1030+}
1031+
1032+void Filters::delayedFilterStateChange()
1033+{
1034+ Q_EMIT filterStateChanged();
1035+}
1036+
1037+void Filters::update(QList<unity::scopes::FilterBase::SCPtr> const& filters, bool containsDepartments)
1038+{
1039+ // Primary filter needs to be handled separately and not inserted into the main filters model,
1040+ bool hasPrimaryFilter = containsDepartments;
1041+ QList<unity::scopes::FilterBase::SCPtr> inFilters;
1042+ for (auto f: filters) {
1043+ // we can only have one primary filter. only a single-selection OptionSelectorFilter can be a primary nav.
1044+ bool wantsToBePrimary = (f->display_hints() & unity::scopes::FilterBase::DisplayHints::Primary);
1045+ unity::scopes::OptionSelectorFilter::SCPtr optSelFilter = std::dynamic_pointer_cast<unity::scopes::OptionSelectorFilter const>(f);
1046+ if (optSelFilter == nullptr || optSelFilter->multi_select()) {
1047+ wantsToBePrimary = false;
1048+ }
1049+ if (wantsToBePrimary && !hasPrimaryFilter) {
1050+ hasPrimaryFilter = true;
1051+ //
1052+ const bool hadSamePrimaryFilterBefore = m_primaryFilter && (m_primaryFilter->filterId() == QString::fromStdString(f->id())) && (m_primaryFilter->filterType() == getFilterType(f));
1053+ if (hadSamePrimaryFilterBefore) {
1054+ auto shellFilter = dynamic_cast<FilterUpdateInterface*>(m_primaryFilter.data());
1055+ if (shellFilter) {
1056+ shellFilter->update(f);
1057+ } else {
1058+ // this should never happen
1059+ qCritical() << "Failed to cast filter" << m_primaryFilter->filterId() << "to FilterUpdateInterface";
1060+ }
1061+ } else {
1062+ // we didn't have primary filter before, or it changed substantially, so recreate it
1063+ m_primaryFilter = createFilterObject(f);
1064+ Q_EMIT primaryFilterChanged();
1065+ }
1066+ } else {
1067+ inFilters.append(f);
1068+ }
1069+ }
1070+
1071+ // Did we have primary filter before but not now?
1072+ if (!hasPrimaryFilter && m_primaryFilter != nullptr) {
1073+ m_primaryFilter.reset();
1074+ Q_EMIT primaryFilterChanged();
1075+ }
1076+
1077+ syncModel(inFilters, m_filters,
1078+ // key function for scopes api filter
1079+ [](const unity::scopes::FilterBase::SCPtr& f) -> QString { return QString::fromStdString(f->id()); },
1080+ // key function for shell api filter
1081+ [](const QSharedPointer<unity::shell::scopes::FilterBaseInterface>& f) -> QString { return f->filterId(); },
1082+ // factory function
1083+ [this](const unity::scopes::FilterBase::SCPtr& f) -> QSharedPointer<unity::shell::scopes::FilterBaseInterface> {
1084+ return createFilterObject(f);
1085+ },
1086+ // filter update function
1087+ [this](int, const unity::scopes::FilterBase::SCPtr &f1, const QSharedPointer<unity::shell::scopes::FilterBaseInterface>& f2) -> bool {
1088+ qDebug() << "Updating filter" << f2->filterId();
1089+ if (f2->filterId() != QString::fromStdString(f1->id()) || f2->filterType() != getFilterType(f1))
1090+ {
1091+ return false;
1092+ }
1093+ auto shellFilter = dynamic_cast<FilterUpdateInterface*>(f2.data());
1094+ if (shellFilter) {
1095+ shellFilter->update(f1);
1096+ } else {
1097+ // this should never happen
1098+ qCritical() << "Failed to cast filter" << f2->filterId() << "to FilterUpdateInterface";
1099+ }
1100+ return true;
1101+ });
1102+}
1103+//
1104+// Update current filters model and primary filter pointer with filters coming from scope.
1105+void Filters::update(unity::scopes::FilterState const& filterState)
1106+{
1107+ m_filterState.reset(new unity::scopes::FilterState(filterState));
1108+
1109+ if (m_primaryFilter) {
1110+ auto shellFilter = dynamic_cast<FilterUpdateInterface*>(m_primaryFilter.data());
1111+ if (shellFilter) {
1112+ shellFilter->update(m_filterState);
1113+ } else {
1114+ // this should never happen
1115+ qCritical() << "Failed to cast filter" << m_primaryFilter->filterId() << "to FilterUpdateInterface";
1116+ }
1117+ }
1118+ for (auto f: m_filters) {
1119+ auto shellFilter = dynamic_cast<FilterUpdateInterface*>(f.data());
1120+ if (shellFilter) {
1121+ shellFilter->update(m_filterState);
1122+ } else {
1123+ // this should never happen
1124+ qCritical() << "Failed to cast filter" << f->filterId() << "to FilterUpdateInterface";
1125+ }
1126+ }
1127+}
1128+
1129+QSharedPointer<unity::shell::scopes::FilterBaseInterface> Filters::createFilterObject(unity::scopes::FilterBase::SCPtr const& filter)
1130+{
1131+ QSharedPointer<unity::shell::scopes::FilterBaseInterface> filterObj;
1132+ if (filter->filter_type() == "option_selector")
1133+ {
1134+ unity::scopes::OptionSelectorFilter::SCPtr optfilter = std::dynamic_pointer_cast<unity::scopes::OptionSelectorFilter const>(filter);
1135+ filterObj = QSharedPointer<unity::shell::scopes::FilterBaseInterface>(new scopes_ng::OptionSelectorFilter(optfilter, m_filterState, this));
1136+ }
1137+ else if (filter->filter_type() == "range_input")
1138+ {
1139+ unity::scopes::RangeInputFilter::SCPtr rangefilter = std::dynamic_pointer_cast<unity::scopes::RangeInputFilter const>(filter);
1140+ filterObj = QSharedPointer<unity::shell::scopes::FilterBaseInterface>(new scopes_ng::RangeInputFilter(rangefilter, m_filterState, this));
1141+ }
1142+ else if (filter->filter_type() == "value_slider")
1143+ {
1144+ unity::scopes::ValueSliderFilter::SCPtr valuesliderfilter = std::dynamic_pointer_cast<unity::scopes::ValueSliderFilter const>(filter);
1145+ filterObj = QSharedPointer<unity::shell::scopes::FilterBaseInterface>(new scopes_ng::ValueSliderFilter(valuesliderfilter, m_filterState, this));
1146+ }
1147+
1148+ // Warning!!! Make sure any new filter type created by this factory method is reflected below in getFilterType() method as well!
1149+
1150+ if (filterObj)
1151+ {
1152+ QQmlEngine::setObjectOwnership(filterObj.data(), QQmlEngine::CppOwnership);
1153+ connect(filterObj.data(), SIGNAL(filterStateChanged()), this, SLOT(onFilterStateChanged()));
1154+ }
1155+ else
1156+ {
1157+ qWarning() << "Unsupported filter type:" << QString::fromStdString(filter->filter_type());
1158+ }
1159+
1160+ return filterObj;
1161+}
1162+
1163+unity::shell::scopes::FiltersInterface::FilterType Filters::getFilterType(unity::scopes::FilterBase::SCPtr const& filter)
1164+{
1165+ // WARNING! Some filters may rely on inheritance, in that case the dynamic casts below need to be in correct order
1166+ // (e.g. more concrete types first, base/generic types last).
1167+ if (std::dynamic_pointer_cast<unity::scopes::OptionSelectorFilter const>(filter))
1168+ {
1169+ return unity::shell::scopes::FiltersInterface::FilterType::OptionSelectorFilter;
1170+ }
1171+ if (std::dynamic_pointer_cast<unity::scopes::RangeInputFilter const>(filter))
1172+ {
1173+ return unity::shell::scopes::FiltersInterface::FilterType::RangeInputFilter;
1174+ }
1175+ if (std::dynamic_pointer_cast<unity::scopes::ValueSliderFilter const>(filter))
1176+ {
1177+ return unity::shell::scopes::FiltersInterface::FilterType::ValueSliderFilter;
1178+ }
1179+ qFatal("getFilterType(): Unknown filter type: %s\n", filter->filter_type().c_str());
1180+ return unity::shell::scopes::FiltersInterface::FilterType::Invalid;
1181+}
1182+
1183+unity::scopes::FilterState Filters::filterState() const
1184+{
1185+ if (m_filterState) {
1186+ return *m_filterState;
1187+ }
1188+ return unity::scopes::FilterState();
1189+}
1190+
1191+QSharedPointer<unity::shell::scopes::FilterBaseInterface> Filters::primaryFilter() const
1192+{
1193+ return m_primaryFilter;
1194+}
1195+
1196+int Filters::activeFiltersCount() const
1197+{
1198+ int count = 0;
1199+ for (auto filter: m_filters) {
1200+ auto shellFilter = dynamic_cast<FilterUpdateInterface*>(filter.data());
1201+ if (shellFilter->isActive()) {
1202+ qDebug() << "activeFiltersCount: filter" << filter->filterId() << "is active";
1203+ ++count;
1204+ }
1205+ }
1206+ return count;
1207+}
1208+
1209+}
1210
1211=== added file 'src/Unity/filters.h'
1212--- src/Unity/filters.h 1970-01-01 00:00:00 +0000
1213+++ src/Unity/filters.h 2016-03-16 11:17:35 +0000
1214@@ -0,0 +1,94 @@
1215+/*
1216+ * Copyright (C) 2015 Canonical, Ltd.
1217+ *
1218+ * Authors:
1219+ * Pawel Stolowski <pawel.stolowski@canonical.com>
1220+ *
1221+ * This program is free software; you can redistribute it and/or modify
1222+ * it under the terms of the GNU General Public License as published by
1223+ * the Free Software Foundation; version 3.
1224+ *
1225+ * This program is distributed in the hope that it will be useful,
1226+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1227+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1228+ * GNU General Public License for more details.
1229+ *
1230+ * You should have received a copy of the GNU General Public License
1231+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1232+ */
1233+
1234+#ifndef NG_FILTERS_H
1235+#define NG_FILTERS_H
1236+
1237+#include <unity/shell/scopes/FiltersInterface.h>
1238+#include <unity/shell/scopes/ScopeInterface.h>
1239+#include <unity/scopes/FilterBase.h>
1240+#include <unity/scopes/FilterState.h>
1241+#include <unity/shell/scopes/FilterBaseInterface.h>
1242+#include "modelupdate.h"
1243+
1244+#include <QList>
1245+#include <QSharedPointer>
1246+#include <QTimer>
1247+
1248+namespace scopes_ng
1249+{
1250+
1251+class FilterUpdateInterface
1252+{
1253+ public:
1254+ // Apply potential updates of filter definition coming from scope.
1255+ virtual void update(unity::scopes::FilterBase::SCPtr const& filter) = 0;
1256+
1257+ // Apply filter state change (e.g. after user executed a canned query).
1258+ virtual void update(unity::scopes::FilterState::SPtr const& filterState) = 0;
1259+
1260+ // Check if the filter is active (i.e. has non-default values set).
1261+ virtual bool isActive() const = 0;
1262+
1263+ // Reset filter to defaults.
1264+ virtual void reset() = 0;
1265+};
1266+
1267+class Q_DECL_EXPORT Filters :
1268+ public ModelUpdate<unity::shell::scopes::FiltersInterface,
1269+ QList<unity::scopes::FilterBase::SCPtr>,
1270+ QList<QSharedPointer<unity::shell::scopes::FilterBaseInterface>>>
1271+{
1272+ Q_OBJECT
1273+
1274+public:
1275+ explicit Filters(unity::scopes::FilterState const& filterState, unity::shell::scopes::ScopeInterface *parent = nullptr);
1276+ int rowCount(const QModelIndex& parent = QModelIndex()) const override;
1277+ QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
1278+ void update(QList<unity::scopes::FilterBase::SCPtr> const& filters, bool containsDepartments = false);
1279+ void update(unity::scopes::FilterState const& filterState);
1280+
1281+ unity::scopes::FilterState filterState() const;
1282+ QSharedPointer<unity::shell::scopes::FilterBaseInterface> primaryFilter() const;
1283+ int activeFiltersCount() const;
1284+
1285+public Q_SLOTS:
1286+ void clear();
1287+ void reset();
1288+
1289+private Q_SLOTS:
1290+ void onFilterStateChanged();
1291+ void delayedFilterStateChange();
1292+
1293+Q_SIGNALS:
1294+ void filterStateChanged();
1295+ void primaryFilterChanged();
1296+
1297+private:
1298+ static unity::shell::scopes::FiltersInterface::FilterType getFilterType(unity::scopes::FilterBase::SCPtr const& filter);
1299+ QSharedPointer<unity::shell::scopes::FilterBaseInterface> createFilterObject(unity::scopes::FilterBase::SCPtr const& filter);
1300+ QList<QSharedPointer<unity::shell::scopes::FilterBaseInterface>> m_filters;
1301+ QSharedPointer<unity::shell::scopes::FilterBaseInterface> m_primaryFilter;
1302+ unity::scopes::FilterState::SPtr m_filterState;
1303+ QTimer m_filterStateChangeTimer;
1304+};
1305+
1306+} // namespace scopes_ng
1307+
1308+#endif
1309
1310=== added file 'src/Unity/modelupdate.h'
1311--- src/Unity/modelupdate.h 1970-01-01 00:00:00 +0000
1312+++ src/Unity/modelupdate.h 2016-03-16 11:17:35 +0000
1313@@ -0,0 +1,143 @@
1314+/*
1315+ * Copyright (C) 2015 Canonical, Ltd.
1316+ *
1317+ * Authors:
1318+ * Pawel Stolowski <pawel.stolowski@canonical.com>
1319+ *
1320+ * This program is free software; you can redistribute it and/or modify
1321+ * it under the terms of the GNU General Public License as published by
1322+ * the Free Software Foundation; version 3.
1323+ *
1324+ * This program is distributed in the hope that it will be useful,
1325+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1326+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1327+ * GNU General Public License for more details.
1328+ *
1329+ * You should have received a copy of the GNU General Public License
1330+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1331+ */
1332+
1333+#ifndef NG_MODEL_UPDATE_H
1334+#define NG_MODEL_UPDATE_H
1335+
1336+#include <QSet>
1337+#include <functional>
1338+
1339+template <class ModelBase, class InputContainer, class OutputContainer, class KeyType=QString>
1340+class ModelUpdate: public ModelBase
1341+{
1342+public:
1343+ using InputKeyFunc = std::function<KeyType(typename InputContainer::value_type)>;
1344+ using OutputKeyFunc = std::function<KeyType(typename OutputContainer::value_type)>;
1345+ using CreateFunc = std::function<typename OutputContainer::value_type(typename InputContainer::value_type const&)>;
1346+ using UpdateFunc = std::function<bool(int, typename InputContainer::value_type const&, typename OutputContainer::value_type const&)>;
1347+
1348+ ModelUpdate(QObject *parent = nullptr): ModelBase(parent) {}
1349+
1350+ void syncModel(InputContainer const& input,
1351+ OutputContainer &model,
1352+ const InputKeyFunc& inKeyFunc,
1353+ const OutputKeyFunc& outKeyFunc,
1354+ const CreateFunc& createFunc,
1355+ const UpdateFunc& updateFunc)
1356+ {
1357+ QMap<KeyType, int> newItems; // lookup for recevied objects and their desired rows in the model
1358+ QSet<KeyType> oldItems; // lookup for objects that were already displayed
1359+
1360+ {
1361+ int pos = 0;
1362+ for (auto item: input)
1363+ {
1364+ newItems[inKeyFunc(item)] = pos++;
1365+ }
1366+ }
1367+
1368+ // iterate over old objects, remove objects that are not present anymore
1369+ {
1370+ int row = 0;
1371+ for (auto it = model.begin(); it != model.end();)
1372+ {
1373+ const KeyType id = outKeyFunc(*it);
1374+ if (!newItems.contains(id))
1375+ {
1376+ this->beginRemoveRows(QModelIndex(), row, row);
1377+ it = model.erase(it);
1378+ this->endRemoveRows();
1379+ }
1380+ else
1381+ {
1382+ oldItems.insert(id);
1383+ ++it;
1384+ ++row;
1385+ }
1386+ }
1387+ }
1388+
1389+ // iterate over new objects and insert them in the model
1390+ {
1391+ int row = 0;
1392+ for (auto const& item: input)
1393+ {
1394+ if (!oldItems.contains(inKeyFunc(item)))
1395+ {
1396+ auto obj = createFunc(item);
1397+ if (obj)
1398+ {
1399+ this->beginInsertRows(QModelIndex(), row, row);
1400+ model.insert(row, obj);
1401+ this->endInsertRows();
1402+ }
1403+ }
1404+ row++;
1405+ }
1406+ }
1407+
1408+ // move objects if position changed
1409+ for (int i = 0; i<model.size(); )
1410+ {
1411+ auto const id = outKeyFunc(model[i]);
1412+ int pos = newItems.value(id, -1);
1413+ if (pos >= 0 && pos != i) {
1414+ this->beginMoveRows(QModelIndex(), i, i, QModelIndex(), pos + (pos > i ? 1 : 0));
1415+ model.move(i, pos);
1416+ this->endMoveRows();
1417+ continue;
1418+ }
1419+ i++;
1420+ }
1421+
1422+ // check if any of the existing objects which didn't change position needs updating
1423+ /*{
1424+ int row = 0;
1425+ for (auto const& in: input)
1426+ {
1427+ if (oldItems.contains(inKeyFunc(in)))
1428+ {
1429+ if (!updateFunc(in, model[row]))
1430+ {
1431+ model[row] = createFunc(in);
1432+ Q_EMIT this->dataChanged(this->index(row, 0), this->index(row, 0)); // or beginRemoveRows & beginInsertRows ?
1433+ }
1434+ }
1435+ ++row;
1436+ }
1437+ }*/
1438+
1439+ // call updateFunc for all objects to synchornize changes to properties
1440+ {
1441+ int row = 0;
1442+ for (auto const& in: input)
1443+ {
1444+ if (!updateFunc(row, in, model[row]))
1445+ {
1446+ model[row] = createFunc(in);
1447+ Q_EMIT this->dataChanged(this->index(row, 0), this->index(row, 0)); // or beginRemoveRows & beginInsertRows ?
1448+ }
1449+ ++row;
1450+ }
1451+ }
1452+
1453+ }
1454+};
1455+
1456+#endif
1457
1458=== added file 'src/Unity/optionselectorfilter.cpp'
1459--- src/Unity/optionselectorfilter.cpp 1970-01-01 00:00:00 +0000
1460+++ src/Unity/optionselectorfilter.cpp 2016-03-16 11:17:35 +0000
1461@@ -0,0 +1,173 @@
1462+/*
1463+ * Copyright (C) 2015 Canonical, Ltd.
1464+ *
1465+ * Authors:
1466+ * Pawel Stolowski <pawel.stolowski@canonical.com>
1467+ *
1468+ * This program is free software; you can redistribute it and/or modify
1469+ * it under the terms of the GNU General Public License as published by
1470+ * the Free Software Foundation; version 3.
1471+ *
1472+ * This program is distributed in the hope that it will be useful,
1473+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1474+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1475+ * GNU General Public License for more details.
1476+ *
1477+ * You should have received a copy of the GNU General Public License
1478+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1479+ */
1480+
1481+#include "optionselectorfilter.h"
1482+#include <QQmlEngine>
1483+#include <QDebug>
1484+
1485+namespace scopes_ng
1486+{
1487+
1488+OptionSelectorFilter::OptionSelectorFilter(unity::scopes::OptionSelectorFilter::SCPtr const& filter, unity::scopes::FilterState::SPtr const& filterState, unity::shell::scopes::FiltersInterface *parent)
1489+ : unity::shell::scopes::OptionSelectorFilterInterface(parent),
1490+ m_id(QString::fromStdString(filter->id())),
1491+ m_title(QString::fromStdString(filter->title())),
1492+ m_multiSelect(filter->multi_select()),
1493+ m_label(QString::fromStdString(filter->label())),
1494+ m_options(new OptionSelectorOptions(this, filter->options(), filter->active_options(*filterState))),
1495+ m_filterState(filterState),
1496+ m_filter(filter)
1497+{
1498+ QQmlEngine::setObjectOwnership(m_options.data(), QQmlEngine::CppOwnership);
1499+ connect(m_options.data(), SIGNAL(optionChecked(const QString&, bool)), this, SLOT(onOptionChecked(const QString&, bool)));
1500+}
1501+
1502+QString OptionSelectorFilter::filterId() const
1503+{
1504+ return m_id;
1505+}
1506+
1507+QString OptionSelectorFilter::title() const
1508+{
1509+ return m_title;
1510+}
1511+
1512+unity::shell::scopes::FiltersInterface::FilterType OptionSelectorFilter::filterType() const
1513+{
1514+ return unity::shell::scopes::FiltersInterface::FilterType::OptionSelectorFilter;
1515+}
1516+
1517+QString OptionSelectorFilter::label() const
1518+{
1519+ return m_label;
1520+}
1521+
1522+bool OptionSelectorFilter::multiSelect() const
1523+{
1524+ return m_multiSelect;
1525+}
1526+
1527+void OptionSelectorFilter::reset()
1528+{
1529+ if (auto state = m_filterState.lock())
1530+ {
1531+ state->remove(m_filter->id());
1532+ qDebug() << "Removing filter state for filter" << QString::fromStdString(m_filter->id());
1533+ m_options->update(m_filter->active_options(*state), true);
1534+ Q_EMIT filterStateChanged();
1535+ }
1536+}
1537+
1538+void OptionSelectorFilter::onOptionChecked(const QString& id, bool checked)
1539+{
1540+ if (auto state = m_filterState.lock())
1541+ {
1542+ auto const optid = id.toStdString();
1543+ for (auto const opt: m_filter->options())
1544+ {
1545+ if (opt->id() == optid)
1546+ {
1547+ m_filter->update_state(*state, opt, checked);
1548+ // onOptionChecked signal is triggered by the user, but we need to updated filter
1549+ // with new state since the state of other options may have changed if this is a single-selection filter.
1550+ // However, we pass allow_defaults = false, so that user is able to unselect all options and they are
1551+ // not forcefully reset to defaults if this happens.
1552+ m_options->update(m_filter->active_options(*state), false);
1553+ Q_EMIT filterStateChanged();
1554+ return;
1555+ }
1556+ }
1557+ qDebug() << "Removing filter state for filter" << QString::fromStdString(m_filter->id());
1558+ state->remove(m_filter->id());
1559+ }
1560+}
1561+
1562+unity::shell::scopes::OptionSelectorOptionsInterface* OptionSelectorFilter::options() const
1563+{
1564+ return m_options.data();
1565+}
1566+
1567+void OptionSelectorFilter::update(unity::scopes::FilterState::SPtr const& filterState)
1568+{
1569+ m_filterState = filterState;
1570+ m_options->update(m_filter->active_options(*filterState), true);
1571+}
1572+
1573+void OptionSelectorFilter::update(unity::scopes::FilterBase::SCPtr const& filter)
1574+{
1575+ unity::scopes::OptionSelectorFilter::SCPtr optselfilter = std::dynamic_pointer_cast<unity::scopes::OptionSelectorFilter const>(filter);
1576+ if (!optselfilter) {
1577+ qWarning() << "OptionSelectorFilter::update(): Unexpected filter" << QString::fromStdString(filter->id()) << "of type" << QString::fromStdString(filter->filter_type());
1578+ return;
1579+ }
1580+
1581+ m_filter = optselfilter;
1582+
1583+ if (optselfilter->multi_select() != m_multiSelect)
1584+ {
1585+ m_multiSelect = optselfilter->multi_select();
1586+ Q_EMIT multiSelectChanged(m_multiSelect);
1587+ }
1588+
1589+ if (optselfilter->title() != m_title.toStdString())
1590+ {
1591+ m_title = QString::fromStdString(optselfilter->title());
1592+ Q_EMIT titleChanged();
1593+ }
1594+
1595+ if (QString::fromStdString(optselfilter->label()) != m_label)
1596+ {
1597+ m_label = QString::fromStdString(optselfilter->label());
1598+ Q_EMIT labelChanged(m_label);
1599+ }
1600+
1601+ m_options->update(optselfilter->options());
1602+ if (auto state = m_filterState.lock()) {
1603+ m_options->update(m_filter->active_options(*state), false);
1604+ }
1605+}
1606+
1607+bool OptionSelectorFilter::isActive() const
1608+{
1609+ if (auto state = m_filterState.lock()) {
1610+ return m_filter->has_active_option(*state);
1611+ }
1612+ return false;
1613+}
1614+
1615+QString OptionSelectorFilter::filterTag() const
1616+{
1617+ if (m_multiSelect) {
1618+ // multi-selection filter cannot be used as primary navigation
1619+ return "";
1620+ }
1621+ if (auto state = m_filterState.lock()) {
1622+ auto selected = m_filter->active_options(*state);
1623+ if (selected.size() > 0) {
1624+ for (auto opt: m_filter->options()) {
1625+ if (selected.find(opt) != selected.end()) {
1626+ return QString::fromStdString(opt->label()); // TODO: i18n
1627+ }
1628+ }
1629+ }
1630+ }
1631+ return "";
1632+}
1633+
1634+}
1635
1636=== added file 'src/Unity/optionselectorfilter.h'
1637--- src/Unity/optionselectorfilter.h 1970-01-01 00:00:00 +0000
1638+++ src/Unity/optionselectorfilter.h 2016-03-16 11:17:35 +0000
1639@@ -0,0 +1,69 @@
1640+/*
1641+ * Copyright (C) 2015 Canonical, Ltd.
1642+ *
1643+ * Authors:
1644+ * Pawel Stolowski <pawel.stolowski@canonical.com>
1645+ *
1646+ * This program is free software; you can redistribute it and/or modify
1647+ * it under the terms of the GNU General Public License as published by
1648+ * the Free Software Foundation; version 3.
1649+ *
1650+ * This program is distributed in the hope that it will be useful,
1651+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1652+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1653+ * GNU General Public License for more details.
1654+ *
1655+ * You should have received a copy of the GNU General Public License
1656+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1657+ */
1658+
1659+#ifndef NG_OPTIONSELECTORFILTER_H
1660+#define NG_OPTIONSELECTORFILTER_H
1661+
1662+#include <unity/shell/scopes/OptionSelectorFilterInterface.h>
1663+#include <unity/shell/scopes/FiltersInterface.h>
1664+#include "filters.h"
1665+#include <unity/scopes/OptionSelectorFilter.h>
1666+#include "optionselectoroptions.h"
1667+#include <QScopedPointer>
1668+
1669+namespace scopes_ng
1670+{
1671+
1672+class Q_DECL_EXPORT OptionSelectorFilter : public unity::shell::scopes::OptionSelectorFilterInterface, public FilterUpdateInterface
1673+{
1674+ Q_OBJECT
1675+
1676+public:
1677+ OptionSelectorFilter(unity::scopes::OptionSelectorFilter::SCPtr const& filter, unity::scopes::FilterState::SPtr const& filterState, unity::shell::scopes::FiltersInterface *parent = nullptr);
1678+ QString filterId() const override;
1679+ QString title() const override;
1680+ unity::shell::scopes::FiltersInterface::FilterType filterType() const override;
1681+ QString label() const override;
1682+ bool multiSelect() const override;
1683+ unity::shell::scopes::OptionSelectorOptionsInterface* options() const override;
1684+ void update(unity::scopes::FilterBase::SCPtr const& filter) override;
1685+ void update(unity::scopes::FilterState::SPtr const& filterState) override;
1686+ bool isActive() const override;
1687+ QString filterTag() const override;
1688+ void reset() override;
1689+
1690+Q_SIGNALS:
1691+ void filterStateChanged();
1692+
1693+protected Q_SLOTS:
1694+ void onOptionChecked(const QString& id, bool checked);
1695+
1696+private:
1697+ QString m_id;
1698+ QString m_title;
1699+ bool m_multiSelect;
1700+ QString m_label;
1701+ QScopedPointer<OptionSelectorOptions> m_options;
1702+ std::weak_ptr<unity::scopes::FilterState> m_filterState;
1703+ unity::scopes::OptionSelectorFilter::SCPtr m_filter;
1704+};
1705+
1706+} // namespace scopes_ng
1707+
1708+#endif
1709
1710=== added file 'src/Unity/optionselectoroptions.cpp'
1711--- src/Unity/optionselectoroptions.cpp 1970-01-01 00:00:00 +0000
1712+++ src/Unity/optionselectoroptions.cpp 2016-03-16 11:17:35 +0000
1713@@ -0,0 +1,164 @@
1714+/*
1715+ * Copyright (C) 2015 Canonical, Ltd.
1716+ *
1717+ * Authors:
1718+ * Pawel Stolowski <pawel.stolowski@canonical.com>
1719+ *
1720+ * This program is free software; you can redistribute it and/or modify
1721+ * it under the terms of the GNU General Public License as published by
1722+ * the Free Software Foundation; version 3.
1723+ *
1724+ * This program is distributed in the hope that it will be useful,
1725+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1726+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1727+ * GNU General Public License for more details.
1728+ *
1729+ * You should have received a copy of the GNU General Public License
1730+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1731+ */
1732+
1733+#include "optionselectoroptions.h"
1734+#include "optionselectorfilter.h"
1735+#include <QSet>
1736+#include <QDebug>
1737+
1738+namespace scopes_ng
1739+{
1740+
1741+struct OptionSelectorOption
1742+{
1743+ QString id;
1744+ QString label;
1745+ bool checked;
1746+ bool default_checked;
1747+
1748+ OptionSelectorOption(const QString& id, const QString &label, bool default_checked) :
1749+ id(id),
1750+ label(label),
1751+ checked(false),
1752+ default_checked(default_checked)
1753+ {}
1754+};
1755+
1756+OptionSelectorOptions::OptionSelectorOptions(OptionSelectorFilter *parent, std::list<unity::scopes::FilterOption::SCPtr> const& options,
1757+ std::set<unity::scopes::FilterOption::SCPtr> const& activeOptions)
1758+ : ModelUpdate(parent)
1759+{
1760+ bool const use_defaults = (activeOptions.size() == 0);
1761+
1762+ for (auto const& opt: options) {
1763+ auto shellOpt = QSharedPointer<OptionSelectorOption>(new OptionSelectorOption(QString::fromStdString(opt->id()), QString::fromStdString(opt->label()),
1764+ opt->default_value()));
1765+ m_options.append(shellOpt);
1766+ if (use_defaults) {
1767+ shellOpt->checked = shellOpt->default_checked;
1768+ } else {
1769+ if (activeOptions.find(opt) != activeOptions.end()) {
1770+ shellOpt->checked = true;
1771+ }
1772+ }
1773+ }
1774+}
1775+
1776+void OptionSelectorOptions::update(const std::set<unity::scopes::FilterOption::SCPtr>& activeOptions, bool allow_defaults)
1777+{
1778+ bool const use_defaults = (allow_defaults && activeOptions.size() == 0);
1779+ if (use_defaults) {
1780+ for (int row = 0; row<m_options.size(); row++) {
1781+ auto &opt = m_options[row];
1782+ if (opt->checked != opt->default_checked) {
1783+ opt->checked = opt->default_checked;
1784+ Q_EMIT dataChanged(index(row, 0), index(row, 0), { unity::shell::scopes::OptionSelectorOptionsInterface::Roles::RoleOptionChecked });
1785+ }
1786+ }
1787+ return;
1788+ }
1789+
1790+ QSet<QString> actOpts;
1791+ for (auto const& opt: activeOptions) {
1792+ actOpts.insert(QString::fromStdString(opt->id()));
1793+ }
1794+
1795+ for (int row = 0; row<m_options.size(); row++) {
1796+ auto &opt = m_options[row];
1797+ if (actOpts.find(opt->id) != actOpts.end()) {
1798+ if (!opt->checked) {
1799+ opt->checked = true;
1800+ Q_EMIT dataChanged(index(row, 0), index(row, 0), { unity::shell::scopes::OptionSelectorOptionsInterface::Roles::RoleOptionChecked });
1801+ }
1802+ } else {
1803+ if (opt->checked) {
1804+ opt->checked = false;
1805+ Q_EMIT dataChanged(index(row, 0), index(row, 0), { unity::shell::scopes::OptionSelectorOptionsInterface::Roles::RoleOptionChecked });
1806+ }
1807+ }
1808+ }
1809+}
1810+
1811+void OptionSelectorOptions::update(const std::list<unity::scopes::FilterOption::SCPtr>& options)
1812+{
1813+ QVector<int> roles;
1814+ roles.append(unity::shell::scopes::OptionSelectorOptionsInterface::Roles::RoleOptionLabel);
1815+
1816+ syncModel(options, m_options,
1817+ // key function for scopes api filter option
1818+ [](const unity::scopes::FilterOption::SCPtr& opt) -> QString { return QString::fromStdString(opt->id()); },
1819+ // key function for shell api filter option
1820+ [](const QSharedPointer<OptionSelectorOption>& opt) -> QString { return opt->id; },
1821+ // factory function for creating shell filter option from scopes api filter option
1822+ [this](const unity::scopes::FilterOption::SCPtr& opt) -> QSharedPointer<OptionSelectorOption> {
1823+ auto optObj = QSharedPointer<OptionSelectorOption>(
1824+ new OptionSelectorOption(QString::fromStdString(opt->id()), QString::fromStdString(opt->label()), opt->default_value()));
1825+ return optObj;
1826+ },
1827+ // filter option update function
1828+ [&roles, this](int row, const unity::scopes::FilterOption::SCPtr& op1, const QSharedPointer<OptionSelectorOption>& op2) -> bool {
1829+ if (op2->id != QString::fromStdString(op1->id())) {
1830+ return false;
1831+ }
1832+ if (op2->label != QString::fromStdString(op1->label())) {
1833+ op2->label = QString::fromStdString(op1->label());
1834+ Q_EMIT dataChanged(index(row, 0), index(row, 0), roles);
1835+ }
1836+ return true;
1837+ });
1838+}
1839+
1840+int OptionSelectorOptions::rowCount(const QModelIndex& /* parent */) const
1841+{
1842+ return m_options.count();
1843+}
1844+
1845+void OptionSelectorOptions::setChecked(int row, bool checked)
1846+{
1847+ if (row >= 0 && row < m_options.count())
1848+ {
1849+ auto opt = m_options.at(row);
1850+ if (checked != opt->checked)
1851+ {
1852+ Q_EMIT optionChecked(opt->id, checked);
1853+ }
1854+ }
1855+}
1856+
1857+QVariant OptionSelectorOptions::data(const QModelIndex& index, int role) const
1858+{
1859+ if (index.row() >= m_options.count())
1860+ {
1861+ return QVariant();
1862+ }
1863+ switch (role)
1864+ {
1865+ case Qt::DisplayRole:
1866+ case RoleOptionId:
1867+ return QVariant(m_options.at(index.row())->id);
1868+ case RoleOptionLabel:
1869+ return QVariant(m_options.at(index.row())->label);
1870+ case RoleOptionChecked:
1871+ return QVariant(m_options.at(index.row())->checked);
1872+ default:
1873+ return QVariant();
1874+ }
1875+}
1876+
1877+}
1878
1879=== added file 'src/Unity/optionselectoroptions.h'
1880--- src/Unity/optionselectoroptions.h 1970-01-01 00:00:00 +0000
1881+++ src/Unity/optionselectoroptions.h 2016-03-16 11:17:35 +0000
1882@@ -0,0 +1,64 @@
1883+/*
1884+ * Copyright (C) 2015 Canonical, Ltd.
1885+ *
1886+ * Authors:
1887+ * Pawel Stolowski <pawel.stolowski@canonical.com>
1888+ *
1889+ * This program is free software; you can redistribute it and/or modify
1890+ * it under the terms of the GNU General Public License as published by
1891+ * the Free Software Foundation; version 3.
1892+ *
1893+ * This program is distributed in the hope that it will be useful,
1894+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1895+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1896+ * GNU General Public License for more details.
1897+ *
1898+ * You should have received a copy of the GNU General Public License
1899+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1900+ */
1901+
1902+#ifndef NG_OPTIONSELECTOROPTIONS_H
1903+#define NG_OPTIONSELECTOROPTIONS_H
1904+
1905+#include <unity/shell/scopes/OptionSelectorOptionsInterface.h>
1906+#include <unity/scopes/FilterOption.h>
1907+#include "modelupdate.h"
1908+#include <QSharedPointer>
1909+#include <QList>
1910+#include <list>
1911+#include <set>
1912+#include <functional>
1913+
1914+namespace scopes_ng
1915+{
1916+
1917+class OptionSelectorFilter;
1918+class OptionSelectorOption;
1919+
1920+class Q_DECL_EXPORT OptionSelectorOptions :
1921+ public ModelUpdate<unity::shell::scopes::OptionSelectorOptionsInterface,
1922+ std::list<unity::scopes::FilterOption::SCPtr>,
1923+ QList<QSharedPointer<OptionSelectorOption>>>
1924+{
1925+ Q_OBJECT
1926+
1927+public:
1928+ OptionSelectorOptions(OptionSelectorFilter *parent,
1929+ std::list<unity::scopes::FilterOption::SCPtr> const& options,
1930+ std::set<unity::scopes::FilterOption::SCPtr> const& activeOptions);
1931+ void update(const std::list<unity::scopes::FilterOption::SCPtr>& options);
1932+ void update(const std::set<unity::scopes::FilterOption::SCPtr>& activeOptions, bool allow_defaults);
1933+ int rowCount(const QModelIndex& parent) const override;
1934+ QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
1935+ Q_INVOKABLE void setChecked(int row, bool checked) override;
1936+
1937+Q_SIGNALS:
1938+ void optionChecked(const QString&, bool);
1939+
1940+private:
1941+ QList<QSharedPointer<OptionSelectorOption>> m_options;
1942+};
1943+
1944+} // namespace scopes_ng
1945+
1946+#endif
1947
1948=== modified file 'src/Unity/plugin.cpp'
1949--- src/Unity/plugin.cpp 2016-02-11 15:35:47 +0000
1950+++ src/Unity/plugin.cpp 2016-03-16 11:17:35 +0000
1951@@ -32,6 +32,12 @@
1952 #include "previewmodel.h"
1953 #include "previewwidgetmodel.h"
1954 #include "settingsmodel.h"
1955+#include <unity/shell/scopes/FiltersInterface.h>
1956+#include <unity/shell/scopes/OptionSelectorFilterInterface.h>
1957+#include <unity/shell/scopes/OptionSelectorOptionsInterface.h>
1958+#include <unity/shell/scopes/RangeInputFilterInterface.h>
1959+#include <unity/shell/scopes/ValueSliderFilterInterface.h>
1960+#include <unity/shell/scopes/ValueSliderValuesInterface.h>
1961 #include "localization.h"
1962
1963 void UnityPlugin::registerTypes(const char *uri)
1964@@ -49,6 +55,12 @@
1965 qmlRegisterUncreatableType<scopes_ng::ResultsModel>(uri, 0, 2, "ResultsModel", QStringLiteral("Can't create new ResultsModel in QML. Get them from Categories instance."));
1966 qmlRegisterUncreatableType<unity::shell::scopes::PreviewModelInterface>(uri, 0, 2, "PreviewModel", QStringLiteral("Can't create new PreviewModel in QML. Get them from Scope instance."));
1967 qmlRegisterUncreatableType<scopes_ng::PreviewWidgetModel>(uri, 0, 2, "PreviewWidgetModel", QStringLiteral("Can't create new PreviewWidgetModel in QML. Get them from PreviewModel instance."));
1968+ qmlRegisterUncreatableType<unity::shell::scopes::FiltersInterface>(uri, 0, 2, "Filters", "Can't create Filters object in QML. Get them from Scope instance.");
1969+ qmlRegisterUncreatableType<unity::shell::scopes::FilterBaseInterface>(uri, 0, 2, "Filter", "Can't create Filter object in QML. Get them from Scope instance.");
1970+ qmlRegisterUncreatableType<unity::shell::scopes::OptionSelectorOptionsInterface>(uri, 0, 2, "OptionSelectorOptions", "Can't create Filters object in QML. Get them from OptionSelector instance.");
1971+ qmlRegisterUncreatableType<unity::shell::scopes::RangeInputFilterInterface>(uri, 0, 2, "RangeInputFilter", "Can't create new RangeInputFilter in QML. Get them from Filters instance.");
1972+ qmlRegisterUncreatableType<unity::shell::scopes::ValueSliderFilterInterface>(uri, 0, 2, "ValueSliderFilter", "Can't create new ValueSliderFilter in QML. Get them from Filters instance.");
1973+ qmlRegisterUncreatableType<unity::shell::scopes::ValueSliderValuesInterface>(uri, 0, 2, "ValueSliderValues", "Can't create new ValueSliderValues in QML. Get them from ValueSlideriFilter instance.");
1974 }
1975
1976 void UnityPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
1977
1978=== added file 'src/Unity/rangeinputfilter.cpp'
1979--- src/Unity/rangeinputfilter.cpp 1970-01-01 00:00:00 +0000
1980+++ src/Unity/rangeinputfilter.cpp 2016-03-16 11:17:35 +0000
1981@@ -0,0 +1,298 @@
1982+/*
1983+ * Copyright (C) 2015 Canonical, Ltd.
1984+ *
1985+ * Authors:
1986+ * Pawel Stolowski <pawel.stolowski@canonical.com>
1987+ *
1988+ * This program is free software; you can redistribute it and/or modify
1989+ * it under the terms of the GNU General Public License as published by
1990+ * the Free Software Foundation; version 3.
1991+ *
1992+ * This program is distributed in the hope that it will be useful,
1993+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1994+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1995+ * GNU General Public License for more details.
1996+ *
1997+ * You should have received a copy of the GNU General Public License
1998+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1999+ */
2000+
2001+#include "rangeinputfilter.h"
2002+#include "utils.h"
2003+#include <cmath>
2004+#include <functional>
2005+#include <QDebug>
2006+
2007+using namespace unity::scopes;
2008+
2009+namespace scopes_ng
2010+{
2011+
2012+RangeInputFilter::RangeInputFilter(unity::scopes::RangeInputFilter::SCPtr const& filter, unity::scopes::FilterState::SPtr const& filterState, unity::shell::scopes::FiltersInterface *parent)
2013+ : unity::shell::scopes::RangeInputFilterInterface(parent),
2014+ m_id(QString::fromStdString(filter->id())),
2015+ m_title(QString::fromStdString(filter->title())),
2016+ m_startPrefixLabel(QString::fromStdString(filter->start_prefix_label())),
2017+ m_startPostfixLabel(QString::fromStdString(filter->start_postfix_label())),
2018+ m_centralLabel(QString::fromStdString(filter->central_label())),
2019+ m_endPrefixLabel(QString::fromStdString(filter->end_prefix_label())),
2020+ m_endPostfixLabel(QString::fromStdString(filter->end_postfix_label())),
2021+ m_defaultStart(filter->default_start_value()),
2022+ m_defaultEnd(filter->default_end_value()),
2023+ m_filterState(filterState),
2024+ m_filter(filter)
2025+{
2026+ const bool use_defaults = !filterState->has_filter(m_filter->id());
2027+ const unity::scopes::Variant start = m_filter->has_start_value(*filterState) ? Variant(m_filter->start_value(*filterState)) : (use_defaults ?
2028+ m_filter->default_start_value() : Variant::null());
2029+ m_start = start;
2030+ const unity::scopes::Variant end = m_filter->has_end_value(*filterState) ? Variant(m_filter->end_value(*filterState)) : (use_defaults ?
2031+ m_filter->default_end_value() : Variant::null());
2032+ m_end = end;
2033+}
2034+
2035+QString RangeInputFilter::filterId() const
2036+{
2037+ return m_id;
2038+}
2039+
2040+QString RangeInputFilter::title() const
2041+{
2042+ return m_title;
2043+}
2044+
2045+unity::shell::scopes::FiltersInterface::FilterType RangeInputFilter::filterType() const
2046+{
2047+ return unity::shell::scopes::FiltersInterface::FilterType::RangeInputFilter;
2048+}
2049+
2050+double RangeInputFilter::startValue() const
2051+{
2052+ if (m_start.which() == Variant::Double) {
2053+ return m_start.get_double();
2054+ }
2055+ qWarning() << "Requested startValue for filter" << m_id << ", but value is not set";
2056+ return 0.0f;
2057+}
2058+
2059+double RangeInputFilter::endValue() const
2060+{
2061+ if (m_end.which() == Variant::Double) {
2062+ return m_end.get_double();
2063+ }
2064+ qWarning() << "Requested endValue for filter" << m_id << ", but value is not set";
2065+ return 0.0f;
2066+}
2067+
2068+void RangeInputFilter::setStartValue(double value)
2069+{
2070+ const unity::scopes::Variant newValue(value);
2071+ setStartValue(newValue);
2072+}
2073+
2074+void RangeInputFilter::setEndValue(double value)
2075+{
2076+ const unity::scopes::Variant newValue(value);
2077+ setEndValue(newValue);
2078+}
2079+
2080+void RangeInputFilter::labelChange(std::string const& srcLabel, QString& destLabel, std::function<void()> const& emitLabelChangeSignal)
2081+{
2082+ auto const srclbl = QString::fromStdString(srcLabel);
2083+ if (srclbl != destLabel) {
2084+ destLabel = srclbl;
2085+ emitLabelChangeSignal();
2086+ }
2087+}
2088+
2089+void RangeInputFilter::update(unity::scopes::FilterState::SPtr const& filterState)
2090+{
2091+ m_filterState = filterState;
2092+
2093+ const bool use_defaults = !filterState->has_filter(m_filter->id());
2094+ const unity::scopes::Variant start = m_filter->has_start_value(*filterState) ? Variant(m_filter->start_value(*filterState)) : (use_defaults ?
2095+ m_filter->default_start_value() : Variant::null());
2096+ if (!compare(start, m_start)) {
2097+ m_start = start;
2098+ if (m_start.is_null()) {
2099+ Q_EMIT hasStartValueChanged();
2100+ }
2101+ Q_EMIT startValueChanged();
2102+ }
2103+
2104+ const unity::scopes::Variant end = m_filter->has_end_value(*filterState) ? Variant(m_filter->end_value(*filterState)) : (use_defaults ?
2105+ m_filter->default_end_value() : Variant::null());
2106+ if (!compare(end, m_end)) {
2107+ m_end = end;
2108+ if (m_end.is_null()) {
2109+ Q_EMIT hasEndValueChanged();
2110+ }
2111+ Q_EMIT endValueChanged();
2112+ }
2113+}
2114+
2115+void RangeInputFilter::update(unity::scopes::FilterBase::SCPtr const& filter)
2116+{
2117+ unity::scopes::RangeInputFilter::SCPtr rangefilter = std::dynamic_pointer_cast<unity::scopes::RangeInputFilter const>(filter);
2118+ if (!rangefilter) {
2119+ qWarning() << "RangeInputFilter::update(): Unexpected filter" << QString::fromStdString(filter->id()) << "of type" << QString::fromStdString(filter->filter_type());
2120+ return;
2121+ }
2122+
2123+ m_filter = rangefilter;
2124+
2125+ if (rangefilter->title() != m_title.toStdString())
2126+ {
2127+ m_title = QString::fromStdString(rangefilter->title());
2128+ Q_EMIT titleChanged();
2129+ }
2130+
2131+ labelChange(m_filter->start_prefix_label(), m_startPrefixLabel, [this]() { Q_EMIT startPrefixLabelChanged(); });
2132+ labelChange(m_filter->start_postfix_label(), m_startPostfixLabel, [this]() { Q_EMIT startPostfixLabelChanged(); });
2133+ labelChange(m_filter->central_label(), m_centralLabel, [this]() { Q_EMIT centralLabelChanged(); });
2134+ labelChange(m_filter->end_prefix_label(), m_endPrefixLabel, [this]() { Q_EMIT endPrefixLabelChanged(); });
2135+ labelChange(m_filter->end_postfix_label(), m_endPostfixLabel, [this]() { Q_EMIT endPostfixLabelChanged(); });
2136+}
2137+
2138+bool RangeInputFilter::isActive() const
2139+{
2140+ if (auto state = m_filterState.lock()) {
2141+ // check if current value from filter state is equal to default value
2142+ if (m_filter->has_start_value(*state) && !compare(m_filter->start_value(*state), m_filter->default_start_value())) {
2143+ return true;
2144+ }
2145+ if (m_filter->has_end_value(*state) && !compare(m_filter->end_value(*state), m_filter->default_end_value())) {
2146+ return true;
2147+ }
2148+ }
2149+ return false;
2150+}
2151+
2152+QString RangeInputFilter::filterTag() const
2153+{
2154+ return ""; // range input filter can't be a primary navigation filter
2155+}
2156+
2157+QString RangeInputFilter::startPrefixLabel() const
2158+{
2159+ return m_startPrefixLabel;
2160+}
2161+
2162+QString RangeInputFilter::startPostfixLabel() const
2163+{
2164+ return m_startPostfixLabel;
2165+}
2166+
2167+QString RangeInputFilter::centralLabel() const
2168+{
2169+ return m_centralLabel;
2170+}
2171+
2172+QString RangeInputFilter::endPrefixLabel() const
2173+{
2174+ return m_endPrefixLabel;
2175+}
2176+
2177+QString RangeInputFilter::endPostfixLabel() const
2178+{
2179+ return m_endPostfixLabel;
2180+}
2181+
2182+bool RangeInputFilter::hasStartValue() const
2183+{
2184+ return m_start.which() == Variant::Double;
2185+}
2186+
2187+bool RangeInputFilter::hasEndValue() const
2188+{
2189+ return m_end.which() == Variant::Double;
2190+}
2191+
2192+void RangeInputFilter::eraseStartValue()
2193+{
2194+ setStartValue(Variant::null());
2195+}
2196+
2197+void RangeInputFilter::eraseEndValue()
2198+{
2199+ setEndValue(Variant::null());
2200+}
2201+
2202+void RangeInputFilter::setStartValue(Variant const& value)
2203+{
2204+ if (auto state = m_filterState.lock()) {
2205+ try {
2206+ if (!compare(value, m_start)) {
2207+ qDebug() << "Changing startValue of filter" << m_id;
2208+ m_start = value;
2209+
2210+ m_filter->update_state(*state, m_start, m_end);
2211+
2212+ if (value.is_null()) {
2213+ Q_EMIT hasStartValueChanged();
2214+ }
2215+ Q_EMIT startValueChanged();
2216+ Q_EMIT filterStateChanged();
2217+ }
2218+ }
2219+ catch (std::exception const& err)
2220+ {
2221+ // this is ok, it's user input and we may get partial input
2222+ qWarning() << "Could not set start value of filter" << m_id << ":" << QString::fromStdString(err.what());
2223+ }
2224+ }
2225+}
2226+
2227+void RangeInputFilter::setEndValue(Variant const& value)
2228+{
2229+ if (auto state = m_filterState.lock()) {
2230+ try {
2231+ if (!compare(value, m_end)) {
2232+ qDebug() << "Changing endValue of filter" << m_id;
2233+ m_end = value;
2234+
2235+ m_filter->update_state(*state, m_start, m_end);
2236+
2237+ if (value.is_null()) {
2238+ Q_EMIT hasEndValueChanged();
2239+ }
2240+ Q_EMIT endValueChanged();
2241+ Q_EMIT filterStateChanged();
2242+ }
2243+ }
2244+ catch (std::exception const& err)
2245+ {
2246+ // this is ok, it's user input and we may get partial input
2247+ qWarning() << "Could not set start value of filter" << m_id << ":" << QString::fromStdString(err.what());
2248+ }
2249+ }
2250+}
2251+
2252+bool RangeInputFilter::compare(Variant const& v1, Variant const& v2)
2253+{
2254+ if (v1 == v2) {
2255+ return true;
2256+ }
2257+ if (v1.which() == Variant::Double && v2.which() == Variant::Double) {
2258+ return std::abs(v1.get_double() - v2.get_double()) < 0.0000001f;
2259+ }
2260+ return false;
2261+}
2262+
2263+bool RangeInputFilter::compare(double v1, Variant const& v2)
2264+{
2265+ if (v2.which() == Variant::Double) {
2266+ return std::abs(v1 - v2.get_double()) < 0.0000001f;
2267+ }
2268+ return false;
2269+}
2270+
2271+void RangeInputFilter::reset()
2272+{
2273+ // Filters::onFilterStateChanged will delay actual refresh,
2274+ // so it's ok to make two independent updates and emit two signals
2275+ setStartValue(m_filter->default_start_value());
2276+ setEndValue(m_filter->default_end_value());
2277+}
2278+
2279+}
2280
2281=== added file 'src/Unity/rangeinputfilter.h'
2282--- src/Unity/rangeinputfilter.h 1970-01-01 00:00:00 +0000
2283+++ src/Unity/rangeinputfilter.h 2016-03-16 11:17:35 +0000
2284@@ -0,0 +1,93 @@
2285+/*
2286+ * Copyright (C) 2015 Canonical, Ltd.
2287+ *
2288+ * Authors:
2289+ * Pawel Stolowski <pawel.stolowski@canonical.com>
2290+ *
2291+ * This program is free software; you can redistribute it and/or modify
2292+ * it under the terms of the GNU General Public License as published by
2293+ * the Free Software Foundation; version 3.
2294+ *
2295+ * This program is distributed in the hope that it will be useful,
2296+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2297+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2298+ * GNU General Public License for more details.
2299+ *
2300+ * You should have received a copy of the GNU General Public License
2301+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2302+ */
2303+
2304+#ifndef NG_RANGEINPUTFILTER_H
2305+#define NG_RANGEINPUTFILTER_H
2306+
2307+#include <unity/shell/scopes/RangeInputFilterInterface.h>
2308+#include <unity/shell/scopes/FiltersInterface.h>
2309+#include "filters.h"
2310+#include <unity/scopes/RangeInputFilter.h>
2311+#include <QScopedPointer>
2312+
2313+namespace scopes_ng
2314+{
2315+
2316+class Q_DECL_EXPORT RangeInputFilter : public unity::shell::scopes::RangeInputFilterInterface, public FilterUpdateInterface
2317+{
2318+ Q_OBJECT
2319+
2320+public:
2321+ RangeInputFilter(unity::scopes::RangeInputFilter::SCPtr const& filter, unity::scopes::FilterState::SPtr const& filterState, unity::shell::scopes::FiltersInterface *parent = nullptr);
2322+ QString filterId() const override;
2323+ QString title() const override;
2324+ unity::shell::scopes::FiltersInterface::FilterType filterType() const override;
2325+ double startValue() const override;
2326+ double endValue() const override;
2327+
2328+ QString startPrefixLabel() const override;
2329+ QString startPostfixLabel() const override;
2330+ QString centralLabel() const override;
2331+ QString endPrefixLabel() const override;
2332+ QString endPostfixLabel() const override;
2333+
2334+ void setStartValue(double value) override;
2335+ void setEndValue(double value) override;
2336+
2337+ bool hasStartValue() const override;
2338+ bool hasEndValue() const override;
2339+
2340+ Q_INVOKABLE void eraseStartValue() override;
2341+ Q_INVOKABLE void eraseEndValue() override;
2342+
2343+ void update(unity::scopes::FilterBase::SCPtr const& filter) override;
2344+ void update(unity::scopes::FilterState::SPtr const& filterState) override;
2345+ bool isActive() const override;
2346+ QString filterTag() const override;
2347+ void reset() override;
2348+
2349+Q_SIGNALS:
2350+ void filterStateChanged();
2351+
2352+private:
2353+ void setStartValue(unity::scopes::Variant const& value);
2354+ void setEndValue(unity::scopes::Variant const& value);
2355+ static void labelChange(std::string const& srcLabel, QString& destLabel, std::function<void()> const& emitLabelChangeSignal);
2356+
2357+ QString m_id;
2358+ QString m_title;
2359+ QString m_startPrefixLabel;
2360+ QString m_startPostfixLabel;
2361+ QString m_centralLabel;
2362+ QString m_endPrefixLabel;
2363+ QString m_endPostfixLabel;
2364+ unity::scopes::Variant m_defaultStart;
2365+ unity::scopes::Variant m_defaultEnd;
2366+ unity::scopes::Variant m_start;
2367+ unity::scopes::Variant m_end;
2368+ std::weak_ptr<unity::scopes::FilterState> m_filterState;
2369+ unity::scopes::RangeInputFilter::SCPtr m_filter;
2370+
2371+ static bool compare(double v1, unity::scopes::Variant const& v2);
2372+ static bool compare(unity::scopes::Variant const& v1, unity::scopes::Variant const& v2);
2373+};
2374+
2375+} // namespace scopes_ng
2376+
2377+#endif
2378
2379=== modified file 'src/Unity/resultsmodel.cpp'
2380--- src/Unity/resultsmodel.cpp 2016-02-15 17:43:12 +0000
2381+++ src/Unity/resultsmodel.cpp 2016-03-16 11:17:35 +0000
2382@@ -325,6 +325,8 @@
2383 return QVariant();
2384 case RoleQuickPreviewData:
2385 return componentValue(result, "quick-preview-data");
2386+ case RoleSocialActions:
2387+ return componentValue(result, "social-actions");
2388 default:
2389 return QVariant();
2390 }
2391
2392=== modified file 'src/Unity/scope.cpp'
2393--- src/Unity/scope.cpp 2016-02-26 12:38:28 +0000
2394+++ src/Unity/scope.cpp 2016-03-16 11:17:35 +0000
2395@@ -1,8 +1,9 @@
2396 /*
2397- * Copyright (C) 2013 Canonical, Ltd.
2398+ * Copyright (C) 2015 Canonical, Ltd.
2399 *
2400 * Authors:
2401 * Michal Hruby <michal.hruby@canonical.com>
2402+ * Pawel Stolowski <pawel.stolowski@canonical.com>
2403 *
2404 * This program is free software; you can redistribute it and/or modify
2405 * it under the terms of the GNU General Public License as published by
2406@@ -81,13 +82,13 @@
2407 Scope::Scope(scopes_ng::Scopes* parent) :
2408 m_query_id(0)
2409 , m_formFactor(QStringLiteral("phone"))
2410+ , m_activeFiltersCount(0)
2411 , m_isActive(false)
2412 , m_searchInProgress(false)
2413 , m_activationInProgress(false)
2414 , m_resultsDirty(false)
2415 , m_delayedSearchProcessing(false)
2416 , m_hasNavigation(false)
2417- , m_hasAltNavigation(false)
2418 , m_favorite(false)
2419 , m_initialQueryDone(false)
2420 , m_searchController(new CollectionController)
2421@@ -96,6 +97,12 @@
2422 {
2423 QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership);
2424 m_categories.reset(new Categories(this));
2425+ m_filters.reset(new Filters(m_filterState, this));
2426+
2427+ connect(m_filters.data(), SIGNAL(primaryFilterChanged()), this, SIGNAL(primaryNavigationFilterChanged()));
2428+
2429+ QQmlEngine::setObjectOwnership(m_filters.data(), QQmlEngine::CppOwnership);
2430+ connect(m_filters.data(), SIGNAL(filterStateChanged()), this, SLOT(filterStateChanged()));
2431
2432 setScopesInstance(parent);
2433
2434@@ -125,17 +132,15 @@
2435 CollectorBase::Status status;
2436 QList<std::shared_ptr<scopes::CategorisedResult>> results;
2437 scopes::Department::SCPtr rootDepartment;
2438- scopes::OptionSelectorFilter::SCPtr sortOrderFilter;
2439- scopes::FilterState filterState;
2440+ QList<scopes::FilterBase::SCPtr> filters;
2441
2442- status = pushEvent->collectSearchResults(results, rootDepartment, sortOrderFilter, filterState);
2443+ status = pushEvent->collectSearchResults(results, rootDepartment, filters);
2444 if (status == CollectorBase::Status::CANCELLED) {
2445 return;
2446 }
2447
2448 m_rootDepartment = rootDepartment;
2449- m_sortOrderFilter = sortOrderFilter;
2450- m_receivedFilterState = filterState;
2451+ m_receivedFilters = filters;
2452
2453 if (m_cachedResults.empty()) {
2454 m_cachedResults.swap(results);
2455@@ -404,17 +409,14 @@
2456 }
2457
2458 m_lastRootDepartment = m_rootDepartment;
2459+ bool containsDepartments = (m_rootDepartment.get() != nullptr);
2460
2461 //
2462 // only consider resetting current department id if we are in final flushUpdates
2463 // or received departments already. We don't know if we should reset it
2464 // until query finishes because departments may still arrive.
2465- if (finalize || m_rootDepartment.get() != nullptr)
2466+ if (finalize || containsDepartments)
2467 {
2468- bool containsDepartments = m_rootDepartment.get() != nullptr;
2469- // design decision - no navigation when doing searches
2470- containsDepartments &= m_searchQuery.isEmpty();
2471-
2472 if (containsDepartments != m_hasNavigation) {
2473 m_hasNavigation = containsDepartments;
2474 Q_EMIT hasNavigationChanged();
2475@@ -425,54 +427,30 @@
2476 m_currentNavigationId = QLatin1String("");
2477 Q_EMIT currentNavigationIdChanged();
2478 }
2479- }
2480-
2481- // process the alt navigation (sort order filter)
2482- QString currentAltNav(m_currentAltNavigationId);
2483-
2484- if (m_sortOrderFilter && m_sortOrderFilter != m_lastSortOrderFilter) {
2485- // build the nodes
2486- m_altNavTree.reset(new DepartmentNode);
2487- m_altNavTree->initializeForFilter(m_sortOrderFilter);
2488-
2489- if (m_sortOrderFilter->has_active_option(m_receivedFilterState)) {
2490- auto active_options = m_sortOrderFilter->active_options(m_receivedFilterState);
2491- scopes::FilterOption::SCPtr active_option = *active_options.begin();
2492- if (active_option) {
2493- currentAltNav = QString::fromStdString(active_option->id());
2494- }
2495- }
2496- }
2497-
2498- m_lastSortOrderFilter = m_sortOrderFilter;
2499-
2500- //
2501- // only consider resetting alt nav id if we are in final flushUpdates
2502- // or received alt nav filter already. We don't know if we should reset it
2503- // until query finishes because filter may still arrive.
2504- if (finalize || m_sortOrderFilter.get() != nullptr)
2505+ processPrimaryNavigationTag(m_currentNavigationId);
2506+ }
2507+
2508+ // process filters
2509+ if (finalize || m_receivedFilters.size() > 0)
2510 {
2511- bool containsAltNav = m_sortOrderFilter.get() != nullptr;
2512- // design decision - no navigation when doing searches
2513- containsAltNav &= m_searchQuery.isEmpty();
2514-
2515- if (containsAltNav != m_hasAltNavigation) {
2516- m_hasAltNavigation = containsAltNav;
2517- Q_EMIT hasAltNavigationChanged();
2518- }
2519-
2520- if (!containsAltNav && !m_currentAltNavigationId.isEmpty()) {
2521- qDebug() << "Resetting alt nav id";
2522- m_currentAltNavigationId = QLatin1String("");
2523- Q_EMIT currentAltNavigationIdChanged();
2524- }
2525-
2526- if (containsAltNav && currentAltNav != m_currentAltNavigationId) {
2527- m_currentAltNavigationId = currentAltNav;
2528- Q_EMIT currentAltNavigationIdChanged();
2529-
2530- // update the alt navigation models
2531- updateNavigationModels(m_altNavTree.data(), m_altNavModels, m_currentAltNavigationId);
2532+ qDebug() << "Processing" << m_receivedFilters.size() << "filters";
2533+ const bool containsFilters = (m_receivedFilters.size() > 0);
2534+ const bool haveFiltersAlready = (m_filters->rowCount() > 0);
2535+ if (containsFilters) {
2536+ m_filters->update(m_receivedFilters, containsDepartments);
2537+ processPrimaryNavigationTag(m_currentNavigationId);
2538+ if (!haveFiltersAlready) {
2539+ Q_EMIT filtersChanged();
2540+ }
2541+ qDebug() << "Current number of filters:" << m_filters->rowCount();
2542+ }
2543+ else
2544+ {
2545+ qDebug() << "Removing all filters";
2546+ m_filters->clear();
2547+ if (haveFiltersAlready) {
2548+ Q_EMIT filtersChanged();
2549+ }
2550 }
2551 }
2552 }
2553@@ -698,6 +676,7 @@
2554 {
2555 if (m_currentNavigationId != id) {
2556 qDebug() << "Setting current nav id:" << this->id() << id;
2557+ processPrimaryNavigationTag(id);
2558 m_currentNavigationId = id;
2559 Q_EMIT currentNavigationIdChanged();
2560 }
2561@@ -935,13 +914,6 @@
2562 }
2563 }
2564
2565-/*
2566-Filters* Scope::filters() const
2567-{
2568- return m_filters.get();
2569-}
2570-*/
2571-
2572 unity::shell::scopes::NavigationInterface* Scope::getNavigation(QString const& navId)
2573 {
2574 if (!m_departmentTree) return nullptr;
2575@@ -962,54 +934,19 @@
2576 return navModel;
2577 }
2578
2579-unity::shell::scopes::NavigationInterface* Scope::getAltNavigation(QString const& navId)
2580-{
2581- if (!m_altNavTree) return nullptr;
2582-
2583- DepartmentNode* node = m_altNavTree->findNodeById(navId);
2584- if (!node) return nullptr;
2585-
2586- Department* navModel = new Department;
2587- navModel->setScopeId(this->id());
2588- navModel->loadFromDepartmentNode(node);
2589- navModel->markSubdepartmentActive(m_currentAltNavigationId);
2590-
2591- // sharing m_inverseDepartments with getNavigation
2592- m_altNavModels.insert(navId, navModel);
2593- m_inverseDepartments.insert(navModel, navId);
2594- QObject::connect(navModel, &QObject::destroyed, this, &Scope::departmentModelDestroyed);
2595-
2596- return navModel;
2597-}
2598-
2599-QString Scope::buildQuery(QString const& scopeId, QString const& searchQuery, QString const& departmentId, QString const& primaryFilterId, QString const& primaryOptionId)
2600+QString Scope::buildQuery(QString const& scopeId, QString const& searchQuery, QString const& departmentId, unity::scopes::FilterState const& filterState)
2601 {
2602 scopes::CannedQuery q(scopeId.toStdString());
2603 q.set_query_string(searchQuery.toStdString());
2604 q.set_department_id(departmentId.toStdString());
2605-
2606- if (!primaryFilterId.isEmpty() && !primaryOptionId.isEmpty()) {
2607- scopes::FilterState filter_state;
2608- scopes::OptionSelectorFilter::update_state(filter_state, primaryFilterId.toStdString(), primaryOptionId.toStdString(), true);
2609- q.set_filter_state(filter_state);
2610- }
2611-
2612+ q.set_filter_state(filterState);
2613 return QString::fromStdString(q.to_uri());
2614 }
2615
2616-void Scope::setNavigationState(QString const& navId, bool altNavigation)
2617+void Scope::setNavigationState(QString const& navId)
2618 {
2619- QString primaryFilterId;
2620- if (m_sortOrderFilter) {
2621- primaryFilterId = QString::fromStdString(m_sortOrderFilter->id());
2622- }
2623- if (!altNavigation) {
2624- // switch current department id
2625- performQuery(buildQuery(id(), m_searchQuery, navId, primaryFilterId, m_currentAltNavigationId));
2626- } else {
2627- // switch current primary filter
2628- performQuery(buildQuery(id(), m_searchQuery, m_currentNavigationId, primaryFilterId, navId));
2629- }
2630+ // switch current department id
2631+ performQuery(buildQuery(id(), m_searchQuery, navId, m_filterState));
2632 }
2633
2634 void Scope::departmentModelDestroyed(QObject* obj)
2635@@ -1020,7 +957,6 @@
2636 if (it == m_inverseDepartments.end()) return;
2637
2638 m_departmentModels.remove(it.value(), navigation);
2639- m_altNavModels.remove(it.value(), navigation);
2640 m_inverseDepartments.erase(it);
2641 }
2642
2643@@ -1082,21 +1018,16 @@
2644 return m_hasNavigation;
2645 }
2646
2647-QString Scope::currentAltNavigationId() const
2648-{
2649- return m_currentAltNavigationId;
2650-}
2651-
2652-bool Scope::hasAltNavigation() const
2653-{
2654- return m_hasAltNavigation;
2655-}
2656-
2657 QVariantMap Scope::customizations() const
2658 {
2659 return m_customizations;
2660 }
2661
2662+int Scope::activeFiltersCount() const
2663+{
2664+ return m_activeFiltersCount;
2665+}
2666+
2667 void Scope::setSearchQuery(const QString& search_query)
2668 {
2669 // this method is called by the shell when user types in search string,
2670@@ -1133,11 +1064,6 @@
2671 }
2672 m_searchQuery = search_query;
2673
2674- // atm only empty query can have a filter state
2675- if (!m_searchQuery.isEmpty()) {
2676- m_filterState = scopes::FilterState();
2677- }
2678-
2679 // only use typing delay if scope is active, otherwise apply immediately
2680 if (m_isActive) {
2681 m_typingTimer.start();
2682@@ -1348,6 +1274,19 @@
2683 }
2684 }
2685
2686+void Scope::resetPrimaryNavigationTag()
2687+{
2688+ qDebug() << "resetPrimaryNavigationTag()";
2689+ setCurrentNavigationId("");
2690+ m_filters->update(unity::scopes::FilterState());
2691+ filterStateChanged();
2692+}
2693+
2694+void Scope::resetFilters()
2695+{
2696+ m_filters->reset();
2697+}
2698+
2699 void Scope::closeScope(unity::shell::scopes::ScopeInterface* scope)
2700 {
2701 if (m_scopesInstance) {
2702@@ -1392,4 +1331,82 @@
2703 return m_initialQueryDone;
2704 }
2705
2706+unity::shell::scopes::FiltersInterface* Scope::filters() const
2707+{
2708+ if (m_filters && m_filters->rowCount() == 0) {
2709+ return nullptr;
2710+ }
2711+ return m_filters.data();
2712+}
2713+
2714+unity::shell::scopes::FilterBaseInterface* Scope::primaryNavigationFilter() const
2715+{
2716+ return m_filters->primaryFilter().data();
2717+}
2718+
2719+QString Scope::primaryNavigationTag() const
2720+{
2721+ return m_primaryNavigationTag;
2722+}
2723+
2724+void Scope::filterStateChanged()
2725+{
2726+ qDebug() << "Filters changed";
2727+ m_filterState = m_filters->filterState();
2728+ processPrimaryNavigationTag(m_currentNavigationId);
2729+ processActiveFiltersCount();
2730+ invalidateResults();
2731+}
2732+
2733+//
2734+// Iterate over all filters to calculate the number of active ones.
2735+void Scope::processActiveFiltersCount()
2736+{
2737+ const int count = m_filters->activeFiltersCount();
2738+ if (count != m_activeFiltersCount) {
2739+ m_activeFiltersCount = count;
2740+ Q_EMIT activeFiltersCountChanged();
2741+ }
2742+ qDebug() << "active filters count:" << m_activeFiltersCount;
2743+}
2744+
2745+//
2746+// Determine primary navigation tag (the "brick" in search bar) from
2747+// current department (if departments are present) or primary navigation
2748+// filter (if scopes doesn't have departments but has filters and one of
2749+// them has 'Primary' flag set.
2750+void Scope::processPrimaryNavigationTag(QString const &targetDepartmentId)
2751+{
2752+ QString tag;
2753+ // has departments?
2754+ if (m_rootDepartment) {
2755+ auto it = m_departmentModels.constFind(targetDepartmentId);
2756+ if (it != m_departmentModels.constEnd()) {
2757+ tag = (targetDepartmentId == "" ? "" : it.value()->label());
2758+ } else {
2759+ it = m_departmentModels.constFind(m_currentNavigationId);
2760+ if (it != m_departmentModels.constEnd()) {
2761+ auto subDept = (*it)->findSubdepartment(targetDepartmentId);
2762+ if (subDept) {
2763+ tag = subDept->label;
2764+ } else {
2765+ qWarning() << "Scope::processPrimaryNavigationTag(): no subdepartment '" << targetDepartmentId << "'";
2766+ }
2767+ } else {
2768+ qWarning() << "Scope::processPrimaryNavigationTag(): no department model for '" << m_currentNavigationId << "'";
2769+ }
2770+ }
2771+ } else {
2772+ auto pf = m_filters->primaryFilter();
2773+ if (pf) {
2774+ tag = pf->filterTag();
2775+ }
2776+ }
2777+ qDebug() << "Scope::processPrimaryNavigationTag(): tag is '" << tag << "'";
2778+ if (m_primaryNavigationTag != tag) {
2779+ m_primaryNavigationTag = tag;
2780+ Q_EMIT primaryNavigationTagChanged();
2781+ }
2782+}
2783+
2784 } // namespace scopes_ng
2785
2786=== modified file 'src/Unity/scope.h'
2787--- src/Unity/scope.h 2016-02-18 15:46:26 +0000
2788+++ src/Unity/scope.h 2016-03-16 11:17:35 +0000
2789@@ -38,6 +38,7 @@
2790 #include <unity/scopes/ScopeMetadata.h>
2791 #include <unity/shell/scopes/ScopeInterface.h>
2792
2793+#include "filters.h"
2794 #include "collectors.h"
2795 #include "departmentnode.h"
2796 #include "department.h"
2797@@ -129,6 +130,11 @@
2798 unity::shell::scopes::ScopeInterface::Status status() const override;
2799 unity::shell::scopes::CategoriesInterface* categories() const override;
2800 unity::shell::scopes::SettingsModelInterface* settings() const override;
2801+ unity::shell::scopes::FiltersInterface* filters() const override;
2802+ unity::shell::scopes::FilterBaseInterface* primaryNavigationFilter() const override;
2803+ QString primaryNavigationTag() const override;
2804+ int activeFiltersCount() const override;
2805+
2806 bool require_child_scopes_refresh() const;
2807 void update_child_scopes();
2808 QString searchQuery() const override;
2809@@ -137,8 +143,6 @@
2810 bool isActive() const override;
2811 QString currentNavigationId() const override;
2812 bool hasNavigation() const override;
2813- QString currentAltNavigationId() const override;
2814- bool hasAltNavigation() const override;
2815 QVariantMap customizations() const override;
2816
2817 /* setters */
2818@@ -153,10 +157,11 @@
2819 Q_INVOKABLE void cancelActivation() override;
2820 Q_INVOKABLE void closeScope(unity::shell::scopes::ScopeInterface* scope) override;
2821 Q_INVOKABLE unity::shell::scopes::NavigationInterface* getNavigation(QString const& id) override;
2822- Q_INVOKABLE unity::shell::scopes::NavigationInterface* getAltNavigation(QString const& id) override;
2823- Q_INVOKABLE void setNavigationState(QString const& navId, bool altNavigation) override;
2824+ Q_INVOKABLE void setNavigationState(QString const& navId) override;
2825 Q_INVOKABLE void performQuery(QString const& cannedQuery) override;
2826 Q_INVOKABLE void refresh() override;
2827+ Q_INVOKABLE void resetPrimaryNavigationTag() override;
2828+ Q_INVOKABLE void resetFilters() override;
2829
2830 void setScopeData(unity::scopes::ScopeMetadata const& data);
2831 void handleActivation(std::shared_ptr<unity::scopes::ActivationResponse> const&, unity::scopes::Result::SPtr const&, QString const& categoryId="");
2832@@ -191,6 +196,7 @@
2833 void flushUpdates(bool finalize = false);
2834 void metadataRefreshed();
2835 void departmentModelDestroyed(QObject* obj);
2836+ void filterStateChanged();
2837 void previewModelDestroyed(QObject *obj);
2838
2839 protected:
2840@@ -206,12 +212,14 @@
2841
2842 private:
2843 static void updateNavigationModels(DepartmentNode* rootNode, QMultiMap<QString, Department*>& navigationModels, QString const& activeNavigation);
2844- static QString buildQuery(QString const& scopeId, QString const& searchQuery, QString const& departmentId, QString const& primaryFilterId, QString const& primaryOptionId);
2845+ static QString buildQuery(QString const& scopeId, QString const& searchQuery, QString const& departmentId, unity::scopes::FilterState const& filterState);
2846 void setScopesInstance(Scopes*);
2847 void startTtlTimer();
2848 void setCurrentNavigationId(QString const& id);
2849 void setFilterState(unity::scopes::FilterState const& filterState);
2850 void processSearchChunk(PushEvent* pushEvent);
2851+ void processPrimaryNavigationTag(QString const &targetDepartmentId);
2852+ void processActiveFiltersCount();
2853 void setCannedQuery(unity::scopes::CannedQuery const& query);
2854 void executeCannedQuery(unity::scopes::CannedQuery const& query, bool allowDelayedActivation);
2855 void handlePreviewUpdate(unity::scopes::Result::SPtr const& result, unity::scopes::PreviewWidgetList const& widgets);
2856@@ -227,16 +235,16 @@
2857 QString m_noResultsHint;
2858 QString m_formFactor;
2859 QString m_currentNavigationId;
2860- QString m_currentAltNavigationId;
2861+ QString m_primaryNavigationTag;
2862 QVariantMap m_customizations;
2863 std::unique_ptr<unity::scopes::Variant> m_queryUserData;
2864+ int m_activeFiltersCount;
2865 bool m_isActive;
2866 bool m_searchInProgress;
2867 bool m_activationInProgress;
2868 bool m_resultsDirty;
2869 bool m_delayedSearchProcessing;
2870 bool m_hasNavigation;
2871- bool m_hasAltNavigation;
2872 bool m_favorite;
2873 bool m_initialQueryDone;
2874
2875@@ -248,20 +256,18 @@
2876 std::shared_ptr<unity::scopes::ActivationResponse> m_delayedActivation;
2877 unity::scopes::Department::SCPtr m_rootDepartment;
2878 unity::scopes::Department::SCPtr m_lastRootDepartment;
2879- unity::scopes::OptionSelectorFilter::SCPtr m_sortOrderFilter;
2880- unity::scopes::OptionSelectorFilter::SCPtr m_lastSortOrderFilter;
2881 unity::scopes::FilterState m_filterState;
2882- unity::scopes::FilterState m_receivedFilterState;
2883 unity::shell::scopes::ScopeInterface::Status m_status;
2884+ QList<unity::scopes::FilterBase::SCPtr> m_receivedFilters;
2885+ QScopedPointer<Filters> m_filters;
2886+
2887 QScopedPointer<SettingsModel> m_settingsModel;
2888 QSharedPointer<DepartmentNode> m_departmentTree;
2889- QSharedPointer<DepartmentNode> m_altNavTree;
2890 QTimer m_typingTimer;
2891 QTimer m_searchProcessingDelayTimer;
2892 QTimer m_invalidateTimer;
2893 QList<std::shared_ptr<unity::scopes::CategorisedResult>> m_cachedResults;
2894 QMultiMap<QString, Department*> m_departmentModels;
2895- QMultiMap<QString, Department*> m_altNavModels;
2896 QMap<Department*, QString> m_inverseDepartments;
2897 QMetaObject::Connection m_metadataConnection;
2898 QSharedPointer<LocationService> m_locationService;
2899
2900=== modified file 'src/Unity/utils.cpp'
2901--- src/Unity/utils.cpp 2015-09-08 12:32:05 +0000
2902+++ src/Unity/utils.cpp 2016-03-16 11:17:35 +0000
2903@@ -78,6 +78,8 @@
2904 return scopes::Variant(static_cast<int64_t>(variant.toUInt()));
2905 case QMetaType::Double:
2906 return scopes::Variant(variant.toDouble());
2907+ case QMetaType::Float:
2908+ return scopes::Variant(variant.toDouble());
2909 case QMetaType::QString:
2910 return scopes::Variant(variant.toString().toStdString());
2911 case QMetaType::QVariantMap: {
2912
2913=== added file 'src/Unity/valuesliderfilter.cpp'
2914--- src/Unity/valuesliderfilter.cpp 1970-01-01 00:00:00 +0000
2915+++ src/Unity/valuesliderfilter.cpp 2016-03-16 11:17:35 +0000
2916@@ -0,0 +1,155 @@
2917+/*
2918+ * Copyright (C) 2015 Canonical, Ltd.
2919+ *
2920+ * Authors:
2921+ * Pawel Stolowski <pawel.stolowski@canonical.com>
2922+ *
2923+ * This program is free software; you can redistribute it and/or modify
2924+ * it under the terms of the GNU General Public License as published by
2925+ * the Free Software Foundation; version 3.
2926+ *
2927+ * This program is distributed in the hope that it will be useful,
2928+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2929+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2930+ * GNU General Public License for more details.
2931+ *
2932+ * You should have received a copy of the GNU General Public License
2933+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2934+ */
2935+
2936+#include "valuesliderfilter.h"
2937+#include "utils.h"
2938+#include <cmath>
2939+#include <functional>
2940+#include <QQmlEngine>
2941+#include <QDebug>
2942+
2943+using namespace unity::scopes;
2944+
2945+namespace scopes_ng
2946+{
2947+
2948+ValueSliderFilter::ValueSliderFilter(unity::scopes::ValueSliderFilter::SCPtr const& filter, unity::scopes::FilterState::SPtr const& filterState, unity::shell::scopes::FiltersInterface *parent)
2949+ : unity::shell::scopes::ValueSliderFilterInterface(parent),
2950+ m_id(QString::fromStdString(filter->id())),
2951+ m_title(QString::fromStdString(filter->title())),
2952+ m_min(filter->min()),
2953+ m_max(filter->max()),
2954+ m_values(new ValueSliderValues(this)),
2955+ m_filterState(filterState),
2956+ m_filter(filter)
2957+{
2958+ QQmlEngine::setObjectOwnership(m_values.data(), QQmlEngine::CppOwnership);
2959+ m_value = (filter->has_value(*filterState) ? filter->value(*filterState) : filter->default_value());
2960+}
2961+
2962+QString ValueSliderFilter::filterId() const
2963+{
2964+ return m_id;
2965+}
2966+
2967+QString ValueSliderFilter::title() const
2968+{
2969+ return m_title;
2970+}
2971+
2972+unity::shell::scopes::FiltersInterface::FilterType ValueSliderFilter::filterType() const
2973+{
2974+ return unity::shell::scopes::FiltersInterface::FilterType::ValueSliderFilter;
2975+}
2976+
2977+double ValueSliderFilter::value() const
2978+{
2979+ return m_value;
2980+}
2981+
2982+void ValueSliderFilter::setValue(double value)
2983+{
2984+ if (auto state = m_filterState.lock()) {
2985+ if (value != m_value) {
2986+ qDebug() << "Changing value of filter" << m_id;
2987+
2988+ m_filter->update_state(*state, m_value = value);
2989+
2990+ Q_EMIT valueChanged();
2991+ Q_EMIT filterStateChanged();
2992+ }
2993+ }
2994+}
2995+
2996+double ValueSliderFilter::minValue() const
2997+{
2998+ return m_min;
2999+}
3000+
3001+double ValueSliderFilter::maxValue() const
3002+{
3003+ return m_max;
3004+}
3005+
3006+unity::shell::scopes::ValueSliderValuesInterface* ValueSliderFilter::values() const
3007+{
3008+ return m_values.data();
3009+}
3010+
3011+void ValueSliderFilter::update(unity::scopes::FilterState::SPtr const& filterState)
3012+{
3013+ m_filterState = filterState;
3014+
3015+ const double value = (m_filter->has_value(*filterState) ? m_filter->value(*filterState) : m_filter->default_value());
3016+ if (value != m_value) {
3017+ m_value = value;
3018+ Q_EMIT valueChanged();
3019+ }
3020+
3021+ if (std::abs(m_filter->min() - m_min) < 0.0000001f) {
3022+ m_min = m_filter->min();
3023+ Q_EMIT minValueChanged();
3024+ }
3025+
3026+ if (std::abs(m_filter->max() - m_max) < 0.0000001f) {
3027+ m_max = m_filter->max();
3028+ Q_EMIT maxValueChanged();
3029+ }
3030+}
3031+
3032+void ValueSliderFilter::update(unity::scopes::FilterBase::SCPtr const& filter)
3033+{
3034+ unity::scopes::ValueSliderFilter::SCPtr valueslider = std::dynamic_pointer_cast<unity::scopes::ValueSliderFilter const>(filter);
3035+ if (!valueslider) {
3036+ qWarning() << "ValueSliderFilter::update(): Unexpected filter" << QString::fromStdString(filter->id()) << "of type" << QString::fromStdString(filter->filter_type());
3037+ return;
3038+ }
3039+
3040+ m_filter = valueslider;
3041+
3042+ if (valueslider->title() != m_title.toStdString())
3043+ {
3044+ m_title = QString::fromStdString(valueslider->title());
3045+ Q_EMIT titleChanged();
3046+ }
3047+
3048+ m_values->update(valueslider->labels(), valueslider->min(), valueslider->max());
3049+}
3050+
3051+bool ValueSliderFilter::isActive() const
3052+{
3053+ if (auto state = m_filterState.lock()) {
3054+ if (m_filter->has_value(*state) && m_filter->value(*state) != m_filter->default_value()) {
3055+ return true;
3056+ }
3057+ }
3058+ return false;
3059+}
3060+
3061+QString ValueSliderFilter::filterTag() const
3062+{
3063+ return ""; // slider filter can't be a primary navigation filter
3064+}
3065+
3066+void ValueSliderFilter::reset()
3067+{
3068+ setValue(m_filter->default_value());
3069+}
3070+
3071+}
3072
3073=== added file 'src/Unity/valuesliderfilter.h'
3074--- src/Unity/valuesliderfilter.h 1970-01-01 00:00:00 +0000
3075+++ src/Unity/valuesliderfilter.h 2016-03-16 11:17:35 +0000
3076@@ -0,0 +1,70 @@
3077+/*
3078+ * Copyright (C) 2015 Canonical, Ltd.
3079+ *
3080+ * Authors:
3081+ * Pawel Stolowski <pawel.stolowski@canonical.com>
3082+ *
3083+ * This program is free software; you can redistribute it and/or modify
3084+ * it under the terms of the GNU General Public License as published by
3085+ * the Free Software Foundation; version 3.
3086+ *
3087+ * This program is distributed in the hope that it will be useful,
3088+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
3089+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3090+ * GNU General Public License for more details.
3091+ *
3092+ * You should have received a copy of the GNU General Public License
3093+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
3094+ */
3095+
3096+#ifndef NG_VALUESLIDERFILTER_H
3097+#define NG_VALUESLIDERFILTER_H
3098+
3099+#include <unity/shell/scopes/ValueSliderFilterInterface.h>
3100+#include <unity/shell/scopes/FiltersInterface.h>
3101+#include "filters.h"
3102+#include "valueslidervalues.h"
3103+#include <unity/scopes/ValueSliderFilter.h>
3104+
3105+namespace scopes_ng
3106+{
3107+
3108+class Q_DECL_EXPORT ValueSliderFilter : public unity::shell::scopes::ValueSliderFilterInterface, public FilterUpdateInterface
3109+{
3110+ Q_OBJECT
3111+
3112+public:
3113+ ValueSliderFilter(unity::scopes::ValueSliderFilter::SCPtr const& filter, unity::scopes::FilterState::SPtr const& filterState, unity::shell::scopes::FiltersInterface *parent = nullptr);
3114+ QString filterId() const override;
3115+ QString title() const override;
3116+ unity::shell::scopes::FiltersInterface::FilterType filterType() const override;
3117+ double value() const override;
3118+
3119+ void setValue(double value) override;
3120+ double minValue() const override;
3121+ double maxValue() const override;
3122+ unity::shell::scopes::ValueSliderValuesInterface* values() const override;
3123+
3124+ void update(unity::scopes::FilterBase::SCPtr const& filter) override;
3125+ void update(unity::scopes::FilterState::SPtr const& filterState) override;
3126+ bool isActive() const override;
3127+ QString filterTag() const override;
3128+ void reset() override;
3129+
3130+Q_SIGNALS:
3131+ void filterStateChanged();
3132+
3133+private:
3134+ QString m_id;
3135+ QString m_title;
3136+ double m_min;
3137+ double m_max;
3138+ double m_value;
3139+ QScopedPointer<ValueSliderValues> m_values;
3140+ std::weak_ptr<unity::scopes::FilterState> m_filterState;
3141+ unity::scopes::ValueSliderFilter::SCPtr m_filter;
3142+};
3143+
3144+} // namespace scopes_ng
3145+
3146+#endif
3147
3148=== added file 'src/Unity/valueslidervalues.cpp'
3149--- src/Unity/valueslidervalues.cpp 1970-01-01 00:00:00 +0000
3150+++ src/Unity/valueslidervalues.cpp 2016-03-16 11:17:35 +0000
3151@@ -0,0 +1,82 @@
3152+/*
3153+ * Copyright (C) 2015 Canonical, Ltd.
3154+ *
3155+ * Authors:
3156+ * Pawel Stolowski <pawel.stolowski@canonical.com>
3157+ *
3158+ * This program is free software; you can redistribute it and/or modify
3159+ * it under the terms of the GNU General Public License as published by
3160+ * the Free Software Foundation; version 3.
3161+ *
3162+ * This program is distributed in the hope that it will be useful,
3163+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
3164+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3165+ * GNU General Public License for more details.
3166+ *
3167+ * You should have received a copy of the GNU General Public License
3168+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
3169+ */
3170+
3171+#include "valueslidervalues.h"
3172+#include "valuesliderfilter.h"
3173+#include <utility>
3174+
3175+namespace scopes_ng
3176+{
3177+
3178+ValueSliderValues::ValueSliderValues(ValueSliderFilter *parent)
3179+ : ModelUpdate(parent)
3180+{
3181+}
3182+
3183+void ValueSliderValues::update(const unity::scopes::ValueSliderLabels& values, int min, int max)
3184+{
3185+ unity::scopes::ValueLabelPairList labels;
3186+ labels.push_back(std::make_pair(min, values.min_label()));
3187+ for (auto const v: values.extra_labels())
3188+ {
3189+ labels.push_back(v);
3190+ }
3191+ labels.push_back(std::make_pair(max, values.max_label()));
3192+ syncModel(labels, m_values,
3193+ [](const unity::scopes::ValueLabelPair& p) -> int { return p.first; },
3194+ [](const QSharedPointer<QPair<int, QString>>& p) -> int { return p->first; },
3195+ [](const unity::scopes::ValueLabelPair& p) -> QSharedPointer<QPair<int, QString>> {
3196+ return QSharedPointer<QPair<int, QString>>(new QPair<int, QString>(p.first, QString::fromStdString(p.second)));
3197+ },
3198+ [this](int row, const unity::scopes::ValueLabelPair& v1, const QSharedPointer<QPair<int, QString>>& v2) -> bool {
3199+ if (v1.first != v2->first) {
3200+ return false;
3201+ }
3202+
3203+ if (v1.second != v2->second.toStdString()) {
3204+ Q_EMIT dataChanged(index(row, 0), index(row, 0), { unity::shell::scopes::ValueSliderValuesInterface::Roles::RoleLabel });
3205+ }
3206+ return true;
3207+ });
3208+}
3209+
3210+int ValueSliderValues::rowCount(const QModelIndex&) const
3211+{
3212+ return m_values.count();
3213+}
3214+
3215+QVariant ValueSliderValues::data(const QModelIndex& index, int role) const
3216+{
3217+ if (index.row() >= m_values.count())
3218+ {
3219+ return QVariant();
3220+ }
3221+ switch (role)
3222+ {
3223+ case Qt::DisplayRole:
3224+ case RoleValue:
3225+ return QVariant(m_values.at(index.row())->first);
3226+ case RoleLabel:
3227+ return QVariant(m_values.at(index.row())->second);
3228+ default:
3229+ return QVariant();
3230+ }
3231+}
3232+
3233+}
3234
3235=== added file 'src/Unity/valueslidervalues.h'
3236--- src/Unity/valueslidervalues.h 1970-01-01 00:00:00 +0000
3237+++ src/Unity/valueslidervalues.h 2016-03-16 11:17:35 +0000
3238@@ -0,0 +1,56 @@
3239+/*
3240+ * Copyright (C) 2015 Canonical, Ltd.
3241+ *
3242+ * Authors:
3243+ * Pawel Stolowski <pawel.stolowski@canonical.com>
3244+ *
3245+ * This program is free software; you can redistribute it and/or modify
3246+ * it under the terms of the GNU General Public License as published by
3247+ * the Free Software Foundation; version 3.
3248+ *
3249+ * This program is distributed in the hope that it will be useful,
3250+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
3251+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3252+ * GNU General Public License for more details.
3253+ *
3254+ * You should have received a copy of the GNU General Public License
3255+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
3256+ */
3257+
3258+#ifndef NG_VALUESLIDERVALUES_H
3259+#define NG_VALUESLIDERVALUES_H
3260+
3261+#include <unity/shell/scopes/ValueSliderValuesInterface.h>
3262+#include "modelupdate.h"
3263+#include <QPair>
3264+#include <QSharedPointer>
3265+#include <QString>
3266+#include <unity/scopes/ValueSliderLabels.h>
3267+
3268+namespace scopes_ng
3269+{
3270+
3271+class ValueSliderFilter;
3272+
3273+class Q_DECL_EXPORT ValueSliderValues :
3274+ public ModelUpdate<unity::shell::scopes::ValueSliderValuesInterface,
3275+ unity::scopes::ValueLabelPairList,
3276+ QList<QSharedPointer<QPair<int, QString>>>,
3277+ int
3278+ >
3279+{
3280+ Q_OBJECT
3281+
3282+public:
3283+ explicit ValueSliderValues(ValueSliderFilter *parent = nullptr);
3284+ void update(const unity::scopes::ValueSliderLabels& values, int min, int max);
3285+ int rowCount(const QModelIndex& parent) const override;
3286+ QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
3287+
3288+private:
3289+ QList<QSharedPointer<QPair<int, QString>>> m_values;
3290+};
3291+
3292+}
3293+
3294+#endif
3295
3296=== modified file 'src/python/CMakeLists.txt'
3297--- src/python/CMakeLists.txt 2015-03-03 10:52:45 +0000
3298+++ src/python/CMakeLists.txt 2016-03-16 11:17:35 +0000
3299@@ -1,3 +1,3 @@
3300 add_subdirectory(scope_harness)
3301-add_test(python_bindings ${CMAKE_CURRENT_BINARY_DIR}/python-test.py ${PROJECT_BINARY_DIR}/tests/data)
3302+#add_test(python_bindings ${CMAKE_CURRENT_BINARY_DIR}/python-test.py ${PROJECT_BINARY_DIR}/tests/data)
3303 configure_file(python-test.py ${CMAKE_CURRENT_BINARY_DIR}/python-test.py COPYONLY)
3304
3305=== modified file 'src/scope-harness/test-utils.cpp'
3306--- src/scope-harness/test-utils.cpp 2016-02-17 15:49:37 +0000
3307+++ src/scope-harness/test-utils.cpp 2016-03-16 11:17:35 +0000
3308@@ -135,6 +135,13 @@
3309 QCOMPARE(scope->searchInProgress(), false);
3310 }
3311
3312+void TestUtils::waitForFilterStateChange(QSharedPointer<ss::ScopeInterface> scope)
3313+{
3314+ QSignalSpy spy(scope->filters(), SIGNAL(filterStateChanged()));
3315+ QVERIFY(spy.wait());
3316+ QCOMPARE(spy.count(), 1);
3317+}
3318+
3319 void TestUtils::setFavouriteScopes(const QStringList& cannedQueries)
3320 {
3321 setenv("GSETTINGS_BACKEND", "memory", 1);
3322
3323=== modified file 'src/scope-harness/test-utils.h'
3324--- src/scope-harness/test-utils.h 2016-02-17 15:49:37 +0000
3325+++ src/scope-harness/test-utils.h 2016-03-16 11:17:35 +0000
3326@@ -51,6 +51,9 @@
3327 static void performSearch(QSharedPointer<shell::scopes::ScopeInterface> scope, QString const& searchString);
3328
3329 Q_DECL_EXPORT
3330+static void waitForFilterStateChange(QSharedPointer<shell::scopes::ScopeInterface> scope);
3331+
3332+Q_DECL_EXPORT
3333 static void waitForResultsChange(QSharedPointer<shell::scopes::ScopeInterface> scope);
3334
3335 Q_DECL_EXPORT
3336
3337=== modified file 'src/scope-harness/view/results-view.cpp'
3338--- src/scope-harness/view/results-view.cpp 2015-05-18 09:33:17 +0000
3339+++ src/scope-harness/view/results-view.cpp 2016-03-16 11:17:35 +0000
3340@@ -96,42 +96,28 @@
3341
3342 results::Department browseDepartment(const string& id, bool altNavigation)
3343 {
3344+ if (altNavigation) {
3345+ throw std::domain_error("ResultsView::browseAltDepartment(): altNavigation is deprecated");
3346+ }
3347+
3348 checkActiveScope();
3349
3350 QSharedPointer<ss::NavigationInterface> navigationModel;
3351- if (altNavigation)
3352- {
3353- navigationModel.reset(m_active_scope->getAltNavigation(QString::fromStdString(id)));
3354- }
3355- else
3356- {
3357- navigationModel.reset(m_active_scope->getNavigation(QString::fromStdString(id)));
3358- }
3359+ navigationModel.reset(m_active_scope->getNavigation(QString::fromStdString(id)));
3360 TestUtils::throwIfNot(bool(navigationModel), "Unknown department: '" + id + "'");
3361
3362 QSignalSpy spy(navigationModel.data(), SIGNAL(loadedChanged()));
3363
3364 bool shouldUpdate = false;
3365
3366- if (altNavigation)
3367- {
3368- if (m_active_scope->currentAltNavigationId().toStdString() != id)
3369- {
3370- shouldUpdate = true;
3371- }
3372- }
3373- else
3374- {
3375- if (m_active_scope->currentNavigationId().toStdString() != id)
3376- {
3377- shouldUpdate = true;
3378- }
3379+ if (m_active_scope->currentNavigationId().toStdString() != id)
3380+ {
3381+ shouldUpdate = true;
3382 }
3383
3384 if (shouldUpdate)
3385 {
3386- m_active_scope->setNavigationState(QString::fromStdString(id),
3387- altNavigation);
3388+ m_active_scope->setNavigationState(QString::fromStdString(id));
3389 TestUtils::waitForSearchFinish(m_active_scope);
3390 }
3391
3392@@ -288,9 +274,7 @@
3393
3394 bool ResultsView::hasAltDepartments() const
3395 {
3396- p->checkActiveScope();
3397-
3398- return p->m_active_scope->hasAltNavigation();
3399+ throw std::domain_error("ResultsView::hasAltDepartments() is deprecated");
3400 }
3401
3402 string ResultsView::departmentId() const
3403@@ -302,9 +286,7 @@
3404
3405 string ResultsView::altDepartmentId() const
3406 {
3407- p->checkActiveScope();
3408-
3409- return p->m_active_scope->currentAltNavigationId().toStdString();
3410+ throw std::domain_error("ResultsView::altDepartmentId() is deprecated");
3411 }
3412
3413 results::Department ResultsView::browseDepartment(const string& id)
3414@@ -314,7 +296,7 @@
3415
3416 results::Department ResultsView::browseAltDepartment(const string& id)
3417 {
3418- return p->browseDepartment(id, true);
3419+ throw std::domain_error("ResultsView::browseAltDepartment() is deprecated");
3420 }
3421
3422 bool ResultsView::overrideCategoryJson(string const& categoryId, string const& json)
3423
3424=== modified file 'tests/CMakeLists.txt'
3425--- tests/CMakeLists.txt 2015-11-30 09:23:32 +0000
3426+++ tests/CMakeLists.txt 2016-03-16 11:17:35 +0000
3427@@ -34,9 +34,12 @@
3428 add_test(NAME test${CLASSNAME}${_test} COMMAND ${testCommand})
3429 add_custom_target(${_test} ${testCommand})
3430 add_executable(${_test}Exec
3431- ${_test}.cpp
3432+ ${_test}.cpp
3433 ${SCOPES_API_INCLUDEDIR}/unity/shell/scopes/PreviewModelInterface.h
3434 ${SCOPES_API_INCLUDEDIR}/unity/shell/scopes/ScopeInterface.h
3435+ ${SCOPES_API_INCLUDEDIR}/unity/shell/scopes/FilterBaseInterface.h
3436+ ${SCOPES_API_INCLUDEDIR}/unity/shell/scopes/OptionSelectorFilterInterface.h
3437+ ${SCOPES_API_INCLUDEDIR}/unity/shell/scopes/OptionSelectorOptionsInterface.h
3438 ${SCOPES_API_INCLUDEDIR}/unity/shell/scopes/SettingsModelInterface.h
3439 )
3440 qt5_use_modules(${_test}Exec Test Core Qml DBus)
3441@@ -57,7 +60,9 @@
3442 endmacro(run_tests)
3443
3444 run_tests(
3445- departmentstest
3446+ filterstest
3447+ filtersendtoendtest
3448+ optionselectorfiltertest
3449 favoritestest
3450 locationtest
3451 overviewtest
3452
3453=== modified file 'tests/data/CMakeLists.txt'
3454--- tests/data/CMakeLists.txt 2016-02-04 11:43:34 +0000
3455+++ tests/data/CMakeLists.txt 2016-03-16 11:17:35 +0000
3456@@ -4,6 +4,7 @@
3457 add_subdirectory(mock-scope-double-nav)
3458 add_subdirectory(mock-scope-info)
3459 add_subdirectory(mock-scope-ttl)
3460+add_subdirectory(mock-scope-filters)
3461 add_subdirectory(mock-scope-manyresults)
3462
3463 configure_file(Runtime.ini.in Runtime.ini @ONLY)
3464
3465=== added directory 'tests/data/mock-scope-filters'
3466=== added file 'tests/data/mock-scope-filters/CMakeLists.txt'
3467--- tests/data/mock-scope-filters/CMakeLists.txt 1970-01-01 00:00:00 +0000
3468+++ tests/data/mock-scope-filters/CMakeLists.txt 2016-03-16 11:17:35 +0000
3469@@ -0,0 +1,15 @@
3470+pkg_check_modules(SCOPESLIB REQUIRED libunity-scopes>=0.6.15)
3471+
3472+set(SCOPES_BIN_DIR ${SCOPESLIB_LIBDIR})
3473+
3474+include_directories(${SCOPESLIB_INCLUDE_DIRS})
3475+include_directories(${CMAKE_CURRENT_BINARY_DIR})
3476+
3477+set(SCOPE_SOURCES
3478+ mock-scope-filters.cpp
3479+ )
3480+
3481+add_library(mock-scope-filters MODULE ${SCOPE_SOURCES})
3482+target_link_libraries(mock-scope-filters ${SCOPESLIB_LDFLAGS})
3483+
3484+configure_file(mock-scope-filters.ini.in mock-scope-filters.ini)
3485
3486=== added file 'tests/data/mock-scope-filters/mock-scope-filters.cpp'
3487--- tests/data/mock-scope-filters/mock-scope-filters.cpp 1970-01-01 00:00:00 +0000
3488+++ tests/data/mock-scope-filters/mock-scope-filters.cpp 2016-03-16 11:17:35 +0000
3489@@ -0,0 +1,132 @@
3490+/*
3491+ * Copyright (C) 2015 Canonical Ltd
3492+ *
3493+ * This program is free software: you can redistribute it and/or modify
3494+ * it under the terms of the GNU Lesser General Public License version 3 as
3495+ * published by the Free Software Foundation.
3496+ *
3497+ * This program is distributed in the hope that it will be useful,
3498+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
3499+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3500+ * GNU Lesser General Public License for more details.
3501+ *
3502+ * You should have received a copy of the GNU Lesser General Public License
3503+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
3504+ *
3505+ * Authored by: Pawel Stolowski <pawel.stolowski@canonical.com>
3506+ */
3507+
3508+#include <unity/scopes/CategorisedResult.h>
3509+#include <unity/scopes/OptionSelectorFilter.h>
3510+#include <unity/scopes/RangeInputFilter.h>
3511+#include <unity/scopes/ValueSliderFilter.h>
3512+#include <unity/scopes/ValueSliderLabels.h>
3513+#include <unity/scopes/ScopeBase.h>
3514+#include <unity/scopes/SearchReply.h>
3515+
3516+#include <sstream>
3517+
3518+#define EXPORT __attribute__ ((visibility ("default")))
3519+
3520+using namespace std;
3521+using namespace unity::scopes;
3522+
3523+class MyQuery : public SearchQueryBase
3524+{
3525+public:
3526+ MyQuery(CannedQuery const& query, SearchMetadata const& metadata) :
3527+ SearchQueryBase(query, metadata)
3528+ {
3529+ }
3530+
3531+ ~MyQuery()
3532+ {
3533+ }
3534+
3535+ virtual void cancelled() override
3536+ {
3537+ }
3538+
3539+ virtual void run(SearchReplyProxy const& reply) override
3540+ {
3541+ OptionSelectorFilter::UPtr filter1 = OptionSelectorFilter::create("f1", "Filter1");
3542+ if (query().query_string() == "test_primary_filter") {
3543+ filter1->set_display_hints(FilterBase::DisplayHints::Primary);
3544+ }
3545+
3546+ auto opt1 = filter1->add_option("o1", "Option1");
3547+ filter1->add_option("o2", "Option2");
3548+
3549+ RangeInputFilter::SPtr filter2 = RangeInputFilter::create("f2", Variant(2.0f), Variant::null(), "start", "", "", "end", "");
3550+
3551+ auto cat1 = reply->register_category("cat1", "Category 1", "");
3552+ CategorisedResult res1(cat1);
3553+ res1.set_uri("test:uri");
3554+
3555+ auto selected = filter1->active_options(query().filter_state());
3556+ if (selected.size() == 1) {
3557+ res1.set_title("result for option " + (*selected.begin())->id());
3558+ } else {
3559+ res1.set_title("result for: \"" + query().query_string() + "\"");
3560+ }
3561+
3562+ bool has_start_val = filter2->has_start_value(query().filter_state());
3563+ bool has_end_val = filter2->has_end_value(query().filter_state());
3564+
3565+ if (has_start_val || has_end_val)
3566+ {
3567+ res1.set_title("result for range: " +
3568+ (has_start_val ? std::to_string(filter2->start_value(query().filter_state())) : "***") + " - " +
3569+ (has_end_val ? std::to_string(filter2->end_value(query().filter_state())) : "***"));
3570+ }
3571+
3572+ ValueSliderFilter::SPtr filter3 = ValueSliderFilter::create("f3", 1, 99, 50, ValueSliderLabels("Min", "Max", {{33, "One third"}}));
3573+
3574+ Filters filters;
3575+ filters.push_back(std::move(filter1));
3576+ filters.push_back(std::move(filter2));
3577+ filters.push_back(std::move(filter3));
3578+
3579+ reply->push(filters, query().filter_state());
3580+ reply->push(res1);
3581+ }
3582+};
3583+
3584+class MyScope : public ScopeBase
3585+{
3586+public:
3587+ MyScope()
3588+ {
3589+ }
3590+
3591+ virtual SearchQueryBase::UPtr search(CannedQuery const& q, SearchMetadata const& metadata) override
3592+ {
3593+ return SearchQueryBase::UPtr(new MyQuery(q, metadata));
3594+ }
3595+
3596+ virtual PreviewQueryBase::UPtr preview(Result const&, ActionMetadata const&) override
3597+ {
3598+ return nullptr;
3599+ }
3600+};
3601+
3602+extern "C"
3603+{
3604+
3605+ EXPORT
3606+ unity::scopes::ScopeBase*
3607+ // cppcheck-suppress unusedFunction
3608+ UNITY_SCOPE_CREATE_FUNCTION()
3609+ {
3610+ return new MyScope;
3611+ }
3612+
3613+ EXPORT
3614+ void
3615+ // cppcheck-suppress unusedFunction
3616+ UNITY_SCOPE_DESTROY_FUNCTION(unity::scopes::ScopeBase* scope_base)
3617+ {
3618+ delete scope_base;
3619+ }
3620+
3621+}
3622
3623=== added file 'tests/data/mock-scope-filters/mock-scope-filters.ini.in'
3624--- tests/data/mock-scope-filters/mock-scope-filters.ini.in 1970-01-01 00:00:00 +0000
3625+++ tests/data/mock-scope-filters/mock-scope-filters.ini.in 2016-03-16 11:17:35 +0000
3626@@ -0,0 +1,8 @@
3627+[ScopeConfig]
3628+DisplayName = mock-scope-filters.DisplayName
3629+Description = mock-scope-filters.Description
3630+Art = /mock-scope-filters.Art
3631+Icon = /mock-scope-filters.Icon
3632+SearchHint = mock-scope-filters.SearchHint
3633+HotKey = mock-scope-filters.HotKey
3634+Author = mock-scope-filters.Author
3635
3636=== modified file 'tests/departmentstest.cpp'
3637--- tests/departmentstest.cpp 2015-01-27 16:45:19 +0000
3638+++ tests/departmentstest.cpp 2016-03-16 11:17:35 +0000
3639@@ -70,7 +70,6 @@
3640 m_resultsView->setQuery("foo");
3641
3642 QVERIFY(!m_resultsView->hasDepartments());
3643- QVERIFY(!m_resultsView->hasAltDepartments());
3644 }
3645
3646 void testRootDepartment()
3647@@ -79,7 +78,6 @@
3648 m_resultsView->setQuery("");
3649
3650 QVERIFY(m_resultsView->hasDepartments());
3651- QVERIFY(!m_resultsView->hasAltDepartments());
3652 QVERIFY(m_resultsView->departmentId().empty());
3653
3654 auto departments = m_resultsView->browseDepartment();
3655@@ -228,7 +226,6 @@
3656 auto root = m_resultsView->browseDepartment();
3657
3658 QVERIFY(m_resultsView->hasDepartments());
3659- QVERIFY(m_resultsView->hasAltDepartments());
3660 QVERIFY(m_resultsView->departmentId().empty());
3661 QCOMPARE(m_resultsView->altDepartmentId(), string("featured"));
3662
3663@@ -295,7 +292,6 @@
3664 auto root = m_resultsView->browseDepartment();
3665
3666 QVERIFY(m_resultsView->hasDepartments());
3667- QVERIFY(!m_resultsView->hasAltDepartments());
3668 QVERIFY(m_resultsView->departmentId().empty());
3669
3670 QVERIFY_MATCHRESULT(
3671
3672=== added file 'tests/filtersendtoendtest.cpp'
3673--- tests/filtersendtoendtest.cpp 1970-01-01 00:00:00 +0000
3674+++ tests/filtersendtoendtest.cpp 2016-03-16 11:17:35 +0000
3675@@ -0,0 +1,367 @@
3676+/*
3677+ * Copyright (C) 2015 Canonical, Ltd.
3678+ *
3679+ * This program is free software; you can redistribute it and/or modify
3680+ * it under the terms of the GNU General Public License as published by
3681+ * the Free Software Foundation; version 3.
3682+ *
3683+ * This program is distributed in the hope that it will be useful,
3684+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
3685+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3686+ * GNU General Public License for more details.
3687+ *
3688+ * You should have received a copy of the GNU General Public License
3689+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
3690+ *
3691+ * Authors:
3692+ * Pawel Stolowski <pawel.stolowski@canonical.com>
3693+ */
3694+
3695+#include <QSignalSpy>
3696+#include <QScopedPointer>
3697+#include <QTest>
3698+#include <scopes.h>
3699+#include <scope.h>
3700+#include "filters.h"
3701+#include "categories.h"
3702+#include "optionselectorfilter.h"
3703+#include "rangeinputfilter.h"
3704+#include "valuesliderfilter.h"
3705+#include <scope-harness/registry/pre-existing-registry.h>
3706+#include <scope-harness/test-utils.h>
3707+#include <unity/shell/scopes/CategoriesInterface.h>
3708+#include <unity/shell/scopes/OptionSelectorFilterInterface.h>
3709+
3710+#include <unity/scopes/OptionSelectorFilter.h>
3711+#include <unity/scopes/RangeInputFilter.h>
3712+#include <unity/scopes/ValueSliderFilter.h>
3713+
3714+using namespace unity::scopeharness;
3715+using namespace unity::scopeharness::registry;
3716+using namespace scopes_ng;
3717+namespace uss = unity::shell::scopes;
3718+
3719+// FIXME: use scope harness wrapper once it exposes filters
3720+class FiltersEndToEndTest : public QObject
3721+{
3722+ Q_OBJECT
3723+
3724+private Q_SLOTS:
3725+
3726+ void initTestCase()
3727+ {
3728+ m_registry.reset(new PreExistingRegistry(TEST_RUNTIME_CONFIG));
3729+ m_registry->start();
3730+ }
3731+
3732+ void cleanupTestCase()
3733+ {
3734+ m_registry.reset();
3735+ }
3736+
3737+ void init()
3738+ {
3739+ const QStringList favs {"scope://mock-scope-filters"};
3740+ TestUtils::setFavouriteScopes(favs);
3741+
3742+ m_scopes.reset(new Scopes(nullptr));
3743+
3744+ // wait till the registry spawns
3745+ QSignalSpy spy(m_scopes.data(), SIGNAL(loadedChanged()));
3746+ QVERIFY(spy.wait());
3747+ QCOMPARE(m_scopes->loaded(), true);
3748+
3749+ // get scope proxy
3750+ m_scope = m_scopes->getScopeById("mock-scope-filters");
3751+ QVERIFY(m_scope != nullptr);
3752+ m_scope->setActive(true);
3753+ }
3754+
3755+ void cleanup()
3756+ {
3757+ m_scopes.reset();
3758+ m_scope.reset();
3759+ }
3760+
3761+ void testBasic()
3762+ {
3763+ TestUtils::performSearch(m_scope, "");
3764+
3765+ QCOMPARE(m_scope->primaryNavigationTag(), QString(""));
3766+ QVERIFY(m_scope->primaryNavigationFilter() == nullptr);
3767+
3768+ auto filters = m_scope->filters();
3769+ QVERIFY(filters != nullptr);
3770+ QCOMPARE(filters->rowCount(), 3);
3771+
3772+ auto idx = filters->index(0, 0);
3773+ QCOMPARE(filters->data(idx, unity::shell::scopes::FiltersInterface::Roles::RoleFilterId).toString(), QString("f1"));
3774+ idx = filters->index(1, 0);
3775+ QCOMPARE(filters->data(idx, unity::shell::scopes::FiltersInterface::Roles::RoleFilterId).toString(), QString("f2"));
3776+ idx = filters->index(2, 0);
3777+ QCOMPARE(filters->data(idx, unity::shell::scopes::FiltersInterface::Roles::RoleFilterId).toString(), QString("f3"));
3778+ }
3779+
3780+ void testOptionSelectorFilter()
3781+ {
3782+ TestUtils::performSearch(m_scope, "");
3783+
3784+ auto categories = m_scope->categories();
3785+ QVERIFY(categories != nullptr);
3786+ auto results = categories->data(categories->index(0, 0),
3787+ Categories::RoleResultsSPtr).value<QSharedPointer<unity::shell::scopes::ResultsModelInterface>>();
3788+ QVERIFY(results != nullptr);
3789+
3790+ auto filters = m_scope->filters();
3791+ QVERIFY(filters != nullptr);
3792+ QCOMPARE(filters->rowCount(), 3);
3793+
3794+ auto idx = filters->index(0, 0);
3795+ auto f1 = filters->data(idx, uss::FiltersInterface::Roles::RoleFilter).value<OptionSelectorFilter*>();
3796+ QVERIFY(f1 != nullptr);
3797+
3798+ // check options
3799+ auto opts = f1->options();
3800+ QVERIFY(opts != nullptr);
3801+ QCOMPARE(opts->rowCount(), 2);
3802+
3803+ QCOMPARE(f1->filterId(), QString("f1"));
3804+ QCOMPARE(f1->label(), QString("Filter1"));
3805+ QVERIFY(!f1->multiSelect());
3806+
3807+ idx = opts->index(0, 0);
3808+ QCOMPARE(opts->data(idx, uss::OptionSelectorOptionsInterface::Roles::RoleOptionId).toString(), QString("o1"));
3809+ QVERIFY(!opts->data(idx, uss::OptionSelectorOptionsInterface::Roles::RoleOptionChecked).toBool());
3810+ idx = opts->index(1, 0);
3811+ QCOMPARE(opts->data(idx, uss::OptionSelectorOptionsInterface::Roles::RoleOptionId).toString(), QString("o2"));
3812+ QVERIFY(!opts->data(idx, uss::OptionSelectorOptionsInterface::Roles::RoleOptionChecked).toBool());
3813+
3814+ // select option 1
3815+ opts->setChecked(0, true);
3816+ TestUtils::waitForFilterStateChange(m_scope);
3817+ TestUtils::waitForSearchFinish(m_scope);
3818+ QCOMPARE(filters, m_scope->filters());
3819+ QCOMPARE(results->data(results->index(0, 0), unity::shell::scopes::ResultsModelInterface::RoleTitle).toString(), QString("result for option o1"));
3820+
3821+ // select option 2
3822+ opts->setChecked(1, true);
3823+ TestUtils::waitForFilterStateChange(m_scope);
3824+ TestUtils::waitForSearchFinish(m_scope);
3825+ QCOMPARE(filters, m_scope->filters());
3826+ QCOMPARE(results->data(results->index(0, 0), unity::shell::scopes::ResultsModelInterface::RoleTitle).toString(), QString("result for option o2"));
3827+
3828+ // deselect option 2
3829+ opts->setChecked(1, false);
3830+ TestUtils::waitForFilterStateChange(m_scope);
3831+ TestUtils::waitForSearchFinish(m_scope);
3832+ QCOMPARE(results->data(results->index(0, 0), unity::shell::scopes::ResultsModelInterface::RoleTitle).toString(), QString("result for: \"\""));
3833+
3834+ QCOMPARE(filters, m_scope->filters());
3835+ }
3836+
3837+ void testPrimaryFilter()
3838+ {
3839+ TestUtils::performSearch(m_scope, "test_primary_filter");
3840+
3841+ auto filters = m_scope->filters();
3842+ QVERIFY(filters != nullptr);
3843+ QCOMPARE(filters->rowCount(), 2);
3844+
3845+ auto idx = filters->index(0, 0);
3846+ QCOMPARE(filters->data(idx, unity::shell::scopes::FiltersInterface::Roles::RoleFilterId).toString(), QString("f2"));
3847+ idx = filters->index(1, 0);
3848+ QCOMPARE(filters->data(idx, unity::shell::scopes::FiltersInterface::Roles::RoleFilterId).toString(), QString("f3"));
3849+
3850+ auto f1 = dynamic_cast<unity::shell::scopes::OptionSelectorFilterInterface *>(m_scope->primaryNavigationFilter());
3851+ QVERIFY(f1 != nullptr);
3852+ QCOMPARE(f1->filterId(), QString("f1"));
3853+
3854+ auto opts = f1->options();
3855+
3856+ // select option 1
3857+ opts->setChecked(0, true);
3858+ TestUtils::waitForFilterStateChange(m_scope);
3859+ TestUtils::waitForSearchFinish(m_scope);
3860+
3861+ QCOMPARE(m_scope->primaryNavigationTag(), QString("Option1"));
3862+ }
3863+
3864+ void testRangeInputFilter()
3865+ {
3866+ TestUtils::performSearch(m_scope, "");
3867+
3868+ auto categories = m_scope->categories();
3869+ QVERIFY(categories != nullptr);
3870+ auto results = categories->data(categories->index(0, 0),
3871+ Categories::RoleResultsSPtr).value<QSharedPointer<unity::shell::scopes::ResultsModelInterface>>();
3872+ QVERIFY(results != nullptr);
3873+
3874+ auto filters = m_scope->filters();
3875+ QVERIFY(filters != nullptr);
3876+ QCOMPARE(filters->rowCount(), 3);
3877+
3878+ auto idx = filters->index(1, 0);
3879+ auto f2 = filters->data(idx, uss::FiltersInterface::Roles::RoleFilter).value<RangeInputFilter*>();
3880+ QVERIFY(f2 != nullptr);
3881+
3882+ QCOMPARE(f2->hasStartValue(), true);
3883+ QCOMPARE(f2->hasEndValue(), false);
3884+
3885+ {
3886+ f2->setStartValue(111.0f);
3887+ TestUtils::waitForFilterStateChange(m_scope);
3888+ TestUtils::waitForSearchFinish(m_scope);
3889+
3890+ QCOMPARE(filters, m_scope->filters());
3891+ QCOMPARE(f2->hasStartValue(), true);
3892+ QCOMPARE(f2->hasEndValue(), false);
3893+
3894+ auto resultIdx = filters->index(0, 0);
3895+ QCOMPARE(results->data(resultIdx, unity::shell::scopes::ResultsModelInterface::RoleTitle).toString(), QString("result for range: 111.000000 - ***"));
3896+ }
3897+
3898+ {
3899+ QCOMPARE(f2, filters->data(idx, uss::FiltersInterface::Roles::RoleFilter).value<RangeInputFilter*>());
3900+ f2->setEndValue(300.5f);
3901+ TestUtils::waitForFilterStateChange(m_scope);
3902+ TestUtils::waitForSearchFinish(m_scope);
3903+
3904+ QCOMPARE(f2->hasStartValue(), true);
3905+ QCOMPARE(f2->hasEndValue(), true);
3906+
3907+ auto resultIdx = filters->index(0, 0);
3908+ QCOMPARE(filters, m_scope->filters());
3909+ QCOMPARE(results->data(resultIdx, unity::shell::scopes::ResultsModelInterface::RoleTitle).toString(), QString("result for range: 111.000000 - 300.500000"));
3910+ }
3911+
3912+ // erase start value, end value still present
3913+ {
3914+ f2->eraseStartValue();
3915+ TestUtils::waitForFilterStateChange(m_scope);
3916+ TestUtils::waitForSearchFinish(m_scope);
3917+
3918+ QCOMPARE(filters, m_scope->filters());
3919+ QCOMPARE(f2->hasStartValue(), false);
3920+ QCOMPARE(f2->hasEndValue(), true);
3921+
3922+ auto resultIdx = filters->index(0, 0);
3923+ QCOMPARE(results->data(resultIdx, unity::shell::scopes::ResultsModelInterface::RoleTitle).toString(), QString("result for range: *** - 300.500000"));
3924+ }
3925+ }
3926+
3927+ void testValueSliderFilter()
3928+ {
3929+ TestUtils::performSearch(m_scope, "");
3930+
3931+ auto categories = m_scope->categories();
3932+ QVERIFY(categories != nullptr);
3933+ auto results = categories->data(categories->index(0, 0),
3934+ Categories::RoleResultsSPtr).value<QSharedPointer<unity::shell::scopes::ResultsModelInterface>>();
3935+ QVERIFY(results != nullptr);
3936+
3937+ auto filters = m_scope->filters();
3938+ QVERIFY(filters != nullptr);
3939+ QCOMPARE(filters->rowCount(), 3);
3940+
3941+ auto idx = filters->index(2, 0);
3942+ auto f3 = filters->data(idx, uss::FiltersInterface::Roles::RoleFilter).value<ValueSliderFilter*>();
3943+ QVERIFY(f3 != nullptr);
3944+
3945+ QCOMPARE(static_cast<int>(f3->minValue()), 1);
3946+ QCOMPARE(static_cast<int>(f3->maxValue()), 99);
3947+ QCOMPARE(static_cast<int>(f3->value()), 50);
3948+
3949+ auto valuesModel = f3->values();
3950+ QVERIFY(valuesModel != nullptr);
3951+
3952+ QCOMPARE(valuesModel->rowCount(), 3);
3953+ QCOMPARE(valuesModel->data(valuesModel->index(0, 0), uss::ValueSliderValuesInterface::Roles::RoleValue).toInt(), 1);
3954+ QCOMPARE(valuesModel->data(valuesModel->index(0, 0), uss::ValueSliderValuesInterface::Roles::RoleLabel).toString(), QString("Min"));
3955+ QCOMPARE(valuesModel->data(valuesModel->index(1, 0), uss::ValueSliderValuesInterface::Roles::RoleValue).toInt(), 33);
3956+ QCOMPARE(valuesModel->data(valuesModel->index(1, 0), uss::ValueSliderValuesInterface::Roles::RoleLabel).toString(), QString("One third"));
3957+ QCOMPARE(valuesModel->data(valuesModel->index(2, 0), uss::ValueSliderValuesInterface::Roles::RoleValue).toInt(), 99);
3958+ QCOMPARE(valuesModel->data(valuesModel->index(2, 0), uss::ValueSliderValuesInterface::Roles::RoleLabel).toString(), QString("Max"));
3959+
3960+ f3->setValue(75);
3961+ TestUtils::waitForFilterStateChange(m_scope);
3962+ TestUtils::waitForSearchFinish(m_scope);
3963+
3964+ QCOMPARE(filters->rowCount(), 3);
3965+
3966+ // filter object shouldn't be recreated
3967+ QCOMPARE(filters->data(idx, uss::FiltersInterface::Roles::RoleFilter).value<ValueSliderFilter*>(), f3);
3968+
3969+ QCOMPARE(filters, m_scope->filters());
3970+ QCOMPARE(static_cast<int>(f3->value()), 75);
3971+ }
3972+
3973+ void testResetToDefault()
3974+ {
3975+ TestUtils::performSearch(m_scope, "");
3976+
3977+ auto filters = m_scope->filters();
3978+ QVERIFY(filters != nullptr);
3979+ QCOMPARE(filters->rowCount(), 3);
3980+
3981+ auto idx = filters->index(1, 0);
3982+ auto f2 = filters->data(idx, uss::FiltersInterface::Roles::RoleFilter).value<RangeInputFilter*>();
3983+ QVERIFY(f2 != nullptr);
3984+ QCOMPARE(f2->startValue(), 2.0f); //QCOMPARE does fuzzy comparison for floats/doubles
3985+
3986+ idx = filters->index(2, 0);
3987+ auto f3 = filters->data(idx, uss::FiltersInterface::Roles::RoleFilter).value<ValueSliderFilter*>();
3988+ QVERIFY(f3 != nullptr);
3989+ QCOMPARE(static_cast<int>(f3->value()), 50);
3990+ f2->setStartValue(5.0f);
3991+ f3->setValue(75);
3992+ TestUtils::waitForFilterStateChange(m_scope);
3993+ TestUtils::waitForSearchFinish(m_scope);
3994+ QCOMPARE(f2->startValue(), 5.0f);
3995+ QCOMPARE(static_cast<int>(f3->value()), 75);
3996+
3997+ m_scope->resetFilters();
3998+ TestUtils::waitForFilterStateChange(m_scope);
3999+ TestUtils::waitForSearchFinish(m_scope);
4000+ QCOMPARE(f2->startValue(), 2.0f); //QCOMPARE does fuzzy comparison for floats/doubles
4001+ QCOMPARE(static_cast<int>(f3->value()), 50);
4002+ }
4003+
4004+ void testCancel()
4005+ {
4006+ TestUtils::performSearch(m_scope, "");
4007+
4008+ auto filters = m_scope->filters();
4009+ QVERIFY(filters != nullptr);
4010+ QCOMPARE(filters->rowCount(), 3);
4011+
4012+ auto idx = filters->index(1, 0);
4013+ auto f2 = filters->data(idx, uss::FiltersInterface::Roles::RoleFilter).value<RangeInputFilter*>();
4014+ QVERIFY(f2 != nullptr);
4015+
4016+ idx = filters->index(2, 0);
4017+ auto f3 = filters->data(idx, uss::FiltersInterface::Roles::RoleFilter).value<ValueSliderFilter*>();
4018+ QVERIFY(f3 != nullptr);
4019+ QCOMPARE(static_cast<int>(f3->value()), 50);
4020+ f2->setStartValue(5.0f);
4021+ f3->setValue(75);
4022+ TestUtils::waitForFilterStateChange(m_scope);
4023+ TestUtils::waitForSearchFinish(m_scope);
4024+ QCOMPARE(f2->startValue(), 5.0f);
4025+ QCOMPARE(static_cast<int>(f3->value()), 75);
4026+
4027+ m_scope->resetPrimaryNavigationTag();
4028+
4029+ TestUtils::waitForSearchFinish(m_scope);
4030+ QCOMPARE(f2->startValue(), 2.0f); //QCOMPARE does fuzzy comparison for floats/doubles
4031+ QCOMPARE(static_cast<int>(f3->value()), 50);
4032+ QCOMPARE(QString(), m_scope->primaryNavigationTag());
4033+ }
4034+
4035+private:
4036+ QScopedPointer<Scopes> m_scopes;
4037+ Scope::Ptr m_scope;
4038+ Registry::UPtr m_registry;
4039+};
4040+
4041+QTEST_GUILESS_MAIN(FiltersEndToEndTest)
4042+#include <filtersendtoendtest.moc>
4043
4044=== added file 'tests/filterstest.cpp'
4045--- tests/filterstest.cpp 1970-01-01 00:00:00 +0000
4046+++ tests/filterstest.cpp 2016-03-16 11:17:35 +0000
4047@@ -0,0 +1,230 @@
4048+/*
4049+ * Copyright (C) 2015 Canonical, Ltd.
4050+ *
4051+ * This program is free software; you can redistribute it and/or modify
4052+ * it under the terms of the GNU General Public License as published by
4053+ * the Free Software Foundation; version 3.
4054+ *
4055+ * This program is distributed in the hope that it will be useful,
4056+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
4057+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4058+ * GNU General Public License for more details.
4059+ *
4060+ * You should have received a copy of the GNU General Public License
4061+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
4062+ *
4063+ * Authors:
4064+ * Pawel Stolowski <pawel.stolowski@canonical.com>
4065+ */
4066+
4067+#include <QSignalSpy>
4068+#include <QScopedPointer>
4069+#include <QTest>
4070+#include <QList>
4071+#include "filters.h"
4072+#include "optionselectorfilter.h"
4073+
4074+#include <unity/scopes/OptionSelectorFilter.h>
4075+
4076+using namespace scopes_ng;
4077+namespace uss = unity::shell::scopes;
4078+
4079+class FiltersTest : public QObject
4080+{
4081+ Q_OBJECT
4082+
4083+private Q_SLOTS:
4084+
4085+ void init()
4086+ {
4087+ unity::scopes::FilterState filterState;
4088+ filtersModel.reset(new Filters(filterState));
4089+
4090+ f1 = unity::scopes::OptionSelectorFilter::create("f1", "Filter1", false);
4091+ f1o1 = f1->add_option("o1", "Option1");
4092+ f1o2 = f1->add_option("o2", "Option2");
4093+
4094+ f2 = unity::scopes::OptionSelectorFilter::create("f2", "Filter2", false);
4095+ f2o1 = f2->add_option("o1", "Option1");
4096+ f2o2 = f2->add_option("o2", "Option2");
4097+ }
4098+
4099+ void testFiltersModelInit()
4100+ {
4101+ unity::scopes::FilterState filterState;
4102+
4103+ QSignalSpy filtersSpy(filtersModel.data(), SIGNAL(rowsInserted(const QModelIndex&, int, int)));
4104+ {
4105+ QList<unity::scopes::FilterBase::SCPtr> backendFilters;
4106+ backendFilters.append(f1);
4107+
4108+ f1->update_state(filterState, f1o1, true);
4109+
4110+ filtersModel->update(backendFilters);
4111+ filtersModel->update(filterState);
4112+ }
4113+
4114+ QCOMPARE(filtersSpy.count(), 1); // 1 filter object added
4115+
4116+ // check filters model data
4117+ auto idx = filtersModel->index(0, 0);
4118+ QCOMPARE(filtersModel->rowCount(), 1);
4119+ QCOMPARE(filtersModel->data(idx, unity::shell::scopes::FiltersInterface::Roles::RoleFilterId).toString(), QString("f1"));
4120+ QCOMPARE(filtersModel->data(idx, unity::shell::scopes::FiltersInterface::Roles::RoleFilterType).toInt(),
4121+ static_cast<int>(unity::shell::scopes::FiltersInterface::FilterType::OptionSelectorFilter));
4122+
4123+ // get filter object from the model
4124+ auto opf = filtersModel->data(idx, uss::FiltersInterface::Roles::RoleFilter).value<OptionSelectorFilter*>();
4125+ QVERIFY(opf != nullptr);
4126+ QCOMPARE(opf->filterId(), QString("f1"));
4127+ QCOMPARE(opf->label(), QString("Filter1"));
4128+ QVERIFY(!opf->multiSelect());
4129+ }
4130+
4131+ void testFiltersModelInsert()
4132+ {
4133+ QSignalSpy rowsInsertedSignal(filtersModel.data(), SIGNAL(rowsInserted(const QModelIndex&, int, int)));
4134+ QSignalSpy rowsRemovedSignal(filtersModel.data(), SIGNAL(rowsRemoved(const QModelIndex&, int, int)));
4135+ QSignalSpy rowsMovedSignal(filtersModel.data(), SIGNAL(rowsMoved(const QModelIndex&, int, int, const QModelIndex&, int)));
4136+
4137+ {
4138+ QList<unity::scopes::FilterBase::SCPtr> backendFilters;
4139+ backendFilters.append(f1);
4140+
4141+ filtersModel->update(backendFilters);
4142+ }
4143+
4144+ QCOMPARE(rowsMovedSignal.count(), 0);
4145+ QCOMPARE(rowsRemovedSignal.count(), 0);
4146+ QCOMPARE(rowsInsertedSignal.count(), 1);
4147+ // verify arguments of rowsInsertedSignal
4148+ {
4149+ auto args = rowsInsertedSignal.takeFirst();
4150+ auto row = args.at(1).toInt();
4151+ QCOMPARE(row, 0);
4152+ }
4153+
4154+ rowsInsertedSignal.clear();
4155+
4156+ // insert new filter
4157+ {
4158+ QList<unity::scopes::FilterBase::SCPtr> backendFilters;
4159+ backendFilters.append(f1);
4160+ backendFilters.append(f2);
4161+
4162+ filtersModel->update(backendFilters);
4163+ }
4164+
4165+ QCOMPARE(rowsMovedSignal.count(), 0);
4166+ QCOMPARE(rowsRemovedSignal.count(), 0);
4167+ QCOMPARE(rowsInsertedSignal.count(), 1);
4168+ // verify arguments of rowsInsertedSignal
4169+ {
4170+ auto args = rowsInsertedSignal.takeFirst();
4171+ auto row = args.at(1).toInt();
4172+ QCOMPARE(row, 1);
4173+ }
4174+
4175+ auto idx1 = filtersModel->index(0, 0);
4176+ auto idx2 = filtersModel->index(1, 0);
4177+ QCOMPARE(filtersModel->rowCount(), 2);
4178+ QCOMPARE(filtersModel->data(idx1, unity::shell::scopes::FiltersInterface::Roles::RoleFilterId).toString(), QString("f1"));
4179+ QCOMPARE(filtersModel->data(idx2, unity::shell::scopes::FiltersInterface::Roles::RoleFilterId).toString(), QString("f2"));
4180+ QCOMPARE(filtersModel->data(idx2, unity::shell::scopes::FiltersInterface::Roles::RoleFilterType).toInt(),
4181+ static_cast<int>(unity::shell::scopes::FiltersInterface::FilterType::OptionSelectorFilter));
4182+
4183+ }
4184+
4185+ void testFiltersModelMove()
4186+ {
4187+ {
4188+ QList<unity::scopes::FilterBase::SCPtr> backendFilters;
4189+ backendFilters.append(f1);
4190+ backendFilters.append(f2);
4191+
4192+ filtersModel->update(backendFilters);
4193+ }
4194+
4195+ QSignalSpy rowsInsertedSignal(filtersModel.data(), SIGNAL(rowsInserted(const QModelIndex&, int, int)));
4196+ QSignalSpy rowsRemovedSignal(filtersModel.data(), SIGNAL(rowsRemoved(const QModelIndex&, int, int)));
4197+ QSignalSpy rowsMovedSignal(filtersModel.data(), SIGNAL(rowsMoved(const QModelIndex&, int, int, const QModelIndex&, int)));
4198+
4199+ // change filters positions
4200+ {
4201+ QList<unity::scopes::FilterBase::SCPtr> backendFilters;
4202+
4203+ backendFilters.append(f2);
4204+ backendFilters.append(f1);
4205+
4206+ filtersModel->update(backendFilters);
4207+ }
4208+
4209+ QCOMPARE(rowsInsertedSignal.count(), 0); // no change
4210+ QCOMPARE(rowsRemovedSignal.count(), 0);
4211+ QCOMPARE(rowsMovedSignal.count(), 1);
4212+
4213+ QCOMPARE(filtersModel->rowCount(), 2);
4214+
4215+ auto idx1 = filtersModel->index(0, 0);
4216+ auto idx2 = filtersModel->index(1, 0);
4217+ QCOMPARE(filtersModel->data(idx1, unity::shell::scopes::FiltersInterface::Roles::RoleFilterId).toString(), QString("f2"));
4218+ QCOMPARE(filtersModel->data(idx2, unity::shell::scopes::FiltersInterface::Roles::RoleFilterId).toString(), QString("f1"));
4219+
4220+ // verify arguments of rowsMovedSignal
4221+ {
4222+ auto args = rowsMovedSignal.takeFirst();
4223+ auto srcRow = args.at(1).toInt();
4224+ auto dstRow = args.at(4).toInt();
4225+ QCOMPARE(srcRow, 0);
4226+ QCOMPARE(dstRow, 2);
4227+ }
4228+ }
4229+
4230+ void testFiltersModelRemove()
4231+ {
4232+ {
4233+ QList<unity::scopes::FilterBase::SCPtr> backendFilters;
4234+ backendFilters.append(f2);
4235+ backendFilters.append(f1);
4236+
4237+ filtersModel->update(backendFilters);
4238+ }
4239+
4240+ QSignalSpy rowsInsertedSignal(filtersModel.data(), SIGNAL(rowsInserted(const QModelIndex&, int, int)));
4241+ QSignalSpy rowsRemovedSignal(filtersModel.data(), SIGNAL(rowsRemoved(const QModelIndex&, int, int)));
4242+ QSignalSpy rowsMovedSignal(filtersModel.data(), SIGNAL(rowsMoved(const QModelIndex&, int, int, const QModelIndex&, int)));
4243+
4244+ // remove a filter
4245+ {
4246+ QList<unity::scopes::FilterBase::SCPtr> backendFilters;
4247+ backendFilters.append(f2); // filter1 not present (removed)
4248+
4249+ filtersModel->update(backendFilters);
4250+ }
4251+
4252+ QCOMPARE(rowsInsertedSignal.count(), 0);
4253+ QCOMPARE(rowsMovedSignal.count(), 0);
4254+ QCOMPARE(rowsRemovedSignal.count(), 1);
4255+ // verify arguments of rowsRemovedSignal
4256+ {
4257+ auto args = rowsRemovedSignal.takeFirst();
4258+ auto first = args.at(1).toInt();
4259+ auto last = args.at(2).toInt();
4260+ QCOMPARE(first, 1);
4261+ QCOMPARE(last, 1);
4262+ }
4263+
4264+ QCOMPARE(filtersModel->rowCount(), 1);
4265+
4266+ auto idx1 = filtersModel->index(0, 0);
4267+ QCOMPARE(filtersModel->data(idx1, unity::shell::scopes::FiltersInterface::Roles::RoleFilterId).toString(), QString("f2"));
4268+ }
4269+
4270+private:
4271+ QScopedPointer<Filters> filtersModel;
4272+ unity::scopes::OptionSelectorFilter::SPtr f1, f2;
4273+ unity::scopes::FilterOption::SCPtr f1o1, f1o2, f2o1, f2o2;
4274+};
4275+
4276+QTEST_GUILESS_MAIN(FiltersTest)
4277+#include <filterstest.moc>
4278
4279=== added file 'tests/optionselectorfiltertest.cpp'
4280--- tests/optionselectorfiltertest.cpp 1970-01-01 00:00:00 +0000
4281+++ tests/optionselectorfiltertest.cpp 2016-03-16 11:17:35 +0000
4282@@ -0,0 +1,263 @@
4283+/*
4284+ * Copyright (C) 2015 Canonical, Ltd.
4285+ *
4286+ * This program is free software; you can redistribute it and/or modify
4287+ * it under the terms of the GNU General Public License as published by
4288+ * the Free Software Foundation; version 3.
4289+ *
4290+ * This program is distributed in the hope that it will be useful,
4291+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
4292+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4293+ * GNU General Public License for more details.
4294+ *
4295+ * You should have received a copy of the GNU General Public License
4296+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
4297+ *
4298+ * Authors:
4299+ * Pawel Stolowski <pawel.stolowski@canonical.com>
4300+ */
4301+
4302+#include <QSignalSpy>
4303+#include <QScopedPointer>
4304+#include <QTest>
4305+#include <QList>
4306+#include "filters.h"
4307+#include "optionselectorfilter.h"
4308+
4309+#include <unity/scopes/OptionSelectorFilter.h>
4310+
4311+using namespace scopes_ng;
4312+namespace uss = unity::shell::scopes;
4313+
4314+class OptionSelectorFilterTest: public QObject
4315+{
4316+ Q_OBJECT
4317+
4318+private Q_SLOTS:
4319+
4320+ void init()
4321+ {
4322+ unity::scopes::FilterState filterState;
4323+ filtersModel.reset(new Filters(filterState));
4324+
4325+ f1 = unity::scopes::OptionSelectorFilter::create("f1", "Filter1", false);
4326+ f1o1 = f1->add_option("f1o1", "Option1");
4327+ f1o2 = f1->add_option("f1o2", "Option2");
4328+
4329+ f2 = unity::scopes::OptionSelectorFilter::create("f2", "Filter2", true);
4330+ f2o1 = f2->add_option("f2o1", "Option1");
4331+ f2o2 = f2->add_option("f2o2", "Option2");
4332+
4333+ backendFilters.clear();
4334+ backendFilters.append(f1);
4335+ backendFilters.append(f2);
4336+ filtersModel->update(backendFilters);
4337+
4338+ f1->update_state(filterState, f1o1, true);
4339+
4340+ filtersModel->update(filterState);
4341+ }
4342+
4343+ void testOptions()
4344+ {
4345+ // check filters model data
4346+ auto idx1 = filtersModel->index(0, 0);
4347+ auto idx2 = filtersModel->index(1, 0);
4348+
4349+ QCOMPARE(filtersModel->rowCount(), 2);
4350+ QCOMPARE(filtersModel->data(idx1, unity::shell::scopes::FiltersInterface::Roles::RoleFilterId).toString(), QString("f1"));
4351+ QCOMPARE(filtersModel->data(idx1, unity::shell::scopes::FiltersInterface::Roles::RoleFilterType).toInt(),
4352+ static_cast<int>(unity::shell::scopes::FiltersInterface::FilterType::OptionSelectorFilter));
4353+ QCOMPARE(filtersModel->data(idx1, unity::shell::scopes::FiltersInterface::Roles::RoleFilterId).toString(), QString("f1"));
4354+ QCOMPARE(filtersModel->data(idx1, unity::shell::scopes::FiltersInterface::Roles::RoleFilterType).toInt(),
4355+ static_cast<int>(unity::shell::scopes::FiltersInterface::FilterType::OptionSelectorFilter));
4356+
4357+ {
4358+ // get 1st option selector filter
4359+ auto opf = filtersModel->data(idx1, uss::FiltersInterface::Roles::RoleFilter).value<OptionSelectorFilter*>();
4360+ QVERIFY(opf != nullptr);
4361+ QCOMPARE(opf->filterId(), QString("f1"));
4362+ QCOMPARE(opf->label(), QString("Filter1"));
4363+ QVERIFY(!opf->multiSelect());
4364+
4365+ // check options
4366+ auto opts = opf->options();
4367+ QVERIFY(opts != nullptr);
4368+ QCOMPARE(opts->rowCount(), 2);
4369+
4370+ auto idx = opts->index(0, 0);
4371+ QCOMPARE(opts->data(idx, uss::OptionSelectorOptionsInterface::Roles::RoleOptionId).toString(), QString("f1o1"));
4372+ QVERIFY(opts->data(idx, uss::OptionSelectorOptionsInterface::Roles::RoleOptionChecked).toBool()); // option1 is checked
4373+ idx = opts->index(1, 0);
4374+ QCOMPARE(opts->data(idx, uss::OptionSelectorOptionsInterface::Roles::RoleOptionId).toString(), QString("f1o2"));
4375+ QVERIFY(!opts->data(idx, uss::OptionSelectorOptionsInterface::Roles::RoleOptionChecked).toBool());
4376+ }
4377+
4378+ {
4379+ // get 2nd option selector filter
4380+ auto opf = filtersModel->data(idx2, uss::FiltersInterface::Roles::RoleFilter).value<OptionSelectorFilter*>();
4381+ QVERIFY(opf != nullptr);
4382+ QCOMPARE(opf->filterId(), QString("f2"));
4383+ QCOMPARE(opf->label(), QString("Filter2"));
4384+ QVERIFY(opf->multiSelect());
4385+
4386+ // check options
4387+ auto opts = opf->options();
4388+ QVERIFY(opts != nullptr);
4389+ QCOMPARE(opts->rowCount(), 2);
4390+
4391+ auto idx = opts->index(0, 0);
4392+ QCOMPARE(opts->data(idx, uss::OptionSelectorOptionsInterface::Roles::RoleOptionId).toString(), QString("f2o1"));
4393+ QVERIFY(!opts->data(idx, uss::OptionSelectorOptionsInterface::Roles::RoleOptionChecked).toBool());
4394+ idx = opts->index(1, 0);
4395+ QCOMPARE(opts->data(idx, uss::OptionSelectorOptionsInterface::Roles::RoleOptionId).toString(), QString("f2o2"));
4396+ QVERIFY(!opts->data(idx, uss::OptionSelectorOptionsInterface::Roles::RoleOptionChecked).toBool());
4397+ }
4398+ }
4399+
4400+ void testBackendCheckedStateChange()
4401+ {
4402+ auto idx1 = filtersModel->index(0, 0);
4403+ {
4404+ // get 1st option selector filter
4405+ auto opf = filtersModel->data(idx1, uss::FiltersInterface::Roles::RoleFilter).value<OptionSelectorFilter*>();
4406+ QVERIFY(opf != nullptr);
4407+ auto opts = opf->options();
4408+ QVERIFY(opts != nullptr);
4409+ QCOMPARE(opts->rowCount(), 2);
4410+
4411+ QSignalSpy dataChangedSignal(opts, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&, const QVector<int>&)));
4412+ QSignalSpy stateChangeSignal(filtersModel.data(), SIGNAL(filterStateChanged()));
4413+
4414+ // enable second option (which disables 1st option)
4415+ f1->update_state(filterState, f1o2, true);
4416+ filtersModel->update(filterState);
4417+
4418+ QCOMPARE(dataChangedSignal.count(), 2);
4419+ QCOMPARE(stateChangeSignal.count(), 0); // change initiated by backend, so no state change signal
4420+
4421+ // verify arguments of dataChanged signal
4422+ {
4423+ auto args = dataChangedSignal.takeFirst();
4424+ auto topLeft = args.at(0).value<QModelIndex>();
4425+ auto roles = args.at(2).value<QVector<int>>();
4426+ QCOMPARE(topLeft.row(), 0);
4427+ QCOMPARE(roles[0], static_cast<int>(uss::OptionSelectorOptionsInterface::Roles::RoleOptionChecked));
4428+ }
4429+
4430+ auto idx = opts->index(0, 0);
4431+ QCOMPARE(opts->data(idx, uss::OptionSelectorOptionsInterface::Roles::RoleOptionId).toString(), QString("f1o1"));
4432+ QVERIFY(!opts->data(idx, uss::OptionSelectorOptionsInterface::Roles::RoleOptionChecked).toBool()); // option 1 is now off
4433+ idx = opts->index(1, 0);
4434+ QCOMPARE(opts->data(idx, uss::OptionSelectorOptionsInterface::Roles::RoleOptionId).toString(), QString("f1o2"));
4435+ QVERIFY(opts->data(idx, uss::OptionSelectorOptionsInterface::Roles::RoleOptionChecked).toBool()); // option 2 is now on
4436+ }
4437+ }
4438+
4439+ void testBackendOptionLabelChange()
4440+ {
4441+ auto idx = filtersModel->index(1, 0);
4442+ {
4443+ // get 1st option selector filter
4444+ auto opf = filtersModel->data(idx, uss::FiltersInterface::Roles::RoleFilter).value<OptionSelectorFilter*>();
4445+ QVERIFY(opf != nullptr);
4446+ QCOMPARE(opf->filterId(), QString("f2"));
4447+ auto opts = opf->options();
4448+ QVERIFY(opts != nullptr);
4449+ QCOMPARE(opts->rowCount(), 2);
4450+
4451+ auto idx1 = opts->index(0, 0);
4452+ auto idx2 = opts->index(1, 0);
4453+ QCOMPARE(opts->data(idx1, uss::OptionSelectorOptionsInterface::Roles::RoleOptionLabel).toString(), QString("Option1"));
4454+ QCOMPARE(opts->data(idx2, uss::OptionSelectorOptionsInterface::Roles::RoleOptionLabel).toString(), QString("Option2"));
4455+
4456+ QSignalSpy dataChangedSignal(opts, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&, const QVector<int>&)));
4457+ QSignalSpy stateChangeSignal(filtersModel.data(), SIGNAL(filterStateChanged()));
4458+
4459+ unity::scopes::OptionSelectorFilter::SPtr f3 = unity::scopes::OptionSelectorFilter::create("f2", "Filter2", true);
4460+ auto f3o1 = f3->add_option("f2o1", "Option1");
4461+ auto f3o2 = f3->add_option("f2o2", "UpdatedOption2");
4462+
4463+ QList<unity::scopes::FilterBase::SCPtr> backendFilters2;
4464+ backendFilters2.append(f1);
4465+ backendFilters2.append(f3);
4466+
4467+ // sync model with new backend filters
4468+ filtersModel->update(backendFilters2);
4469+
4470+ if (dataChangedSignal.empty()) {
4471+ QVERIFY(dataChangedSignal.wait());
4472+ }
4473+ QCOMPARE(dataChangedSignal.count(), 1);
4474+
4475+ QCOMPARE(opts->data(idx1, uss::OptionSelectorOptionsInterface::Roles::RoleOptionLabel).toString(), QString("Option1"));
4476+ QCOMPARE(opts->data(idx2, uss::OptionSelectorOptionsInterface::Roles::RoleOptionLabel).toString(), QString("UpdatedOption2"));
4477+
4478+ QCOMPARE(stateChangeSignal.count(), 0); // no state change signal
4479+
4480+ // verify arguments of dataChanged signal
4481+ {
4482+ auto args = dataChangedSignal.takeFirst();
4483+ auto topLeft = args.at(0).value<QModelIndex>();
4484+ auto roles = args.at(2).value<QVector<int>>();
4485+ QCOMPARE(topLeft.row(), 1);
4486+ QCOMPARE(roles[0], static_cast<int>(uss::OptionSelectorOptionsInterface::Roles::RoleOptionLabel));
4487+ }
4488+ }
4489+ }
4490+
4491+ void testUICheckedStateChange()
4492+ {
4493+ auto idx1 = filtersModel->index(0, 0);
4494+ {
4495+ // get 1st option selector filter
4496+ auto opf = filtersModel->data(idx1, uss::FiltersInterface::Roles::RoleFilter).value<OptionSelectorFilter*>();
4497+ QVERIFY(opf != nullptr);
4498+ auto opts = opf->options();
4499+ QVERIFY(opts != nullptr);
4500+ QCOMPARE(opts->rowCount(), 2);
4501+
4502+ QSignalSpy dataChangedSignal(opts, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&, const QVector<int>&)));
4503+ QSignalSpy stateChangeSignal(filtersModel.data(), SIGNAL(filterStateChanged()));
4504+
4505+ // enable second option (which disables 1st option)
4506+ opts->setChecked(1, true);
4507+
4508+ if (stateChangeSignal.empty()) {
4509+ stateChangeSignal.wait();
4510+ }
4511+ if (dataChangedSignal.empty()) {
4512+ dataChangedSignal.wait();
4513+ }
4514+ QCOMPARE(stateChangeSignal.count(), 1);
4515+ QCOMPARE(dataChangedSignal.count(), 2);
4516+
4517+ // verify arguments of dataChanged signal
4518+ {
4519+ auto args = dataChangedSignal.takeFirst();
4520+ auto topLeft = args.at(0).value<QModelIndex>();
4521+ auto roles = args.at(2).value<QVector<int>>();
4522+ QCOMPARE(topLeft.row(), 0);
4523+ QCOMPARE(roles[0], static_cast<int>(uss::OptionSelectorOptionsInterface::Roles::RoleOptionChecked));
4524+ }
4525+
4526+ auto idx = opts->index(0, 0);
4527+ QCOMPARE(opts->data(idx, uss::OptionSelectorOptionsInterface::Roles::RoleOptionId).toString(), QString("f1o1"));
4528+ QVERIFY(!opts->data(idx, uss::OptionSelectorOptionsInterface::Roles::RoleOptionChecked).toBool()); // option 1 is now off
4529+ idx = opts->index(1, 0);
4530+ QCOMPARE(opts->data(idx, uss::OptionSelectorOptionsInterface::Roles::RoleOptionId).toString(), QString("f1o2"));
4531+ QVERIFY(opts->data(idx, uss::OptionSelectorOptionsInterface::Roles::RoleOptionChecked).toBool()); // option 2 is now on
4532+ }
4533+
4534+ }
4535+
4536+private:
4537+ unity::scopes::FilterState filterState;
4538+ QScopedPointer<Filters> filtersModel;
4539+ unity::scopes::OptionSelectorFilter::SPtr f1, f2;
4540+ unity::scopes::FilterOption::SCPtr f1o1, f1o2, f2o1, f2o2;
4541+ QList<unity::scopes::FilterBase::SCPtr> backendFilters;
4542+};
4543+
4544+QTEST_GUILESS_MAIN(OptionSelectorFilterTest)
4545+#include <optionselectorfiltertest.moc>

Subscribers

People subscribed via source and target branches

to all changes: