Merge lp:~stolowski/unity-scopes-shell/favoriting-fixes into lp:unity-scopes-shell

Proposed by Paweł Stołowski
Status: Merged
Approved by: Marcus Tomlinson
Approved revision: 341
Merged at revision: 346
Proposed branch: lp:~stolowski/unity-scopes-shell/favoriting-fixes
Merge into: lp:unity-scopes-shell
Diff against target: 626 lines (+309/-126)
7 files modified
src/Unity/CMakeLists.txt (+1/-0)
src/Unity/favorites.cpp (+160/-0)
src/Unity/favorites.h (+63/-0)
src/Unity/scope.cpp (+4/-4)
src/Unity/scope.h (+2/-2)
src/Unity/scopes.cpp (+76/-118)
src/Unity/scopes.h (+3/-2)
To merge this branch: bzr merge lp:~stolowski/unity-scopes-shell/favoriting-fixes
Reviewer Review Type Date Requested Status
Marcus Tomlinson (community) Approve
Review via email: mp+302253@code.launchpad.net

Commit message

Optimize scope favoriting. Make sure click scope is favorited back if reinstalled.

Description of the change

Optimize and refactor scope favoriting. Make sure click scope is favorited back if reinstalled.

The optimization is mostly achieved by temporarily disabling gesttings signals when storing an updated list of favorites.

To post a comment you must log in.
Revision history for this message
Marcus Tomlinson (marcustomlinson) wrote :

