blob: 2d732989e8e176de6b4897bdb08c861972298e76 [file] [log] [blame]
Stepan Salenikovicha3557452015-02-20 14:14:12 -05001/*
Stepan Salenikovichbe87d2c2016-01-25 14:14:34 -05002 * Copyright (C) 2015-2016 Savoir-faire Linux Inc.
Stepan Salenikovicha3557452015-02-20 14:14:12 -05003 * Author: Stepan Salenikovich <stepan.salenikovich@savoirfairelinux.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Stepan Salenikovicha3557452015-02-20 14:14:12 -050018 */
19
20#include "gtkqtreemodel.h"
21#include <gtk/gtk.h>
22#include <QtCore/QAbstractItemModel>
23#include <QtCore/QDebug>
24#include "gtkaccessproxymodel.h"
25
26typedef union _int_ptr_t
27{
28 gint value;
Stepan Salenikovichd2dbcee2015-02-27 16:52:28 -050029 void *ptr;
Stepan Salenikovicha3557452015-02-20 14:14:12 -050030} int_ptr_t;
31
32typedef struct _QIter {
33 gint stamp;
34 int_ptr_t row;
35 int_ptr_t column;
Stepan Salenikovichd2dbcee2015-02-27 16:52:28 -050036 void *id;
Stepan Salenikovicha3557452015-02-20 14:14:12 -050037 gpointer user_data;
38} QIter;
39
40typedef struct _GtkQTreeModelPrivate GtkQTreeModelPrivate;
41
42struct _GtkQTreeModel
43{
44 GObject parent;
45
46 /* private */
47 GtkQTreeModelPrivate *priv;
48};
49
50struct _GtkQTreeModelClass
51{
52 GObjectClass parent_class;
53};
54
55struct _GtkQTreeModelPrivate
56{
57 GType *column_headers;
58 gint *column_roles;
59
60 gint stamp;
61 gint n_columns;
62
63 GtkAccessProxyModel *model;
64};
65
66/* static prototypes */
67
68/* GtkTreeModel prototypes */
69static void gtk_q_tree_model_tree_model_init (GtkTreeModelIface * );
70static void gtk_q_tree_model_finalize (GObject * );
71static GtkTreeModelFlags gtk_q_tree_model_get_flags (GtkTreeModel * );
72static gint gtk_q_tree_model_get_n_columns (GtkTreeModel * );
73static GType gtk_q_tree_model_get_column_type (GtkTreeModel *,
74 gint );
75static gboolean gtk_q_tree_model_get_iter (GtkTreeModel *,
76 GtkTreeIter *,
77 GtkTreePath * );
78static GtkTreePath * gtk_q_tree_model_get_path (GtkTreeModel *,
79 GtkTreeIter * );
80static void gtk_q_tree_model_get_value (GtkTreeModel *,
81 GtkTreeIter *,
82 gint,
83 GValue * );
84static gboolean gtk_q_tree_model_iter_next (GtkTreeModel *,
85 GtkTreeIter * );
86static gboolean gtk_q_tree_model_iter_previous (GtkTreeModel *,
87 GtkTreeIter * );
88static gboolean gtk_q_tree_model_iter_children (GtkTreeModel *,
89 GtkTreeIter *,
90 GtkTreeIter * );
91static gboolean gtk_q_tree_model_iter_has_child (GtkTreeModel *,
92 GtkTreeIter * );
93static gint gtk_q_tree_model_iter_n_children (GtkTreeModel *,
94 GtkTreeIter * );
95static gboolean gtk_q_tree_model_iter_nth_child (GtkTreeModel *,
96 GtkTreeIter *,
97 GtkTreeIter *,
98 gint );
99static gboolean gtk_q_tree_model_iter_parent (GtkTreeModel *,
100 GtkTreeIter *,
101 GtkTreeIter * );
102
103/* implementation prototypes */
104static void qmodelindex_to_iter (const QModelIndex &,
105 GtkTreeIter * );
106// static void gtk_q_tree_model_increment_stamp (GtkQTreeModel * );
107
108static gint gtk_q_tree_model_length (GtkQTreeModel * );
109
110static void gtk_q_tree_model_set_n_columns (GtkQTreeModel *,
111 gint );
112static void gtk_q_tree_model_set_column_type (GtkQTreeModel *,
113 gint,
114 gint,
115 GType );
116
117/* End private prototypes */
118
119/* define type, inherit from GObject, define implemented interface(s) */
120G_DEFINE_TYPE_WITH_CODE (GtkQTreeModel, gtk_q_tree_model, G_TYPE_OBJECT,
121 G_ADD_PRIVATE (GtkQTreeModel)
122 G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL,
123 gtk_q_tree_model_tree_model_init))
124
125#define GTK_Q_TREEMODEL_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_Q_TREE_MODEL, GtkQTreeModelPrivate))
126
127#define Q_ITER(iter) ((QIter *)iter)
128
129static void
130gtk_q_tree_model_class_init(GtkQTreeModelClass *klass)
131{
132 G_OBJECT_CLASS(klass)->finalize = gtk_q_tree_model_finalize;
133}
134
135static void
136gtk_q_tree_model_tree_model_init(GtkTreeModelIface *iface)
137{
138 iface->get_flags = gtk_q_tree_model_get_flags;
139 iface->get_n_columns = gtk_q_tree_model_get_n_columns;
140 iface->get_column_type = gtk_q_tree_model_get_column_type;
141 iface->get_iter = gtk_q_tree_model_get_iter;
142 iface->get_path = gtk_q_tree_model_get_path;
143 iface->get_value = gtk_q_tree_model_get_value;
144 iface->iter_next = gtk_q_tree_model_iter_next;
145 iface->iter_previous = gtk_q_tree_model_iter_previous;
146 iface->iter_children = gtk_q_tree_model_iter_children;
147 iface->iter_has_child = gtk_q_tree_model_iter_has_child;
148 iface->iter_n_children = gtk_q_tree_model_iter_n_children;
149 iface->iter_nth_child = gtk_q_tree_model_iter_nth_child;
150 iface->iter_parent = gtk_q_tree_model_iter_parent;
151}
152
153static void
154gtk_q_tree_model_init(GtkQTreeModel *q_tree_model)
155{
156 GtkQTreeModelPrivate *priv = GTK_Q_TREEMODEL_GET_PRIVATE(q_tree_model);
157 q_tree_model->priv = priv;
158
159 priv->stamp = g_random_int();
160 priv->model = NULL;
161}
162
163/**
164 * gtk_q_tree_model_get_qmodel
165 * returns the original model from which this GtkQTreeModel is created
166 */
167QAbstractItemModel *
168gtk_q_tree_model_get_qmodel(GtkQTreeModel *q_tree_model)
169{
170 GtkQTreeModelPrivate *priv = GTK_Q_TREEMODEL_GET_PRIVATE(q_tree_model);
171 return priv->model->sourceModel();
172}
173
174/**
175 * gtk_q_tree_model_get_source_idx
176 * Returns the index of the original model used to create this GtkQTreeModel from
177 * the given iter, if there is one.
178 */
179QModelIndex
180gtk_q_tree_model_get_source_idx(GtkQTreeModel *q_tree_model, GtkTreeIter *iter)
181{
182 GtkQTreeModelPrivate *priv = GTK_Q_TREEMODEL_GET_PRIVATE(q_tree_model);
183 /* get the call */
184 QIter *qiter = Q_ITER(iter);
185 GtkAccessProxyModel *proxy_model = priv->model;
186 QModelIndex proxy_idx = proxy_model->indexFromId(qiter->row.value, qiter->column.value, qiter->id);
187 if (proxy_idx.isValid()) {
188 /* we have the proxy model idx, now get the actual idx so we can get the call object */
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500189 return proxy_model->mapToSource(proxy_idx);
190 } else {
Stepan Salenikovich69771842015-02-24 18:11:45 -0500191 g_warning("could not get valid QModelIndex from given GtkTreeIter");
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500192 return QModelIndex();
193 }
194}
195
196/**
197 * Takes a QModelIndex from the original QAbstractItemModel and returns a valid GtkTreeIter in the corresponding
198 * GtkQTreeModel
199 */
200gboolean
201gtk_q_tree_model_source_index_to_iter(GtkQTreeModel *q_tree_model, const QModelIndex &idx, GtkTreeIter *iter)
202{
203 GtkQTreeModelPrivate *priv = GTK_Q_TREEMODEL_GET_PRIVATE(q_tree_model);
204
Stepan Salenikovich69771842015-02-24 18:11:45 -0500205 /* the the proxy_idx from the source idx */
206 QModelIndex proxy_idx = priv->model->mapFromSource(idx);
207 if (!proxy_idx.isValid())
208 return FALSE;
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500209
210 /* make sure iter is valid */
211 iter->stamp = priv->stamp;
212
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500213 /* map the proxy idx to iter */
214 Q_ITER(iter)->row.value = proxy_idx.row();
Stepan Salenikovich7ff47962015-03-16 15:10:14 -0400215 Q_ITER(iter)->column.value = proxy_idx.column();
Stepan Salenikovichd2dbcee2015-02-27 16:52:28 -0500216 Q_ITER(iter)->id = proxy_idx.internalPointer();
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500217 return TRUE;
218}
219
220/**
Stepan Salenikovich22de0592015-10-21 10:27:20 -0400221 * helper method which recursively adds all the children of the given QModelIndex
222 */
223static void
224insert_children(const QModelIndex &idx, GtkQTreeModel *gtk_model)
225{
226 const auto children = gtk_model->priv->model->rowCount(idx);
227 for (int i = 0; i < children; ++i) {
228 GtkTreeIter iter_child;
229 auto idx_child = gtk_model->priv->model->index(i, 0, idx);
230 if (idx_child.isValid()) {
231 iter_child.stamp = gtk_model->priv->stamp;
232 qmodelindex_to_iter(idx_child, &iter_child);
233 if (auto path_child = gtk_q_tree_model_get_path(GTK_TREE_MODEL(gtk_model), &iter_child)) {
234 gtk_tree_model_row_inserted(GTK_TREE_MODEL(gtk_model), path_child, &iter_child);
235 gtk_tree_path_free(path_child);
236 }
237 insert_children(idx_child, gtk_model);
238 }
239 }
240}
241
242/**
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500243 * gtk_q_tree_model_new:
244 * @model: QAbstractItemModel to which this model will bind.
245 * @n_columns: number of columns in the list store
246 * @...: all #GType follwed by the #Role pair for each column.
247 *
248 * Return value: a new #GtkQTreeModel
249 */
250GtkQTreeModel *
251gtk_q_tree_model_new(QAbstractItemModel *model, size_t n_columns, ...)
252{
253 GtkQTreeModel *retval;
254 va_list args;
255 gint i;
256
257 g_return_val_if_fail(n_columns > 0, NULL);
258
259 retval = (GtkQTreeModel *)g_object_new(GTK_TYPE_Q_TREE_MODEL, NULL);
260 gtk_q_tree_model_set_n_columns(retval, n_columns);
261
262 /* get proxy model from given model */
263 GtkAccessProxyModel* proxy_model = new GtkAccessProxyModel();
264 proxy_model->setSourceModel(model);
265 retval->priv->model = proxy_model;
266 gint stamp = retval->priv->stamp;
267
268 n_columns = 2*n_columns;
269 va_start (args, n_columns);
270
271 for (i = 0; i < (gint)(n_columns/2); i++) {
272 /* first get the role of the QModel */
273 gint role = va_arg(args, gint);
274
275 /* then get the type the role will be interpreted as */
276 GType type = va_arg(args, GType);
277
278 /* TODO: check if its a type supported by our model
279 * if (! _gtk_tree_data_list_check_type (type))
280 * {
281 * g_warning ("%s: Invalid type %s\n", G_STRLOC, g_type_name (type));
282 * g_object_unref (retval);
283 * va_end (args);
284 *
285 * return NULL;
286 * }
287 */
288
289 gtk_q_tree_model_set_column_type (retval, i, role, type);
290 }
291
292 va_end (args);
293
294 gtk_q_tree_model_length(retval);
295
296 /* connect signals */
297 QObject::connect(
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400298 proxy_model,
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500299 &QAbstractItemModel::rowsInserted,
300 [=](const QModelIndex & parent, int first, int last) {
301 for( int row = first; row <= last; row++) {
Stepan Salenikovich8573f862015-08-17 16:54:50 -0400302 GtkTreeIter iter;
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400303 QModelIndex idx = proxy_model->index(row, 0, parent);
Stepan Salenikovich8573f862015-08-17 16:54:50 -0400304 iter.stamp = stamp;
305 qmodelindex_to_iter(idx, &iter);
306 GtkTreePath *path = gtk_q_tree_model_get_path(GTK_TREE_MODEL(retval), &iter);
307 gtk_tree_model_row_inserted(GTK_TREE_MODEL(retval), path, &iter);
Stepan Salenikovichd2dfc162015-08-17 16:40:28 -0400308 gtk_tree_path_free(path);
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500309 }
310 }
311 );
312
313 QObject::connect(
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400314 proxy_model,
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500315 &QAbstractItemModel::rowsAboutToBeMoved,
Stepan Salenikovich69771842015-02-24 18:11:45 -0500316 [=](const QModelIndex & sourceParent, int sourceStart, int sourceEnd,
Stepan Salenikovich0ba98102015-10-21 15:07:52 -0400317 const QModelIndex & destinationParent, G_GNUC_UNUSED int destinationRow) {
318 /* if the sourceParent and the destinationParent are the same, then we can use the
319 * GtkTreeModel API to move the rows, otherwise we must first delete them and then
320 * re-insert them under the new parent */
321 if (sourceParent == destinationParent)
322 return;
323
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500324 /* first remove the row from old location
325 * then insert them at the new location on the "rowsMoved signal */
326 for( int row = sourceStart; row <= sourceEnd; row++) {
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400327 QModelIndex idx = proxy_model->index(row, 0, sourceParent);
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500328 GtkTreeIter iter_old;
Stepan Salenikovichfcf8fc62015-10-21 11:08:08 -0400329 iter_old.stamp = stamp;
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500330 qmodelindex_to_iter(idx, &iter_old);
331 GtkTreePath *path_old = gtk_q_tree_model_get_path(GTK_TREE_MODEL(retval), &iter_old);
332 gtk_tree_model_row_deleted(GTK_TREE_MODEL(retval), path_old);
Stepan Salenikovichd2dfc162015-08-17 16:40:28 -0400333 gtk_tree_path_free(path_old);
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500334 }
335 }
336 );
337
338 QObject::connect(
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400339 proxy_model,
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500340 &QAbstractItemModel::rowsMoved,
Stepan Salenikovich0ba98102015-10-21 15:07:52 -0400341 [=](const QModelIndex & sourceParent, int sourceStart, int sourceEnd,
Stepan Salenikovich69771842015-02-24 18:11:45 -0500342 const QModelIndex & destinationParent, int destinationRow) {
Stepan Salenikovich0ba98102015-10-21 15:07:52 -0400343 /* if the sourceParent and the destinationParent are the same, then we can use the
344 * GtkTreeModel API to move the rows, otherwise we must first delete them and then
345 * re-insert them under the new parent */
346 if (sourceParent == destinationParent) {
347 GtkTreeIter *iter_parent = nullptr;
348 GtkTreePath *path_parent = nullptr;
349 if (sourceParent.isValid()) {
350 iter_parent = g_new0(GtkTreeIter, 1);
351 iter_parent->stamp = stamp;
352 qmodelindex_to_iter(sourceParent, iter_parent);
353 path_parent = gtk_q_tree_model_get_path(GTK_TREE_MODEL(retval), iter_parent);
354 } else {
355 path_parent = gtk_tree_path_new();
356 }
357
358 /* gtk_tree_model_rows_reordered takes an array of integers mapping the current
359 * position of each child to its old position before the re-ordering,
360 * i.e. new_order [newpos] = oldpos
361 */
362 const auto rows = proxy_model->rowCount(sourceParent);
Stepan Salenikovich365dfd12015-10-21 18:52:38 -0400363 gint new_order[rows];
Stepan Salenikovich0ba98102015-10-21 15:07:52 -0400364 const auto destinationRowLast = destinationRow + (sourceEnd - sourceStart);
Stepan Salenikovichcaa182a2015-10-22 11:50:09 -0400365 const auto totalMoved = sourceEnd - sourceStart + 1;
Stepan Salenikovich0ba98102015-10-21 15:07:52 -0400366 for (int row = 0; row < rows; ++row ) {
367 if ( (row < sourceStart && row < destinationRow)
368 || (row > sourceEnd && row > destinationRowLast) ) {
369 // if the row is outside of the bounds of change, then it stayed in the same place
370 new_order[row] = row;
371 } else {
372 if (row < destinationRow) {
373 // in front of the destination, so it used to be behind the rows that moved
Stepan Salenikovichcaa182a2015-10-22 11:50:09 -0400374 new_order[row] = row + totalMoved;
Stepan Salenikovich0ba98102015-10-21 15:07:52 -0400375 } else if (row >= destinationRow && row <= destinationRowLast) {
376 // within the destination, so it was within the rows that moved
377 new_order[row] = sourceStart + (row - destinationRow);
378 } else {
379 // behind the destination, so it used to be in front of the rows that moved
Stepan Salenikovichcaa182a2015-10-22 11:50:09 -0400380 new_order[row] = row - totalMoved;
Stepan Salenikovich0ba98102015-10-21 15:07:52 -0400381 }
382 }
383 }
384 gtk_tree_model_rows_reordered(GTK_TREE_MODEL(retval), path_parent, iter_parent, new_order);
385
386 g_free(iter_parent);
387 gtk_tree_path_free(path_parent);
388 } else {
389 /* these rows should have been removed in the "rowsAboutToBeMoved" handler
390 * now insert them in the new location */
391 for( int row = sourceStart; row <= sourceEnd; row++) {
392 GtkTreeIter iter_new;
393 QModelIndex idx = proxy_model->index(destinationRow, 0, destinationParent);
394 iter_new.stamp = stamp;
395 qmodelindex_to_iter(idx, &iter_new);
396 GtkTreePath *path_new = gtk_q_tree_model_get_path(GTK_TREE_MODEL(retval), &iter_new);
397 gtk_tree_model_row_inserted(GTK_TREE_MODEL(retval), path_new, &iter_new);
398 gtk_tree_path_free(path_new);
399 insert_children(idx, retval);
400 destinationRow++;
401 }
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500402 }
403 }
404 );
405
406 QObject::connect(
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400407 proxy_model,
Stepan Salenikoviche17a2932015-06-11 14:50:02 -0400408 &QAbstractItemModel::rowsRemoved,
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500409 [=](const QModelIndex & parent, int first, int last) {
Stepan Salenikoviche17a2932015-06-11 14:50:02 -0400410 GtkTreePath *parent_path = NULL;
411 if (parent.isValid()) {
412 GtkTreeIter iter_parent;
413 iter_parent.stamp = stamp;
414 qmodelindex_to_iter(parent, &iter_parent);
415 parent_path = gtk_q_tree_model_get_path(GTK_TREE_MODEL(retval), &iter_parent);
416 } else {
417 parent_path = gtk_tree_path_new();
418 }
419
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400420 /* go last to first, since the rows are being deleted */
421 for( int row = last; row >= first; --row) {
422 // g_debug("deleting row: %d", row);
Stepan Salenikoviche17a2932015-06-11 14:50:02 -0400423 GtkTreePath *path = gtk_tree_path_copy(parent_path);
424 gtk_tree_path_append_index(path, row);
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500425 gtk_tree_model_row_deleted(GTK_TREE_MODEL(retval), path);
Stepan Salenikovichd2dfc162015-08-17 16:40:28 -0400426 gtk_tree_path_free(path);
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500427 }
Stepan Salenikoviche17a2932015-06-11 14:50:02 -0400428
429 gtk_tree_path_free(parent_path);
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500430 }
431 );
432
433 QObject::connect(
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400434 proxy_model,
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500435 &QAbstractItemModel::dataChanged,
Stepan Salenikovich69771842015-02-24 18:11:45 -0500436 [=](const QModelIndex & topLeft, const QModelIndex & bottomRight,
437 G_GNUC_UNUSED const QVector<int> & roles = QVector<int> ()) {
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500438 /* we have to assume only one column */
439 int first = topLeft.row();
440 int last = bottomRight.row();
441 if (topLeft.column() != bottomRight.column() ) {
442 g_warning("more than one column is not supported!");
443 }
444 /* the first idx IS topLeft, the reset are his siblings */
Stepan Salenikovich8573f862015-08-17 16:54:50 -0400445 GtkTreeIter iter;
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500446 QModelIndex idx = topLeft;
Stepan Salenikovich8573f862015-08-17 16:54:50 -0400447 iter.stamp = stamp;
448 qmodelindex_to_iter(idx, &iter);
449 GtkTreePath *path = gtk_q_tree_model_get_path(GTK_TREE_MODEL(retval), &iter);
450 gtk_tree_model_row_changed(GTK_TREE_MODEL(retval), path, &iter);
Stepan Salenikovichd2dfc162015-08-17 16:40:28 -0400451 gtk_tree_path_free(path);
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500452 for( int row = first + 1; row <= last; row++) {
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500453 idx = topLeft.sibling(row, 0);
Stepan Salenikovich8573f862015-08-17 16:54:50 -0400454 qmodelindex_to_iter(idx, &iter);
455 path = gtk_q_tree_model_get_path(GTK_TREE_MODEL(retval), &iter);
456 gtk_tree_model_row_changed(GTK_TREE_MODEL(retval), path, &iter);
Stepan Salenikovichd2dfc162015-08-17 16:40:28 -0400457 gtk_tree_path_free(path);
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500458 }
459 }
460 );
461
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400462 QObject::connect(
463 proxy_model,
464 &QAbstractItemModel::modelAboutToBeReset,
465 [=] () {
466 // g_debug("model about to be reset");
467 /* nothing equvivalent eixists in GtkTreeModel, so simply delete all
468 * rows, and add all rows when the model is reset;
469 * we must delete the rows in ascending order */
470 int row_count = proxy_model->rowCount();
471 for (int row = row_count; row > 0; --row) {
472 // g_debug("deleting row %d", row -1);
473 QModelIndex idx = proxy_model->index(row - 1, 0);
474 GtkTreeIter iter;
475 iter.stamp = stamp;
476 qmodelindex_to_iter(idx, &iter);
477 GtkTreePath *path = gtk_q_tree_model_get_path(GTK_TREE_MODEL(retval), &iter);
478 gtk_tree_model_row_deleted(GTK_TREE_MODEL(retval), path);
Stepan Salenikovichd2dfc162015-08-17 16:40:28 -0400479 gtk_tree_path_free(path);
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400480 }
481 }
482 );
483
484 QObject::connect(
485 proxy_model,
486 &QAbstractItemModel::modelReset,
487 [=] () {
488 // g_debug("model reset");
Stepan Salenikovich22de0592015-10-21 10:27:20 -0400489 /* now recursively add all the (new) rows */
490 insert_children(QModelIndex(), retval);
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400491 }
492 );
493
Stepan Salenikovich5d3506e2015-03-30 11:01:29 -0400494// QObject::connect(
495// proxy_model,
496// &QAbstractItemModel::layoutAboutToBeChanged,
497// [=] () {
498// // g_debug("layout about to be changed");
499// /* nothing equvivalent eixists in GtkTreeModel, so simply delete all
500// * rows, and add all rows when the model is reset;
501// * we must delete the rows in ascending order */
502// int row_count = proxy_model->rowCount();
503// for (int row = row_count; row > 0; --row) {
504// // g_debug("deleting row %d", row -1);
505// QModelIndex idx = proxy_model->index(row - 1, 0);
506// GtkTreeIter iter;
507// iter.stamp = stamp;
508// qmodelindex_to_iter(idx, &iter);
509// GtkTreePath *path = gtk_q_tree_model_get_path(GTK_TREE_MODEL(retval), &iter);
510// gtk_tree_model_row_deleted(GTK_TREE_MODEL(retval), path);
511// }
512// }
513// );
514//
515// QObject::connect(
516// proxy_model,
517// &QAbstractItemModel::layoutChanged,
518// [=] () {
519// // g_debug("layout changed");
520// /* now add all the (new) rows */
521// int row_count = proxy_model->rowCount();
522// for (int row = 0; row < row_count; row++) {
523// // g_debug("adding row %d", row);
524// GtkTreeIter *iter_new = g_new0(GtkTreeIter, 1);
525// QModelIndex idx = proxy_model->index(row, 0);
526// iter_new->stamp = stamp;
527// qmodelindex_to_iter(idx, iter_new);
528// GtkTreePath *path_new = gtk_q_tree_model_get_path(GTK_TREE_MODEL(retval), iter_new);
529// gtk_tree_model_row_inserted(GTK_TREE_MODEL(retval), path_new, iter_new);
530// }
531// }
532// );
533
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500534 return retval;
535}
536
537static gint
538gtk_q_tree_model_length(GtkQTreeModel *q_tree_model)
539{
540 GtkQTreeModelPrivate *priv = q_tree_model->priv;
541 return priv->model->rowCount();
542}
543
544static void
545gtk_q_tree_model_set_n_columns(GtkQTreeModel *q_tree_model,
546 gint n_columns)
547{
548 GtkQTreeModelPrivate *priv = q_tree_model->priv;
549 int i;
550
551 if (priv->n_columns == n_columns)
552 return;
553
554 priv->column_headers = g_renew (GType, priv->column_headers, n_columns);
555 for (i = priv->n_columns; i < n_columns; i++)
556 priv->column_headers[i] = G_TYPE_INVALID;
557 priv->n_columns = n_columns;
558
559 priv->column_roles = g_renew (gint, priv->column_roles, n_columns);
560 for (i = priv->n_columns; i < n_columns; i++)
561 priv->column_roles[i] = -1;
562}
563
564static void
565gtk_q_tree_model_set_column_type(GtkQTreeModel *q_tree_model,
566 gint column,
567 gint role,
568 GType type)
569{
570 GtkQTreeModelPrivate *priv = q_tree_model->priv;
571
572 /* TODO: check if its a type supported by our model
573 * if (!_gtk_tree_data_list_check_type (type))
574 * {
575 * g_warning ("%s: Invalid type %s\n", G_STRLOC, g_type_name (type));
576 * return;
577 * }
578 */
579
580 priv->column_headers[column] = type;
581 priv->column_roles[column] = role;
582}
583
584
585static void
586gtk_q_tree_model_finalize(GObject *object)
587{
588 GtkQTreeModel *q_tree_model = GTK_Q_TREE_MODEL (object);
589 GtkQTreeModelPrivate *priv = q_tree_model->priv;
590
591 g_free(priv->column_headers);
592 g_free(priv->column_roles);
593
594 /* delete the created proxy model */
595 delete priv->model;
596
597 G_OBJECT_CLASS(gtk_q_tree_model_parent_class)->finalize (object);
598}
599
600static gboolean
601iter_is_valid(GtkTreeIter *iter,
602 GtkQTreeModel *q_tree_model)
603{
604 gboolean retval;
605 g_return_val_if_fail(iter != NULL, FALSE);
606 QIter *qiter = Q_ITER(iter);
607 retval = q_tree_model->priv->model->indexFromId(qiter->row.value, qiter->column.value, qiter->id).isValid();
608 return retval;
609}
610
611static void
612qmodelindex_to_iter(const QModelIndex &idx, GtkTreeIter *iter)
613{
614 Q_ITER(iter)->row.value = idx.row();
Stepan Salenikovich7ff47962015-03-16 15:10:14 -0400615 Q_ITER(iter)->column.value = idx.column();
Stepan Salenikovichd2dbcee2015-02-27 16:52:28 -0500616 Q_ITER(iter)->id = idx.internalPointer();
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500617}
618
619static gboolean
620validate_index(gint stamp, const QModelIndex &idx, GtkTreeIter *iter)
621{
622 if (idx.isValid()) {
623 iter->stamp = stamp;
624 qmodelindex_to_iter(idx, iter);
625 } else {
626 iter->stamp = 0;
627 return FALSE;
628 }
629 return TRUE;
630}
631
632/* Start Fulfill the GtkTreeModel requirements */
633
634/* flags supported by this interface */
635static GtkTreeModelFlags
636gtk_q_tree_model_get_flags(G_GNUC_UNUSED GtkTreeModel *tree_model)
637{
638 // TODO: possibly return based on the model?
639 return (GtkTreeModelFlags)(GTK_TREE_MODEL_ITERS_PERSIST);
640}
641
642/* number of columns supported by this tree model */
643static gint
644gtk_q_tree_model_get_n_columns(GtkTreeModel *tree_model)
645{
646 GtkQTreeModel *q_tree_model = GTK_Q_TREE_MODEL(tree_model);
647 GtkQTreeModelPrivate *priv = q_tree_model->priv;
648
649 return priv->model->columnCount();
650}
651
652/* get given column type */
653static GType
654gtk_q_tree_model_get_column_type(GtkTreeModel *tree_model,
655 gint index)
656{
657 GtkQTreeModel *q_tree_model = GTK_Q_TREE_MODEL (tree_model);
658 GtkQTreeModelPrivate *priv = q_tree_model->priv;
659
660 g_return_val_if_fail (index < gtk_q_tree_model_get_n_columns(tree_model), G_TYPE_INVALID);
661
662 return priv->column_headers[index];
663}
664
665/* Sets @iter to a valid iterator pointing to @path. If @path does
666 * not exist, @iter is set to an invalid iterator and %FALSE is returned.
667 */
668static gboolean
669gtk_q_tree_model_get_iter(GtkTreeModel *tree_model,
670 GtkTreeIter *iter,
671 GtkTreePath *path)
672{
673 GtkQTreeModel *q_tree_model = GTK_Q_TREE_MODEL (tree_model);
674 GtkQTreeModelPrivate *priv = q_tree_model->priv;
675
676 /* GtkTreePath is a list of indices, each one indicates
677 * the child of the previous one.
678 * Since GtkTreeModel only has rows (columns are only used to
679 * store data in each item), each index is basically the row
680 * at the given tree level.
681 * To get the iter, we want to get the QModelIndex. To get
682 * the QModelIndex we simply start at the first level and
683 * traverse the model the number of layers equal to the number
684 * of indices in the path.
685 */
686 gint depth;
687 gint* indices = gtk_tree_path_get_indices_with_depth(path, &depth);
688 QModelIndex idx = priv->model->index(indices[0], 0);
689 for(int layer = 1; layer < depth; layer++ ) {
690 /* check if previous iter is valid */
691 if (!idx.isValid()) {
692 break;
693 } else {
694 idx = idx.child(indices[layer], 0);
695 }
696 }
697
698 if (!idx.isValid()) {
699 iter->stamp = 0;
700 return FALSE;
701 } else {
702 /* we have a valid QModelIndex; turn it into an iter */
703 iter->stamp = priv->stamp;
704 qmodelindex_to_iter(idx, iter);
705 }
706
707 return TRUE;
708}
709
710/* Returns a newly-created #GtkTreePath referenced by @iter.
711 *
712 * This path should be freed with gtk_tree_path_free().
713 */
714static GtkTreePath *
715gtk_q_tree_model_get_path(GtkTreeModel *tree_model,
716 GtkTreeIter *iter)
717{
718 GtkQTreeModel *q_tree_model = GTK_Q_TREE_MODEL (tree_model);
719 GtkQTreeModelPrivate *priv = q_tree_model->priv;
720 GtkTreePath *path;
721
722 g_return_val_if_fail (iter->stamp == priv->stamp, NULL);
723 g_return_val_if_fail (iter_is_valid(iter, q_tree_model), NULL);
724
725 /* To get the path, we have to traverse from the child all the way up */
726 path = gtk_tree_path_new();
727 QIter *qiter = Q_ITER(iter);
728 QModelIndex idx = priv->model->indexFromId(qiter->row.value, qiter->column.value, qiter->id);
729 while( idx.isValid() ){
730 gtk_tree_path_prepend_index(path, idx.row());
731 idx = idx.parent();
732 }
733 return path;
734}
735
736static inline GType
737get_fundamental_type(GType type)
738{
739 GType result;
740
741 result = G_TYPE_FUNDAMENTAL (type);
742
743 if (result == G_TYPE_INTERFACE) {
744 if (g_type_is_a (type, G_TYPE_OBJECT))
745 result = G_TYPE_OBJECT;
746 }
747
748 return result;
749}
750
751/* Initializes and sets @value to that at @column. */
752static void
753gtk_q_tree_model_get_value(GtkTreeModel *tree_model,
754 GtkTreeIter *iter,
755 gint column,
756 GValue *value)
757{
758 GtkQTreeModel *q_tree_model = GTK_Q_TREE_MODEL (tree_model);
759 GtkQTreeModelPrivate *priv = q_tree_model->priv;
760
761 g_return_if_fail (column < priv->n_columns);
762 g_return_if_fail (iter_is_valid(iter, q_tree_model));
763
764 /* get the data */
765 QIter *qiter = Q_ITER(iter);
766 QModelIndex idx = priv->model->indexFromId(qiter->row.value, qiter->column.value, qiter->id);
767 int role = priv->column_roles[column];
768 QVariant var = priv->model->data(idx, role);
769 GType type = priv->column_headers[column];
770 g_value_init (value, type);
771 switch (get_fundamental_type (type))
772 {
773 /* TODO: implement the rest of the data types the we plan to support
774 * otherwise get rid of the code and make sure un-implemented types
775 * are not allowed
776 */
777 case G_TYPE_BOOLEAN:
778 g_value_set_boolean (value, (gboolean) var.toBool());
779 break;
780 // case G_TYPE_CHAR:
781 // g_value_set_schar (value, (gchar) list->data.v_char);
782 // break;
783 // case G_TYPE_UCHAR:
784 // g_value_set_uchar (value, (guchar) list->data.v_uchar);
785 // break;
786 case G_TYPE_INT:
787 g_value_set_int (value, (gint) var.toInt());
788 break;
789 case G_TYPE_UINT:
790 g_value_set_uint (value, (guint) var.toUInt());
791 break;
792 // case G_TYPE_LONG:
793 // g_value_set_long (value, list->data.v_long);
794 // break;
795 // case G_TYPE_ULONG:
796 // g_value_set_ulong (value, list->data.v_ulong);
797 // break;
798 // case G_TYPE_INT64:
799 // g_value_set_int64 (value, list->data.v_int64);
800 // break;
801 // case G_TYPE_UINT64:
802 // g_value_set_uint64 (value, list->data.v_uint64);
803 // break;
804 // case G_TYPE_ENUM:
805 // g_value_set_enum (value, list->data.v_int);
806 // break;
807 // case G_TYPE_FLAGS:
808 // g_value_set_flags (value, list->data.v_uint);
809 // break;
810 // case G_TYPE_FLOAT:
811 // g_value_set_float (value, (gfloat) list->data.v_float);
812 // break;
813 // case G_TYPE_DOUBLE:
814 // g_value_set_double (value, (gdouble) list->data.v_double);
815 // break;
816 case G_TYPE_STRING:
Stepan Salenikovich69771842015-02-24 18:11:45 -0500817 {
Stepan Salenikovich6f687072015-03-26 10:43:37 -0400818 QByteArray ba = var.toString().toUtf8();
819 g_value_set_string(value, (gchar *)var.toByteArray().constData());
Stepan Salenikovich69771842015-02-24 18:11:45 -0500820 }
821 break;
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500822 // case G_TYPE_POINTER:
823 // g_value_set_pointer (value, (gpointer) list->data.v_pointer);
824 // break;
825 // case G_TYPE_BOXED:
826 // g_value_set_boxed (value, (gpointer) list->data.v_pointer);
827 // break;
828 // case G_TYPE_VARIANT:
829 // g_value_set_variant (value, (gpointer) list->data.v_pointer);
830 // break;
831 // case G_TYPE_OBJECT:
832 // g_value_set_object (value, (GObject *) list->data.v_pointer);
833 // break;
834 default:
835 g_warning ("%s: Unsupported type (%s) retrieved.", G_STRLOC, g_type_name (value->g_type));
836 break;
837 }
838
839 return;
840}
841
842/* Sets @iter to point to the node following it at the current level.
843 *
844 * If there is no next @iter, %FALSE is returned and @iter is set
845 * to be invalid.
846 */
847static gboolean
848gtk_q_tree_model_iter_next(GtkTreeModel *tree_model,
849 GtkTreeIter *iter)
850{
851 GtkQTreeModel *q_tree_model = GTK_Q_TREE_MODEL (tree_model);
852 GtkQTreeModelPrivate *priv = q_tree_model->priv;
853
854 g_return_val_if_fail (iter_is_valid(iter, q_tree_model), FALSE);
855
856 QIter *qiter = Q_ITER(iter);
857 QModelIndex idx = priv->model->indexFromId(qiter->row.value, qiter->column.value, qiter->id);
858 idx = idx.sibling(idx.row()+1, idx.column());
859
860 /* validate */
861 if (validate_index(priv->stamp, idx, iter) ) {
862 GtkTreePath *path = gtk_q_tree_model_get_path(tree_model, iter);
863 gtk_tree_path_free(path);
864 return TRUE;
865 } else {
866 return FALSE;
867 }
868}
869
870/* Sets @iter to point to the previous node at the current level.
871 *
872 * If there is no previous @iter, %FALSE is returned and @iter is
873 * set to be invalid.
874 */
875static gboolean
876gtk_q_tree_model_iter_previous(GtkTreeModel *tree_model,
877 GtkTreeIter *iter)
878{
879 GtkQTreeModel *q_tree_model = GTK_Q_TREE_MODEL (tree_model);
880 GtkQTreeModelPrivate *priv = q_tree_model->priv;
881
882 g_return_val_if_fail (iter_is_valid(iter, q_tree_model), FALSE);
883
884 QIter *qiter = Q_ITER(iter);
885 QModelIndex idx = priv->model->indexFromId(qiter->row.value, qiter->column.value, qiter->id);
886 idx = idx.sibling(idx.row()-1, idx.column());
887
888 /* validate */
889 return validate_index(priv->stamp, idx, iter);
890}
891
892/* Sets @iter to point to the first child of @parent.
893 *
894 * If @parent has no children, %FALSE is returned and @iter is
895 * set to be invalid. @parent will remain a valid node after this
896 * function has been called.
897 *
898 * If @parent is %NULL returns the first node
899 */
900static gboolean
901gtk_q_tree_model_iter_children(GtkTreeModel *tree_model,
902 GtkTreeIter *iter,
903 GtkTreeIter *parent)
904{
905 GtkQTreeModel *q_tree_model = GTK_Q_TREE_MODEL (tree_model);
906 GtkQTreeModelPrivate *priv = q_tree_model->priv;
907 QModelIndex idx;
908
909 /* make sure parent if valid node if its not NULL */
910 if (parent)
911 g_return_val_if_fail(iter_is_valid(parent, q_tree_model), FALSE);
912
913 if (parent) {
914 /* get first child */
915 QIter *qparent = Q_ITER(parent);
916 idx = priv->model->indexFromId(qparent->row.value, qparent->column.value, qparent->id);
917 idx = idx.child(0, 0);
918 } else {
919 /* parent is NULL, get the first node */
920 idx = priv->model->index(0, 0);
921 }
922
923 /* validate child */
924 return validate_index(priv->stamp, idx, iter);
925}
926
927/* Returns %TRUE if @iter has children, %FALSE otherwise. */
928static gboolean
929gtk_q_tree_model_iter_has_child (GtkTreeModel *tree_model,
930 GtkTreeIter *iter)
931{
932 GtkQTreeModel *q_tree_model = GTK_Q_TREE_MODEL (tree_model);
933 GtkQTreeModelPrivate *priv = q_tree_model->priv;
934 g_return_val_if_fail(iter_is_valid(iter, q_tree_model), FALSE);
935
936 QIter *qiter = Q_ITER(iter);
937 QModelIndex idx = priv->model->indexFromId(qiter->row.value, qiter->column.value, qiter->id);
938 return priv->model->hasChildren(idx);
939}
940
941/* Returns the number of children that @iter has.
942 *
943 * As a special case, if @iter is %NULL, then the number
944 * of toplevel nodes is returned.
945 */
946static gint
947gtk_q_tree_model_iter_n_children (GtkTreeModel *tree_model,
948 GtkTreeIter *iter)
949{
950 GtkQTreeModel *q_tree_model = GTK_Q_TREE_MODEL (tree_model);
951 GtkQTreeModelPrivate *priv = q_tree_model->priv;
952
953 if (iter == NULL)
954 return gtk_q_tree_model_length(q_tree_model);
955
956 g_return_val_if_fail(iter_is_valid(iter, q_tree_model), -1);
957 QIter *qiter = Q_ITER(iter);
958 QModelIndex idx = priv->model->indexFromId(qiter->row.value, qiter->column.value, qiter->id);
959 return priv->model->rowCount(idx);
960}
961
962/* Sets @iter to be the child of @parent, using the given index.
963 *
964 * The first index is 0. If @n is too big, or @parent has no children,
965 * @iter is set to an invalid iterator and %FALSE is returned. @parent
966 * will remain a valid node after this function has been called. As a
967 * special case, if @parent is %NULL, then the @n<!-- -->th root node
968 * is set.
969 */
970static gboolean
971gtk_q_tree_model_iter_nth_child(GtkTreeModel *tree_model,
972 GtkTreeIter *iter,
973 GtkTreeIter *parent,
974 gint n)
975{
976 GtkQTreeModel *q_tree_model = GTK_Q_TREE_MODEL (tree_model);
977 GtkQTreeModelPrivate *priv = q_tree_model->priv;
978 QModelIndex idx;
979
980 if (parent) {
981 g_return_val_if_fail(iter_is_valid(parent, q_tree_model), FALSE);
982
983 /* get the nth child */
984 QIter *qparent = Q_ITER(parent);
985 idx = priv->model->indexFromId(qparent->row.value, qparent->column.value, qparent->id);
986 idx = idx.child(n, 0);
987 } else {
988 idx = priv->model->index(n, 0);
989 }
990
991 /* validate */
992 return validate_index(priv->stamp, idx, iter);
993}
994
995/* Sets @iter to be the parent of @child.
996 *
997 * If @child is at the toplevel, and doesn't have a parent, then
998 * @iter is set to an invalid iterator and %FALSE is returned.
999 * @child will remain a valid node after this function has been
1000 * called.
1001 */
1002static gboolean
1003gtk_q_tree_model_iter_parent(GtkTreeModel *tree_model,
1004 GtkTreeIter *iter,
1005 GtkTreeIter *child)
1006{
1007 GtkQTreeModel *q_tree_model = GTK_Q_TREE_MODEL (tree_model);
1008 GtkQTreeModelPrivate *priv = q_tree_model->priv;
1009 QModelIndex idx;
1010
1011 g_return_val_if_fail(iter_is_valid(child, q_tree_model), FALSE);
1012
1013 QIter *qchild = Q_ITER(child);
1014 idx = priv->model->indexFromId(qchild->row.value, qchild->column.value, qchild->id);
1015 idx = idx.parent();
1016
1017 /* validate */
1018 return validate_index(priv->stamp, idx, iter);
1019}
1020
Stepan Salenikovich69771842015-02-24 18:11:45 -05001021/* End Fulfill the GtkTreeModel requirements */