Merge lp:~mhr3/unity-scopes-shell/department-support into lp:unity-scopes-shell
- department-support
- Merge into trunk
Status: | Superseded |
---|---|
Proposed branch: | lp:~mhr3/unity-scopes-shell/department-support |
Merge into: | lp:unity-scopes-shell |
Diff against target: |
1806 lines (+1206/-70) 23 files modified
debian/changelog (+6/-0) debian/control (+2/-2) src/Unity/CMakeLists.txt (+4/-1) src/Unity/collectors.cpp (+24/-10) src/Unity/collectors.h (+4/-3) src/Unity/department.cpp (+167/-0) src/Unity/department.h (+87/-0) src/Unity/departmentnode.cpp (+126/-0) src/Unity/departmentnode.h (+71/-0) src/Unity/plugin.cpp (+2/-0) src/Unity/scope.cpp (+162/-29) src/Unity/scope.h (+71/-4) src/Unity/scopes.cpp (+1/-1) tests/CMakeLists.txt (+1/-0) tests/data/CMakeLists.txt (+1/-0) tests/data/mock-scope-departments/CMakeLists.txt (+16/-0) tests/data/mock-scope-departments/mock-scope-departments.cpp (+201/-0) tests/data/mock-scope-departments/mock-scope-departments.ini.in (+8/-0) tests/data/mock-scope/CMakeLists.txt (+1/-1) tests/data/mock-scope/mock-scope.cpp (+4/-3) tests/departmentstest.cpp (+236/-0) tests/previewtest.cpp (+3/-2) tests/resultstest.cpp (+8/-14) |
To merge this branch: | bzr merge lp:~mhr3/unity-scopes-shell/department-support |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Albert Astals Cid (community) | Needs Fixing | ||
PS Jenkins bot (community) | continuous-integration | Needs Fixing | |
Paweł Stołowski (community) | Needs Fixing | ||
Review via email: mp+221883@code.launchpad.net |
This proposal has been superseded by a proposal from 2014-06-06.
Commit message
Added support for departments.
Description of the change
Added support for departments. Note that this requires changes to lp:unity-api (https:/
PS Jenkins bot (ps-jenkins) wrote : | # |
Paweł Stołowski (stolowski) : | # |
- 124. By Michal Hruby
-
Let's not forget to commit this
Albert Astals Cid (aacid) wrote : | # |
Some small comments over a quick code review.
Will check tests tomorrow.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:124
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
Albert Astals Cid (aacid) wrote : | # |
My small comments were indeed a "Needs fixing"
Paweł Stołowski (stolowski) : | # |
Albert Astals Cid (aacid) wrote : | # |
Besides my small inline comments from https:/
Michal Hruby (mhr3) : | # |
Michal Hruby (mhr3) wrote : | # |
Should be all addressed now.
Michal Hruby (mhr3) wrote : | # |
Eh, in the superseding branch...
Unmerged revisions
Preview Diff
1 | === modified file 'debian/changelog' |
2 | --- debian/changelog 2014-05-22 09:46:47 +0000 |
3 | +++ debian/changelog 2014-06-03 15:51:24 +0000 |
4 | @@ -1,3 +1,9 @@ |
5 | +unity-scopes-shell (0.4.8-0ubuntu1) UNRELEASED; urgency=medium |
6 | + |
7 | + * Added support for departments |
8 | + |
9 | + -- Michal Hruby <michal.hruby@canonical.com> Tue, 27 May 2014 14:20:15 +0200 |
10 | + |
11 | unity-scopes-shell (0.4.0+14.10.20140522-0ubuntu1) utopic; urgency=low |
12 | |
13 | [ Albert Astals ] |
14 | |
15 | === modified file 'debian/control' |
16 | --- debian/control 2014-05-22 08:03:59 +0000 |
17 | +++ debian/control 2014-06-03 15:51:24 +0000 |
18 | @@ -3,8 +3,8 @@ |
19 | Section: libs |
20 | Build-Depends: cmake, |
21 | debhelper (>= 9), |
22 | - libunity-api-dev (>= 7.81), |
23 | - libunity-scopes-dev (>= 0.4.6~), |
24 | + libunity-api-dev (>= 7.82), |
25 | + libunity-scopes-dev (>= 0.4.8~), |
26 | libgsettings-qt-dev (>= 0.1), |
27 | pkg-config, |
28 | qt5-default, |
29 | |
30 | === modified file 'src/Unity/CMakeLists.txt' |
31 | --- src/Unity/CMakeLists.txt 2014-05-22 09:18:45 +0000 |
32 | +++ src/Unity/CMakeLists.txt 2014-06-03 15:51:24 +0000 |
33 | @@ -4,7 +4,7 @@ |
34 | # Dependencies |
35 | include(FindPkgConfig) |
36 | pkg_check_modules(SCOPES_API REQUIRED unity-shell-scopes=1) |
37 | -pkg_check_modules(SCOPESLIB REQUIRED libunity-scopes>=0.4.0) |
38 | +pkg_check_modules(SCOPESLIB REQUIRED libunity-scopes>=0.4.8) |
39 | pkg_check_modules(GSETTINGSQT REQUIRED gsettings-qt) |
40 | |
41 | include_directories( |
42 | @@ -17,6 +17,8 @@ |
43 | set(QMLPLUGIN_SRC |
44 | categories.cpp |
45 | collectors.cpp |
46 | + department.cpp |
47 | + departmentnode.cpp |
48 | previewmodel.cpp |
49 | previewstack.cpp |
50 | previewwidgetmodel.cpp |
51 | @@ -29,6 +31,7 @@ |
52 | # We need these headers here so moc runs and we get the moc-stuff |
53 | # compiled in, otherwise we miss some symbols |
54 | ${SCOPES_API_INCLUDEDIR}/unity/shell/scopes/CategoriesInterface.h |
55 | + ${SCOPES_API_INCLUDEDIR}/unity/shell/scopes/DepartmentInterface.h |
56 | ${SCOPES_API_INCLUDEDIR}/unity/shell/scopes/PreviewModelInterface.h |
57 | ${SCOPES_API_INCLUDEDIR}/unity/shell/scopes/PreviewStackInterface.h |
58 | ${SCOPES_API_INCLUDEDIR}/unity/shell/scopes/PreviewWidgetModelInterface.h |
59 | |
60 | === modified file 'src/Unity/collectors.cpp' |
61 | --- src/Unity/collectors.cpp 2014-03-25 16:34:26 +0000 |
62 | +++ src/Unity/collectors.cpp 2014-06-03 15:51:24 +0000 |
63 | @@ -71,15 +71,15 @@ |
64 | return m_timer.elapsed(); |
65 | } |
66 | |
67 | -class ResultCollector: public CollectorBase |
68 | +class SearchDataCollector: public CollectorBase |
69 | { |
70 | public: |
71 | - ResultCollector(): CollectorBase() |
72 | + SearchDataCollector(): CollectorBase() |
73 | { |
74 | } |
75 | |
76 | // Returns bool indicating whether this resultset was already posted |
77 | - bool addResult(std::shared_ptr<scopes::CategorisedResult> const& result) |
78 | + bool addResult(scopes::CategorisedResult::SPtr const& result) |
79 | { |
80 | QMutexLocker locker(&m_mutex); |
81 | m_results.append(result); |
82 | @@ -87,7 +87,13 @@ |
83 | return m_posted; |
84 | } |
85 | |
86 | - Status collect(QList<std::shared_ptr<scopes::CategorisedResult>>& out_results) |
87 | + void setDepartment(scopes::Department::SCPtr const& department) |
88 | + { |
89 | + QMutexLocker locker(&m_mutex); |
90 | + m_rootDepartment = department; |
91 | + } |
92 | + |
93 | + Status collect(QList<scopes::CategorisedResult::SPtr>& out_results, scopes::Department::SPtr& rootDepartment) |
94 | { |
95 | Status status; |
96 | |
97 | @@ -98,12 +104,14 @@ |
98 | } |
99 | status = m_status; |
100 | m_results.swap(out_results); |
101 | + rootDepartment = std::const_pointer_cast<scopes::Department>(m_rootDepartment); |
102 | |
103 | return status; |
104 | } |
105 | |
106 | private: |
107 | - QList<std::shared_ptr<scopes::CategorisedResult>> m_results; |
108 | + QList<scopes::CategorisedResult::SPtr> m_results; |
109 | + scopes::Department::SCPtr m_rootDepartment; |
110 | }; |
111 | |
112 | class PreviewDataCollector: public CollectorBase |
113 | @@ -214,10 +222,10 @@ |
114 | return m_collector->msecsSinceStart(); |
115 | } |
116 | |
117 | -CollectorBase::Status PushEvent::collectSearchResults(QList<std::shared_ptr<scopes::CategorisedResult>>& out_results) |
118 | +CollectorBase::Status PushEvent::collectSearchResults(QList<scopes::CategorisedResult::SPtr>& out_results, scopes::Department::SPtr& rootDepartment) |
119 | { |
120 | - auto collector = std::dynamic_pointer_cast<ResultCollector>(m_collector); |
121 | - return collector->collect(out_results); |
122 | + auto collector = std::dynamic_pointer_cast<SearchDataCollector>(m_collector); |
123 | + return collector->collect(out_results, rootDepartment); |
124 | } |
125 | |
126 | CollectorBase::Status PushEvent::collectPreviewData(scopes::ColumnLayoutList& out_columns, scopes::PreviewWidgetList& out_widgets, QHash<QString, QVariant>& out_data) |
127 | @@ -257,9 +265,9 @@ |
128 | } |
129 | |
130 | SearchResultReceiver::SearchResultReceiver(QObject* receiver): |
131 | - ScopeDataReceiverBase(receiver, PushEvent::SEARCH, std::shared_ptr<CollectorBase>(new ResultCollector)) |
132 | + ScopeDataReceiverBase(receiver, PushEvent::SEARCH, std::shared_ptr<CollectorBase>(new SearchDataCollector)) |
133 | { |
134 | - m_collector = collectorAs<ResultCollector>(); |
135 | + m_collector = collectorAs<SearchDataCollector>(); |
136 | } |
137 | |
138 | // this will be called from non-main thread, (might even be multiple different threads) |
139 | @@ -273,6 +281,12 @@ |
140 | } |
141 | } |
142 | |
143 | +// this will be called from non-main thread, (might even be multiple different threads) |
144 | +void SearchResultReceiver::push(scopes::Department::SCPtr const& department) |
145 | +{ |
146 | + m_collector->setDepartment(department); |
147 | +} |
148 | + |
149 | // this might be called from any thread (might be main, might be any other thread) |
150 | void SearchResultReceiver::finished(scopes::ListenerBase::Reason reason, std::string const& error_msg) |
151 | { |
152 | |
153 | === modified file 'src/Unity/collectors.h' |
154 | --- src/Unity/collectors.h 2014-03-25 16:34:26 +0000 |
155 | +++ src/Unity/collectors.h 2014-06-03 15:51:24 +0000 |
156 | @@ -37,7 +37,7 @@ |
157 | namespace scopes_ng |
158 | { |
159 | |
160 | -class ResultCollector; |
161 | +class SearchDataCollector; |
162 | class PreviewDataCollector; |
163 | class ActivationCollector; |
164 | |
165 | @@ -73,7 +73,7 @@ |
166 | PushEvent(Type event_type, std::shared_ptr<CollectorBase> collector); |
167 | Type type(); |
168 | |
169 | - CollectorBase::Status collectSearchResults(QList<std::shared_ptr<unity::scopes::CategorisedResult>>& out_results); |
170 | + CollectorBase::Status collectSearchResults(QList<std::shared_ptr<unity::scopes::CategorisedResult>>& out_results, unity::scopes::Department::SPtr& rootDepartment); |
171 | CollectorBase::Status collectPreviewData(unity::scopes::ColumnLayoutList& out_columns, unity::scopes::PreviewWidgetList& out_widgets, QHash<QString, QVariant>& out_data); |
172 | CollectorBase::Status collectActivationResponse(std::shared_ptr<unity::scopes::ActivationResponse>& out_response, std::shared_ptr<unity::scopes::Result>& out_result); |
173 | |
174 | @@ -104,12 +104,13 @@ |
175 | { |
176 | public: |
177 | virtual void push(unity::scopes::CategorisedResult result) override; |
178 | + virtual void push(unity::scopes::Department::SCPtr const& department) override; |
179 | virtual void finished(unity::scopes::ListenerBase::Reason reason, std::string const& error_msg) override; |
180 | |
181 | SearchResultReceiver(QObject* receiver); |
182 | |
183 | private: |
184 | - std::shared_ptr<ResultCollector> m_collector; |
185 | + std::shared_ptr<SearchDataCollector> m_collector; |
186 | }; |
187 | |
188 | class PreviewDataReceiver: public unity::scopes::PreviewListenerBase, public ScopeDataReceiverBase |
189 | |
190 | === added file 'src/Unity/department.cpp' |
191 | --- src/Unity/department.cpp 1970-01-01 00:00:00 +0000 |
192 | +++ src/Unity/department.cpp 2014-06-03 15:51:24 +0000 |
193 | @@ -0,0 +1,167 @@ |
194 | +/* |
195 | + * Copyright (C) 2014 Canonical, Ltd. |
196 | + * |
197 | + * Authors: |
198 | + * Michal Hruby <michal.hruby@canonical.com> |
199 | + * |
200 | + * This program is free software; you can redistribute it and/or modify |
201 | + * it under the terms of the GNU General Public License as published by |
202 | + * the Free Software Foundation; version 3. |
203 | + * |
204 | + * This program is distributed in the hope that it will be useful, |
205 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
206 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
207 | + * GNU General Public License for more details. |
208 | + * |
209 | + * You should have received a copy of the GNU General Public License |
210 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
211 | + */ |
212 | + |
213 | +#include "department.h" |
214 | + |
215 | +namespace scopes_ng |
216 | +{ |
217 | + |
218 | +using namespace unity; |
219 | + |
220 | +Department::Department(QObject* parent) : |
221 | + unity::shell::scopes::DepartmentInterface(parent), |
222 | + m_loaded(false), |
223 | + m_isRoot(false) |
224 | +{ |
225 | +} |
226 | + |
227 | +void Department::loadFromDepartmentNode(DepartmentNode* treeNode) |
228 | +{ |
229 | + if (treeNode == nullptr) { |
230 | + qWarning("Tried to set null DepartmentNode!"); |
231 | + return; |
232 | + } |
233 | + m_departmentId = treeNode->id(); |
234 | + m_label = treeNode->label(); |
235 | + m_allLabel = treeNode->allLabel(); |
236 | + m_loaded = !treeNode->isLeaf() && treeNode->childCount() > 0; |
237 | + m_isRoot = treeNode->isRoot(); |
238 | + |
239 | + DepartmentNode* parentNode = treeNode->parent(); |
240 | + m_parentDepartmentId = parentNode ? parentNode->id() : ""; |
241 | + m_parentLabel = parentNode ? parentNode->label() : ""; |
242 | + |
243 | + beginResetModel(); |
244 | + |
245 | + m_subdepartments.clear(); |
246 | + Q_FOREACH (DepartmentNode* node, treeNode->childNodes()) { |
247 | + QSharedPointer<SubdepartmentData> subdept(new SubdepartmentData); |
248 | + subdept->id = node->id(); |
249 | + subdept->label = node->label(); |
250 | + subdept->hasChildren = node->hasSubdepartments(); |
251 | + subdept->isActive = false; |
252 | + m_subdepartments.append(subdept); |
253 | + } |
254 | + |
255 | + endResetModel(); |
256 | + |
257 | + Q_EMIT departmentIdChanged(); |
258 | + Q_EMIT labelChanged(); |
259 | + Q_EMIT allLabelChanged(); |
260 | + Q_EMIT parentDepartmentIdChanged(); |
261 | + Q_EMIT parentLabelChanged(); |
262 | + Q_EMIT loadedChanged(); |
263 | + Q_EMIT countChanged(); |
264 | + Q_EMIT isRootChanged(); |
265 | +} |
266 | + |
267 | +void Department::markSubdepartmentActive(QString const& subdepartmentId) |
268 | +{ |
269 | + int idx = -1; |
270 | + bool isActiveReset = false; |
271 | + for (int i = 0; i < m_subdepartments.count(); i++) { |
272 | + if (m_subdepartments[i]->id == subdepartmentId) { |
273 | + m_subdepartments[i]->isActive = true; |
274 | + idx = i; |
275 | + } else if (m_subdepartments[i]->isActive) { |
276 | + // only one department can be active |
277 | + m_subdepartments[i]->isActive = false; |
278 | + isActiveReset = true; |
279 | + } |
280 | + } |
281 | + |
282 | + if (idx < 0) return; |
283 | + |
284 | + QVector<int> roles; |
285 | + roles.append(Roles::RoleIsActive); |
286 | + |
287 | + QModelIndex startIndex(index(isActiveReset ? 0 : idx)); |
288 | + QModelIndex endIndex(index(isActiveReset ? m_subdepartments.count() - 1 : idx)); |
289 | + dataChanged(startIndex, endIndex, roles); |
290 | +} |
291 | + |
292 | +QVariant Department::data(const QModelIndex& index, int role) const |
293 | +{ |
294 | + SubdepartmentData* data = m_subdepartments[index.row()].data(); |
295 | + switch (role) { |
296 | + case RoleDepartmentId: return data->id; |
297 | + case RoleLabel: return data->label; |
298 | + case RoleHasChildren: return data->hasChildren; |
299 | + case RoleIsActive: return data->isActive; |
300 | + default: return QVariant(); |
301 | + } |
302 | +} |
303 | + |
304 | +int Department::rowCount(const QModelIndex& parent) const |
305 | +{ |
306 | + Q_UNUSED(parent); |
307 | + return m_subdepartments.size(); |
308 | +} |
309 | + |
310 | +QHash<int, QByteArray> Department::roleNames() const |
311 | +{ |
312 | + QHash<int, QByteArray> roles; |
313 | + roles[RoleDepartmentId] = "departmentId"; |
314 | + roles[RoleLabel] = "label"; |
315 | + roles[RoleHasChildren] = "hasChildren"; |
316 | + roles[RoleIsActive] = "isActive"; |
317 | + return roles; |
318 | +} |
319 | + |
320 | +QString Department::departmentId() const |
321 | +{ |
322 | + return m_departmentId; |
323 | +} |
324 | + |
325 | +QString Department::label() const |
326 | +{ |
327 | + return m_label; |
328 | +} |
329 | + |
330 | +QString Department::allLabel() const |
331 | +{ |
332 | + return m_allLabel; |
333 | +} |
334 | + |
335 | +QString Department::parentDepartmentId() const |
336 | +{ |
337 | + return m_parentDepartmentId; |
338 | +} |
339 | + |
340 | +QString Department::parentLabel() const |
341 | +{ |
342 | + return m_parentLabel; |
343 | +} |
344 | + |
345 | +bool Department::loaded() const |
346 | +{ |
347 | + return m_loaded; |
348 | +} |
349 | + |
350 | +bool Department::isRoot() const |
351 | +{ |
352 | + return m_isRoot; |
353 | +} |
354 | + |
355 | +int Department::count() const |
356 | +{ |
357 | + return rowCount(); |
358 | +} |
359 | + |
360 | +} // namespace scopes_ng |
361 | |
362 | === added file 'src/Unity/department.h' |
363 | --- src/Unity/department.h 1970-01-01 00:00:00 +0000 |
364 | +++ src/Unity/department.h 2014-06-03 15:51:24 +0000 |
365 | @@ -0,0 +1,87 @@ |
366 | +/* |
367 | + * Copyright (C) 2014 Canonical, Ltd. |
368 | + * |
369 | + * Authors: |
370 | + * Michal Hruby <michal.hruby@canonical.com> |
371 | + * |
372 | + * This program is free software; you can redistribute it and/or modify |
373 | + * it under the terms of the GNU General Public License as published by |
374 | + * the Free Software Foundation; version 3. |
375 | + * |
376 | + * This program is distributed in the hope that it will be useful, |
377 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
378 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
379 | + * GNU General Public License for more details. |
380 | + * |
381 | + * You should have received a copy of the GNU General Public License |
382 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
383 | + */ |
384 | + |
385 | + |
386 | +#ifndef NG_DEPARTMENT_H |
387 | +#define NG_DEPARTMENT_H |
388 | + |
389 | +#include <unity/shell/scopes/DepartmentInterface.h> |
390 | + |
391 | +#include <QString> |
392 | +#include <QSharedPointer> |
393 | +#include <QPointer> |
394 | +#include <QAbstractListModel> |
395 | + |
396 | +#include <unity/scopes/Result.h> |
397 | +#include <unity/scopes/Department.h> |
398 | + |
399 | +#include "departmentnode.h" |
400 | + |
401 | +namespace scopes_ng |
402 | +{ |
403 | + |
404 | +struct SubdepartmentData |
405 | +{ |
406 | + QString id; |
407 | + QString label; |
408 | + bool hasChildren; |
409 | + bool isActive; |
410 | +}; |
411 | + |
412 | +class Q_DECL_EXPORT Department : public unity::shell::scopes::DepartmentInterface |
413 | +{ |
414 | + Q_OBJECT |
415 | + |
416 | +public: |
417 | + explicit Department(QObject* parent = 0); |
418 | + void loadFromDepartmentNode(DepartmentNode* treeNode); |
419 | + void markSubdepartmentActive(QString const& subdepartmentId); |
420 | + |
421 | + QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; |
422 | + int rowCount(const QModelIndex& parent = QModelIndex()) const override; |
423 | + QHash<int, QByteArray> roleNames() const override; |
424 | + |
425 | + QString departmentId() const override; |
426 | + QString label() const override; |
427 | + QString allLabel() const override; |
428 | + QString parentDepartmentId() const override; |
429 | + QString parentLabel() const override; |
430 | + bool loaded() const override; |
431 | + bool isRoot() const override; |
432 | + int count() const override; |
433 | + |
434 | +Q_SIGNALS: |
435 | + |
436 | +private: |
437 | + QString m_departmentId; |
438 | + QString m_label; |
439 | + QString m_allLabel; |
440 | + QString m_parentDepartmentId; |
441 | + QString m_parentLabel; |
442 | + bool m_loaded; |
443 | + bool m_isRoot; |
444 | + |
445 | + QList<QSharedPointer<SubdepartmentData>> m_subdepartments; |
446 | +}; |
447 | + |
448 | +} // namespace scopes_ng |
449 | + |
450 | +Q_DECLARE_METATYPE(scopes_ng::Department*) |
451 | + |
452 | +#endif // NG_DEPARTMENT_H |
453 | |
454 | === added file 'src/Unity/departmentnode.cpp' |
455 | --- src/Unity/departmentnode.cpp 1970-01-01 00:00:00 +0000 |
456 | +++ src/Unity/departmentnode.cpp 2014-06-03 15:51:24 +0000 |
457 | @@ -0,0 +1,126 @@ |
458 | +/* |
459 | + * Copyright (C) 2014 Canonical, Ltd. |
460 | + * |
461 | + * Authors: |
462 | + * Michal Hruby <michal.hruby@canonical.com> |
463 | + * |
464 | + * This program is free software; you can redistribute it and/or modify |
465 | + * it under the terms of the GNU General Public License as published by |
466 | + * the Free Software Foundation; version 3. |
467 | + * |
468 | + * This program is distributed in the hope that it will be useful, |
469 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
470 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
471 | + * GNU General Public License for more details. |
472 | + * |
473 | + * You should have received a copy of the GNU General Public License |
474 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
475 | + */ |
476 | + |
477 | +#include "departmentnode.h" |
478 | + |
479 | +namespace scopes_ng |
480 | +{ |
481 | + |
482 | +using namespace unity; |
483 | + |
484 | +DepartmentNode::DepartmentNode(DepartmentNode* parent) : m_parent(parent), m_isRoot(false) |
485 | +{ |
486 | +} |
487 | + |
488 | +DepartmentNode::~DepartmentNode() |
489 | +{ |
490 | + clearChildren(); |
491 | +} |
492 | + |
493 | +void DepartmentNode::initializeForDepartment(scopes::Department::SCPtr const& dep) |
494 | +{ |
495 | + m_id = QString::fromStdString(dep->id()); |
496 | + m_label = QString::fromStdString(dep->label()); |
497 | + m_allLabel = QString::fromStdString(dep->alternate_label()); |
498 | + m_hasSubdepartments = dep->has_subdepartments(); |
499 | + |
500 | + clearChildren(); |
501 | + |
502 | + auto subdeps = dep->subdepartments(); |
503 | + for (auto it = subdeps.begin(); it != subdeps.end(); ++it) { |
504 | + DepartmentNode* subdep = new DepartmentNode(this); |
505 | + subdep->initializeForDepartment(*it); |
506 | + this->appendChild(subdep); |
507 | + } |
508 | +} |
509 | + |
510 | +void DepartmentNode::setIsRoot(bool isRoot) |
511 | +{ |
512 | + m_isRoot = isRoot; |
513 | +} |
514 | + |
515 | +bool DepartmentNode::isRoot() const |
516 | +{ |
517 | + return m_isRoot; |
518 | +} |
519 | + |
520 | +DepartmentNode* DepartmentNode::findNodeById(QString const& id) |
521 | +{ |
522 | + if (id == m_id) return this; |
523 | + |
524 | + Q_FOREACH(DepartmentNode* child, m_children) { |
525 | + DepartmentNode* node = child->findNodeById(id); |
526 | + if (node) return node; |
527 | + } |
528 | + |
529 | + return nullptr; |
530 | +} |
531 | + |
532 | +QString DepartmentNode::id() const |
533 | +{ |
534 | + return m_id; |
535 | +} |
536 | + |
537 | +QString DepartmentNode::label() const |
538 | +{ |
539 | + return m_label; |
540 | +} |
541 | + |
542 | +QString DepartmentNode::allLabel() const |
543 | +{ |
544 | + return m_allLabel; |
545 | +} |
546 | + |
547 | +bool DepartmentNode::hasSubdepartments() const |
548 | +{ |
549 | + return m_hasSubdepartments; |
550 | +} |
551 | + |
552 | +void DepartmentNode::appendChild(DepartmentNode* child) |
553 | +{ |
554 | + m_children.append(child); |
555 | +} |
556 | + |
557 | +int DepartmentNode::childCount() const |
558 | +{ |
559 | + return m_children.count(); |
560 | +} |
561 | + |
562 | +QList<DepartmentNode*> DepartmentNode::childNodes() const |
563 | +{ |
564 | + return m_children; |
565 | +} |
566 | + |
567 | +DepartmentNode* DepartmentNode::parent() const |
568 | +{ |
569 | + return m_parent; |
570 | +} |
571 | + |
572 | +bool DepartmentNode::isLeaf() const |
573 | +{ |
574 | + return m_children.count() == 0 && !m_hasSubdepartments; |
575 | +} |
576 | + |
577 | +void DepartmentNode::clearChildren() |
578 | +{ |
579 | + qDeleteAll(m_children); |
580 | + m_children.clear(); |
581 | +} |
582 | + |
583 | +} // namespace scopes_ng |
584 | |
585 | === added file 'src/Unity/departmentnode.h' |
586 | --- src/Unity/departmentnode.h 1970-01-01 00:00:00 +0000 |
587 | +++ src/Unity/departmentnode.h 2014-06-03 15:51:24 +0000 |
588 | @@ -0,0 +1,71 @@ |
589 | +/* |
590 | + * Copyright (C) 2014 Canonical, Ltd. |
591 | + * |
592 | + * Authors: |
593 | + * Michal Hruby <michal.hruby@canonical.com> |
594 | + * |
595 | + * This program is free software; you can redistribute it and/or modify |
596 | + * it under the terms of the GNU General Public License as published by |
597 | + * the Free Software Foundation; version 3. |
598 | + * |
599 | + * This program is distributed in the hope that it will be useful, |
600 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
601 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
602 | + * GNU General Public License for more details. |
603 | + * |
604 | + * You should have received a copy of the GNU General Public License |
605 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
606 | + */ |
607 | + |
608 | + |
609 | +#ifndef NG_DEPARTMENT_NODE_H |
610 | +#define NG_DEPARTMENT_NODE_H |
611 | + |
612 | +#include <QSharedPointer> |
613 | +#include <QMultiMap> |
614 | +#include <QStringList> |
615 | +#include <QPointer> |
616 | + |
617 | +#include <unity-scopes.h> |
618 | + |
619 | +namespace scopes_ng |
620 | +{ |
621 | + |
622 | +class Q_DECL_EXPORT DepartmentNode |
623 | +{ |
624 | +public: |
625 | + DepartmentNode(DepartmentNode* parent = nullptr); |
626 | + ~DepartmentNode(); |
627 | + |
628 | + void initializeForDepartment(unity::scopes::Department::SCPtr const& dep); |
629 | + DepartmentNode* findNodeById(QString const& id); |
630 | + |
631 | + QString id() const; |
632 | + QString label() const; |
633 | + QString allLabel() const; |
634 | + bool hasSubdepartments() const; |
635 | + bool isRoot() const; |
636 | + |
637 | + void setIsRoot(bool isRoot); |
638 | + |
639 | + void appendChild(DepartmentNode* child); |
640 | + int childCount() const; |
641 | + bool isLeaf() const; |
642 | + QList<DepartmentNode*> childNodes() const; |
643 | + DepartmentNode* parent() const; |
644 | + |
645 | +private: |
646 | + void clearChildren(); |
647 | + |
648 | + DepartmentNode* m_parent; |
649 | + QList<DepartmentNode*> m_children; |
650 | + QString m_id; |
651 | + QString m_label; |
652 | + QString m_allLabel; |
653 | + bool m_hasSubdepartments; |
654 | + bool m_isRoot; |
655 | +}; |
656 | + |
657 | +} // namespace scopes_ng |
658 | + |
659 | +#endif // NG_DEPARTMENT_NODE_H |
660 | |
661 | === modified file 'src/Unity/plugin.cpp' |
662 | --- src/Unity/plugin.cpp 2014-05-19 11:10:04 +0000 |
663 | +++ src/Unity/plugin.cpp 2014-06-03 15:51:24 +0000 |
664 | @@ -27,6 +27,7 @@ |
665 | #include "scopes.h" |
666 | #include "scope.h" |
667 | #include "categories.h" |
668 | +#include "department.h" |
669 | #include "resultsmodel.h" |
670 | #include "previewstack.h" |
671 | #include "previewmodel.h" |
672 | @@ -39,6 +40,7 @@ |
673 | // new Scopes classes |
674 | qmlRegisterType<scopes_ng::Scopes>(uri, 0, 2, "Scopes"); |
675 | qmlRegisterUncreatableType<unity::shell::scopes::ScopeInterface>(uri, 0, 2, "Scope", "Can't create Scope object in QML. Get them from Scopes instance."); |
676 | + qmlRegisterUncreatableType<unity::shell::scopes::DepartmentInterface>(uri, 0, 2, "Department", "Can't create Department object in QML. Get them from Scope instance."); |
677 | qmlRegisterUncreatableType<unity::shell::scopes::CategoriesInterface>(uri, 0, 2, "Categories", "Can't create Categories object in QML. Get them from Scope instance."); |
678 | qmlRegisterUncreatableType<scopes_ng::ResultsModel>(uri, 0, 2, "ResultsModel", "Can't create new ResultsModel in QML. Get them from Categories instance."); |
679 | qmlRegisterUncreatableType<unity::shell::scopes::PreviewModelInterface>(uri, 0, 2, "PreviewModel", "Can't create new PreviewModel in QML. Get them from PreviewStack instance."); |
680 | |
681 | === modified file 'src/Unity/scope.cpp' |
682 | --- src/Unity/scope.cpp 2014-05-22 08:03:59 +0000 |
683 | +++ src/Unity/scope.cpp 2014-06-03 15:51:24 +0000 |
684 | @@ -69,6 +69,9 @@ |
685 | , m_searchInProgress(false) |
686 | , m_resultsDirty(false) |
687 | , m_delayedClear(false) |
688 | + , m_hasDepartments(false) |
689 | + , m_searchController(new CollectionController) |
690 | + , m_activationController(new CollectionController) |
691 | { |
692 | m_categories = new Categories(this); |
693 | |
694 | @@ -86,24 +89,21 @@ |
695 | |
696 | Scope::~Scope() |
697 | { |
698 | - if (m_lastSearch) { |
699 | - std::dynamic_pointer_cast<ScopeDataReceiverBase>(m_lastSearch)->invalidate(); |
700 | - } |
701 | - if (m_lastActivation) { |
702 | - std::dynamic_pointer_cast<ScopeDataReceiverBase>(m_lastActivation)->invalidate(); |
703 | - } |
704 | } |
705 | |
706 | void Scope::processSearchChunk(PushEvent* pushEvent) |
707 | { |
708 | CollectorBase::Status status; |
709 | QList<std::shared_ptr<scopes::CategorisedResult>> results; |
710 | + scopes::Department::SPtr rootDepartment; |
711 | |
712 | - status = pushEvent->collectSearchResults(results); |
713 | + status = pushEvent->collectSearchResults(results, rootDepartment); |
714 | if (status == CollectorBase::Status::CANCELLED) { |
715 | return; |
716 | } |
717 | |
718 | + m_rootDepartment = rootDepartment; |
719 | + |
720 | if (m_cachedResults.empty()) { |
721 | m_cachedResults.swap(results); |
722 | } else { |
723 | @@ -213,10 +213,12 @@ |
724 | |
725 | QString scopeId(QString::fromStdString(query.scope_id())); |
726 | QString searchString(QString::fromStdString(query.query_string())); |
727 | + QString departmentId(QString::fromStdString(query.department_id())); |
728 | // figure out if this scope is already favourited |
729 | Scope* scope = scopes->getScopeById(scopeId); |
730 | if (scope != nullptr) { |
731 | - // TODO: change department, filters? |
732 | + // TODO: change filters? |
733 | + scope->setCurrentDepartmentId(departmentId); |
734 | scope->setSearchQuery(searchString); |
735 | Q_EMIT gotoScope(scopeId); |
736 | } else { |
737 | @@ -225,6 +227,7 @@ |
738 | if (meta_sptr) { |
739 | scope = new scopes_ng::Scope(this); |
740 | scope->setScopeData(*meta_sptr); |
741 | + scope->setCurrentDepartmentId(departmentId); |
742 | scope->setSearchQuery(searchString); |
743 | m_tempScopes.insert(scope); |
744 | Q_EMIT openScope(scope); |
745 | @@ -252,6 +255,85 @@ |
746 | } |
747 | |
748 | processResultSet(m_cachedResults); // clears the result list |
749 | + |
750 | + // process departments |
751 | + if (m_rootDepartment != m_lastRootDepartment) { |
752 | + // build / append to the tree |
753 | + DepartmentNode* node = nullptr; |
754 | + if (m_departmentTree) { |
755 | + QString departmentId(QString::fromStdString(m_rootDepartment->id())); |
756 | + node = m_departmentTree->findNodeById(departmentId); |
757 | + if (node == nullptr) { |
758 | + node = m_departmentTree.data(); |
759 | + } |
760 | + auto depNode = findDepartmentById(m_rootDepartment, m_currentDepartmentId.toStdString()); |
761 | + if (depNode && depNode->has_subdepartments()) { |
762 | + node->initializeForDepartment(m_rootDepartment); |
763 | + } |
764 | + // as far as we know, this is the root, re-initializing might have unset the flag |
765 | + m_departmentTree->setIsRoot(true); |
766 | + |
767 | + // update corresponding models |
768 | + QString activeDepartment(m_currentDepartmentId); |
769 | + node = m_departmentTree->findNodeById(activeDepartment); |
770 | + DepartmentNode* parentNode = nullptr; |
771 | + if (node != nullptr) { |
772 | + auto it = m_departmentModels.find(activeDepartment); |
773 | + while (it != m_departmentModels.end() && it.key() == activeDepartment) { |
774 | + it.value()->loadFromDepartmentNode(node); |
775 | + ++it; |
776 | + } |
777 | + // if this node is a leaf, we need to update models for the parent |
778 | + parentNode = node->isLeaf() ? node->parent() : nullptr; |
779 | + } |
780 | + if (parentNode != nullptr) { |
781 | + auto it = m_departmentModels.find(parentNode->id()); |
782 | + while (it != m_departmentModels.end() && it.key() == parentNode->id()) { |
783 | + it.value()->markSubdepartmentActive(activeDepartment); |
784 | + ++it; |
785 | + } |
786 | + } |
787 | + } else { |
788 | + m_departmentTree.reset(new DepartmentNode); |
789 | + m_departmentTree->initializeForDepartment(m_rootDepartment); |
790 | + // as far as we know, this is the root, changing our mind later |
791 | + // is better than pretending it isn't |
792 | + m_departmentTree->setIsRoot(true); |
793 | + } |
794 | + |
795 | + m_lastRootDepartment = m_rootDepartment; |
796 | + } |
797 | + |
798 | + bool containsDepartments = m_rootDepartment.get() != nullptr; |
799 | + // design decision - no departments when doing searches |
800 | + containsDepartments &= m_searchQuery.isEmpty(); |
801 | + |
802 | + if (containsDepartments != m_hasDepartments) { |
803 | + m_hasDepartments = containsDepartments; |
804 | + Q_EMIT hasDepartmentsChanged(); |
805 | + } |
806 | + |
807 | + if (!containsDepartments && !m_currentDepartmentId.isEmpty()) { |
808 | + m_currentDepartmentId = ""; |
809 | + Q_EMIT currentDepartmentIdChanged(); |
810 | + } |
811 | +} |
812 | + |
813 | +scopes::Department::SCPtr Scope::findDepartmentById(scopes::Department::SCPtr const& root, std::string const& id) |
814 | +{ |
815 | + if (root->id() == id) return root; |
816 | + |
817 | + auto sub_deps = root->subdepartments(); |
818 | + for (auto it = sub_deps.begin(); it != sub_deps.end(); ++it) { |
819 | + if ((*it)->id() == id) { |
820 | + return *it; |
821 | + } else { |
822 | + auto node = findDepartmentById(*it, id); |
823 | + if (node) return node; |
824 | + } |
825 | + } |
826 | + |
827 | + return nullptr; |
828 | } |
829 | |
830 | void Scope::processResultSet(QList<std::shared_ptr<scopes::CategorisedResult>>& result_set) |
831 | @@ -289,14 +371,7 @@ |
832 | |
833 | void Scope::invalidateLastSearch() |
834 | { |
835 | - if (m_lastSearch) { |
836 | - std::dynamic_pointer_cast<SearchResultReceiver>(m_lastSearch)->invalidate(); |
837 | - m_lastSearch.reset(); |
838 | - } |
839 | - if (m_lastSearchQuery) { |
840 | - m_lastSearchQuery->cancel(); |
841 | - m_lastSearchQuery.reset(); |
842 | - } |
843 | + m_searchController->invalidate(); |
844 | if (m_aggregatorTimer.isActive()) { |
845 | m_aggregatorTimer.stop(); |
846 | } |
847 | @@ -338,6 +413,14 @@ |
848 | } |
849 | } |
850 | |
851 | +void Scope::setCurrentDepartmentId(QString const& id) |
852 | +{ |
853 | + if (m_currentDepartmentId != id) { |
854 | + m_currentDepartmentId = id; |
855 | + Q_EMIT currentDepartmentIdChanged(); |
856 | + } |
857 | +} |
858 | + |
859 | void Scope::dispatchSearch() |
860 | { |
861 | invalidateLastSearch(); |
862 | @@ -364,7 +447,7 @@ |
863 | if (m_resultsDirty) |
864 | { |
865 | m_resultsDirty = false; |
866 | - resultsDirtyChanged(false); |
867 | + resultsDirtyChanged(); |
868 | } |
869 | |
870 | setSearchInProgress(true); |
871 | @@ -377,9 +460,11 @@ |
872 | meta["no-internet"] = true; |
873 | } |
874 | } |
875 | - m_lastSearch.reset(new SearchResultReceiver(this)); |
876 | + scopes::SearchListenerBase::SPtr listener(new SearchResultReceiver(this)); |
877 | + m_searchController->setListener(listener); |
878 | try { |
879 | - m_lastSearchQuery = m_proxy->search(m_searchQuery.toStdString(), meta, m_lastSearch); |
880 | + scopes::QueryCtrlProxy controller = m_proxy->search(m_searchQuery.toStdString(), m_currentDepartmentId.toStdString(), scopes::FilterState(), meta, listener); |
881 | + m_searchController->setController(controller); |
882 | } catch (std::exception& e) { |
883 | qWarning("Caught an error from create_query(): %s", e.what()); |
884 | } catch (...) { |
885 | @@ -387,7 +472,7 @@ |
886 | } |
887 | } |
888 | |
889 | - if (!m_lastSearchQuery) { |
890 | + if (!m_searchController->isValid()) { |
891 | // something went wrong, reset search state |
892 | setSearchInProgress(false); |
893 | } |
894 | @@ -470,6 +555,44 @@ |
895 | } |
896 | */ |
897 | |
898 | +unity::shell::scopes::DepartmentInterface* Scope::getDepartment(QString const& departmentId) |
899 | +{ |
900 | + if (!m_departmentTree) return nullptr; |
901 | + |
902 | + DepartmentNode* node = m_departmentTree->findNodeById(departmentId); |
903 | + if (!node) return nullptr; |
904 | + |
905 | + Department* departmentModel = new Department; |
906 | + departmentModel->loadFromDepartmentNode(node); |
907 | + |
908 | + m_departmentModels.insert(departmentId, departmentModel); |
909 | + m_inverseDepartments.insert(departmentModel, departmentId); |
910 | + QObject::connect(departmentModel, &QObject::destroyed, this, &Scope::departmentModelDestroyed); |
911 | + |
912 | + return departmentModel; |
913 | +} |
914 | + |
915 | +void Scope::departmentModelDestroyed(QObject* obj) |
916 | +{ |
917 | + scopes_ng::Department* department = reinterpret_cast<scopes_ng::Department*>(obj); |
918 | + |
919 | + auto it = m_inverseDepartments.find(department); |
920 | + if (it == m_inverseDepartments.end()) return; |
921 | + |
922 | + m_departmentModels.remove(it.value(), department); |
923 | + m_inverseDepartments.remove(department); |
924 | +} |
925 | + |
926 | +void Scope::loadDepartment(QString const& departmentId) |
927 | +{ |
928 | + if (departmentId != m_currentDepartmentId) { |
929 | + m_currentDepartmentId = departmentId; |
930 | + Q_EMIT currentDepartmentIdChanged(); |
931 | + |
932 | + dispatchSearch(); |
933 | + } |
934 | +} |
935 | + |
936 | QString Scope::searchQuery() const |
937 | { |
938 | return m_searchQuery; |
939 | @@ -490,6 +613,16 @@ |
940 | return m_isActive; |
941 | } |
942 | |
943 | +QString Scope::currentDepartmentId() const |
944 | +{ |
945 | + return m_currentDepartmentId; |
946 | +} |
947 | + |
948 | +bool Scope::hasDepartments() const |
949 | +{ |
950 | + return m_hasDepartments; |
951 | +} |
952 | + |
953 | void Scope::setSearchQuery(const QString& search_query) |
954 | { |
955 | /* Checking for m_searchQuery.isNull() which returns true only when the string |
956 | @@ -526,7 +659,7 @@ |
957 | void Scope::setActive(const bool active) { |
958 | if (active != m_isActive) { |
959 | m_isActive = active; |
960 | - Q_EMIT isActiveChanged(m_isActive); |
961 | + Q_EMIT isActiveChanged(); |
962 | |
963 | if (active && m_resultsDirty) { |
964 | dispatchSearch(); |
965 | @@ -551,11 +684,14 @@ |
966 | activateUri(QString::fromStdString(result->uri())); |
967 | } else { |
968 | try { |
969 | + cancelActivation(); |
970 | + scopes::ActivationListenerBase::SPtr listener(new ActivationReceiver(this, result)); |
971 | + m_activationController->setListener(listener); |
972 | + |
973 | auto proxy = result->target_scope_proxy(); |
974 | - // FIXME: don't block |
975 | unity::scopes::ActionMetadata metadata(QLocale::system().name().toStdString(), m_formFactor.toStdString()); |
976 | - m_lastActivation.reset(new ActivationReceiver(this, result)); |
977 | - proxy->activate(*(result.get()), metadata, m_lastActivation); |
978 | + scopes::QueryCtrlProxy controller = proxy->activate(*(result.get()), metadata, listener); |
979 | + m_activationController->setController(controller); |
980 | } catch (std::exception& e) { |
981 | qWarning("Caught an error from activate(): %s", e.what()); |
982 | } catch (...) { |
983 | @@ -585,10 +721,7 @@ |
984 | |
985 | void Scope::cancelActivation() |
986 | { |
987 | - if (m_lastActivation) { |
988 | - std::dynamic_pointer_cast<ScopeDataReceiverBase>(m_lastActivation)->invalidate(); |
989 | - m_lastActivation.reset(); |
990 | - } |
991 | + m_activationController->invalidate(); |
992 | } |
993 | |
994 | void Scope::invalidateResults() |
995 | @@ -600,7 +733,7 @@ |
996 | if (!m_resultsDirty) |
997 | { |
998 | m_resultsDirty = true; |
999 | - resultsDirtyChanged(true); |
1000 | + resultsDirtyChanged(); |
1001 | } |
1002 | } |
1003 | } |
1004 | |
1005 | === modified file 'src/Unity/scope.h' |
1006 | --- src/Unity/scope.h 2014-05-22 08:03:59 +0000 |
1007 | +++ src/Unity/scope.h 2014-06-03 15:51:24 +0000 |
1008 | @@ -26,6 +26,7 @@ |
1009 | #include <QTimer> |
1010 | #include <QMetaType> |
1011 | #include <QPointer> |
1012 | +#include <QMultiMap> |
1013 | #include <QSet> |
1014 | #include <QGSettings> |
1015 | |
1016 | @@ -36,6 +37,10 @@ |
1017 | #include <unity/scopes/ScopeMetadata.h> |
1018 | #include <unity/shell/scopes/ScopeInterface.h> |
1019 | |
1020 | +#include "collectors.h" |
1021 | +#include "departmentnode.h" |
1022 | +#include "department.h" |
1023 | + |
1024 | namespace scopes_ng |
1025 | { |
1026 | |
1027 | @@ -43,6 +48,54 @@ |
1028 | class PushEvent; |
1029 | class PreviewStack; |
1030 | |
1031 | +class CollectionController |
1032 | +{ |
1033 | +public: |
1034 | + CollectionController() {} |
1035 | + ~CollectionController() |
1036 | + { |
1037 | + if (m_receiver) { |
1038 | + m_receiver->invalidate(); |
1039 | + } |
1040 | + // shouldn't call QueryCtrlProxy->cancel() cause the Runtime might be |
1041 | + // in the process of being destroyed |
1042 | + } |
1043 | + |
1044 | + bool isValid() |
1045 | + { |
1046 | + return m_listener && m_controller; |
1047 | + } |
1048 | + |
1049 | + void invalidate() |
1050 | + { |
1051 | + if (m_receiver) { |
1052 | + m_receiver->invalidate(); |
1053 | + m_receiver.reset(); |
1054 | + } |
1055 | + m_listener.reset(); |
1056 | + if (m_controller) { |
1057 | + m_controller->cancel(); |
1058 | + m_controller.reset(); |
1059 | + } |
1060 | + } |
1061 | + |
1062 | + void setListener(unity::scopes::ListenerBase::SPtr const& listener) |
1063 | + { |
1064 | + m_listener = listener; |
1065 | + m_receiver = std::dynamic_pointer_cast<ScopeDataReceiverBase>(listener); |
1066 | + } |
1067 | + |
1068 | + void setController(unity::scopes::QueryCtrlProxy const& controller) |
1069 | + { |
1070 | + m_controller = controller; |
1071 | + } |
1072 | + |
1073 | +private: |
1074 | + unity::scopes::ListenerBase::SPtr m_listener; |
1075 | + std::shared_ptr<ScopeDataReceiverBase> m_receiver; |
1076 | + unity::scopes::QueryCtrlProxy m_controller; |
1077 | +}; |
1078 | + |
1079 | class Q_DECL_EXPORT Scope : public unity::shell::scopes::ScopeInterface |
1080 | { |
1081 | Q_OBJECT |
1082 | @@ -67,6 +120,8 @@ |
1083 | QString noResultsHint() const override; |
1084 | QString formFactor() const override; |
1085 | bool isActive() const override; |
1086 | + QString currentDepartmentId() const override; |
1087 | + bool hasDepartments() const override; |
1088 | |
1089 | /* setters */ |
1090 | void setSearchQuery(const QString& search_query) override; |
1091 | @@ -78,6 +133,8 @@ |
1092 | Q_INVOKABLE unity::shell::scopes::PreviewStackInterface* preview(QVariant const& result) override; |
1093 | Q_INVOKABLE void cancelActivation() override; |
1094 | Q_INVOKABLE void closeScope(unity::shell::scopes::ScopeInterface* scope) override; |
1095 | + Q_INVOKABLE unity::shell::scopes::DepartmentInterface* getDepartment(QString const& id) override; |
1096 | + Q_INVOKABLE void loadDepartment(QString const& id) override; |
1097 | |
1098 | void setScopeData(unity::scopes::ScopeMetadata const& data); |
1099 | void handleActivation(std::shared_ptr<unity::scopes::ActivationResponse> const&, unity::scopes::Result::SPtr const&); |
1100 | @@ -89,16 +146,18 @@ |
1101 | void invalidateResults(); |
1102 | |
1103 | Q_SIGNALS: |
1104 | - void resultsDirtyChanged(bool resultsDirty); |
1105 | + void resultsDirtyChanged(); |
1106 | |
1107 | private Q_SLOTS: |
1108 | void flushUpdates(); |
1109 | void metadataRefreshed(); |
1110 | void internetFlagChanged(QString const& key); |
1111 | + void departmentModelDestroyed(QObject* obj); |
1112 | |
1113 | private: |
1114 | void startTtlTimer(); |
1115 | void setSearchInProgress(bool searchInProgress); |
1116 | + void setCurrentDepartmentId(QString const& id); |
1117 | void processSearchChunk(PushEvent* pushEvent); |
1118 | void executeCannedQuery(unity::scopes::CannedQuery const& query, bool allowDelayedActivation); |
1119 | |
1120 | @@ -106,27 +165,35 @@ |
1121 | void dispatchSearch(); |
1122 | void invalidateLastSearch(); |
1123 | |
1124 | + static unity::scopes::Department::SCPtr findDepartmentById(unity::scopes::Department::SCPtr const& root, std::string const& id); |
1125 | + |
1126 | QString m_searchQuery; |
1127 | QString m_noResultsHint; |
1128 | QString m_formFactor; |
1129 | + QString m_currentDepartmentId; |
1130 | bool m_isActive; |
1131 | bool m_searchInProgress; |
1132 | bool m_resultsDirty; |
1133 | bool m_delayedClear; |
1134 | + bool m_hasDepartments; |
1135 | |
1136 | + std::unique_ptr<CollectionController> m_searchController; |
1137 | + std::unique_ptr<CollectionController> m_activationController; |
1138 | unity::scopes::ScopeProxy m_proxy; |
1139 | unity::scopes::ScopeMetadata::SPtr m_scopeMetadata; |
1140 | - unity::scopes::SearchListenerBase::SPtr m_lastSearch; |
1141 | - unity::scopes::QueryCtrlProxy m_lastSearchQuery; |
1142 | - unity::scopes::ActivationListenerBase::SPtr m_lastActivation; |
1143 | std::shared_ptr<unity::scopes::ActivationResponse> m_delayedActivation; |
1144 | + unity::scopes::Department::SPtr m_rootDepartment; |
1145 | + unity::scopes::Department::SPtr m_lastRootDepartment; |
1146 | QGSettings* m_settings; |
1147 | Categories* m_categories; |
1148 | + QSharedPointer<DepartmentNode> m_departmentTree; |
1149 | QTimer m_aggregatorTimer; |
1150 | QTimer m_clearTimer; |
1151 | QTimer m_invalidateTimer; |
1152 | QList<std::shared_ptr<unity::scopes::CategorisedResult>> m_cachedResults; |
1153 | QSet<unity::shell::scopes::ScopeInterface*> m_tempScopes; |
1154 | + QMultiMap<QString, Department*> m_departmentModels; |
1155 | + QMap<Department*, QString> m_inverseDepartments; |
1156 | }; |
1157 | |
1158 | } // namespace scopes_ng |
1159 | |
1160 | === modified file 'src/Unity/scopes.cpp' |
1161 | --- src/Unity/scopes.cpp 2014-05-19 09:58:14 +0000 |
1162 | +++ src/Unity/scopes.cpp 2014-06-03 15:51:24 +0000 |
1163 | @@ -162,7 +162,7 @@ |
1164 | endResetModel(); |
1165 | |
1166 | m_loaded = true; |
1167 | - Q_EMIT loadedChanged(m_loaded); |
1168 | + Q_EMIT loadedChanged(); |
1169 | Q_EMIT metadataRefreshed(); |
1170 | |
1171 | m_listThread = nullptr; |
1172 | |
1173 | === modified file 'tests/CMakeLists.txt' |
1174 | --- tests/CMakeLists.txt 2014-05-22 08:03:59 +0000 |
1175 | +++ tests/CMakeLists.txt 2014-06-03 15:51:24 +0000 |
1176 | @@ -48,6 +48,7 @@ |
1177 | endmacro(run_tests) |
1178 | |
1179 | run_tests( |
1180 | + departmentstest |
1181 | resultstest |
1182 | previewtest |
1183 | utilstest |
1184 | |
1185 | === modified file 'tests/data/CMakeLists.txt' |
1186 | --- tests/data/CMakeLists.txt 2014-05-02 15:26:40 +0000 |
1187 | +++ tests/data/CMakeLists.txt 2014-06-03 15:51:24 +0000 |
1188 | @@ -1,4 +1,5 @@ |
1189 | add_subdirectory(mock-scope) |
1190 | +add_subdirectory(mock-scope-departments) |
1191 | add_subdirectory(mock-scope-ttl) |
1192 | |
1193 | configure_file(Runtime.ini.in Runtime.ini @ONLY) |
1194 | |
1195 | === added directory 'tests/data/mock-scope-departments' |
1196 | === added file 'tests/data/mock-scope-departments/CMakeLists.txt' |
1197 | --- tests/data/mock-scope-departments/CMakeLists.txt 1970-01-01 00:00:00 +0000 |
1198 | +++ tests/data/mock-scope-departments/CMakeLists.txt 2014-06-03 15:51:24 +0000 |
1199 | @@ -0,0 +1,16 @@ |
1200 | +include(FindPkgConfig) |
1201 | +pkg_check_modules(SCOPESLIB REQUIRED libunity-scopes>=0.4.0) |
1202 | + |
1203 | +set(SCOPES_BIN_DIR ${SCOPESLIB_LIBDIR}) |
1204 | + |
1205 | +include_directories(${SCOPESLIB_INCLUDE_DIRS}) |
1206 | +include_directories(${CMAKE_CURRENT_BINARY_DIR}) |
1207 | + |
1208 | +set(SCOPE_SOURCES |
1209 | + mock-scope-departments.cpp |
1210 | + ) |
1211 | + |
1212 | +add_library(mock-scope-departments MODULE ${SCOPE_SOURCES}) |
1213 | +target_link_libraries(mock-scope-departments ${SCOPESLIB_LDFLAGS}) |
1214 | + |
1215 | +configure_file(mock-scope-departments.ini.in mock-scope-departments.ini) |
1216 | |
1217 | === added file 'tests/data/mock-scope-departments/mock-scope-departments.cpp' |
1218 | --- tests/data/mock-scope-departments/mock-scope-departments.cpp 1970-01-01 00:00:00 +0000 |
1219 | +++ tests/data/mock-scope-departments/mock-scope-departments.cpp 2014-06-03 15:51:24 +0000 |
1220 | @@ -0,0 +1,201 @@ |
1221 | +/* |
1222 | + * Copyright (C) 2014 Canonical Ltd |
1223 | + * |
1224 | + * This program is free software: you can redistribute it and/or modify |
1225 | + * it under the terms of the GNU Lesser General Public License version 3 as |
1226 | + * published by the Free Software Foundation. |
1227 | + * |
1228 | + * This program is distributed in the hope that it will be useful, |
1229 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1230 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1231 | + * GNU Lesser General Public License for more details. |
1232 | + * |
1233 | + * You should have received a copy of the GNU Lesser General Public License |
1234 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1235 | + * |
1236 | + * Authored by: Pete Woods <pete.woods@canonical.com> |
1237 | + */ |
1238 | + |
1239 | +#include <unity/scopes/CategorisedResult.h> |
1240 | +#include <unity/scopes/ScopeBase.h> |
1241 | +#include <unity/scopes/SearchReply.h> |
1242 | + |
1243 | +#include <iostream> |
1244 | +#include <thread> |
1245 | +#include <atomic> |
1246 | +#include <sstream> |
1247 | + |
1248 | +#define EXPORT __attribute__ ((visibility ("default"))) |
1249 | + |
1250 | +using namespace std; |
1251 | +using namespace unity::scopes; |
1252 | + |
1253 | +class MyQuery : public SearchQueryBase |
1254 | +{ |
1255 | +public: |
1256 | + MyQuery(CannedQuery const& query) : |
1257 | + query_(query), department_id_(query.department_id()) |
1258 | + { |
1259 | + } |
1260 | + |
1261 | + ~MyQuery() |
1262 | + { |
1263 | + } |
1264 | + |
1265 | + virtual void cancelled() override |
1266 | + { |
1267 | + } |
1268 | + |
1269 | + static Department::SPtr create_root_dep(CannedQuery const& query) |
1270 | + { |
1271 | + Department::SPtr child_dep; |
1272 | + Department::SPtr root_dep; |
1273 | + root_dep = Department::create("", query, "All departments"); |
1274 | + |
1275 | + child_dep = Department::create("books", query, "Books"); |
1276 | + child_dep->set_has_subdepartments(); |
1277 | + root_dep->add_subdepartment(child_dep); |
1278 | + |
1279 | + child_dep = Department::create("movies", query, "Movies, TV, Music"); |
1280 | + child_dep->set_has_subdepartments(); |
1281 | + root_dep->add_subdepartment(child_dep); |
1282 | + |
1283 | + child_dep = Department::create("electronics", query, "Electronics"); |
1284 | + child_dep->set_has_subdepartments(); |
1285 | + root_dep->add_subdepartment(child_dep); |
1286 | + |
1287 | + child_dep = Department::create("home", query, "Home, Garden & DIY"); |
1288 | + child_dep->set_has_subdepartments(); |
1289 | + root_dep->add_subdepartment(child_dep); |
1290 | + |
1291 | + child_dep = Department::create("toys", query, "Toys, Children & Baby"); |
1292 | + child_dep->set_has_subdepartments(); |
1293 | + root_dep->add_subdepartment(child_dep); |
1294 | + |
1295 | + return root_dep; |
1296 | + } |
1297 | + |
1298 | + static Department::SPtr get_department_by_id(Department::SPtr root_dep, std::string const& dep_id) |
1299 | + { |
1300 | + auto children = root_dep->subdepartments(); |
1301 | + for (auto it = children.begin(); it != children.end(); ++it) |
1302 | + { |
1303 | + if ((*it)->id() == dep_id) return const_pointer_cast<Department>(*it); |
1304 | + } |
1305 | + return Department::SPtr(); |
1306 | + } |
1307 | + |
1308 | + virtual void run(SearchReplyProxy const& reply) override |
1309 | + { |
1310 | + Department::SPtr child_dep; |
1311 | + Department::SPtr root_dep; |
1312 | + Department::SPtr active_dep; |
1313 | + |
1314 | + root_dep = create_root_dep(query_); |
1315 | + |
1316 | + if (department_id_.compare(0, 5, "books") == 0) |
1317 | + { |
1318 | + active_dep = get_department_by_id(root_dep, "books"); |
1319 | + child_dep = Department::create("books-kindle", query_, "Kindle Books"); |
1320 | + active_dep->add_subdepartment(child_dep); |
1321 | + |
1322 | + child_dep = Department::create("books-study", query_, "Books for Study"); |
1323 | + active_dep->add_subdepartment(child_dep); |
1324 | + |
1325 | + child_dep = Department::create("books-audio", query_, "Audiobooks"); |
1326 | + active_dep->add_subdepartment(child_dep); |
1327 | + } |
1328 | + |
1329 | + if (department_id_.compare(0, 4, "home") == 0) |
1330 | + { |
1331 | + active_dep = get_department_by_id(root_dep, "home"); |
1332 | + child_dep = Department::create("home-garden", query_, "Garden & Outdoors"); |
1333 | + active_dep->add_subdepartment(child_dep); |
1334 | + |
1335 | + child_dep = Department::create("home-furniture", query_, "Homeware & Furniture"); |
1336 | + active_dep->add_subdepartment(child_dep); |
1337 | + |
1338 | + child_dep = Department::create("home-kitchen", query_, "Kitchen & Dining"); |
1339 | + active_dep->add_subdepartment(child_dep); |
1340 | + } |
1341 | + |
1342 | + if (department_id_.compare(0, 4, "toys") == 0) |
1343 | + { |
1344 | + active_dep = get_department_by_id(root_dep, "toys"); |
1345 | + child_dep = Department::create("toys-games", query_, "Toys & Games"); |
1346 | + active_dep->add_subdepartment(child_dep); |
1347 | + |
1348 | + child_dep = Department::create("toys-baby", query_, "Baby"); |
1349 | + active_dep->add_subdepartment(child_dep); |
1350 | + } |
1351 | + |
1352 | + // provide only partial tree for this leaf |
1353 | + if (department_id_ == "toys-games") |
1354 | + { |
1355 | + root_dep = Department::create("", query_, "All departments"); |
1356 | + child_dep = Department::create("toys", query_, "Toys, Children & Baby"); |
1357 | + root_dep->add_subdepartment(child_dep); |
1358 | + active_dep = Department::create("toys-games", query_, "Toys & Games"); |
1359 | + child_dep->add_subdepartment(active_dep); |
1360 | + } |
1361 | + |
1362 | + reply->register_departments(root_dep); |
1363 | + |
1364 | + auto cat1 = reply->register_category("cat1", "Category 1", ""); |
1365 | + CategorisedResult res1(cat1); |
1366 | + res1.set_uri("test:uri"); |
1367 | + res1.set_title("result for: \"" + query_.query_string() + "\""); |
1368 | + reply->push(res1); |
1369 | + } |
1370 | + |
1371 | +protected: |
1372 | + CannedQuery query_; |
1373 | + string department_id_; |
1374 | +}; |
1375 | + |
1376 | +class MyScope : public ScopeBase |
1377 | +{ |
1378 | +public: |
1379 | + MyScope() |
1380 | + { |
1381 | + } |
1382 | + |
1383 | + virtual int start(string const&, RegistryProxy const&) override |
1384 | + { |
1385 | + return VERSION; |
1386 | + } |
1387 | + |
1388 | + virtual void stop() override { |
1389 | + } |
1390 | + |
1391 | + virtual SearchQueryBase::UPtr search(CannedQuery const& q, SearchMetadata const&) override |
1392 | + { |
1393 | + return SearchQueryBase::UPtr(new MyQuery(q)); |
1394 | + } |
1395 | + |
1396 | + virtual PreviewQueryBase::UPtr preview(Result const&, ActionMetadata const&) override |
1397 | + { |
1398 | + return nullptr; |
1399 | + } |
1400 | +}; |
1401 | + |
1402 | +extern "C" |
1403 | +{ |
1404 | + |
1405 | + EXPORT |
1406 | + unity::scopes::ScopeBase* |
1407 | + // cppcheck-suppress unusedFunction |
1408 | + UNITY_SCOPE_CREATE_FUNCTION() |
1409 | + { |
1410 | + return new MyScope; |
1411 | + } |
1412 | + |
1413 | + EXPORT |
1414 | + void |
1415 | + // cppcheck-suppress unusedFunction |
1416 | + UNITY_SCOPE_DESTROY_FUNCTION(unity::scopes::ScopeBase* scope_base) |
1417 | + { |
1418 | + delete scope_base; |
1419 | + } |
1420 | + |
1421 | +} |
1422 | |
1423 | === added file 'tests/data/mock-scope-departments/mock-scope-departments.ini.in' |
1424 | --- tests/data/mock-scope-departments/mock-scope-departments.ini.in 1970-01-01 00:00:00 +0000 |
1425 | +++ tests/data/mock-scope-departments/mock-scope-departments.ini.in 2014-06-03 15:51:24 +0000 |
1426 | @@ -0,0 +1,8 @@ |
1427 | +[ScopeConfig] |
1428 | +DisplayName = mock-departments.DisplayName |
1429 | +Description = mock-departments.Description |
1430 | +Art = /mock-departments.Art |
1431 | +Icon = /mock-departments.Icon |
1432 | +SearchHint = mock-departments.SearchHint |
1433 | +HotKey = mock-departments.HotKey |
1434 | +Author = mock-departments.Author |
1435 | |
1436 | === modified file 'tests/data/mock-scope/CMakeLists.txt' |
1437 | --- tests/data/mock-scope/CMakeLists.txt 2014-05-12 13:13:59 +0000 |
1438 | +++ tests/data/mock-scope/CMakeLists.txt 2014-06-03 15:51:24 +0000 |
1439 | @@ -1,5 +1,5 @@ |
1440 | include(FindPkgConfig) |
1441 | -pkg_check_modules(SCOPESLIB REQUIRED libunity-scopes>=0.4.0) |
1442 | +pkg_check_modules(SCOPESLIB REQUIRED libunity-scopes>=0.4.8) |
1443 | |
1444 | include_directories(${SCOPESLIB_INCLUDE_DIRS}) |
1445 | |
1446 | |
1447 | === modified file 'tests/data/mock-scope/mock-scope.cpp' |
1448 | --- tests/data/mock-scope/mock-scope.cpp 2014-04-30 17:22:57 +0000 |
1449 | +++ tests/data/mock-scope/mock-scope.cpp 2014-06-03 15:51:24 +0000 |
1450 | @@ -31,8 +31,8 @@ |
1451 | class MyQuery : public SearchQueryBase |
1452 | { |
1453 | public: |
1454 | - MyQuery(string const& query) : |
1455 | - query_(query) |
1456 | + MyQuery(string const& query, string const& department_id) : |
1457 | + query_(query), department_id_(department_id) |
1458 | { |
1459 | } |
1460 | |
1461 | @@ -217,6 +217,7 @@ |
1462 | |
1463 | private: |
1464 | string query_; |
1465 | + string department_id_; |
1466 | }; |
1467 | |
1468 | class MyPreview : public PreviewQueryBase |
1469 | @@ -351,7 +352,7 @@ |
1470 | |
1471 | virtual SearchQueryBase::UPtr search(CannedQuery const& q, SearchMetadata const&) override |
1472 | { |
1473 | - SearchQueryBase::UPtr query(new MyQuery(q.query_string())); |
1474 | + SearchQueryBase::UPtr query(new MyQuery(q.query_string(), q.department_id())); |
1475 | cout << "scope-A: created query: \"" << q.query_string() << "\"" << endl; |
1476 | return query; |
1477 | } |
1478 | |
1479 | === added file 'tests/departmentstest.cpp' |
1480 | --- tests/departmentstest.cpp 1970-01-01 00:00:00 +0000 |
1481 | +++ tests/departmentstest.cpp 2014-06-03 15:51:24 +0000 |
1482 | @@ -0,0 +1,236 @@ |
1483 | +/* |
1484 | + * Copyright (C) 2013-2014 Canonical, Ltd. |
1485 | + * |
1486 | + * This program is free software; you can redistribute it and/or modify |
1487 | + * it under the terms of the GNU General Public License as published by |
1488 | + * the Free Software Foundation; version 3. |
1489 | + * |
1490 | + * This program is distributed in the hope that it will be useful, |
1491 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1492 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1493 | + * GNU General Public License for more details. |
1494 | + * |
1495 | + * You should have received a copy of the GNU General Public License |
1496 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1497 | + * |
1498 | + * Authors: |
1499 | + * Michal Hruby <michal.hruby@canonical.com> |
1500 | + */ |
1501 | + |
1502 | +#include <QObject> |
1503 | +#include <QTest> |
1504 | +#include <QJsonValue> |
1505 | +#include <QJsonObject> |
1506 | +#include <QThread> |
1507 | +#include <QScopedPointer> |
1508 | +#include <QSignalSpy> |
1509 | +#include <QVariantList> |
1510 | +#include <QDBusConnection> |
1511 | + |
1512 | +#include <scopes.h> |
1513 | +#include <scope.h> |
1514 | +#include <categories.h> |
1515 | +#include <resultsmodel.h> |
1516 | +#include <previewmodel.h> |
1517 | +#include <previewstack.h> |
1518 | +#include <previewwidgetmodel.h> |
1519 | +#include <department.h> |
1520 | + |
1521 | +#include "registry-spawner.h" |
1522 | +#include "test-utils.h" |
1523 | + |
1524 | +using namespace scopes_ng; |
1525 | +using namespace unity::shell::scopes; |
1526 | + |
1527 | +class DepartmentsTest : public QObject |
1528 | +{ |
1529 | + Q_OBJECT |
1530 | +private: |
1531 | + QScopedPointer<Scopes> m_scopes; |
1532 | + Scope* m_scope; |
1533 | + QScopedPointer<RegistrySpawner> m_registry; |
1534 | + |
1535 | +private Q_SLOTS: |
1536 | + void initTestCase() |
1537 | + { |
1538 | + m_registry.reset(new RegistrySpawner); |
1539 | + } |
1540 | + |
1541 | + void cleanupTestCase() |
1542 | + { |
1543 | + m_registry.reset(); |
1544 | + } |
1545 | + |
1546 | + void init() |
1547 | + { |
1548 | + m_scopes.reset(new Scopes(nullptr)); |
1549 | + // no scopes on startup |
1550 | + QCOMPARE(m_scopes->rowCount(), 0); |
1551 | + QCOMPARE(m_scopes->loaded(), false); |
1552 | + QSignalSpy spy(m_scopes.data(), SIGNAL(loadedChanged())); |
1553 | + // wait till the registry spawns |
1554 | + QVERIFY(spy.wait()); |
1555 | + QCOMPARE(m_scopes->loaded(), true); |
1556 | + // should have one scope now |
1557 | + QVERIFY(m_scopes->rowCount() > 1); |
1558 | + |
1559 | + // get scope proxy |
1560 | + m_scope = qobject_cast<scopes_ng::Scope*>(m_scopes->getScope(QString("mock-scope-departments"))); |
1561 | + QVERIFY(m_scope != nullptr); |
1562 | + } |
1563 | + |
1564 | + void cleanup() |
1565 | + { |
1566 | + m_scopes.reset(); |
1567 | + m_scope = nullptr; |
1568 | + } |
1569 | + |
1570 | + void testNoDepartments() |
1571 | + { |
1572 | + performSearch(m_scope, QString("foo")); |
1573 | + |
1574 | + QCOMPARE(m_scope->hasDepartments(), false); |
1575 | + } |
1576 | + |
1577 | + void testRootDepartment() |
1578 | + { |
1579 | + performSearch(m_scope, QString("")); |
1580 | + |
1581 | + QCOMPARE(m_scope->hasDepartments(), true); |
1582 | + QCOMPARE(m_scope->currentDepartmentId(), QString("")); |
1583 | + QScopedPointer<DepartmentInterface> departmentModel(m_scope->getDepartment(m_scope->currentDepartmentId())); |
1584 | + QVERIFY(departmentModel != nullptr); |
1585 | + |
1586 | + QVERIFY(departmentModel->departmentId().isEmpty()); |
1587 | + QCOMPARE(departmentModel->label(), QString("All departments")); |
1588 | + QCOMPARE(departmentModel->allLabel(), QString("")); |
1589 | + QCOMPARE(departmentModel->parentDepartmentId(), QString()); |
1590 | + QCOMPARE(departmentModel->parentLabel(), QString()); |
1591 | + QCOMPARE(departmentModel->loaded(), true); |
1592 | + QCOMPARE(departmentModel->isRoot(), true); |
1593 | + |
1594 | + QCOMPARE(departmentModel->rowCount(), 5); |
1595 | + QModelIndex idx; |
1596 | + |
1597 | + idx = departmentModel->index(0); |
1598 | + QCOMPARE(departmentModel->data(idx, Department::Roles::RoleDepartmentId), QVariant(QString("books"))); |
1599 | + QCOMPARE(departmentModel->data(idx, Department::Roles::RoleLabel), QVariant(QString("Books"))); |
1600 | + QCOMPARE(departmentModel->data(idx, Department::Roles::RoleHasChildren), QVariant(true)); |
1601 | + QCOMPARE(departmentModel->data(idx, Department::Roles::RoleIsActive), QVariant(false)); |
1602 | + |
1603 | + idx = departmentModel->index(4); |
1604 | + QCOMPARE(departmentModel->data(idx, Department::Roles::RoleDepartmentId), QVariant(QString("toys"))); |
1605 | + QCOMPARE(departmentModel->data(idx, Department::Roles::RoleLabel), QVariant(QString("Toys, Children & Baby"))); |
1606 | + QCOMPARE(departmentModel->data(idx, Department::Roles::RoleHasChildren), QVariant(true)); |
1607 | + QCOMPARE(departmentModel->data(idx, Department::Roles::RoleIsActive), QVariant(false)); |
1608 | + } |
1609 | + |
1610 | + void testChildDepartmentModel() |
1611 | + { |
1612 | + performSearch(m_scope, QString("")); |
1613 | + |
1614 | + QCOMPARE(m_scope->currentDepartmentId(), QString("")); |
1615 | + QScopedPointer<DepartmentInterface> departmentModel(m_scope->getDepartment(QString("toys"))); |
1616 | + QVERIFY(departmentModel != nullptr); |
1617 | + |
1618 | + QSignalSpy spy(departmentModel.data(), SIGNAL(loadedChanged())); |
1619 | + |
1620 | + QCOMPARE(departmentModel->departmentId(), QString("toys")); |
1621 | + QCOMPARE(departmentModel->label(), QString("Toys, Children & Baby")); |
1622 | + QCOMPARE(departmentModel->allLabel(), QString("")); |
1623 | + QCOMPARE(departmentModel->parentDepartmentId(), QString("")); |
1624 | + QCOMPARE(departmentModel->parentLabel(), QString("All departments")); |
1625 | + QCOMPARE(departmentModel->loaded(), false); |
1626 | + QCOMPARE(departmentModel->isRoot(), false); |
1627 | + |
1628 | + QCOMPARE(departmentModel->rowCount(), 0); |
1629 | + |
1630 | + m_scope->loadDepartment(QString("toys")); |
1631 | + QVERIFY(spy.wait()); |
1632 | + |
1633 | + QCOMPARE(departmentModel->rowCount(), 2); |
1634 | + QCOMPARE(departmentModel->loaded(), true); |
1635 | + QCOMPARE(departmentModel->isRoot(), false); |
1636 | + } |
1637 | + |
1638 | + void testLeafActivationUpdatesModel() |
1639 | + { |
1640 | + performSearch(m_scope, QString("")); |
1641 | + |
1642 | + QCOMPARE(m_scope->currentDepartmentId(), QString("")); |
1643 | + QSignalSpy spy(m_scope, SIGNAL(searchInProgressChanged())); |
1644 | + m_scope->loadDepartment(QString("books")); |
1645 | + QVERIFY(spy.wait()); |
1646 | + QCOMPARE(m_scope->searchInProgress(), false); |
1647 | + QScopedPointer<DepartmentInterface> departmentModel(m_scope->getDepartment(QString("books"))); |
1648 | + QCOMPARE(departmentModel->isRoot(), false); |
1649 | + |
1650 | + // this is a leaf department, so activating it should update the parent model |
1651 | + m_scope->loadDepartment(QString("books-audio")); |
1652 | + QVERIFY(spy.wait()); |
1653 | + QCOMPARE(m_scope->searchInProgress(), false); |
1654 | + QCOMPARE(departmentModel->isRoot(), false); |
1655 | + |
1656 | + bool foundAudiobooks = false; |
1657 | + for (int i = 0; i < departmentModel->rowCount(); i++) { |
1658 | + QModelIndex idx(departmentModel->index(i)); |
1659 | + QVariant data = departmentModel->data(idx, Department::Roles::RoleDepartmentId); |
1660 | + if (data.toString() == QString("books-audio")) { |
1661 | + QCOMPARE(departmentModel->data(idx, Department::Roles::RoleIsActive).toBool(), true); |
1662 | + foundAudiobooks = true; |
1663 | + } |
1664 | + } |
1665 | + QCOMPARE(foundAudiobooks, true); |
1666 | + } |
1667 | + |
1668 | + void testGoingBack() |
1669 | + { |
1670 | + performSearch(m_scope, QString("")); |
1671 | + |
1672 | + QCOMPARE(m_scope->currentDepartmentId(), QString("")); |
1673 | + QSignalSpy spy(m_scope, SIGNAL(searchInProgressChanged())); |
1674 | + m_scope->loadDepartment(QString("books")); |
1675 | + QVERIFY(spy.wait()); |
1676 | + QCOMPARE(m_scope->searchInProgress(), false); |
1677 | + QScopedPointer<DepartmentInterface> departmentModel(m_scope->getDepartment(QString("books"))); |
1678 | + QCOMPARE(departmentModel->isRoot(), false); |
1679 | + |
1680 | + // get the root again without actually loading the department |
1681 | + departmentModel.reset(m_scope->getDepartment(departmentModel->parentDepartmentId())); |
1682 | + QCOMPARE(departmentModel->isRoot(), true); |
1683 | + QEXPECT_FAIL("", "We have the department in cache, to it kind of is loaded", Continue); |
1684 | + QCOMPARE(departmentModel->loaded(), false); |
1685 | + } |
1686 | + |
1687 | + void testIncompleteTreeOnLeaf() |
1688 | + { |
1689 | + QScopedPointer<DepartmentInterface> departmentModel; |
1690 | + performSearch(m_scope, QString("")); |
1691 | + |
1692 | + QCOMPARE(m_scope->currentDepartmentId(), QString("")); |
1693 | + QCOMPARE(m_scope->hasDepartments(), true); |
1694 | + |
1695 | + QSignalSpy spy(m_scope, SIGNAL(searchInProgressChanged())); |
1696 | + m_scope->loadDepartment(QString("toys")); |
1697 | + QVERIFY(spy.wait()); |
1698 | + QCOMPARE(m_scope->searchInProgress(), false); |
1699 | + |
1700 | + departmentModel.reset(m_scope->getDepartment(QString("toys"))); |
1701 | + QCOMPARE(departmentModel->isRoot(), false); |
1702 | + QCOMPARE(departmentModel->rowCount(), 2); |
1703 | + |
1704 | + m_scope->loadDepartment(QString("toys-games")); |
1705 | + QVERIFY(spy.wait()); |
1706 | + QCOMPARE(m_scope->searchInProgress(), false); |
1707 | + |
1708 | + // after getting the parent department model, it should still have |
1709 | + // all the leaves, even though the leaf served just itself |
1710 | + departmentModel.reset(m_scope->getDepartment(QString("toys"))); |
1711 | + QCOMPARE(departmentModel->isRoot(), false); |
1712 | + QCOMPARE(departmentModel->rowCount(), 2); |
1713 | + } |
1714 | + |
1715 | +}; |
1716 | + |
1717 | +QTEST_GUILESS_MAIN(DepartmentsTest) |
1718 | +#include <departmentstest.moc> |
1719 | |
1720 | === modified file 'tests/previewtest.cpp' |
1721 | --- tests/previewtest.cpp 2014-05-22 08:03:59 +0000 |
1722 | +++ tests/previewtest.cpp 2014-06-03 15:51:24 +0000 |
1723 | @@ -62,18 +62,19 @@ |
1724 | // no scopes on startup |
1725 | QCOMPARE(m_scopes->rowCount(), 0); |
1726 | QCOMPARE(m_scopes->loaded(), false); |
1727 | - QSignalSpy spy(m_scopes.data(), SIGNAL(loadedChanged(bool))); |
1728 | + QSignalSpy spy(m_scopes.data(), SIGNAL(loadedChanged())); |
1729 | // wait till the registry spawns |
1730 | QVERIFY(spy.wait()); |
1731 | QCOMPARE(m_scopes->loaded(), true); |
1732 | // should have one scope now |
1733 | - QCOMPARE(m_scopes->rowCount(), 2); |
1734 | + QCOMPARE(m_scopes->rowCount(), 3); |
1735 | |
1736 | QVariant scope_var = m_scopes->data(m_scopes->index(0), Scopes::Roles::RoleScope); |
1737 | QVERIFY(scope_var.canConvert<Scope*>()); |
1738 | |
1739 | // get scope proxy |
1740 | m_scope = scope_var.value<Scope*>(); |
1741 | + QCOMPARE(m_scope->id(), QString("mock-scope")); |
1742 | } |
1743 | |
1744 | void cleanup() |
1745 | |
1746 | === modified file 'tests/resultstest.cpp' |
1747 | --- tests/resultstest.cpp 2014-05-22 08:03:59 +0000 |
1748 | +++ tests/resultstest.cpp 2014-06-03 15:51:24 +0000 |
1749 | @@ -107,22 +107,20 @@ |
1750 | // no scopes on startup |
1751 | QCOMPARE(m_scopes->rowCount(), 0); |
1752 | QCOMPARE(m_scopes->loaded(), false); |
1753 | - QSignalSpy spy(m_scopes.data(), SIGNAL(loadedChanged(bool))); |
1754 | + QSignalSpy spy(m_scopes.data(), SIGNAL(loadedChanged())); |
1755 | // wait till the registry spawns |
1756 | QVERIFY(spy.wait()); |
1757 | QCOMPARE(m_scopes->loaded(), true); |
1758 | // should have one scope now |
1759 | - QCOMPARE(m_scopes->rowCount(), 2); |
1760 | + QCOMPARE(m_scopes->rowCount(), 3); |
1761 | |
1762 | // get scope proxy |
1763 | - QVariant scope_var = m_scopes->data(m_scopes->index(0), Scopes::Roles::RoleScope); |
1764 | - QVERIFY(scope_var.canConvert<Scope*>()); |
1765 | - m_scope = scope_var.value<Scope*>(); |
1766 | + m_scope = qobject_cast<scopes_ng::Scope*>(m_scopes->getScope(QString("mock-scope"))); |
1767 | + QVERIFY(m_scope != nullptr); |
1768 | |
1769 | // get scope proxy for TTL scope |
1770 | - scope_var = m_scopes->data(m_scopes->index(1), Scopes::Roles::RoleScope); |
1771 | - QVERIFY(scope_var.canConvert<Scope*>()); |
1772 | - m_scope_ttl = scope_var.value<Scope*>(); |
1773 | + m_scope_ttl = qobject_cast<scopes_ng::Scope*>(m_scopes->getScope(QString("mock-scope-ttl"))); |
1774 | + QVERIFY(m_scope != nullptr); |
1775 | } |
1776 | |
1777 | void cleanup() |
1778 | @@ -307,7 +305,7 @@ |
1779 | |
1780 | void testInactiveTtlScope() |
1781 | { |
1782 | - QSignalSpy dirtySpy(m_scope_ttl, SIGNAL(resultsDirtyChanged(bool))); |
1783 | + QSignalSpy dirtySpy(m_scope_ttl, SIGNAL(resultsDirtyChanged())); |
1784 | |
1785 | m_scope_ttl->setActive(false); |
1786 | performSearch(m_scope_ttl, "banana"); |
1787 | @@ -318,10 +316,6 @@ |
1788 | } |
1789 | |
1790 | // Model should go dirty |
1791 | - QList<QVariantList> expected; |
1792 | - expected << (QVariantList() << true); |
1793 | - QVERIFY(dirtySpy == expected); |
1794 | - |
1795 | QVERIFY(m_scope_ttl->resultsDirty()); |
1796 | } |
1797 | |
1798 | @@ -623,7 +617,7 @@ |
1799 | |
1800 | QSignalSpy spy(m_scopes.data(), SIGNAL(metadataRefreshed())); |
1801 | QSignalSpy spy2(m_scope, SIGNAL(gotoScope(QString))); |
1802 | - QSignalSpy spy3(m_scope, SIGNAL(openScope(scopes_ng::Scope*))); |
1803 | + QSignalSpy spy3(m_scope, SIGNAL(openScope(unity::shell::scopes::ScopeInterface*))); |
1804 | // this tries to activate non-existing scope |
1805 | m_scope->activate(QVariant::fromValue(result)); |
1806 | QVERIFY(spy.wait()); |
FAILED: Continuous integration, rev:123 jenkins. qa.ubuntu. com/job/ unity-scopes- shell-ci/ 124/ jenkins. qa.ubuntu. com/job/ unity-scopes- shell-utopic- amd64-ci/ 21/console jenkins. qa.ubuntu. com/job/ unity-scopes- shell-utopic- armhf-ci/ 21/console jenkins. qa.ubuntu. com/job/ unity-scopes- shell-utopic- i386-ci/ 21/console
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/unity- scopes- shell-ci/ 124/rebuild
http://