blob: 0a18e771d7e8ac906621acee4ce37717e5f44f7e [file] [log] [blame]
Stepan Salenikovicha3557452015-02-20 14:14:12 -05001/*
2 * Copyright (C) 2015 Savoir-Faire Linux Inc.
3 * 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.
18 *
19 * Additional permission under GNU GPL version 3 section 7:
20 *
21 * If you modify this program, or any covered work, by linking or
22 * combining it with the OpenSSL project's OpenSSL library (or a
23 * modified version of that library), containing parts covered by the
24 * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
25 * grants you additional permission to convey the resulting work.
26 * Corresponding Source for a non-source form of such a combination
27 * shall include the source code for the parts of OpenSSL used as well
28 * as that of the covered work.
29 */
30
31#include "gtkqtreemodel.h"
32#include <gtk/gtk.h>
33#include <QtCore/QAbstractItemModel>
34#include <QtCore/QDebug>
35#include "gtkaccessproxymodel.h"
36
37typedef union _int_ptr_t
38{
39 gint value;
Stepan Salenikovichd2dbcee2015-02-27 16:52:28 -050040 void *ptr;
Stepan Salenikovicha3557452015-02-20 14:14:12 -050041} int_ptr_t;
42
43typedef struct _QIter {
44 gint stamp;
45 int_ptr_t row;
46 int_ptr_t column;
Stepan Salenikovichd2dbcee2015-02-27 16:52:28 -050047 void *id;
Stepan Salenikovicha3557452015-02-20 14:14:12 -050048 gpointer user_data;
49} QIter;
50
51typedef struct _GtkQTreeModelPrivate GtkQTreeModelPrivate;
52
53struct _GtkQTreeModel
54{
55 GObject parent;
56
57 /* private */
58 GtkQTreeModelPrivate *priv;
59};
60
61struct _GtkQTreeModelClass
62{
63 GObjectClass parent_class;
64};
65
66struct _GtkQTreeModelPrivate
67{
68 GType *column_headers;
69 gint *column_roles;
70
71 gint stamp;
72 gint n_columns;
73
74 GtkAccessProxyModel *model;
75};
76
77/* static prototypes */
78
79/* GtkTreeModel prototypes */
80static void gtk_q_tree_model_tree_model_init (GtkTreeModelIface * );
81static void gtk_q_tree_model_finalize (GObject * );
82static GtkTreeModelFlags gtk_q_tree_model_get_flags (GtkTreeModel * );
83static gint gtk_q_tree_model_get_n_columns (GtkTreeModel * );
84static GType gtk_q_tree_model_get_column_type (GtkTreeModel *,
85 gint );
86static gboolean gtk_q_tree_model_get_iter (GtkTreeModel *,
87 GtkTreeIter *,
88 GtkTreePath * );
89static GtkTreePath * gtk_q_tree_model_get_path (GtkTreeModel *,
90 GtkTreeIter * );
91static void gtk_q_tree_model_get_value (GtkTreeModel *,
92 GtkTreeIter *,
93 gint,
94 GValue * );
95static gboolean gtk_q_tree_model_iter_next (GtkTreeModel *,
96 GtkTreeIter * );
97static gboolean gtk_q_tree_model_iter_previous (GtkTreeModel *,
98 GtkTreeIter * );
99static gboolean gtk_q_tree_model_iter_children (GtkTreeModel *,
100 GtkTreeIter *,
101 GtkTreeIter * );
102static gboolean gtk_q_tree_model_iter_has_child (GtkTreeModel *,
103 GtkTreeIter * );
104static gint gtk_q_tree_model_iter_n_children (GtkTreeModel *,
105 GtkTreeIter * );
106static gboolean gtk_q_tree_model_iter_nth_child (GtkTreeModel *,
107 GtkTreeIter *,
108 GtkTreeIter *,
109 gint );
110static gboolean gtk_q_tree_model_iter_parent (GtkTreeModel *,
111 GtkTreeIter *,
112 GtkTreeIter * );
113
114/* implementation prototypes */
115static void qmodelindex_to_iter (const QModelIndex &,
116 GtkTreeIter * );
117// static void gtk_q_tree_model_increment_stamp (GtkQTreeModel * );
118
119static gint gtk_q_tree_model_length (GtkQTreeModel * );
120
121static void gtk_q_tree_model_set_n_columns (GtkQTreeModel *,
122 gint );
123static void gtk_q_tree_model_set_column_type (GtkQTreeModel *,
124 gint,
125 gint,
126 GType );
127
128/* End private prototypes */
129
130/* define type, inherit from GObject, define implemented interface(s) */
131G_DEFINE_TYPE_WITH_CODE (GtkQTreeModel, gtk_q_tree_model, G_TYPE_OBJECT,
132 G_ADD_PRIVATE (GtkQTreeModel)
133 G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL,
134 gtk_q_tree_model_tree_model_init))
135
136#define GTK_Q_TREEMODEL_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_Q_TREE_MODEL, GtkQTreeModelPrivate))
137
138#define Q_ITER(iter) ((QIter *)iter)
139
140static void
141gtk_q_tree_model_class_init(GtkQTreeModelClass *klass)
142{
143 G_OBJECT_CLASS(klass)->finalize = gtk_q_tree_model_finalize;
144}
145
146static void
147gtk_q_tree_model_tree_model_init(GtkTreeModelIface *iface)
148{
149 iface->get_flags = gtk_q_tree_model_get_flags;
150 iface->get_n_columns = gtk_q_tree_model_get_n_columns;
151 iface->get_column_type = gtk_q_tree_model_get_column_type;
152 iface->get_iter = gtk_q_tree_model_get_iter;
153 iface->get_path = gtk_q_tree_model_get_path;
154 iface->get_value = gtk_q_tree_model_get_value;
155 iface->iter_next = gtk_q_tree_model_iter_next;
156 iface->iter_previous = gtk_q_tree_model_iter_previous;
157 iface->iter_children = gtk_q_tree_model_iter_children;
158 iface->iter_has_child = gtk_q_tree_model_iter_has_child;
159 iface->iter_n_children = gtk_q_tree_model_iter_n_children;
160 iface->iter_nth_child = gtk_q_tree_model_iter_nth_child;
161 iface->iter_parent = gtk_q_tree_model_iter_parent;
162}
163
164static void
165gtk_q_tree_model_init(GtkQTreeModel *q_tree_model)
166{
167 GtkQTreeModelPrivate *priv = GTK_Q_TREEMODEL_GET_PRIVATE(q_tree_model);
168 q_tree_model->priv = priv;
169
170 priv->stamp = g_random_int();
171 priv->model = NULL;
172}
173
174/**
175 * gtk_q_tree_model_get_qmodel
176 * returns the original model from which this GtkQTreeModel is created
177 */
178QAbstractItemModel *
179gtk_q_tree_model_get_qmodel(GtkQTreeModel *q_tree_model)
180{
181 GtkQTreeModelPrivate *priv = GTK_Q_TREEMODEL_GET_PRIVATE(q_tree_model);
182 return priv->model->sourceModel();
183}
184
185/**
186 * gtk_q_tree_model_get_source_idx
187 * Returns the index of the original model used to create this GtkQTreeModel from
188 * the given iter, if there is one.
189 */
190QModelIndex
191gtk_q_tree_model_get_source_idx(GtkQTreeModel *q_tree_model, GtkTreeIter *iter)
192{
193 GtkQTreeModelPrivate *priv = GTK_Q_TREEMODEL_GET_PRIVATE(q_tree_model);
194 /* get the call */
195 QIter *qiter = Q_ITER(iter);
196 GtkAccessProxyModel *proxy_model = priv->model;
197 QModelIndex proxy_idx = proxy_model->indexFromId(qiter->row.value, qiter->column.value, qiter->id);
198 if (proxy_idx.isValid()) {
199 /* 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 -0500200 return proxy_model->mapToSource(proxy_idx);
201 } else {
Stepan Salenikovich69771842015-02-24 18:11:45 -0500202 g_warning("could not get valid QModelIndex from given GtkTreeIter");
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500203 return QModelIndex();
204 }
205}
206
207/**
208 * Takes a QModelIndex from the original QAbstractItemModel and returns a valid GtkTreeIter in the corresponding
209 * GtkQTreeModel
210 */
211gboolean
212gtk_q_tree_model_source_index_to_iter(GtkQTreeModel *q_tree_model, const QModelIndex &idx, GtkTreeIter *iter)
213{
214 GtkQTreeModelPrivate *priv = GTK_Q_TREEMODEL_GET_PRIVATE(q_tree_model);
215
Stepan Salenikovich69771842015-02-24 18:11:45 -0500216 /* the the proxy_idx from the source idx */
217 QModelIndex proxy_idx = priv->model->mapFromSource(idx);
218 if (!proxy_idx.isValid())
219 return FALSE;
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500220
221 /* make sure iter is valid */
222 iter->stamp = priv->stamp;
223
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500224 /* map the proxy idx to iter */
225 Q_ITER(iter)->row.value = proxy_idx.row();
Stepan Salenikovich7ff47962015-03-16 15:10:14 -0400226 Q_ITER(iter)->column.value = proxy_idx.column();
Stepan Salenikovichd2dbcee2015-02-27 16:52:28 -0500227 Q_ITER(iter)->id = proxy_idx.internalPointer();
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500228 return TRUE;
229}
230
231/**
232 * gtk_q_tree_model_new:
233 * @model: QAbstractItemModel to which this model will bind.
234 * @n_columns: number of columns in the list store
235 * @...: all #GType follwed by the #Role pair for each column.
236 *
237 * Return value: a new #GtkQTreeModel
238 */
239GtkQTreeModel *
240gtk_q_tree_model_new(QAbstractItemModel *model, size_t n_columns, ...)
241{
242 GtkQTreeModel *retval;
243 va_list args;
244 gint i;
245
246 g_return_val_if_fail(n_columns > 0, NULL);
247
248 retval = (GtkQTreeModel *)g_object_new(GTK_TYPE_Q_TREE_MODEL, NULL);
249 gtk_q_tree_model_set_n_columns(retval, n_columns);
250
251 /* get proxy model from given model */
252 GtkAccessProxyModel* proxy_model = new GtkAccessProxyModel();
253 proxy_model->setSourceModel(model);
254 retval->priv->model = proxy_model;
255 gint stamp = retval->priv->stamp;
256
257 n_columns = 2*n_columns;
258 va_start (args, n_columns);
259
260 for (i = 0; i < (gint)(n_columns/2); i++) {
261 /* first get the role of the QModel */
262 gint role = va_arg(args, gint);
263
264 /* then get the type the role will be interpreted as */
265 GType type = va_arg(args, GType);
266
267 /* TODO: check if its a type supported by our model
268 * if (! _gtk_tree_data_list_check_type (type))
269 * {
270 * g_warning ("%s: Invalid type %s\n", G_STRLOC, g_type_name (type));
271 * g_object_unref (retval);
272 * va_end (args);
273 *
274 * return NULL;
275 * }
276 */
277
278 gtk_q_tree_model_set_column_type (retval, i, role, type);
279 }
280
281 va_end (args);
282
283 gtk_q_tree_model_length(retval);
284
285 /* connect signals */
286 QObject::connect(
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400287 proxy_model,
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500288 &QAbstractItemModel::rowsInserted,
289 [=](const QModelIndex & parent, int first, int last) {
290 for( int row = first; row <= last; row++) {
Stepan Salenikovich8573f862015-08-17 16:54:50 -0400291 GtkTreeIter iter;
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400292 QModelIndex idx = proxy_model->index(row, 0, parent);
Stepan Salenikovich8573f862015-08-17 16:54:50 -0400293 iter.stamp = stamp;
294 qmodelindex_to_iter(idx, &iter);
295 GtkTreePath *path = gtk_q_tree_model_get_path(GTK_TREE_MODEL(retval), &iter);
296 gtk_tree_model_row_inserted(GTK_TREE_MODEL(retval), path, &iter);
Stepan Salenikovichd2dfc162015-08-17 16:40:28 -0400297 gtk_tree_path_free(path);
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500298 }
299 }
300 );
301
302 QObject::connect(
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400303 proxy_model,
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500304 &QAbstractItemModel::rowsAboutToBeMoved,
Stepan Salenikovich69771842015-02-24 18:11:45 -0500305 [=](const QModelIndex & sourceParent, int sourceStart, int sourceEnd,
306 G_GNUC_UNUSED const QModelIndex & destinationParent, G_GNUC_UNUSED int destinationRow) {
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500307 /* first remove the row from old location
308 * then insert them at the new location on the "rowsMoved signal */
309 for( int row = sourceStart; row <= sourceEnd; row++) {
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400310 QModelIndex idx = proxy_model->index(row, 0, sourceParent);
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500311 GtkTreeIter iter_old;
Stepan Salenikovichfcf8fc62015-10-21 11:08:08 -0400312 iter_old.stamp = stamp;
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500313 qmodelindex_to_iter(idx, &iter_old);
314 GtkTreePath *path_old = gtk_q_tree_model_get_path(GTK_TREE_MODEL(retval), &iter_old);
315 gtk_tree_model_row_deleted(GTK_TREE_MODEL(retval), path_old);
Stepan Salenikovichd2dfc162015-08-17 16:40:28 -0400316 gtk_tree_path_free(path_old);
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500317 }
318 }
319 );
320
321 QObject::connect(
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400322 proxy_model,
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500323 &QAbstractItemModel::rowsMoved,
Stepan Salenikovich69771842015-02-24 18:11:45 -0500324 [=](G_GNUC_UNUSED const QModelIndex & sourceParent, int sourceStart, int sourceEnd,
325 const QModelIndex & destinationParent, int destinationRow) {
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500326 /* these rows should have been removed in the "rowsAboutToBeMoved" handler
327 * now insert them in the new location */
328 for( int row = sourceStart; row <= sourceEnd; row++) {
Stepan Salenikovich8573f862015-08-17 16:54:50 -0400329 GtkTreeIter iter_new;
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400330 QModelIndex idx = proxy_model->index(destinationRow, 0, destinationParent);
Stepan Salenikovich8573f862015-08-17 16:54:50 -0400331 iter_new.stamp = stamp;
332 qmodelindex_to_iter(idx, &iter_new);
333 GtkTreePath *path_new = gtk_q_tree_model_get_path(GTK_TREE_MODEL(retval), &iter_new);
334 gtk_tree_model_row_inserted(GTK_TREE_MODEL(retval), path_new, &iter_new);
Stepan Salenikovichd2dfc162015-08-17 16:40:28 -0400335 gtk_tree_path_free(path_new);
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500336 destinationRow++;
337 }
338 }
339 );
340
341 QObject::connect(
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400342 proxy_model,
Stepan Salenikoviche17a2932015-06-11 14:50:02 -0400343 &QAbstractItemModel::rowsRemoved,
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500344 [=](const QModelIndex & parent, int first, int last) {
Stepan Salenikoviche17a2932015-06-11 14:50:02 -0400345 GtkTreePath *parent_path = NULL;
346 if (parent.isValid()) {
347 GtkTreeIter iter_parent;
348 iter_parent.stamp = stamp;
349 qmodelindex_to_iter(parent, &iter_parent);
350 parent_path = gtk_q_tree_model_get_path(GTK_TREE_MODEL(retval), &iter_parent);
351 } else {
352 parent_path = gtk_tree_path_new();
353 }
354
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400355 /* go last to first, since the rows are being deleted */
356 for( int row = last; row >= first; --row) {
357 // g_debug("deleting row: %d", row);
Stepan Salenikoviche17a2932015-06-11 14:50:02 -0400358 GtkTreePath *path = gtk_tree_path_copy(parent_path);
359 gtk_tree_path_append_index(path, row);
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500360 gtk_tree_model_row_deleted(GTK_TREE_MODEL(retval), path);
Stepan Salenikovichd2dfc162015-08-17 16:40:28 -0400361 gtk_tree_path_free(path);
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500362 }
Stepan Salenikoviche17a2932015-06-11 14:50:02 -0400363
364 gtk_tree_path_free(parent_path);
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500365 }
366 );
367
368 QObject::connect(
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400369 proxy_model,
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500370 &QAbstractItemModel::dataChanged,
Stepan Salenikovich69771842015-02-24 18:11:45 -0500371 [=](const QModelIndex & topLeft, const QModelIndex & bottomRight,
372 G_GNUC_UNUSED const QVector<int> & roles = QVector<int> ()) {
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500373 /* we have to assume only one column */
374 int first = topLeft.row();
375 int last = bottomRight.row();
376 if (topLeft.column() != bottomRight.column() ) {
377 g_warning("more than one column is not supported!");
378 }
379 /* the first idx IS topLeft, the reset are his siblings */
Stepan Salenikovich8573f862015-08-17 16:54:50 -0400380 GtkTreeIter iter;
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500381 QModelIndex idx = topLeft;
Stepan Salenikovich8573f862015-08-17 16:54:50 -0400382 iter.stamp = stamp;
383 qmodelindex_to_iter(idx, &iter);
384 GtkTreePath *path = gtk_q_tree_model_get_path(GTK_TREE_MODEL(retval), &iter);
385 gtk_tree_model_row_changed(GTK_TREE_MODEL(retval), path, &iter);
Stepan Salenikovichd2dfc162015-08-17 16:40:28 -0400386 gtk_tree_path_free(path);
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500387 for( int row = first + 1; row <= last; row++) {
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500388 idx = topLeft.sibling(row, 0);
Stepan Salenikovich8573f862015-08-17 16:54:50 -0400389 qmodelindex_to_iter(idx, &iter);
390 path = gtk_q_tree_model_get_path(GTK_TREE_MODEL(retval), &iter);
391 gtk_tree_model_row_changed(GTK_TREE_MODEL(retval), path, &iter);
Stepan Salenikovichd2dfc162015-08-17 16:40:28 -0400392 gtk_tree_path_free(path);
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500393 }
394 }
395 );
396
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400397 QObject::connect(
398 proxy_model,
399 &QAbstractItemModel::modelAboutToBeReset,
400 [=] () {
401 // g_debug("model about to be reset");
402 /* nothing equvivalent eixists in GtkTreeModel, so simply delete all
403 * rows, and add all rows when the model is reset;
404 * we must delete the rows in ascending order */
405 int row_count = proxy_model->rowCount();
406 for (int row = row_count; row > 0; --row) {
407 // g_debug("deleting row %d", row -1);
408 QModelIndex idx = proxy_model->index(row - 1, 0);
409 GtkTreeIter iter;
410 iter.stamp = stamp;
411 qmodelindex_to_iter(idx, &iter);
412 GtkTreePath *path = gtk_q_tree_model_get_path(GTK_TREE_MODEL(retval), &iter);
413 gtk_tree_model_row_deleted(GTK_TREE_MODEL(retval), path);
Stepan Salenikovichd2dfc162015-08-17 16:40:28 -0400414 gtk_tree_path_free(path);
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400415 }
416 }
417 );
418
419 QObject::connect(
420 proxy_model,
421 &QAbstractItemModel::modelReset,
422 [=] () {
423 // g_debug("model reset");
424 /* now add all the (new) rows */
425 int row_count = proxy_model->rowCount();
426 for (int row = 0; row < row_count; row++) {
427 // g_debug("adding row %d", row);
Stepan Salenikovich8573f862015-08-17 16:54:50 -0400428 GtkTreeIter iter_new;
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400429 QModelIndex idx = proxy_model->index(row, 0);
Stepan Salenikovich8573f862015-08-17 16:54:50 -0400430 iter_new.stamp = stamp;
431 qmodelindex_to_iter(idx, &iter_new);
432 GtkTreePath *path_new = gtk_q_tree_model_get_path(GTK_TREE_MODEL(retval), &iter_new);
433 gtk_tree_model_row_inserted(GTK_TREE_MODEL(retval), path_new, &iter_new);
Stepan Salenikovichd2dfc162015-08-17 16:40:28 -0400434 gtk_tree_path_free(path_new);
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400435 }
436 }
437 );
438
Stepan Salenikovich5d3506e2015-03-30 11:01:29 -0400439// QObject::connect(
440// proxy_model,
441// &QAbstractItemModel::layoutAboutToBeChanged,
442// [=] () {
443// // g_debug("layout about to be changed");
444// /* nothing equvivalent eixists in GtkTreeModel, so simply delete all
445// * rows, and add all rows when the model is reset;
446// * we must delete the rows in ascending order */
447// int row_count = proxy_model->rowCount();
448// for (int row = row_count; row > 0; --row) {
449// // g_debug("deleting row %d", row -1);
450// QModelIndex idx = proxy_model->index(row - 1, 0);
451// GtkTreeIter iter;
452// iter.stamp = stamp;
453// qmodelindex_to_iter(idx, &iter);
454// GtkTreePath *path = gtk_q_tree_model_get_path(GTK_TREE_MODEL(retval), &iter);
455// gtk_tree_model_row_deleted(GTK_TREE_MODEL(retval), path);
456// }
457// }
458// );
459//
460// QObject::connect(
461// proxy_model,
462// &QAbstractItemModel::layoutChanged,
463// [=] () {
464// // g_debug("layout changed");
465// /* now add all the (new) rows */
466// int row_count = proxy_model->rowCount();
467// for (int row = 0; row < row_count; row++) {
468// // g_debug("adding row %d", row);
469// GtkTreeIter *iter_new = g_new0(GtkTreeIter, 1);
470// QModelIndex idx = proxy_model->index(row, 0);
471// iter_new->stamp = stamp;
472// qmodelindex_to_iter(idx, iter_new);
473// GtkTreePath *path_new = gtk_q_tree_model_get_path(GTK_TREE_MODEL(retval), iter_new);
474// gtk_tree_model_row_inserted(GTK_TREE_MODEL(retval), path_new, iter_new);
475// }
476// }
477// );
478
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500479 return retval;
480}
481
482static gint
483gtk_q_tree_model_length(GtkQTreeModel *q_tree_model)
484{
485 GtkQTreeModelPrivate *priv = q_tree_model->priv;
486 return priv->model->rowCount();
487}
488
489static void
490gtk_q_tree_model_set_n_columns(GtkQTreeModel *q_tree_model,
491 gint n_columns)
492{
493 GtkQTreeModelPrivate *priv = q_tree_model->priv;
494 int i;
495
496 if (priv->n_columns == n_columns)
497 return;
498
499 priv->column_headers = g_renew (GType, priv->column_headers, n_columns);
500 for (i = priv->n_columns; i < n_columns; i++)
501 priv->column_headers[i] = G_TYPE_INVALID;
502 priv->n_columns = n_columns;
503
504 priv->column_roles = g_renew (gint, priv->column_roles, n_columns);
505 for (i = priv->n_columns; i < n_columns; i++)
506 priv->column_roles[i] = -1;
507}
508
509static void
510gtk_q_tree_model_set_column_type(GtkQTreeModel *q_tree_model,
511 gint column,
512 gint role,
513 GType type)
514{
515 GtkQTreeModelPrivate *priv = q_tree_model->priv;
516
517 /* TODO: check if its a type supported by our model
518 * if (!_gtk_tree_data_list_check_type (type))
519 * {
520 * g_warning ("%s: Invalid type %s\n", G_STRLOC, g_type_name (type));
521 * return;
522 * }
523 */
524
525 priv->column_headers[column] = type;
526 priv->column_roles[column] = role;
527}
528
529
530static void
531gtk_q_tree_model_finalize(GObject *object)
532{
533 GtkQTreeModel *q_tree_model = GTK_Q_TREE_MODEL (object);
534 GtkQTreeModelPrivate *priv = q_tree_model->priv;
535
536 g_free(priv->column_headers);
537 g_free(priv->column_roles);
538
539 /* delete the created proxy model */
540 delete priv->model;
541
542 G_OBJECT_CLASS(gtk_q_tree_model_parent_class)->finalize (object);
543}
544
545static gboolean
546iter_is_valid(GtkTreeIter *iter,
547 GtkQTreeModel *q_tree_model)
548{
549 gboolean retval;
550 g_return_val_if_fail(iter != NULL, FALSE);
551 QIter *qiter = Q_ITER(iter);
552 retval = q_tree_model->priv->model->indexFromId(qiter->row.value, qiter->column.value, qiter->id).isValid();
553 return retval;
554}
555
556static void
557qmodelindex_to_iter(const QModelIndex &idx, GtkTreeIter *iter)
558{
559 Q_ITER(iter)->row.value = idx.row();
Stepan Salenikovich7ff47962015-03-16 15:10:14 -0400560 Q_ITER(iter)->column.value = idx.column();
Stepan Salenikovichd2dbcee2015-02-27 16:52:28 -0500561 Q_ITER(iter)->id = idx.internalPointer();
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500562}
563
564static gboolean
565validate_index(gint stamp, const QModelIndex &idx, GtkTreeIter *iter)
566{
567 if (idx.isValid()) {
568 iter->stamp = stamp;
569 qmodelindex_to_iter(idx, iter);
570 } else {
571 iter->stamp = 0;
572 return FALSE;
573 }
574 return TRUE;
575}
576
577/* Start Fulfill the GtkTreeModel requirements */
578
579/* flags supported by this interface */
580static GtkTreeModelFlags
581gtk_q_tree_model_get_flags(G_GNUC_UNUSED GtkTreeModel *tree_model)
582{
583 // TODO: possibly return based on the model?
584 return (GtkTreeModelFlags)(GTK_TREE_MODEL_ITERS_PERSIST);
585}
586
587/* number of columns supported by this tree model */
588static gint
589gtk_q_tree_model_get_n_columns(GtkTreeModel *tree_model)
590{
591 GtkQTreeModel *q_tree_model = GTK_Q_TREE_MODEL(tree_model);
592 GtkQTreeModelPrivate *priv = q_tree_model->priv;
593
594 return priv->model->columnCount();
595}
596
597/* get given column type */
598static GType
599gtk_q_tree_model_get_column_type(GtkTreeModel *tree_model,
600 gint index)
601{
602 GtkQTreeModel *q_tree_model = GTK_Q_TREE_MODEL (tree_model);
603 GtkQTreeModelPrivate *priv = q_tree_model->priv;
604
605 g_return_val_if_fail (index < gtk_q_tree_model_get_n_columns(tree_model), G_TYPE_INVALID);
606
607 return priv->column_headers[index];
608}
609
610/* Sets @iter to a valid iterator pointing to @path. If @path does
611 * not exist, @iter is set to an invalid iterator and %FALSE is returned.
612 */
613static gboolean
614gtk_q_tree_model_get_iter(GtkTreeModel *tree_model,
615 GtkTreeIter *iter,
616 GtkTreePath *path)
617{
618 GtkQTreeModel *q_tree_model = GTK_Q_TREE_MODEL (tree_model);
619 GtkQTreeModelPrivate *priv = q_tree_model->priv;
620
621 /* GtkTreePath is a list of indices, each one indicates
622 * the child of the previous one.
623 * Since GtkTreeModel only has rows (columns are only used to
624 * store data in each item), each index is basically the row
625 * at the given tree level.
626 * To get the iter, we want to get the QModelIndex. To get
627 * the QModelIndex we simply start at the first level and
628 * traverse the model the number of layers equal to the number
629 * of indices in the path.
630 */
631 gint depth;
632 gint* indices = gtk_tree_path_get_indices_with_depth(path, &depth);
633 QModelIndex idx = priv->model->index(indices[0], 0);
634 for(int layer = 1; layer < depth; layer++ ) {
635 /* check if previous iter is valid */
636 if (!idx.isValid()) {
637 break;
638 } else {
639 idx = idx.child(indices[layer], 0);
640 }
641 }
642
643 if (!idx.isValid()) {
644 iter->stamp = 0;
645 return FALSE;
646 } else {
647 /* we have a valid QModelIndex; turn it into an iter */
648 iter->stamp = priv->stamp;
649 qmodelindex_to_iter(idx, iter);
650 }
651
652 return TRUE;
653}
654
655/* Returns a newly-created #GtkTreePath referenced by @iter.
656 *
657 * This path should be freed with gtk_tree_path_free().
658 */
659static GtkTreePath *
660gtk_q_tree_model_get_path(GtkTreeModel *tree_model,
661 GtkTreeIter *iter)
662{
663 GtkQTreeModel *q_tree_model = GTK_Q_TREE_MODEL (tree_model);
664 GtkQTreeModelPrivate *priv = q_tree_model->priv;
665 GtkTreePath *path;
666
667 g_return_val_if_fail (iter->stamp == priv->stamp, NULL);
668 g_return_val_if_fail (iter_is_valid(iter, q_tree_model), NULL);
669
670 /* To get the path, we have to traverse from the child all the way up */
671 path = gtk_tree_path_new();
672 QIter *qiter = Q_ITER(iter);
673 QModelIndex idx = priv->model->indexFromId(qiter->row.value, qiter->column.value, qiter->id);
674 while( idx.isValid() ){
675 gtk_tree_path_prepend_index(path, idx.row());
676 idx = idx.parent();
677 }
678 return path;
679}
680
681static inline GType
682get_fundamental_type(GType type)
683{
684 GType result;
685
686 result = G_TYPE_FUNDAMENTAL (type);
687
688 if (result == G_TYPE_INTERFACE) {
689 if (g_type_is_a (type, G_TYPE_OBJECT))
690 result = G_TYPE_OBJECT;
691 }
692
693 return result;
694}
695
696/* Initializes and sets @value to that at @column. */
697static void
698gtk_q_tree_model_get_value(GtkTreeModel *tree_model,
699 GtkTreeIter *iter,
700 gint column,
701 GValue *value)
702{
703 GtkQTreeModel *q_tree_model = GTK_Q_TREE_MODEL (tree_model);
704 GtkQTreeModelPrivate *priv = q_tree_model->priv;
705
706 g_return_if_fail (column < priv->n_columns);
707 g_return_if_fail (iter_is_valid(iter, q_tree_model));
708
709 /* get the data */
710 QIter *qiter = Q_ITER(iter);
711 QModelIndex idx = priv->model->indexFromId(qiter->row.value, qiter->column.value, qiter->id);
712 int role = priv->column_roles[column];
713 QVariant var = priv->model->data(idx, role);
714 GType type = priv->column_headers[column];
715 g_value_init (value, type);
716 switch (get_fundamental_type (type))
717 {
718 /* TODO: implement the rest of the data types the we plan to support
719 * otherwise get rid of the code and make sure un-implemented types
720 * are not allowed
721 */
722 case G_TYPE_BOOLEAN:
723 g_value_set_boolean (value, (gboolean) var.toBool());
724 break;
725 // case G_TYPE_CHAR:
726 // g_value_set_schar (value, (gchar) list->data.v_char);
727 // break;
728 // case G_TYPE_UCHAR:
729 // g_value_set_uchar (value, (guchar) list->data.v_uchar);
730 // break;
731 case G_TYPE_INT:
732 g_value_set_int (value, (gint) var.toInt());
733 break;
734 case G_TYPE_UINT:
735 g_value_set_uint (value, (guint) var.toUInt());
736 break;
737 // case G_TYPE_LONG:
738 // g_value_set_long (value, list->data.v_long);
739 // break;
740 // case G_TYPE_ULONG:
741 // g_value_set_ulong (value, list->data.v_ulong);
742 // break;
743 // case G_TYPE_INT64:
744 // g_value_set_int64 (value, list->data.v_int64);
745 // break;
746 // case G_TYPE_UINT64:
747 // g_value_set_uint64 (value, list->data.v_uint64);
748 // break;
749 // case G_TYPE_ENUM:
750 // g_value_set_enum (value, list->data.v_int);
751 // break;
752 // case G_TYPE_FLAGS:
753 // g_value_set_flags (value, list->data.v_uint);
754 // break;
755 // case G_TYPE_FLOAT:
756 // g_value_set_float (value, (gfloat) list->data.v_float);
757 // break;
758 // case G_TYPE_DOUBLE:
759 // g_value_set_double (value, (gdouble) list->data.v_double);
760 // break;
761 case G_TYPE_STRING:
Stepan Salenikovich69771842015-02-24 18:11:45 -0500762 {
Stepan Salenikovich6f687072015-03-26 10:43:37 -0400763 QByteArray ba = var.toString().toUtf8();
764 g_value_set_string(value, (gchar *)var.toByteArray().constData());
Stepan Salenikovich69771842015-02-24 18:11:45 -0500765 }
766 break;
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500767 // case G_TYPE_POINTER:
768 // g_value_set_pointer (value, (gpointer) list->data.v_pointer);
769 // break;
770 // case G_TYPE_BOXED:
771 // g_value_set_boxed (value, (gpointer) list->data.v_pointer);
772 // break;
773 // case G_TYPE_VARIANT:
774 // g_value_set_variant (value, (gpointer) list->data.v_pointer);
775 // break;
776 // case G_TYPE_OBJECT:
777 // g_value_set_object (value, (GObject *) list->data.v_pointer);
778 // break;
779 default:
780 g_warning ("%s: Unsupported type (%s) retrieved.", G_STRLOC, g_type_name (value->g_type));
781 break;
782 }
783
784 return;
785}
786
787/* Sets @iter to point to the node following it at the current level.
788 *
789 * If there is no next @iter, %FALSE is returned and @iter is set
790 * to be invalid.
791 */
792static gboolean
793gtk_q_tree_model_iter_next(GtkTreeModel *tree_model,
794 GtkTreeIter *iter)
795{
796 GtkQTreeModel *q_tree_model = GTK_Q_TREE_MODEL (tree_model);
797 GtkQTreeModelPrivate *priv = q_tree_model->priv;
798
799 g_return_val_if_fail (iter_is_valid(iter, q_tree_model), FALSE);
800
801 QIter *qiter = Q_ITER(iter);
802 QModelIndex idx = priv->model->indexFromId(qiter->row.value, qiter->column.value, qiter->id);
803 idx = idx.sibling(idx.row()+1, idx.column());
804
805 /* validate */
806 if (validate_index(priv->stamp, idx, iter) ) {
807 GtkTreePath *path = gtk_q_tree_model_get_path(tree_model, iter);
808 gtk_tree_path_free(path);
809 return TRUE;
810 } else {
811 return FALSE;
812 }
813}
814
815/* Sets @iter to point to the previous node at the current level.
816 *
817 * If there is no previous @iter, %FALSE is returned and @iter is
818 * set to be invalid.
819 */
820static gboolean
821gtk_q_tree_model_iter_previous(GtkTreeModel *tree_model,
822 GtkTreeIter *iter)
823{
824 GtkQTreeModel *q_tree_model = GTK_Q_TREE_MODEL (tree_model);
825 GtkQTreeModelPrivate *priv = q_tree_model->priv;
826
827 g_return_val_if_fail (iter_is_valid(iter, q_tree_model), FALSE);
828
829 QIter *qiter = Q_ITER(iter);
830 QModelIndex idx = priv->model->indexFromId(qiter->row.value, qiter->column.value, qiter->id);
831 idx = idx.sibling(idx.row()-1, idx.column());
832
833 /* validate */
834 return validate_index(priv->stamp, idx, iter);
835}
836
837/* Sets @iter to point to the first child of @parent.
838 *
839 * If @parent has no children, %FALSE is returned and @iter is
840 * set to be invalid. @parent will remain a valid node after this
841 * function has been called.
842 *
843 * If @parent is %NULL returns the first node
844 */
845static gboolean
846gtk_q_tree_model_iter_children(GtkTreeModel *tree_model,
847 GtkTreeIter *iter,
848 GtkTreeIter *parent)
849{
850 GtkQTreeModel *q_tree_model = GTK_Q_TREE_MODEL (tree_model);
851 GtkQTreeModelPrivate *priv = q_tree_model->priv;
852 QModelIndex idx;
853
854 /* make sure parent if valid node if its not NULL */
855 if (parent)
856 g_return_val_if_fail(iter_is_valid(parent, q_tree_model), FALSE);
857
858 if (parent) {
859 /* get first child */
860 QIter *qparent = Q_ITER(parent);
861 idx = priv->model->indexFromId(qparent->row.value, qparent->column.value, qparent->id);
862 idx = idx.child(0, 0);
863 } else {
864 /* parent is NULL, get the first node */
865 idx = priv->model->index(0, 0);
866 }
867
868 /* validate child */
869 return validate_index(priv->stamp, idx, iter);
870}
871
872/* Returns %TRUE if @iter has children, %FALSE otherwise. */
873static gboolean
874gtk_q_tree_model_iter_has_child (GtkTreeModel *tree_model,
875 GtkTreeIter *iter)
876{
877 GtkQTreeModel *q_tree_model = GTK_Q_TREE_MODEL (tree_model);
878 GtkQTreeModelPrivate *priv = q_tree_model->priv;
879 g_return_val_if_fail(iter_is_valid(iter, q_tree_model), FALSE);
880
881 QIter *qiter = Q_ITER(iter);
882 QModelIndex idx = priv->model->indexFromId(qiter->row.value, qiter->column.value, qiter->id);
883 return priv->model->hasChildren(idx);
884}
885
886/* Returns the number of children that @iter has.
887 *
888 * As a special case, if @iter is %NULL, then the number
889 * of toplevel nodes is returned.
890 */
891static gint
892gtk_q_tree_model_iter_n_children (GtkTreeModel *tree_model,
893 GtkTreeIter *iter)
894{
895 GtkQTreeModel *q_tree_model = GTK_Q_TREE_MODEL (tree_model);
896 GtkQTreeModelPrivate *priv = q_tree_model->priv;
897
898 if (iter == NULL)
899 return gtk_q_tree_model_length(q_tree_model);
900
901 g_return_val_if_fail(iter_is_valid(iter, q_tree_model), -1);
902 QIter *qiter = Q_ITER(iter);
903 QModelIndex idx = priv->model->indexFromId(qiter->row.value, qiter->column.value, qiter->id);
904 return priv->model->rowCount(idx);
905}
906
907/* Sets @iter to be the child of @parent, using the given index.
908 *
909 * The first index is 0. If @n is too big, or @parent has no children,
910 * @iter is set to an invalid iterator and %FALSE is returned. @parent
911 * will remain a valid node after this function has been called. As a
912 * special case, if @parent is %NULL, then the @n<!-- -->th root node
913 * is set.
914 */
915static gboolean
916gtk_q_tree_model_iter_nth_child(GtkTreeModel *tree_model,
917 GtkTreeIter *iter,
918 GtkTreeIter *parent,
919 gint n)
920{
921 GtkQTreeModel *q_tree_model = GTK_Q_TREE_MODEL (tree_model);
922 GtkQTreeModelPrivate *priv = q_tree_model->priv;
923 QModelIndex idx;
924
925 if (parent) {
926 g_return_val_if_fail(iter_is_valid(parent, q_tree_model), FALSE);
927
928 /* get the nth child */
929 QIter *qparent = Q_ITER(parent);
930 idx = priv->model->indexFromId(qparent->row.value, qparent->column.value, qparent->id);
931 idx = idx.child(n, 0);
932 } else {
933 idx = priv->model->index(n, 0);
934 }
935
936 /* validate */
937 return validate_index(priv->stamp, idx, iter);
938}
939
940/* Sets @iter to be the parent of @child.
941 *
942 * If @child is at the toplevel, and doesn't have a parent, then
943 * @iter is set to an invalid iterator and %FALSE is returned.
944 * @child will remain a valid node after this function has been
945 * called.
946 */
947static gboolean
948gtk_q_tree_model_iter_parent(GtkTreeModel *tree_model,
949 GtkTreeIter *iter,
950 GtkTreeIter *child)
951{
952 GtkQTreeModel *q_tree_model = GTK_Q_TREE_MODEL (tree_model);
953 GtkQTreeModelPrivate *priv = q_tree_model->priv;
954 QModelIndex idx;
955
956 g_return_val_if_fail(iter_is_valid(child, q_tree_model), FALSE);
957
958 QIter *qchild = Q_ITER(child);
959 idx = priv->model->indexFromId(qchild->row.value, qchild->column.value, qchild->id);
960 idx = idx.parent();
961
962 /* validate */
963 return validate_index(priv->stamp, idx, iter);
964}
965
Stepan Salenikovich69771842015-02-24 18:11:45 -0500966/* End Fulfill the GtkTreeModel requirements */