on 1663e5c help sign in
unity8/tests/mocks/Unity/fake_categories.cpp
259 lines 8.8 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::RoleRendererHint] = "rendererHint";
46 m_roles[Categories::RoleHints] = "hints";
47 m_roles[Categories::RoleResults] = "results";
48 m_roles[Categories::RoleCount] = "count";
50 // TODO This should not be needed but accumulatting the count changes
51 // makes the visualization more stable and also makes crashes on fast
52 // change of the search term harder to reproduce
53 m_timer.setSingleShot(true);
54 m_timer.setInterval(50);
55 connect(&m_timer, SIGNAL(timeout()), this, SLOT(onEmitCountChanged()));
58DeeListModel*
59Categories::getFilter(int index) const
61 if (!m_filters.contains(index)) {
62 auto results = new CategoryResults ();
63 results->setCategoryIndex(index);
64 connect(results, SIGNAL(countChanged()), this, SLOT(onCountChanged()));
66 unsigned cat_index = static_cast<unsigned>(index);
67 auto model = getResultsForCategory(cat_index);
68 results->setModel(model);
70 m_filters.insert(index, results);
71 }
73 return m_filters[index];
76void
77Categories::onCountChanged()
79 DeeListModel* filter = qobject_cast<DeeListModel*>(sender());
80 if (filter) {
81 m_timerFilters << filter;
82 m_timer.start();
83 }
86void Categories::onRowCountChanged()
88 QAbstractItemModel* model = qobject_cast<QAbstractItemModel*>(sender());
89 // find the corresponding category index
90 for (auto iter = m_overriddenCategories.begin(); iter != m_overriddenCategories.end(); ++iter) {
91 if (iter.value() == model) {
92 for (int i = 0; i < rowCount(); i++) {
93 auto id = data(index(i), RoleCategoryId).toString();
94 if (id != iter.key()) continue;
95 QVector<int> roles;
96 roles.append(RoleCount);
97 QModelIndex changedIndex = index(i);
98 Q_EMIT dataChanged(changedIndex, changedIndex, roles);
99 break;
100 }
101 }
102 }
105void
106Categories::onEmitCountChanged()
108 QVector<int> roles;
109 roles.append(Categories::RoleCount);
110 Q_FOREACH(DeeListModel* results, m_timerFilters) {
111 auto cat_results = qobject_cast<CategoryResults*>(results);
112 QModelIndex changedIndex = index(cat_results->categoryIndex());
113 Q_EMIT dataChanged(changedIndex, changedIndex, roles);
114 }
115 m_timerFilters.clear();
118QHash<int, QByteArray>
119Categories::roleNames() const
121 return m_roles;
124void Categories::onOverrideModelDestroyed()
126 QObject* model = sender();
127 auto iter = m_overriddenCategories.begin();
128 while (iter != m_overriddenCategories.end()) {
129 if (iter.value() == model) {
130 iter = m_overriddenCategories.erase(iter);
131 continue;
132 }
133 ++iter;
134 }
137void Categories::overrideResults(const QString& categoryId, QAbstractItemModel* model)
139 m_overriddenCategories[categoryId] = model;
140 // watch the model
141 connect(model, &QObject::destroyed, this, &Categories::onOverrideModelDestroyed);
142 connect(model, &QAbstractItemModel::rowsInserted, this, &Categories::onRowCountChanged);
143 connect(model, &QAbstractItemModel::rowsRemoved, this, &Categories::onRowCountChanged);
144 connect(model, &QAbstractItemModel::modelReset, this, &Categories::onRowCountChanged);
146 // emit the dataChanged signal if the category is already in the model
147 for (int i = 0; i < rowCount(); i++) {
148 auto id = data(index(i), RoleCategoryId).toString();
149 if (id != categoryId) continue;
150 QVector<int> roles;
151 roles.append(RoleCount);
152 roles.append(RoleResults);
153 QModelIndex changedIndex = index(i);
154 Q_EMIT dataChanged(changedIndex, changedIndex, roles);
155 break;
156 }
159void Categories::setResultModel(DeeModel* model)
161 // FIXME: should ref it
162 m_dee_results = model;
166static void category_filter_map_func (DeeModel* orig_model,
167 DeeFilterModel* filter_model,
168 gpointer user_data)
170 DeeModelIter* iter;
171 DeeModelIter* end;
172 unsigned index = GPOINTER_TO_UINT(user_data);
174 iter = dee_model_get_first_iter(orig_model);
175 end = dee_model_get_last_iter(orig_model);
176 while (iter != end) {
177 unsigned category_index = dee_model_get_uint32(orig_model, iter, CATEGORY_COLUMN);
178 if (index == category_index) {
179 dee_filter_model_append_iter(filter_model, iter);
180 }
181 iter = dee_model_next(orig_model, iter);
182 }
185static gboolean category_filter_notify_func (DeeModel* orig_model,
186 DeeModelIter* orig_iter,
187 DeeFilterModel* filter_model,
188 gpointer user_data)
190 unsigned index = GPOINTER_TO_UINT(user_data);
191 unsigned category_index = dee_model_get_uint32(orig_model, orig_iter, CATEGORY_COLUMN);
193 if (index != category_index)
194 return FALSE;
196 dee_filter_model_insert_iter_with_original_order(filter_model, orig_iter);
197 return TRUE;
200DeeModel* Categories::getResultsForCategory(unsigned cat_index) const
202 DeeFilter filter;
203 filter.map_func = category_filter_map_func;
204 filter.map_notify = category_filter_notify_func;
205 filter.destroy = nullptr;
206 filter.userdata = GUINT_TO_POINTER(cat_index);
208 DeeModel* filtered_model = dee_filter_model_new(m_dee_results, &filter);
209 return filtered_model;
212QVariant
213Categories::data(const QModelIndex& index, int role) const
215 if (!index.isValid()) {
216 return QVariant();
217 }
219 switch (role) {
220 case RoleCategoryId:
221 return DeeListModel::data(index, CategoryColumn::ID);
222 case RoleName:
223 return DeeListModel::data(index, CategoryColumn::DISPLAY_NAME);
224 case RoleIcon:
225 return DeeListModel::data(index, CategoryColumn::ICON_HINT);
226 case RoleRenderer:
227 return DeeListModel::data(index, CategoryColumn::RENDERER_NAME);
228 case RoleContentType:
229 {
230 auto hints = DeeListModel::data(index, CategoryColumn::HINTS).toHash();
231 return hints.contains("content-type") ? hints["content-type"] : QVariant(QString("default"));
232 }
233 case RoleRendererHint:
234 {
235 auto hints = DeeListModel::data(index, CategoryColumn::HINTS).toHash();
236 return hints.contains("renderer-hint") ? hints["renderer-hint"] : QVariant(QString());
237 }
238 case RoleHints:
239 return DeeListModel::data(index, CategoryColumn::HINTS);
240 case RoleResults:
241 if (m_overriddenCategories.size() > 0)
242 {
243 auto id = DeeListModel::data(index, CategoryColumn::ID).toString();
244 if (m_overriddenCategories.find(id) != m_overriddenCategories.end())
245 return QVariant::fromValue(m_overriddenCategories[id]);
246 }
247 return QVariant::fromValue(getFilter(index.row()));
248 case RoleCount:
249 if (m_overriddenCategories.size() > 0)
250 {
251 auto id = DeeListModel::data(index, CategoryColumn::ID).toString();
252 if (m_overriddenCategories.find(id) != m_overriddenCategories.end())
253 return QVariant::fromValue(m_overriddenCategories[id]->rowCount());
254 }
255 return QVariant::fromValue(getFilter(index.row())->rowCount());
256 default:
257 return QVariant();
258 }