Outstanding work Pawel. +1

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/Unity/CMakeLists.txt'
2--- src/Unity/CMakeLists.txt 2016-06-02 08:49:55 +0000
3+++ src/Unity/CMakeLists.txt 2016-08-10 07:21:23 +0000
4@@ -20,6 +20,7 @@
5 collectors.cpp
6 department.cpp
7 departmentnode.cpp
8+ favorites.cpp
9 filters.cpp
10 filtergroupwidget.cpp
11 optionselectorfilter.cpp
12
13=== added file 'src/Unity/favorites.cpp'
14--- src/Unity/favorites.cpp 1970-01-01 00:00:00 +0000
15+++ src/Unity/favorites.cpp 2016-08-10 07:21:23 +0000
16@@ -0,0 +1,160 @@
17+/*
18+ * Copyright (C) 2016 Canonical, Ltd.
19+ *
20+ * Authors:
21+ * Pawel Stolowski <pawel.stolowski@canonical.com>
22+ *
23+ * This program is free software; you can redistribute it and/or modify
24+ * it under the terms of the GNU General Public License as published by
25+ * the Free Software Foundation; version 3.
26+ *
27+ * This program is distributed in the hope that it will be useful,
28+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
29+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
30+ * GNU General Public License for more details.
31+ *
32+ * You should have received a copy of the GNU General Public License
33+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
34+ */
35+
36+#include <QGSettings>
37+#include <QVariant>
38+#include <QDebug>
39+#include <unity/scopes/CannedQuery.h>
40+#include <unity/UnityExceptions.h>
41+#include <algorithm>
42+#include "favorites.h"
43+
44+namespace scopes_ng
45+{
46+
47+Favorites::Favorites(QObject *parent, QGSettings *dashSettings)
48+ : QObject(parent),
49+ m_dashSettings(dashSettings)
50+{
51+ if (m_dashSettings) {
52+ readFavoritesFromGSettings();
53+ QObject::connect(m_dashSettings, &QGSettings::changed, this, &Favorites::dashSettingsChanged);
54+ }
55+}
56+
57+void Favorites::readFavoritesFromGSettings()
58+{
59+ m_favoriteScopes.clear();
60+ m_positionLookup.clear();
61+
62+ int pos = 0 ;
63+ auto const favs = m_dashSettings->get(QStringLiteral("favoriteScopes")).toList();
64+ for (auto const fv: favs) {
65+ try
66+ {
67+ auto const query = unity::scopes::CannedQuery::from_uri(fv.toString().toStdString());
68+ auto scopeId = QString::fromStdString(query.scope_id());
69+ m_favoriteScopes.append(scopeId);
70+ m_positionLookup[scopeId] = pos++;
71+ }
72+ catch (const unity::InvalidArgumentException &e)
73+ {
74+ qWarning() << "Invalid canned query '" << fv.toString() << "'" << QString::fromStdString(e.what());
75+ }
76+ }
77+}
78+
79+Favorites::~Favorites()
80+{
81+}
82+
83+int Favorites::setFavorite(QString const& scopeId, bool value)
84+{
85+ if (!value) {
86+ int pos = position(scopeId);
87+ if (pos >= 0) {
88+ m_favoriteScopes.removeAt(pos);
89+ m_positionLookup.remove(scopeId);
90+ for (int i = pos; i<m_favoriteScopes.size(); i++) {
91+ m_positionLookup[m_favoriteScopes[i]] = i;
92+ }
93+ Q_ASSERT(m_favoriteScopes.size() == m_positionLookup.size());
94+ storeFavorites();
95+ return pos;
96+ }
97+ } else {
98+ int pos = position(scopeId);
99+ if (pos < 0) {
100+ m_favoriteScopes.push_back(scopeId);
101+ pos = m_favoriteScopes.size() - 1;
102+ m_positionLookup[scopeId] = pos;
103+ }
104+ Q_ASSERT(m_favoriteScopes.size() == m_positionLookup.size());
105+ storeFavorites();
106+ return pos;
107+ }
108+
109+ return -1;
110+}
111+
112+void Favorites::moveFavoriteTo(QString const& scopeId, int pos)
113+{
114+ int oldPos = position(scopeId);
115+ if (oldPos >= 0) {
116+ m_favoriteScopes.move(oldPos, pos);
117+ auto const range = std::minmax(oldPos, pos);
118+ for (int i = range.first; i<=range.second; i++) {
119+ m_positionLookup[m_favoriteScopes[i]] = i;
120+ }
121+ } else {
122+ qWarning() << "Favorites::moveFavoriteTo: no such scope" << scopeId;
123+ }
124+
125+ storeFavorites();
126+
127+ Q_ASSERT(m_favoriteScopes.size() == m_positionLookup.size());
128+}
129+
130+QStringList Favorites::getFavorites()
131+{
132+ return m_favoriteScopes;
133+}
134+
135+bool Favorites::hasScope(QString const& scopeId) const
136+{
137+ return m_positionLookup.find(scopeId) != m_positionLookup.end();
138+}
139+
140+int Favorites::position(QString const& scopeId) const
141+{
142+ auto it = m_positionLookup.find(scopeId);
143+ if (it != m_positionLookup.end()) {
144+ return it.value();
145+ }
146+ return -1;
147+}
148+
149+void Favorites::dashSettingsChanged(QString const &key)
150+{
151+ if (key != QLatin1String("favoriteScopes")) {
152+ return;
153+ }
154+ readFavoritesFromGSettings();
155+ Q_EMIT favoritesChanged();
156+}
157+
158+void Favorites::storeFavorites()
159+{
160+ if (m_dashSettings) {
161+ QStringList cannedQueries;
162+ for (auto const& fav: m_favoriteScopes)
163+ {
164+ const QString query = "scope://" + fav;
165+ cannedQueries.push_back(query);
166+ }
167+
168+ QObject::disconnect(m_dashSettings, &QGSettings::changed, this, &Favorites::dashSettingsChanged);
169+ m_dashSettings->set(QStringLiteral("favoriteScopes"), QVariant(cannedQueries));
170+ QObject::connect(m_dashSettings, &QGSettings::changed, this, &Favorites::dashSettingsChanged);
171+ }
172+}
173+
174+} // namespace scopes_ng
175+
176+#include <favorites.moc>
177
178=== added file 'src/Unity/favorites.h'
179--- src/Unity/favorites.h 1970-01-01 00:00:00 +0000
180+++ src/Unity/favorites.h 2016-08-10 07:21:23 +0000
181@@ -0,0 +1,63 @@
182+/*
183+ * Copyright (C) 2016 Canonical, Ltd.
184+ *
185+ * Authors:
186+ * Pawel Stolowski <pawel.stolowski@canonical.com>
187+ *
188+ * This program is free software; you can redistribute it and/or modify
189+ * it under the terms of the GNU General Public License as published by
190+ * the Free Software Foundation; version 3.
191+ *
192+ * This program is distributed in the hope that it will be useful,
193+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
194+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
195+ * GNU General Public License for more details.
196+ *
197+ * You should have received a copy of the GNU General Public License
198+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
199+ */
200+
201+#ifndef NG_FAVORITES_H
202+#define NG_FAVORITES_H
203+
204+#include <QStringList>
205+#include <QObject>
206+#include <QPointer>
207+#include <QMap>
208+
209+class QGSettings;
210+
211+namespace scopes_ng
212+{
213+
214+class Q_DECL_EXPORT Favorites : public QObject
215+{
216+ Q_OBJECT
217+public:
218+ Favorites(QObject *parent, QGSettings *dashSettings);
219+ ~Favorites();
220+
221+ int setFavorite(QString const& scopeId, bool value);
222+ void moveFavoriteTo(QString const& scopeId, int pos);
223+ bool hasScope(QString const& scopeId) const;
224+ int position(QString const& scopeId) const;
225+ QStringList getFavorites();
226+ void storeFavorites();
227+
228+Q_SIGNALS:
229+ void favoritesChanged();
230+
231+private Q_SLOTS:
232+ void dashSettingsChanged(QString const &key);
233+
234+private:
235+ void readFavoritesFromGSettings();
236+
237+ QPointer<QGSettings> m_dashSettings;
238+ QStringList m_favoriteScopes;
239+ QMap<QString, int> m_positionLookup;
240+};
241+
242+} // namespace scopes_ng
243+
244+#endif // NG_SCOPES_H
245
246=== modified file 'src/Unity/scope.cpp'
247--- src/Unity/scope.cpp 2016-06-24 14:03:59 +0000
248+++ src/Unity/scope.cpp 2016-08-10 07:21:23 +0000
249@@ -75,13 +75,13 @@
250 const int RESULTS_TTL_LARGE = 3600000; // 1 hour
251 const int SEARCH_CARDINALITY = 300; // maximum number of results accepted from a single scope
252
253-Scope::Ptr Scope::newInstance(scopes_ng::Scopes* parent)
254+Scope::Ptr Scope::newInstance(scopes_ng::Scopes* parent, bool favorite)
255 {
256- auto scope = Scope::Ptr(new Scope(parent), &QObject::deleteLater);
257+ auto scope = Scope::Ptr(new Scope(parent, favorite), &QObject::deleteLater);
258 return scope;
259 }
260
261-Scope::Scope(scopes_ng::Scopes* parent) :
262+Scope::Scope(scopes_ng::Scopes* parent, bool favorite) :
263 m_query_id(0)
264 , m_formFactor(QStringLiteral("phone"))
265 , m_activeFiltersCount(0)
266@@ -91,7 +91,7 @@
267 , m_resultsDirty(false)
268 , m_delayedSearchProcessing(false)
269 , m_hasNavigation(false)
270- , m_favorite(false)
271+ , m_favorite(favorite)
272 , m_initialQueryDone(false)
273 , m_childScopesDirty(true)
274 , m_searchController(new CollectionController)
275
276=== modified file 'src/Unity/scope.h'
277--- src/Unity/scope.h 2016-06-23 09:27:45 +0000
278+++ src/Unity/scope.h 2016-08-10 07:21:23 +0000
279@@ -110,7 +110,7 @@
280 public:
281 typedef QSharedPointer<Scope> Ptr;
282
283- static Scope::Ptr newInstance(scopes_ng::Scopes* parent);
284+ static Scope::Ptr newInstance(scopes_ng::Scopes* parent, bool favorite = false);
285
286 virtual ~Scope();
287
288@@ -205,7 +205,7 @@
289 void previewModelDestroyed(QObject *obj);
290
291 protected:
292- explicit Scope(scopes_ng::Scopes* parent);
293+ explicit Scope(scopes_ng::Scopes* parent, bool favorite = false);
294
295 void setStatus(unity::shell::scopes::ScopeInterface::Status status);
296 void invalidateLastSearch();
297
298=== modified file 'src/Unity/scopes.cpp'
299--- src/Unity/scopes.cpp 2016-06-23 12:53:21 +0000
300+++ src/Unity/scopes.cpp 2016-08-10 07:21:23 +0000
301@@ -24,6 +24,7 @@
302 #include "scope.h"
303 #include "overviewscope.h"
304 #include "ubuntulocationservice.h"
305+#include "favorites.h"
306
307 // Qt
308 #include <QDebug>
309@@ -126,10 +127,8 @@
310 QDBusConnection::sessionBus().connect(QString(), QStringLiteral("/com/canonical/unity/scopes"), QStringLiteral("com.canonical.unity.scopes"), QStringLiteral("InvalidateResults"), this, SLOT(invalidateScopeResults(QString)));
311
312 m_dashSettings = QGSettings::isSchemaInstalled("com.canonical.Unity.Dash") ? new QGSettings("com.canonical.Unity.Dash", QByteArray(), this) : nullptr;
313- if (m_dashSettings)
314- {
315- QObject::connect(m_dashSettings, &QGSettings::changed, this, &Scopes::dashSettingsChanged);
316- }
317+ m_favoriteScopes = new Favorites(this, m_dashSettings);
318+ QObject::connect(m_favoriteScopes, &Favorites::favoritesChanged, this, &Scopes::favoritesChanged);
319
320 m_overviewScope = OverviewScope::newInstance(this);
321
322@@ -427,57 +426,41 @@
323
324 void Scopes::processFavoriteScopes()
325 {
326+ qDebug() << "Scopes::processFavoriteScopes()";
327+
328 if (m_noFavorites) {
329 return;
330 }
331
332 //
333- // read the favoriteScopes array value from gsettings.
334- // process it and turn its values into scope ids.
335 // create new Scope objects or remove existing according to the list of favorities.
336 // notify about scopes model changes accordingly.
337 if (m_dashSettings) {
338- QStringList newFavorites;
339- QMap<QString, int> favScopesLut;
340- for (auto const& fv: m_dashSettings->get(QStringLiteral("favoriteScopes")).toList())
341+ for (auto const& fv: m_favoriteScopes->getFavorites())
342 {
343- int pos = 0;
344- try
345- {
346- auto const query = unity::scopes::CannedQuery::from_uri(fv.toString().toStdString());
347- const QString id = QString::fromStdString(query.scope_id());
348-
349- if (m_cachedMetadata.find(id) != m_cachedMetadata.end())
350- {
351- newFavorites.push_back(id);
352- pos = newFavorites.size() - 1;
353- favScopesLut[id] = pos;
354- }
355- else
356- {
357- // If a scope that was favorited no longer exists, unfavorite it in m_dashSettings
358- setFavorite(id, false);
359- }
360- }
361- catch (const InvalidArgumentException &e)
362- {
363- qWarning() << "Invalid canned query '" << fv.toString() << "'" << QString::fromStdString(e.what());
364- }
365- }
366-
367- // this prevents further processing if we get called back when calling scope->setFavorite() below
368- if (m_favoriteScopes == newFavorites)
369- return;
370-
371- m_favoriteScopes = newFavorites;
372-
373- QSet<QString> oldScopes;
374+ // favorited scope not installed?
375+ if (m_cachedMetadata.find(fv) == m_cachedMetadata.end())
376+ {
377+ qDebug() << "Favorited scope" << fv << "is no longer available, un-favoriting";
378+ m_favoriteScopes->setFavorite(fv, false);
379+ }
380+ }
381+
382+ // special-case clickscope; append it to favorites if it was uninstalled (and in consequence removed from favorites) - see LP: #1603186
383+ if (m_cachedMetadata.contains(CLICK_SCOPE_ID) && !m_favoriteScopes->hasScope(CLICK_SCOPE_ID)) {
384+ qDebug() << "Favoriting" << CLICK_SCOPE_ID;
385+ m_favoriteScopes->setFavorite(CLICK_SCOPE_ID, true);
386+ }
387+
388+ QSet<QString> scopesInTheModel;
389 int row = 0;
390 // remove un-favorited scopes
391 for (auto it = m_scopes.begin(); it != m_scopes.end();)
392 {
393- if (!favScopesLut.contains((*it)->id()))
394+ if (!m_favoriteScopes->hasScope((*it)->id()))
395 {
396+ qDebug() << "Scope" << (*it)->id() << "is no longer favorited, removing";
397+
398 beginRemoveRows(QModelIndex(), row, row);
399 Scope::Ptr toDelete = *it;
400 toDelete->setFavorite(false);
401@@ -490,7 +473,7 @@
402 }
403 else
404 {
405- oldScopes.insert((*it)->id());
406+ scopesInTheModel.insert((*it)->id());
407 ++it;
408 ++row;
409 }
410@@ -498,41 +481,35 @@
411
412 // add new favorites
413 row = 0;
414- for (auto favIt = m_favoriteScopes.begin(); favIt != m_favoriteScopes.end(); )
415+ for (auto const& fv: m_favoriteScopes->getFavorites())
416 {
417- auto const fav = *favIt;
418- if (!oldScopes.contains(fav))
419+ if (!scopesInTheModel.contains(fv))
420 {
421- auto it = m_cachedMetadata.find(fav);
422+ auto it = m_cachedMetadata.find(fv);
423 if (it != m_cachedMetadata.end())
424 {
425- Scope::Ptr scope = Scope::newInstance(this);
426+ qDebug() << "Scope" << fv << "is favorited, adding to scopes model";
427+
428+ Scope::Ptr scope = Scope::newInstance(this, true);
429 connect(scope.data(), SIGNAL(isActiveChanged()), this, SLOT(prepopulateNextScopes()));
430 scope->setScopeData(*(it.value()));
431- scope->setFavorite(true);
432 beginInsertRows(QModelIndex(), row, row);
433 m_scopes.insert(row, scope);
434 endInsertRows();
435 }
436- else
437- {
438- qWarning() << "No such scope:" << fav;
439- favIt = m_favoriteScopes.erase(favIt);
440- continue;
441- }
442 }
443 ++row;
444- ++favIt;
445 }
446
447- // iterate over results, move rows if positions changes
448+ // iterate over results, move rows if positions changed
449 for (int i = 0; i<m_scopes.size(); )
450 {
451 auto scope = m_scopes.at(i);
452 const QString id = scope->id();
453- if (favScopesLut.contains(id)) {
454- int pos = favScopesLut[id];
455+ int pos = m_favoriteScopes->position(id);
456+ if (pos >= 0) {
457 if (pos != i) {
458+ qDebug() << "Moving scope" << id << "to row" << pos << "to match position in favorites";
459 beginMoveRows(QModelIndex(), i, i, QModelIndex(), pos + (pos > i ? 1 : 0));
460 m_scopes.move(i, pos);
461 endMoveRows();
462@@ -544,17 +521,13 @@
463 }
464 }
465
466-void Scopes::dashSettingsChanged(QString const& key)
467+void Scopes::favoritesChanged()
468 {
469- if (key != QLatin1String("favoriteScopes")) {
470- return;
471- }
472-
473 processFavoriteScopes();
474
475 if (m_overviewScope)
476 {
477- m_overviewScope->updateFavorites(m_favoriteScopes);
478+ m_overviewScope->updateFavorites(m_favoriteScopes->getFavorites());
479 }
480 }
481
482@@ -675,7 +648,7 @@
483
484 QStringList Scopes::getFavoriteIds() const
485 {
486- return m_favoriteScopes;
487+ return m_favoriteScopes->getFavorites();
488 }
489
490 void Scopes::setFavorite(QString const& scopeId, bool value)
491@@ -685,32 +658,39 @@
492 qWarning() << "Cannot unfavorite" << scopeId;
493 return;
494 }
495- if (m_dashSettings)
496+
497+ int row = m_favoriteScopes->setFavorite(scopeId, value);
498+ if (row >= 0)
499 {
500- QStringList cannedQueries;
501- bool changed = false;
502-
503- for (auto const& fav: m_favoriteScopes)
504- {
505- if (value == false && fav == scopeId) {
506- changed = true;
507- continue; // skip it
508- }
509- // TODO: use CannedQuery::to_uri() when we really support them
510- const QString query = "scope://" + fav;
511- cannedQueries.push_back(query);
512- }
513-
514- if (value && !m_favoriteScopes.contains(scopeId)) {
515- const QString query = "scope://" + scopeId;
516- cannedQueries.push_back(query);
517- changed = true;
518- }
519-
520- if (changed) {
521- // update gsettings entry
522- // note: this will trigger notification, so that new favorites are processed by processFavoriteScopes
523- m_dashSettings->set(QStringLiteral("favoriteScopes"), QVariant(cannedQueries));
524+ if (value) {
525+ auto it = m_cachedMetadata.find(scopeId);
526+ if (it != m_cachedMetadata.end())
527+ {
528+ Scope::Ptr scope = Scope::newInstance(this, true);
529+ connect(scope.data(), SIGNAL(isActiveChanged()), this, SLOT(prepopulateNextScopes()));
530+ scope->setScopeData(*(it.value()));
531+ beginInsertRows(QModelIndex(), row, row);
532+ m_scopes.insert(row, scope);
533+ endInsertRows();
534+ } else {
535+ qWarning() << "setFavorite: unknown scope" << scopeId;
536+ }
537+ } else {
538+ for (auto it = m_scopes.begin(); it != m_scopes.end(); it++)
539+ {
540+ if ((*it)->id() == scopeId) {
541+ beginRemoveRows(QModelIndex(), row, row);
542+ Scope::Ptr toDelete = *it;
543+ toDelete->setFavorite(false);
544+ // we need to delay actual deletion of Scope object so that shell can animate it
545+ m_scopesToDelete.push_back(toDelete);
546+ // if the timer is already active, we just wait a bit longer, which is no problem
547+ m_scopesToDeleteTimer.start();
548+ it = m_scopes.erase(it);
549+ endRemoveRows();
550+ break;
551+ }
552+ }
553 }
554 }
555 }
556@@ -737,34 +717,12 @@
557
558 void Scopes::moveFavoriteTo(QString const& scopeId, int index)
559 {
560- if (m_dashSettings)
561- {
562- QStringList cannedQueries;
563- bool found = false;
564-
565- int i = 0;
566- for (auto const& fav: m_favoriteScopes)
567- {
568- if (fav == scopeId) {
569- if (index == i)
570- return; // same position
571- found = true;
572- } else {
573- const QString query = "scope://" + fav;
574- cannedQueries.push_back(query);
575- }
576-
577- ++i;
578- }
579-
580- if (found) {
581- // insert scopeId at new position
582- const QString query = "scope://" + scopeId;
583- cannedQueries.insert(index, query);
584- // update gsettings entry
585- // note: this will trigger notification, so that new favorites are processed by processFavoriteScopes
586- m_dashSettings->set(QStringLiteral("favoriteScopes"), QVariant(cannedQueries));
587- }
588+ int oldPos = m_favoriteScopes->position(scopeId);
589+ if (oldPos != index) {
590+ m_favoriteScopes->moveFavoriteTo(scopeId, index);
591+ beginMoveRows(QModelIndex(), oldPos, oldPos, QModelIndex(), index + (index > oldPos ? 1 : 0));
592+ m_scopes.move(oldPos, index);
593+ endMoveRows();
594 }
595 }
596
597
598=== modified file 'src/Unity/scopes.h'
599--- src/Unity/scopes.h 2016-06-10 13:41:37 +0000
600+++ src/Unity/scopes.h 2016-08-10 07:21:23 +0000
601@@ -47,6 +47,7 @@
602 class UbuntuLocationService;
603 class LocationAccessHelper;
604 class Scope;
605+class Favorites;
606 class OverviewScope;
607
608 class Q_DECL_EXPORT Scopes : public unity::shell::scopes::ScopesInterface
609@@ -92,7 +93,7 @@
610 virtual QString readPartnerId();
611
612 private Q_SLOTS:
613- void dashSettingsChanged(QString const &key);
614+ void favoritesChanged();
615 void processFavoriteScopes();
616 void populateScopes();
617 void discoveryFinished();
618@@ -117,7 +118,7 @@
619 QList<QSharedPointer<Scope>> m_scopes;
620 QList<QSharedPointer<Scope>> m_scopesToDelete;
621 bool m_noFavorites;
622- QStringList m_favoriteScopes;
623+ Favorites* m_favoriteScopes;
624 QGSettings* m_dashSettings;
625 QMap<QString, unity::scopes::ScopeMetadata::SPtr> m_cachedMetadata;
626 QSharedPointer<OverviewScope> m_overviewScope;

Subscribers

People subscribed via source and target branches

to all changes: