on 5bc8421 help sign in
unity8/tests/mocks/Unity/fake_categories.cpp
253 lines 8.5 kB history raw
1/*
2 * Copyright (C) 2013 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Author: Nick Dedekind <nick.dedekind@canonical.com>
17 */
19// self
20#include "fake_categories.h"
22// local
23#include "categoryresults.h"
25#define CATEGORY_COLUMN 2
27// TODO: use something from libunity once it's public
28enum CategoryColumn {
29 ID,
30 DISPLAY_NAME,
31 ICON_HINT,
32 RENDERER_NAME,
33 HINTS
34};
36Categories::Categories(QObject* parent)
37 : DeeListModel(parent)
39 // FIXME: need to clean up unused filters on countChanged
40 m_roles[Categories::RoleCategoryId] = "categoryId";
41 m_roles[Categories::RoleName] = "name";
42 m_roles[Categories::RoleIcon] = "icon";
43 m_roles[Categories::RoleRenderer] = "renderer";
44 m_roles[Categories::RoleContentType] = "contentType";
45 m_roles[Categories::RoleHints] = "hints";
46 m_roles[Categories::RoleResults] = "results";
47 m_roles[Categories::RoleCount] = "count";
49 // TODO This should not be needed but accumulatting the count changes
50 // makes the visualization more stable and also makes crashes on fast
51 // change of the search term harder to reproduce
52 m_timer.setSingleShot(true);
53 m_timer.setInterval(50);
54 connect(&m_timer, SIGNAL(timeout()), this, SLOT(onEmitCountChanged()));
57DeeListModel*
58Categories::getFilter(int index) const
60 if (!m_filters.contains(index)) {
61 auto results = new CategoryResults ();
62 results->setCategoryIndex(index);
63 connect(results, SIGNAL(countChanged()), this, SLOT(onCountChanged()));
65 unsigned cat_index = static_cast<unsigned>(index);
66 auto model = getResultsForCategory(cat_index);
67 results->setModel(model);
69 m_filters.insert(index, results);
70 }
72 return m_filters[index];
75void
76Categories::onCountChanged()
78 DeeListModel* filter = qobject_cast<DeeListModel*>(sender());
79 if (filter) {
80 m_timerFilters << filter;
81 m_timer.start();
82 }
85void Categories::onRowCountChanged()
87 QAbstractItemModel* model = qobject_cast<QAbstractItemModel*>(sender());
88 // find the corresponding category index
89 for (auto iter = m_overriddenCategories.begin(); iter != m_overriddenCategories.end(); ++iter) {
90 if (iter.value() == model) {
91 for (int i = 0; i < rowCount(); i++) {
92 auto id = data(index(i), RoleCategoryId).toString();
93 if (id != iter.key()) continue;
94 QVector<int> roles;
95 roles.append(RoleCount);
96 QModelIndex changedIndex = index(i);
97 Q_EMIT dataChanged(changedIndex, changedIndex, roles);
98 break;
99 }
100 }
101 }
104void
105Categories::onEmitCountChanged()
107 QVector<int> roles;
108 roles.append(Categories::RoleCount);
109 Q_FOREACH(DeeListModel* results, m_timerFilters) {
110 auto cat_results = qobject_cast<CategoryResults*>(results);
111 QModelIndex changedIndex = index(cat_results->categoryIndex());
112 Q_EMIT dataChanged(changedIndex, changedIndex, roles);
113 }
114 m_timerFilters.clear();
117QHash<int, QByteArray>
118Categories::roleNames() const
120 return m_roles;
123void Categories::onOverrideModelDestroyed()
125 QObject* model = sender();
126 auto iter = m_overriddenCategories.begin();
127 while (iter != m_overriddenCategories.end()) {
128 if (iter.value() == model) {
129 iter = m_overriddenCategories.erase(iter);
130 continue;
131 }
132 ++iter;
133 }
136void Categories::overrideResults(const QString& categoryId, QAbstractItemModel* model)
138 m_overriddenCategories[categoryId] = model;
139 // watch the model
140 connect(model, &QObject::destroyed, this, &Categories::onOverrideModelDestroyed);
141 connect(model, &QAbstractItemModel::rowsInserted, this, &Categories::onRowCountChanged);
142 connect(model, &QAbstractItemModel::rowsRemoved, this, &Categories::onRowCountChanged);
143 connect(model, &QAbstractItemModel::modelReset, this, &Categories::onRowCountChanged);
145 // emit the dataChanged signal if the category is already in the model
146 for (int i = 0; i < rowCount(); i++) {
147 auto id = data(index(i), RoleCategoryId).toString();
148 if (id != categoryId) continue;
149 QVector<int> roles;
150 roles.append(RoleCount);
151 roles.append(RoleResults);
152 QModelIndex changedIndex = index(i);
153 Q_EMIT dataChanged(changedIndex, changedIndex, roles);
154 break;
155 }
158void Categories::setResultModel(DeeModel* model)
160 // FIXME: should ref it
161 m_dee_results = model;
165static void category_filter_map_func (DeeModel* orig_model,
166 DeeFilterModel* filter_model,
167 gpointer user_data)
169 DeeModelIter* iter;
170 DeeModelIter* end;
171 unsigned index = GPOINTER_TO_UINT(user_data);
173 iter = dee_model_get_first_iter(orig_model);
174 end = dee_model_get_last_iter(orig_model);
175 while (iter != end) {
176 unsigned category_index = dee_model_get_uint32(orig_model, iter, CATEGORY_COLUMN);
177 if (index == category_index) {
178 dee_filter_model_append_iter(filter_model, iter);
179 }
180 iter = dee_model_next(orig_model, iter);
181 }
184static gboolean category_filter_notify_func (DeeModel* orig_model,
185 DeeModelIter* orig_iter,
186 DeeFilterModel* filter_model,
187 gpointer user_data)
189 unsigned index = GPOINTER_TO_UINT(user_data);
190 unsigned category_index = dee_model_get_uint32(orig_model, orig_iter, CATEGORY_COLUMN);
192 if (index != category_index)
193 return FALSE;
195 dee_filter_model_insert_iter_with_original_order(filter_model, orig_iter);
196 return TRUE;
199DeeModel* Categories::getResultsForCategory(unsigned cat_index) const
201 DeeFilter filter;
202 filter.map_func = category_filter_map_func;
203 filter.map_notify = category_filter_notify_func;
204 filter.destroy = nullptr;
205 filter.userdata = GUINT_TO_POINTER(cat_index);
207 DeeModel* filtered_model = dee_filter_model_new(m_dee_results, &filter);
208 return filtered_model;
211QVariant
212Categories::data(const QModelIndex& index, int role) const
214 if (!index.isValid()) {
215 return QVariant();
216 }
218 switch (role) {
219 case RoleCategoryId:
220 return DeeListModel::data(index, CategoryColumn::ID);
221 case RoleName:
222 return DeeListModel::data(index, CategoryColumn::DISPLAY_NAME);
223 case RoleIcon:
224 return DeeListModel::data(index, CategoryColumn::ICON_HINT);
225 case RoleRenderer:
226 return DeeListModel::data(index, CategoryColumn::RENDERER_NAME);
227 case RoleContentType:
228 {
229 auto hints = DeeListModel::data(index, CategoryColumn::HINTS).toHash();
230 return hints.contains("content-type") ? hints["content-type"] : QVariant(QString("default"));
231 }
232 case RoleHints:
233 return DeeListModel::data(index, CategoryColumn::HINTS);
234 case RoleResults:
235 if (m_overriddenCategories.size() > 0)
236 {
237 auto id = DeeListModel::data(index, CategoryColumn::ID).toString();
238 if (m_overriddenCategories.find(id) != m_overriddenCategories.end())
239 return QVariant::fromValue(m_overriddenCategories[id]);
240 }
241 return QVariant::fromValue(getFilter(index.row()));
242 case RoleCount:
243 if (m_overriddenCategories.size() > 0)
244 {
245 auto id = DeeListModel::data(index, CategoryColumn::ID).toString();
246 if (m_overriddenCategories.find(id) != m_overriddenCategories.end())
247 return QVariant::fromValue(m_overriddenCategories[id]->rowCount());
248 }
249 return QVariant::fromValue(getFilter(index.row())->rowCount());
250 default:
251 return QVariant();
252 }