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