blob: 37b470a86345bfc08e77a5774e5e251fb2466f60 [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++) {
291 GtkTreeIter *iter = g_new0(GtkTreeIter, 1);
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400292 QModelIndex idx = proxy_model->index(row, 0, parent);
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500293 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;
312 qmodelindex_to_iter(idx, &iter_old);
313 GtkTreePath *path_old = gtk_q_tree_model_get_path(GTK_TREE_MODEL(retval), &iter_old);
314 gtk_tree_model_row_deleted(GTK_TREE_MODEL(retval), path_old);
Stepan Salenikovichd2dfc162015-08-17 16:40:28 -0400315 gtk_tree_path_free(path_old);
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500316 }
317 }
318 );
319
320 QObject::connect(
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400321 proxy_model,
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500322 &QAbstractItemModel::rowsMoved,
Stepan Salenikovich69771842015-02-24 18:11:45 -0500323 [=](G_GNUC_UNUSED const QModelIndex & sourceParent, int sourceStart, int sourceEnd,
324 const QModelIndex & destinationParent, int destinationRow) {
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500325 /* these rows should have been removed in the "rowsAboutToBeMoved" handler
326 * now insert them in the new location */
327 for( int row = sourceStart; row <= sourceEnd; row++) {
328 GtkTreeIter *iter_new = g_new0(GtkTreeIter, 1);
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400329 QModelIndex idx = proxy_model->index(destinationRow, 0, destinationParent);
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500330 iter_new->stamp = stamp;
331 qmodelindex_to_iter(idx, iter_new);
332 GtkTreePath *path_new = gtk_q_tree_model_get_path(GTK_TREE_MODEL(retval), iter_new);
333 gtk_tree_model_row_inserted(GTK_TREE_MODEL(retval), path_new, iter_new);
Stepan Salenikovichd2dfc162015-08-17 16:40:28 -0400334 gtk_tree_path_free(path_new);
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500335 destinationRow++;
336 }
337 }
338 );
339
340 QObject::connect(
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400341 proxy_model,
Stepan Salenikoviche17a2932015-06-11 14:50:02 -0400342 &QAbstractItemModel::rowsRemoved,
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500343 [=](const QModelIndex & parent, int first, int last) {
Stepan Salenikoviche17a2932015-06-11 14:50:02 -0400344 GtkTreePath *parent_path = NULL;
345 if (parent.isValid()) {
346 GtkTreeIter iter_parent;
347 iter_parent.stamp = stamp;
348 qmodelindex_to_iter(parent, &iter_parent);
349 parent_path = gtk_q_tree_model_get_path(GTK_TREE_MODEL(retval), &iter_parent);
350 } else {
351 parent_path = gtk_tree_path_new();
352 }
353
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400354 /* go last to first, since the rows are being deleted */
355 for( int row = last; row >= first; --row) {
356 // g_debug("deleting row: %d", row);
Stepan Salenikoviche17a2932015-06-11 14:50:02 -0400357 GtkTreePath *path = gtk_tree_path_copy(parent_path);
358 gtk_tree_path_append_index(path, row);
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500359 gtk_tree_model_row_deleted(GTK_TREE_MODEL(retval), path);
Stepan Salenikovichd2dfc162015-08-17 16:40:28 -0400360 gtk_tree_path_free(path);
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500361 }
Stepan Salenikoviche17a2932015-06-11 14:50:02 -0400362
363 gtk_tree_path_free(parent_path);
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500364 }
365 );
366
367 QObject::connect(
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400368 proxy_model,
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500369 &QAbstractItemModel::dataChanged,
Stepan Salenikovich69771842015-02-24 18:11:45 -0500370 [=](const QModelIndex & topLeft, const QModelIndex & bottomRight,
371 G_GNUC_UNUSED const QVector<int> & roles = QVector<int> ()) {
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500372 /* we have to assume only one column */
373 int first = topLeft.row();
374 int last = bottomRight.row();
375 if (topLeft.column() != bottomRight.column() ) {
376 g_warning("more than one column is not supported!");
377 }
378 /* the first idx IS topLeft, the reset are his siblings */
379 GtkTreeIter *iter = g_new0(GtkTreeIter, 1);
380 QModelIndex idx = topLeft;
381 iter->stamp = stamp;
382 qmodelindex_to_iter(idx, iter);
383 GtkTreePath *path = gtk_q_tree_model_get_path(GTK_TREE_MODEL(retval), iter);
384 gtk_tree_model_row_changed(GTK_TREE_MODEL(retval), path, iter);
Stepan Salenikovichd2dfc162015-08-17 16:40:28 -0400385 gtk_tree_path_free(path);
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500386 for( int row = first + 1; row <= last; row++) {
387 iter = g_new0(GtkTreeIter, 1);
388 idx = topLeft.sibling(row, 0);
389 iter->stamp = stamp;
390 qmodelindex_to_iter(idx, iter);
391 path = gtk_q_tree_model_get_path(GTK_TREE_MODEL(retval), iter);
392 gtk_tree_model_row_changed(GTK_TREE_MODEL(retval), path, iter);
Stepan Salenikovichd2dfc162015-08-17 16:40:28 -0400393 gtk_tree_path_free(path);
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500394 }
395 }
396 );
397
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400398 QObject::connect(
399 proxy_model,
400 &QAbstractItemModel::modelAboutToBeReset,
401 [=] () {
402 // g_debug("model about to be reset");
403 /* nothing equvivalent eixists in GtkTreeModel, so simply delete all
404 * rows, and add all rows when the model is reset;
405 * we must delete the rows in ascending order */
406 int row_count = proxy_model->rowCount();
407 for (int row = row_count; row > 0; --row) {
408 // g_debug("deleting row %d", row -1);
409 QModelIndex idx = proxy_model->index(row - 1, 0);
410 GtkTreeIter iter;
411 iter.stamp = stamp;
412 qmodelindex_to_iter(idx, &iter);
413 GtkTreePath *path = gtk_q_tree_model_get_path(GTK_TREE_MODEL(retval), &iter);
414 gtk_tree_model_row_deleted(GTK_TREE_MODEL(retval), path);
Stepan Salenikovichd2dfc162015-08-17 16:40:28 -0400415 gtk_tree_path_free(path);
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400416 }
417 }
418 );
419
420 QObject::connect(
421 proxy_model,
422 &QAbstractItemModel::modelReset,
423 [=] () {
424 // g_debug("model reset");
425 /* now add all the (new) rows */
426 int row_count = proxy_model->rowCount();
427 for (int row = 0; row < row_count; row++) {
428 // g_debug("adding row %d", row);
429 GtkTreeIter *iter_new = g_new0(GtkTreeIter, 1);
430 QModelIndex idx = proxy_model->index(row, 0);
431 iter_new->stamp = stamp;
432 qmodelindex_to_iter(idx, iter_new);
433 GtkTreePath *path_new = gtk_q_tree_model_get_path(GTK_TREE_MODEL(retval), iter_new);
434 gtk_tree_model_row_inserted(GTK_TREE_MODEL(retval), path_new, iter_new);
Stepan Salenikovichd2dfc162015-08-17 16:40:28 -0400435 gtk_tree_path_free(path_new);
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400436 }
437 }
438 );
439
Stepan Salenikovich5d3506e2015-03-30 11:01:29 -0400440// QObject::connect(
441// proxy_model,
442// &QAbstractItemModel::layoutAboutToBeChanged,
443// [=] () {
444// // g_debug("layout about to be changed");
445// /* nothing equvivalent eixists in GtkTreeModel, so simply delete all
446// * rows, and add all rows when the model is reset;
447// * we must delete the rows in ascending order */
448// int row_count = proxy_model->rowCount();
449// for (int row = row_count; row > 0; --row) {
450// // g_debug("deleting row %d", row -1);
451// QModelIndex idx = proxy_model->index(row - 1, 0);
452// GtkTreeIter iter;
453// iter.stamp = stamp;
454// qmodelindex_to_iter(idx, &iter);
455// GtkTreePath *path = gtk_q_tree_model_get_path(GTK_TREE_MODEL(retval), &iter);
456// gtk_tree_model_row_deleted(GTK_TREE_MODEL(retval), path);
457// }
458// }
459// );
460//
461// QObject::connect(
462// proxy_model,
463// &QAbstractItemModel::layoutChanged,
464// [=] () {
465// // g_debug("layout changed");
466// /* now add all the (new) rows */
467// int row_count = proxy_model->rowCount();
468// for (int row = 0; row < row_count; row++) {
469// // g_debug("adding row %d", row);
470// GtkTreeIter *iter_new = g_new0(GtkTreeIter, 1);
471// QModelIndex idx = proxy_model->index(row, 0);
472// iter_new->stamp = stamp;
473// qmodelindex_to_iter(idx, iter_new);
474// GtkTreePath *path_new = gtk_q_tree_model_get_path(GTK_TREE_MODEL(retval), iter_new);
475// gtk_tree_model_row_inserted(GTK_TREE_MODEL(retval), path_new, iter_new);
476// }
477// }
478// );
479
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500480 return retval;
481}
482
483static gint
484gtk_q_tree_model_length(GtkQTreeModel *q_tree_model)
485{
486 GtkQTreeModelPrivate *priv = q_tree_model->priv;
487 return priv->model->rowCount();
488}
489
490static void
491gtk_q_tree_model_set_n_columns(GtkQTreeModel *q_tree_model,
492 gint n_columns)
493{
494 GtkQTreeModelPrivate *priv = q_tree_model->priv;
495 int i;
496
497 if (priv->n_columns == n_columns)
498 return;
499
500 priv->column_headers = g_renew (GType, priv->column_headers, n_columns);
501 for (i = priv->n_columns; i < n_columns; i++)
502 priv->column_headers[i] = G_TYPE_INVALID;
503 priv->n_columns = n_columns;
504
505 priv->column_roles = g_renew (gint, priv->column_roles, n_columns);
506 for (i = priv->n_columns; i < n_columns; i++)
507 priv->column_roles[i] = -1;
508}
509
510static void
511gtk_q_tree_model_set_column_type(GtkQTreeModel *q_tree_model,
512 gint column,
513 gint role,
514 GType type)
515{
516 GtkQTreeModelPrivate *priv = q_tree_model->priv;
517
518 /* TODO: check if its a type supported by our model
519 * if (!_gtk_tree_data_list_check_type (type))
520 * {
521 * g_warning ("%s: Invalid type %s\n", G_STRLOC, g_type_name (type));
522 * return;
523 * }
524 */
525
526 priv->column_headers[column] = type;
527 priv->column_roles[column] = role;
528}
529
530
531static void
532gtk_q_tree_model_finalize(GObject *object)
533{
534 GtkQTreeModel *q_tree_model = GTK_Q_TREE_MODEL (object);
535 GtkQTreeModelPrivate *priv = q_tree_model->priv;
536
537 g_free(priv->column_headers);
538 g_free(priv->column_roles);
539
540 /* delete the created proxy model */
541 delete priv->model;
542
543 G_OBJECT_CLASS(gtk_q_tree_model_parent_class)->finalize (object);
544}
545
546static gboolean
547iter_is_valid(GtkTreeIter *iter,
548 GtkQTreeModel *q_tree_model)
549{
550 gboolean retval;
551 g_return_val_if_fail(iter != NULL, FALSE);
552 QIter *qiter = Q_ITER(iter);
553 retval = q_tree_model->priv->model->indexFromId(qiter->row.value, qiter->column.value, qiter->id).isValid();
554 return retval;
555}
556
557static void
558qmodelindex_to_iter(const QModelIndex &idx, GtkTreeIter *iter)
559{
560 Q_ITER(iter)->row.value = idx.row();
Stepan Salenikovich7ff47962015-03-16 15:10:14 -0400561 Q_ITER(iter)->column.value = idx.column();
Stepan Salenikovichd2dbcee2015-02-27 16:52:28 -0500562 Q_ITER(iter)->id = idx.internalPointer();
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500563}
564
565static gboolean
566validate_index(gint stamp, const QModelIndex &idx, GtkTreeIter *iter)
567{
568 if (idx.isValid()) {
569 iter->stamp = stamp;
570 qmodelindex_to_iter(idx, iter);
571 } else {
572 iter->stamp = 0;
573 return FALSE;
574 }
575 return TRUE;
576}
577
578/* Start Fulfill the GtkTreeModel requirements */
579
580/* flags supported by this interface */
581static GtkTreeModelFlags
582gtk_q_tree_model_get_flags(G_GNUC_UNUSED GtkTreeModel *tree_model)
583{
584 // TODO: possibly return based on the model?
585 return (GtkTreeModelFlags)(GTK_TREE_MODEL_ITERS_PERSIST);
586}
587
588/* number of columns supported by this tree model */
589static gint
590gtk_q_tree_model_get_n_columns(GtkTreeModel *tree_model)
591{
592 GtkQTreeModel *q_tree_model = GTK_Q_TREE_MODEL(tree_model);
593 GtkQTreeModelPrivate *priv = q_tree_model->priv;
594
595 return priv->model->columnCount();
596}
597
598/* get given column type */
599static GType
600gtk_q_tree_model_get_column_type(GtkTreeModel *tree_model,
601 gint index)
602{
603 GtkQTreeModel *q_tree_model = GTK_Q_TREE_MODEL (tree_model);
604 GtkQTreeModelPrivate *priv = q_tree_model->priv;
605
606 g_return_val_if_fail (index < gtk_q_tree_model_get_n_columns(tree_model), G_TYPE_INVALID);
607
608 return priv->column_headers[index];
609}
610
611/* Sets @iter to a valid iterator pointing to @path. If @path does
612 * not exist, @iter is set to an invalid iterator and %FALSE is returned.
613 */
614static gboolean
615gtk_q_tree_model_get_iter(GtkTreeModel *tree_model,
616 GtkTreeIter *iter,
617 GtkTreePath *path)
618{
619 GtkQTreeModel *q_tree_model = GTK_Q_TREE_MODEL (tree_model);
620 GtkQTreeModelPrivate *priv = q_tree_model->priv;
621
622 /* GtkTreePath is a list of indices, each one indicates
623 * the child of the previous one.
624 * Since GtkTreeModel only has rows (columns are only used to
625 * store data in each item), each index is basically the row
626 * at the given tree level.
627 * To get the iter, we want to get the QModelIndex. To get
628 * the QModelIndex we simply start at the first level and
629 * traverse the model the number of layers equal to the number
630 * of indices in the path.
631 */
632 gint depth;
633 gint* indices = gtk_tree_path_get_indices_with_depth(path, &depth);
634 QModelIndex idx = priv->model->index(indices[0], 0);
635 for(int layer = 1; layer < depth; layer++ ) {
636 /* check if previous iter is valid */
637 if (!idx.isValid()) {
638 break;
639 } else {
640 idx = idx.child(indices[layer], 0);
641 }
642 }
643
644 if (!idx.isValid()) {
645 iter->stamp = 0;
646 return FALSE;
647 } else {
648 /* we have a valid QModelIndex; turn it into an iter */
649 iter->stamp = priv->stamp;
650 qmodelindex_to_iter(idx, iter);
651 }
652
653 return TRUE;
654}
655
656/* Returns a newly-created #GtkTreePath referenced by @iter.
657 *
658 * This path should be freed with gtk_tree_path_free().
659 */
660static GtkTreePath *
661gtk_q_tree_model_get_path(GtkTreeModel *tree_model,
662 GtkTreeIter *iter)
663{
664 GtkQTreeModel *q_tree_model = GTK_Q_TREE_MODEL (tree_model);
665 GtkQTreeModelPrivate *priv = q_tree_model->priv;
666 GtkTreePath *path;
667
668 g_return_val_if_fail (iter->stamp == priv->stamp, NULL);
669 g_return_val_if_fail (iter_is_valid(iter, q_tree_model), NULL);
670
671 /* To get the path, we have to traverse from the child all the way up */
672 path = gtk_tree_path_new();
673 QIter *qiter = Q_ITER(iter);
674 QModelIndex idx = priv->model->indexFromId(qiter->row.value, qiter->column.value, qiter->id);
675 while( idx.isValid() ){
676 gtk_tree_path_prepend_index(path, idx.row());
677 idx = idx.parent();
678 }
679 return path;
680}
681
682static inline GType
683get_fundamental_type(GType type)
684{
685 GType result;
686
687 result = G_TYPE_FUNDAMENTAL (type);
688
689 if (result == G_TYPE_INTERFACE) {
690 if (g_type_is_a (type, G_TYPE_OBJECT))
691 result = G_TYPE_OBJECT;
692 }
693
694 return result;
695}
696
697/* Initializes and sets @value to that at @column. */
698static void
699gtk_q_tree_model_get_value(GtkTreeModel *tree_model,
700 GtkTreeIter *iter,
701 gint column,
702 GValue *value)
703{
704 GtkQTreeModel *q_tree_model = GTK_Q_TREE_MODEL (tree_model);
705 GtkQTreeModelPrivate *priv = q_tree_model->priv;
706
707 g_return_if_fail (column < priv->n_columns);
708 g_return_if_fail (iter_is_valid(iter, q_tree_model));
709
710 /* get the data */
711 QIter *qiter = Q_ITER(iter);
712 QModelIndex idx = priv->model->indexFromId(qiter->row.value, qiter->column.value, qiter->id);
713 int role = priv->column_roles[column];
714 QVariant var = priv->model->data(idx, role);
715 GType type = priv->column_headers[column];
716 g_value_init (value, type);
717 switch (get_fundamental_type (type))
718 {
719 /* TODO: implement the rest of the data types the we plan to support
720 * otherwise get rid of the code and make sure un-implemented types
721 * are not allowed
722 */
723 case G_TYPE_BOOLEAN:
724 g_value_set_boolean (value, (gboolean) var.toBool());
725 break;
726 // case G_TYPE_CHAR:
727 // g_value_set_schar (value, (gchar) list->data.v_char);
728 // break;
729 // case G_TYPE_UCHAR:
730 // g_value_set_uchar (value, (guchar) list->data.v_uchar);
731 // break;
732 case G_TYPE_INT:
733 g_value_set_int (value, (gint) var.toInt());
734 break;
735 case G_TYPE_UINT:
736 g_value_set_uint (value, (guint) var.toUInt());
737 break;
738 // case G_TYPE_LONG:
739 // g_value_set_long (value, list->data.v_long);
740 // break;
741 // case G_TYPE_ULONG:
742 // g_value_set_ulong (value, list->data.v_ulong);
743 // break;
744 // case G_TYPE_INT64:
745 // g_value_set_int64 (value, list->data.v_int64);
746 // break;
747 // case G_TYPE_UINT64:
748 // g_value_set_uint64 (value, list->data.v_uint64);
749 // break;
750 // case G_TYPE_ENUM:
751 // g_value_set_enum (value, list->data.v_int);
752 // break;
753 // case G_TYPE_FLAGS:
754 // g_value_set_flags (value, list->data.v_uint);
755 // break;
756 // case G_TYPE_FLOAT:
757 // g_value_set_float (value, (gfloat) list->data.v_float);
758 // break;
759 // case G_TYPE_DOUBLE:
760 // g_value_set_double (value, (gdouble) list->data.v_double);
761 // break;
762 case G_TYPE_STRING:
Stepan Salenikovich69771842015-02-24 18:11:45 -0500763 {
Stepan Salenikovich6f687072015-03-26 10:43:37 -0400764 QByteArray ba = var.toString().toUtf8();
765 g_value_set_string(value, (gchar *)var.toByteArray().constData());
Stepan Salenikovich69771842015-02-24 18:11:45 -0500766 }
767 break;
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500768 // case G_TYPE_POINTER:
769 // g_value_set_pointer (value, (gpointer) list->data.v_pointer);
770 // break;
771 // case G_TYPE_BOXED:
772 // g_value_set_boxed (value, (gpointer) list->data.v_pointer);
773 // break;
774 // case G_TYPE_VARIANT:
775 // g_value_set_variant (value, (gpointer) list->data.v_pointer);
776 // break;
777 // case G_TYPE_OBJECT:
778 // g_value_set_object (value, (GObject *) list->data.v_pointer);
779 // break;
780 default:
781 g_warning ("%s: Unsupported type (%s) retrieved.", G_STRLOC, g_type_name (value->g_type));
782 break;
783 }
784
785 return;
786}
787
788/* Sets @iter to point to the node following it at the current level.
789 *
790 * If there is no next @iter, %FALSE is returned and @iter is set
791 * to be invalid.
792 */
793static gboolean
794gtk_q_tree_model_iter_next(GtkTreeModel *tree_model,
795 GtkTreeIter *iter)
796{
797 GtkQTreeModel *q_tree_model = GTK_Q_TREE_MODEL (tree_model);
798 GtkQTreeModelPrivate *priv = q_tree_model->priv;
799
800 g_return_val_if_fail (iter_is_valid(iter, q_tree_model), FALSE);
801
802 QIter *qiter = Q_ITER(iter);
803 QModelIndex idx = priv->model->indexFromId(qiter->row.value, qiter->column.value, qiter->id);
804 idx = idx.sibling(idx.row()+1, idx.column());
805
806 /* validate */
807 if (validate_index(priv->stamp, idx, iter) ) {
808 GtkTreePath *path = gtk_q_tree_model_get_path(tree_model, iter);
809 gtk_tree_path_free(path);
810 return TRUE;
811 } else {
812 return FALSE;
813 }
814}
815
816/* Sets @iter to point to the previous node at the current level.
817 *
818 * If there is no previous @iter, %FALSE is returned and @iter is
819 * set to be invalid.
820 */
821static gboolean
822gtk_q_tree_model_iter_previous(GtkTreeModel *tree_model,
823 GtkTreeIter *iter)
824{
825 GtkQTreeModel *q_tree_model = GTK_Q_TREE_MODEL (tree_model);
826 GtkQTreeModelPrivate *priv = q_tree_model->priv;
827
828 g_return_val_if_fail (iter_is_valid(iter, q_tree_model), FALSE);
829
830 QIter *qiter = Q_ITER(iter);
831 QModelIndex idx = priv->model->indexFromId(qiter->row.value, qiter->column.value, qiter->id);
832 idx = idx.sibling(idx.row()-1, idx.column());
833
834 /* validate */
835 return validate_index(priv->stamp, idx, iter);
836}
837
838/* Sets @iter to point to the first child of @parent.
839 *
840 * If @parent has no children, %FALSE is returned and @iter is
841 * set to be invalid. @parent will remain a valid node after this
842 * function has been called.
843 *
844 * If @parent is %NULL returns the first node
845 */
846static gboolean
847gtk_q_tree_model_iter_children(GtkTreeModel *tree_model,
848 GtkTreeIter *iter,
849 GtkTreeIter *parent)
850{
851 GtkQTreeModel *q_tree_model = GTK_Q_TREE_MODEL (tree_model);
852 GtkQTreeModelPrivate *priv = q_tree_model->priv;
853 QModelIndex idx;
854
855 /* make sure parent if valid node if its not NULL */
856 if (parent)
857 g_return_val_if_fail(iter_is_valid(parent, q_tree_model), FALSE);
858
859 if (parent) {
860 /* get first child */
861 QIter *qparent = Q_ITER(parent);
862 idx = priv->model->indexFromId(qparent->row.value, qparent->column.value, qparent->id);
863 idx = idx.child(0, 0);
864 } else {
865 /* parent is NULL, get the first node */
866 idx = priv->model->index(0, 0);
867 }
868
869 /* validate child */
870 return validate_index(priv->stamp, idx, iter);
871}
872
873/* Returns %TRUE if @iter has children, %FALSE otherwise. */
874static gboolean
875gtk_q_tree_model_iter_has_child (GtkTreeModel *tree_model,
876 GtkTreeIter *iter)
877{
878 GtkQTreeModel *q_tree_model = GTK_Q_TREE_MODEL (tree_model);
879 GtkQTreeModelPrivate *priv = q_tree_model->priv;
880 g_return_val_if_fail(iter_is_valid(iter, q_tree_model), FALSE);
881
882 QIter *qiter = Q_ITER(iter);
883 QModelIndex idx = priv->model->indexFromId(qiter->row.value, qiter->column.value, qiter->id);
884 return priv->model->hasChildren(idx);
885}
886
887/* Returns the number of children that @iter has.
888 *
889 * As a special case, if @iter is %NULL, then the number
890 * of toplevel nodes is returned.
891 */
892static gint
893gtk_q_tree_model_iter_n_children (GtkTreeModel *tree_model,
894 GtkTreeIter *iter)
895{
896 GtkQTreeModel *q_tree_model = GTK_Q_TREE_MODEL (tree_model);
897 GtkQTreeModelPrivate *priv = q_tree_model->priv;
898
899 if (iter == NULL)
900 return gtk_q_tree_model_length(q_tree_model);
901
902 g_return_val_if_fail(iter_is_valid(iter, q_tree_model), -1);
903 QIter *qiter = Q_ITER(iter);
904 QModelIndex idx = priv->model->indexFromId(qiter->row.value, qiter->column.value, qiter->id);
905 return priv->model->rowCount(idx);
906}
907
908/* Sets @iter to be the child of @parent, using the given index.
909 *
910 * The first index is 0. If @n is too big, or @parent has no children,
911 * @iter is set to an invalid iterator and %FALSE is returned. @parent
912 * will remain a valid node after this function has been called. As a
913 * special case, if @parent is %NULL, then the @n<!-- -->th root node
914 * is set.
915 */
916static gboolean
917gtk_q_tree_model_iter_nth_child(GtkTreeModel *tree_model,
918 GtkTreeIter *iter,
919 GtkTreeIter *parent,
920 gint n)
921{
922 GtkQTreeModel *q_tree_model = GTK_Q_TREE_MODEL (tree_model);
923 GtkQTreeModelPrivate *priv = q_tree_model->priv;
924 QModelIndex idx;
925
926 if (parent) {
927 g_return_val_if_fail(iter_is_valid(parent, q_tree_model), FALSE);
928
929 /* get the nth child */
930 QIter *qparent = Q_ITER(parent);
931 idx = priv->model->indexFromId(qparent->row.value, qparent->column.value, qparent->id);
932 idx = idx.child(n, 0);
933 } else {
934 idx = priv->model->index(n, 0);
935 }
936
937 /* validate */
938 return validate_index(priv->stamp, idx, iter);
939}
940
941/* Sets @iter to be the parent of @child.
942 *
943 * If @child is at the toplevel, and doesn't have a parent, then
944 * @iter is set to an invalid iterator and %FALSE is returned.
945 * @child will remain a valid node after this function has been
946 * called.
947 */
948static gboolean
949gtk_q_tree_model_iter_parent(GtkTreeModel *tree_model,
950 GtkTreeIter *iter,
951 GtkTreeIter *child)
952{
953 GtkQTreeModel *q_tree_model = GTK_Q_TREE_MODEL (tree_model);
954 GtkQTreeModelPrivate *priv = q_tree_model->priv;
955 QModelIndex idx;
956
957 g_return_val_if_fail(iter_is_valid(child, q_tree_model), FALSE);
958
959 QIter *qchild = Q_ITER(child);
960 idx = priv->model->indexFromId(qchild->row.value, qchild->column.value, qchild->id);
961 idx = idx.parent();
962
963 /* validate */
964 return validate_index(priv->stamp, idx, iter);
965}
966
Stepan Salenikovich69771842015-02-24 18:11:45 -0500967/* End Fulfill the GtkTreeModel requirements */