blob: 9ce7356869afc7b48a133aff5e41f8f78c3d9d9c [file] [log] [blame]
Stepan Salenikovicha3557452015-02-20 14:14:12 -05001/*
Stepan Salenikovichbe87d2c2016-01-25 14:14:34 -05002 * Copyright (C) 2015-2016 Savoir-faire Linux Inc.
Stepan Salenikovicha3557452015-02-20 14:14:12 -05003 * Author: Stepan Salenikovich <stepan.salenikovich@savoirfairelinux.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Stepan Salenikovicha3557452015-02-20 14:14:12 -050018 */
19
20#include "gtkqtreemodel.h"
21#include <gtk/gtk.h>
22#include <QtCore/QAbstractItemModel>
23#include <QtCore/QDebug>
24#include "gtkaccessproxymodel.h"
25
26typedef union _int_ptr_t
27{
28 gint value;
Stepan Salenikovichd2dbcee2015-02-27 16:52:28 -050029 void *ptr;
Stepan Salenikovicha3557452015-02-20 14:14:12 -050030} int_ptr_t;
31
32typedef struct _QIter {
33 gint stamp;
34 int_ptr_t row;
35 int_ptr_t column;
Stepan Salenikovichd2dbcee2015-02-27 16:52:28 -050036 void *id;
Stepan Salenikovicha3557452015-02-20 14:14:12 -050037 gpointer user_data;
38} QIter;
39
40typedef struct _GtkQTreeModelPrivate GtkQTreeModelPrivate;
41
42struct _GtkQTreeModel
43{
44 GObject parent;
45
46 /* private */
47 GtkQTreeModelPrivate *priv;
48};
49
50struct _GtkQTreeModelClass
51{
52 GObjectClass parent_class;
53};
54
55struct _GtkQTreeModelPrivate
56{
57 GType *column_headers;
58 gint *column_roles;
aviau9bbb19b2016-05-16 15:53:44 -040059 gint *column_model_col;
Stepan Salenikovicha3557452015-02-20 14:14:12 -050060
61 gint stamp;
62 gint n_columns;
63
64 GtkAccessProxyModel *model;
65};
66
67/* static prototypes */
68
69/* GtkTreeModel prototypes */
70static void gtk_q_tree_model_tree_model_init (GtkTreeModelIface * );
71static void gtk_q_tree_model_finalize (GObject * );
72static GtkTreeModelFlags gtk_q_tree_model_get_flags (GtkTreeModel * );
73static gint gtk_q_tree_model_get_n_columns (GtkTreeModel * );
74static GType gtk_q_tree_model_get_column_type (GtkTreeModel *,
75 gint );
76static gboolean gtk_q_tree_model_get_iter (GtkTreeModel *,
77 GtkTreeIter *,
78 GtkTreePath * );
79static GtkTreePath * gtk_q_tree_model_get_path (GtkTreeModel *,
80 GtkTreeIter * );
81static void gtk_q_tree_model_get_value (GtkTreeModel *,
82 GtkTreeIter *,
83 gint,
84 GValue * );
85static gboolean gtk_q_tree_model_iter_next (GtkTreeModel *,
86 GtkTreeIter * );
87static gboolean gtk_q_tree_model_iter_previous (GtkTreeModel *,
88 GtkTreeIter * );
89static gboolean gtk_q_tree_model_iter_children (GtkTreeModel *,
90 GtkTreeIter *,
91 GtkTreeIter * );
92static gboolean gtk_q_tree_model_iter_has_child (GtkTreeModel *,
93 GtkTreeIter * );
94static gint gtk_q_tree_model_iter_n_children (GtkTreeModel *,
95 GtkTreeIter * );
96static gboolean gtk_q_tree_model_iter_nth_child (GtkTreeModel *,
97 GtkTreeIter *,
98 GtkTreeIter *,
99 gint );
100static gboolean gtk_q_tree_model_iter_parent (GtkTreeModel *,
101 GtkTreeIter *,
102 GtkTreeIter * );
103
Stepan Salenikovich1e738fe2016-10-25 10:48:09 -0400104/* Drag and Drop */
105static void gtk_q_tree_model_drag_source_init (GtkTreeDragSourceIface *);
106static void gtk_q_tree_model_drag_dest_init (GtkTreeDragDestIface * );
107static gboolean gtk_q_tree_model_row_draggable (GtkTreeDragSource *,
108 GtkTreePath * );
109static gboolean gtk_q_tree_model_drag_data_delete (GtkTreeDragSource *,
110 GtkTreePath * );
111static gboolean gtk_q_tree_model_drag_data_get (GtkTreeDragSource *,
112 GtkTreePath *,
113 GtkSelectionData * );
114static gboolean gtk_q_tree_model_drag_data_received(GtkTreeDragDest *,
115 GtkTreePath *,
116 GtkSelectionData * );
117static gboolean gtk_q_tree_model_row_drop_possible (GtkTreeDragDest *,
118 GtkTreePath *,
119 GtkSelectionData * );
120
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500121/* implementation prototypes */
122static void qmodelindex_to_iter (const QModelIndex &,
123 GtkTreeIter * );
124// static void gtk_q_tree_model_increment_stamp (GtkQTreeModel * );
125
126static gint gtk_q_tree_model_length (GtkQTreeModel * );
127
128static void gtk_q_tree_model_set_n_columns (GtkQTreeModel *,
129 gint );
130static void gtk_q_tree_model_set_column_type (GtkQTreeModel *,
131 gint,
132 gint,
aviau9bbb19b2016-05-16 15:53:44 -0400133 gint,
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500134 GType );
135
136/* End private prototypes */
137
138/* define type, inherit from GObject, define implemented interface(s) */
139G_DEFINE_TYPE_WITH_CODE (GtkQTreeModel, gtk_q_tree_model, G_TYPE_OBJECT,
140 G_ADD_PRIVATE (GtkQTreeModel)
Stepan Salenikovich1e738fe2016-10-25 10:48:09 -0400141 G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL,
142 gtk_q_tree_model_tree_model_init)
143 G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_SOURCE,
144 gtk_q_tree_model_drag_source_init)
145 G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_DEST,
146 gtk_q_tree_model_drag_dest_init))
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500147
148#define GTK_Q_TREEMODEL_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_Q_TREE_MODEL, GtkQTreeModelPrivate))
149
150#define Q_ITER(iter) ((QIter *)iter)
151
152static void
153gtk_q_tree_model_class_init(GtkQTreeModelClass *klass)
154{
155 G_OBJECT_CLASS(klass)->finalize = gtk_q_tree_model_finalize;
156}
157
158static void
159gtk_q_tree_model_tree_model_init(GtkTreeModelIface *iface)
160{
161 iface->get_flags = gtk_q_tree_model_get_flags;
162 iface->get_n_columns = gtk_q_tree_model_get_n_columns;
163 iface->get_column_type = gtk_q_tree_model_get_column_type;
164 iface->get_iter = gtk_q_tree_model_get_iter;
165 iface->get_path = gtk_q_tree_model_get_path;
166 iface->get_value = gtk_q_tree_model_get_value;
167 iface->iter_next = gtk_q_tree_model_iter_next;
168 iface->iter_previous = gtk_q_tree_model_iter_previous;
169 iface->iter_children = gtk_q_tree_model_iter_children;
170 iface->iter_has_child = gtk_q_tree_model_iter_has_child;
171 iface->iter_n_children = gtk_q_tree_model_iter_n_children;
172 iface->iter_nth_child = gtk_q_tree_model_iter_nth_child;
173 iface->iter_parent = gtk_q_tree_model_iter_parent;
174}
175
176static void
Stepan Salenikovich1e738fe2016-10-25 10:48:09 -0400177gtk_q_tree_model_drag_source_init(GtkTreeDragSourceIface *iface)
178{
179 iface->row_draggable = gtk_q_tree_model_row_draggable;
180 iface->drag_data_delete = gtk_q_tree_model_drag_data_delete;
181 iface->drag_data_get = gtk_q_tree_model_drag_data_get;
182}
183
184static void
185gtk_q_tree_model_drag_dest_init(GtkTreeDragDestIface *iface)
186{
187 iface->drag_data_received = gtk_q_tree_model_drag_data_received;
188 iface->row_drop_possible = gtk_q_tree_model_row_drop_possible;
189}
190
191static void
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500192gtk_q_tree_model_init(GtkQTreeModel *q_tree_model)
193{
194 GtkQTreeModelPrivate *priv = GTK_Q_TREEMODEL_GET_PRIVATE(q_tree_model);
195 q_tree_model->priv = priv;
196
197 priv->stamp = g_random_int();
198 priv->model = NULL;
199}
200
201/**
202 * gtk_q_tree_model_get_qmodel
203 * returns the original model from which this GtkQTreeModel is created
204 */
205QAbstractItemModel *
206gtk_q_tree_model_get_qmodel(GtkQTreeModel *q_tree_model)
207{
208 GtkQTreeModelPrivate *priv = GTK_Q_TREEMODEL_GET_PRIVATE(q_tree_model);
209 return priv->model->sourceModel();
210}
211
212/**
213 * gtk_q_tree_model_get_source_idx
214 * Returns the index of the original model used to create this GtkQTreeModel from
215 * the given iter, if there is one.
216 */
217QModelIndex
218gtk_q_tree_model_get_source_idx(GtkQTreeModel *q_tree_model, GtkTreeIter *iter)
219{
220 GtkQTreeModelPrivate *priv = GTK_Q_TREEMODEL_GET_PRIVATE(q_tree_model);
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500221 QIter *qiter = Q_ITER(iter);
222 GtkAccessProxyModel *proxy_model = priv->model;
223 QModelIndex proxy_idx = proxy_model->indexFromId(qiter->row.value, qiter->column.value, qiter->id);
224 if (proxy_idx.isValid()) {
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500225 return proxy_model->mapToSource(proxy_idx);
226 } else {
Stepan Salenikovich69771842015-02-24 18:11:45 -0500227 g_warning("could not get valid QModelIndex from given GtkTreeIter");
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500228 return QModelIndex();
229 }
230}
231
232/**
233 * Takes a QModelIndex from the original QAbstractItemModel and returns a valid GtkTreeIter in the corresponding
234 * GtkQTreeModel
235 */
236gboolean
237gtk_q_tree_model_source_index_to_iter(GtkQTreeModel *q_tree_model, const QModelIndex &idx, GtkTreeIter *iter)
238{
239 GtkQTreeModelPrivate *priv = GTK_Q_TREEMODEL_GET_PRIVATE(q_tree_model);
240
Stepan Salenikovich69771842015-02-24 18:11:45 -0500241 /* the the proxy_idx from the source idx */
242 QModelIndex proxy_idx = priv->model->mapFromSource(idx);
243 if (!proxy_idx.isValid())
244 return FALSE;
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500245
246 /* make sure iter is valid */
247 iter->stamp = priv->stamp;
248
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500249 /* map the proxy idx to iter */
250 Q_ITER(iter)->row.value = proxy_idx.row();
Stepan Salenikovich7ff47962015-03-16 15:10:14 -0400251 Q_ITER(iter)->column.value = proxy_idx.column();
Stepan Salenikovichd2dbcee2015-02-27 16:52:28 -0500252 Q_ITER(iter)->id = proxy_idx.internalPointer();
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500253 return TRUE;
254}
255
256/**
Stepan Salenikovich22de0592015-10-21 10:27:20 -0400257 * helper method which recursively adds all the children of the given QModelIndex
258 */
259static void
260insert_children(const QModelIndex &idx, GtkQTreeModel *gtk_model)
261{
262 const auto children = gtk_model->priv->model->rowCount(idx);
263 for (int i = 0; i < children; ++i) {
264 GtkTreeIter iter_child;
265 auto idx_child = gtk_model->priv->model->index(i, 0, idx);
266 if (idx_child.isValid()) {
267 iter_child.stamp = gtk_model->priv->stamp;
268 qmodelindex_to_iter(idx_child, &iter_child);
269 if (auto path_child = gtk_q_tree_model_get_path(GTK_TREE_MODEL(gtk_model), &iter_child)) {
270 gtk_tree_model_row_inserted(GTK_TREE_MODEL(gtk_model), path_child, &iter_child);
271 gtk_tree_path_free(path_child);
272 }
273 insert_children(idx_child, gtk_model);
274 }
275 }
276}
277
278/**
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500279 * gtk_q_tree_model_new:
280 * @model: QAbstractItemModel to which this model will bind.
281 * @n_columns: number of columns in the list store
aviau271bcc22016-05-27 17:25:19 -0400282 * @...: model #Column, #GType and #Role for each column.
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500283 *
284 * Return value: a new #GtkQTreeModel
285 */
286GtkQTreeModel *
287gtk_q_tree_model_new(QAbstractItemModel *model, size_t n_columns, ...)
288{
289 GtkQTreeModel *retval;
290 va_list args;
291 gint i;
292
293 g_return_val_if_fail(n_columns > 0, NULL);
294
295 retval = (GtkQTreeModel *)g_object_new(GTK_TYPE_Q_TREE_MODEL, NULL);
296 gtk_q_tree_model_set_n_columns(retval, n_columns);
297
298 /* get proxy model from given model */
299 GtkAccessProxyModel* proxy_model = new GtkAccessProxyModel();
300 proxy_model->setSourceModel(model);
301 retval->priv->model = proxy_model;
302 gint stamp = retval->priv->stamp;
303
aviau9bbb19b2016-05-16 15:53:44 -0400304 n_columns = 3*n_columns;
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500305 va_start (args, n_columns);
306
aviau9bbb19b2016-05-16 15:53:44 -0400307 for (i = 0; i < (gint)(n_columns/3); i++) {
308 /* first get the model column */
309 gint model_col = va_arg(args, gint);
310
aviau271bcc22016-05-27 17:25:19 -0400311 /* then get the role of the QModel */
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500312 gint role = va_arg(args, gint);
313
314 /* then get the type the role will be interpreted as */
315 GType type = va_arg(args, GType);
316
317 /* TODO: check if its a type supported by our model
318 * if (! _gtk_tree_data_list_check_type (type))
319 * {
320 * g_warning ("%s: Invalid type %s\n", G_STRLOC, g_type_name (type));
321 * g_object_unref (retval);
322 * va_end (args);
323 *
324 * return NULL;
325 * }
326 */
327
aviau9bbb19b2016-05-16 15:53:44 -0400328 gtk_q_tree_model_set_column_type (retval, i, model_col, role, type);
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500329 }
330
331 va_end (args);
332
333 gtk_q_tree_model_length(retval);
334
335 /* connect signals */
336 QObject::connect(
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400337 proxy_model,
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500338 &QAbstractItemModel::rowsInserted,
339 [=](const QModelIndex & parent, int first, int last) {
340 for( int row = first; row <= last; row++) {
Stepan Salenikovich8573f862015-08-17 16:54:50 -0400341 GtkTreeIter iter;
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400342 QModelIndex idx = proxy_model->index(row, 0, parent);
Stepan Salenikovich8573f862015-08-17 16:54:50 -0400343 iter.stamp = stamp;
344 qmodelindex_to_iter(idx, &iter);
345 GtkTreePath *path = gtk_q_tree_model_get_path(GTK_TREE_MODEL(retval), &iter);
346 gtk_tree_model_row_inserted(GTK_TREE_MODEL(retval), path, &iter);
Stepan Salenikovichd2dfc162015-08-17 16:40:28 -0400347 gtk_tree_path_free(path);
Stepan Salenikovich4f922f72016-10-27 13:24:31 -0400348
349 // in certain cases (eg: proxy models), its possible for rows to be inserted that
350 // already have children; however no rowsInserted will be emitted for the children,
351 // in these cases we check for the existence of chilren and insert them
352 insert_children(idx, retval);
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500353 }
354 }
355 );
356
357 QObject::connect(
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400358 proxy_model,
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500359 &QAbstractItemModel::rowsAboutToBeMoved,
Stepan Salenikovich69771842015-02-24 18:11:45 -0500360 [=](const QModelIndex & sourceParent, int sourceStart, int sourceEnd,
Stepan Salenikovichede936f2016-10-26 14:42:16 -0400361 const QModelIndex & destinationParent, int destinationRow) {
362
363 /* detect if the source or destination rows are out of bounds (due to some incosistency)
364 * in order to prevent crash */
365 auto max_row = proxy_model->rowCount(sourceParent) - 1;
366 auto destinationEndRow = destinationRow + (sourceEnd - sourceStart);
367 if (sourceStart > max_row || sourceEnd > max_row || destinationRow > max_row || destinationEndRow > max_row) {
368 g_critical("rowsMoved: rows being moved inconsistent with number of rows in the model");
369 return;
370 }
371
Stepan Salenikovich0ba98102015-10-21 15:07:52 -0400372 /* if the sourceParent and the destinationParent are the same, then we can use the
373 * GtkTreeModel API to move the rows, otherwise we must first delete them and then
374 * re-insert them under the new parent */
375 if (sourceParent == destinationParent)
376 return;
377
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500378 /* first remove the row from old location
379 * then insert them at the new location on the "rowsMoved signal */
380 for( int row = sourceStart; row <= sourceEnd; row++) {
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400381 QModelIndex idx = proxy_model->index(row, 0, sourceParent);
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500382 GtkTreeIter iter_old;
Stepan Salenikovichfcf8fc62015-10-21 11:08:08 -0400383 iter_old.stamp = stamp;
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500384 qmodelindex_to_iter(idx, &iter_old);
385 GtkTreePath *path_old = gtk_q_tree_model_get_path(GTK_TREE_MODEL(retval), &iter_old);
386 gtk_tree_model_row_deleted(GTK_TREE_MODEL(retval), path_old);
Stepan Salenikovichd2dfc162015-08-17 16:40:28 -0400387 gtk_tree_path_free(path_old);
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500388 }
389 }
390 );
391
392 QObject::connect(
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400393 proxy_model,
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500394 &QAbstractItemModel::rowsMoved,
Stepan Salenikovich0ba98102015-10-21 15:07:52 -0400395 [=](const QModelIndex & sourceParent, int sourceStart, int sourceEnd,
Stepan Salenikovich69771842015-02-24 18:11:45 -0500396 const QModelIndex & destinationParent, int destinationRow) {
Stepan Salenikovichede936f2016-10-26 14:42:16 -0400397
398 /* detect if the source or destination rows are out of bounds (due to some incosistency)
399 * in order to prevent crash */
400 auto max_row = proxy_model->rowCount(sourceParent) - 1;
401 auto destinationEndRow = destinationRow + (sourceEnd - sourceStart);
402 if (sourceStart > max_row || sourceEnd > max_row || destinationRow > max_row || destinationEndRow > max_row) {
403 g_critical("rowsMoved: rows being moved inconsistent with number of rows in the model");
404 return;
405 }
406
Stepan Salenikovich0ba98102015-10-21 15:07:52 -0400407 /* if the sourceParent and the destinationParent are the same, then we can use the
408 * GtkTreeModel API to move the rows, otherwise we must first delete them and then
409 * re-insert them under the new parent */
410 if (sourceParent == destinationParent) {
411 GtkTreeIter *iter_parent = nullptr;
412 GtkTreePath *path_parent = nullptr;
413 if (sourceParent.isValid()) {
414 iter_parent = g_new0(GtkTreeIter, 1);
415 iter_parent->stamp = stamp;
416 qmodelindex_to_iter(sourceParent, iter_parent);
417 path_parent = gtk_q_tree_model_get_path(GTK_TREE_MODEL(retval), iter_parent);
418 } else {
419 path_parent = gtk_tree_path_new();
420 }
421
422 /* gtk_tree_model_rows_reordered takes an array of integers mapping the current
423 * position of each child to its old position before the re-ordering,
424 * i.e. new_order [newpos] = oldpos
425 */
426 const auto rows = proxy_model->rowCount(sourceParent);
Stepan Salenikovich365dfd12015-10-21 18:52:38 -0400427 gint new_order[rows];
Stepan Salenikovich0ba98102015-10-21 15:07:52 -0400428 const auto destinationRowLast = destinationRow + (sourceEnd - sourceStart);
Stepan Salenikovichcaa182a2015-10-22 11:50:09 -0400429 const auto totalMoved = sourceEnd - sourceStart + 1;
Stepan Salenikovich0ba98102015-10-21 15:07:52 -0400430 for (int row = 0; row < rows; ++row ) {
431 if ( (row < sourceStart && row < destinationRow)
432 || (row > sourceEnd && row > destinationRowLast) ) {
433 // if the row is outside of the bounds of change, then it stayed in the same place
434 new_order[row] = row;
435 } else {
436 if (row < destinationRow) {
437 // in front of the destination, so it used to be behind the rows that moved
Stepan Salenikovichcaa182a2015-10-22 11:50:09 -0400438 new_order[row] = row + totalMoved;
Stepan Salenikovich0ba98102015-10-21 15:07:52 -0400439 } else if (row >= destinationRow && row <= destinationRowLast) {
440 // within the destination, so it was within the rows that moved
441 new_order[row] = sourceStart + (row - destinationRow);
442 } else {
443 // behind the destination, so it used to be in front of the rows that moved
Stepan Salenikovichcaa182a2015-10-22 11:50:09 -0400444 new_order[row] = row - totalMoved;
Stepan Salenikovich0ba98102015-10-21 15:07:52 -0400445 }
446 }
447 }
448 gtk_tree_model_rows_reordered(GTK_TREE_MODEL(retval), path_parent, iter_parent, new_order);
449
450 g_free(iter_parent);
451 gtk_tree_path_free(path_parent);
452 } else {
453 /* these rows should have been removed in the "rowsAboutToBeMoved" handler
454 * now insert them in the new location */
455 for( int row = sourceStart; row <= sourceEnd; row++) {
456 GtkTreeIter iter_new;
457 QModelIndex idx = proxy_model->index(destinationRow, 0, destinationParent);
458 iter_new.stamp = stamp;
459 qmodelindex_to_iter(idx, &iter_new);
460 GtkTreePath *path_new = gtk_q_tree_model_get_path(GTK_TREE_MODEL(retval), &iter_new);
461 gtk_tree_model_row_inserted(GTK_TREE_MODEL(retval), path_new, &iter_new);
462 gtk_tree_path_free(path_new);
463 insert_children(idx, retval);
464 destinationRow++;
465 }
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500466 }
467 }
468 );
469
470 QObject::connect(
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400471 proxy_model,
Stepan Salenikoviche17a2932015-06-11 14:50:02 -0400472 &QAbstractItemModel::rowsRemoved,
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500473 [=](const QModelIndex & parent, int first, int last) {
Stepan Salenikoviche17a2932015-06-11 14:50:02 -0400474 GtkTreePath *parent_path = NULL;
475 if (parent.isValid()) {
476 GtkTreeIter iter_parent;
477 iter_parent.stamp = stamp;
478 qmodelindex_to_iter(parent, &iter_parent);
479 parent_path = gtk_q_tree_model_get_path(GTK_TREE_MODEL(retval), &iter_parent);
480 } else {
481 parent_path = gtk_tree_path_new();
482 }
483
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400484 /* go last to first, since the rows are being deleted */
485 for( int row = last; row >= first; --row) {
486 // g_debug("deleting row: %d", row);
Stepan Salenikoviche17a2932015-06-11 14:50:02 -0400487 GtkTreePath *path = gtk_tree_path_copy(parent_path);
488 gtk_tree_path_append_index(path, row);
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500489 gtk_tree_model_row_deleted(GTK_TREE_MODEL(retval), path);
Stepan Salenikovichd2dfc162015-08-17 16:40:28 -0400490 gtk_tree_path_free(path);
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500491 }
Stepan Salenikoviche17a2932015-06-11 14:50:02 -0400492
493 gtk_tree_path_free(parent_path);
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500494 }
495 );
496
497 QObject::connect(
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400498 proxy_model,
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500499 &QAbstractItemModel::dataChanged,
Stepan Salenikovich69771842015-02-24 18:11:45 -0500500 [=](const QModelIndex & topLeft, const QModelIndex & bottomRight,
501 G_GNUC_UNUSED const QVector<int> & roles = QVector<int> ()) {
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500502 /* we have to assume only one column */
503 int first = topLeft.row();
504 int last = bottomRight.row();
Stepan Salenikovicha647bcf2016-10-04 17:31:34 -0400505
506 /* the first idx IS topLeft, the rest are his siblings */
Stepan Salenikovich8573f862015-08-17 16:54:50 -0400507 GtkTreeIter iter;
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500508 QModelIndex idx = topLeft;
Stepan Salenikovich8573f862015-08-17 16:54:50 -0400509 iter.stamp = stamp;
510 qmodelindex_to_iter(idx, &iter);
511 GtkTreePath *path = gtk_q_tree_model_get_path(GTK_TREE_MODEL(retval), &iter);
512 gtk_tree_model_row_changed(GTK_TREE_MODEL(retval), path, &iter);
Stepan Salenikovichd2dfc162015-08-17 16:40:28 -0400513 gtk_tree_path_free(path);
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500514 for( int row = first + 1; row <= last; row++) {
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500515 idx = topLeft.sibling(row, 0);
Stepan Salenikovich8573f862015-08-17 16:54:50 -0400516 qmodelindex_to_iter(idx, &iter);
517 path = gtk_q_tree_model_get_path(GTK_TREE_MODEL(retval), &iter);
518 gtk_tree_model_row_changed(GTK_TREE_MODEL(retval), path, &iter);
Stepan Salenikovichd2dfc162015-08-17 16:40:28 -0400519 gtk_tree_path_free(path);
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500520 }
521 }
522 );
523
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400524 QObject::connect(
525 proxy_model,
526 &QAbstractItemModel::modelAboutToBeReset,
527 [=] () {
528 // g_debug("model about to be reset");
529 /* nothing equvivalent eixists in GtkTreeModel, so simply delete all
530 * rows, and add all rows when the model is reset;
531 * we must delete the rows in ascending order */
532 int row_count = proxy_model->rowCount();
533 for (int row = row_count; row > 0; --row) {
534 // g_debug("deleting row %d", row -1);
535 QModelIndex idx = proxy_model->index(row - 1, 0);
536 GtkTreeIter iter;
537 iter.stamp = stamp;
538 qmodelindex_to_iter(idx, &iter);
539 GtkTreePath *path = gtk_q_tree_model_get_path(GTK_TREE_MODEL(retval), &iter);
540 gtk_tree_model_row_deleted(GTK_TREE_MODEL(retval), path);
Stepan Salenikovichd2dfc162015-08-17 16:40:28 -0400541 gtk_tree_path_free(path);
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400542 }
543 }
544 );
545
546 QObject::connect(
547 proxy_model,
548 &QAbstractItemModel::modelReset,
549 [=] () {
550 // g_debug("model reset");
Stepan Salenikovich22de0592015-10-21 10:27:20 -0400551 /* now recursively add all the (new) rows */
552 insert_children(QModelIndex(), retval);
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400553 }
554 );
555
Stepan Salenikovich2d4b4722016-10-26 15:33:39 -0400556 QObject::connect(
557 proxy_model,
558 &QAbstractItemModel::layoutChanged,
559 [=] () {
560 // g_debug("layout changed");
561
562 /* GtkTreeModel interface has no signal equivalent to layoutChanged; we implement it by
563 * simply emitting a row changed on each top level item; note that this assumes
564 * layoutChanged is only emitted on top level items (which is be the case for LRC
565 * models) and that they have no children (this is a rare case in LRC)*/
566 int row_count = proxy_model->rowCount();
567 for (int row = 0; row < row_count; row++) {
568 // g_debug("adding row %d", row);
569 GtkTreeIter iter;
570 QModelIndex idx = proxy_model->index(row, 0);
571 iter.stamp = stamp;
572 qmodelindex_to_iter(idx, &iter);
573 GtkTreePath *path = gtk_q_tree_model_get_path(GTK_TREE_MODEL(retval), &iter);
574 gtk_tree_model_row_changed(GTK_TREE_MODEL(retval), path, &iter);
575 gtk_tree_path_free(path);
576 }
577 }
578 );
Stepan Salenikovich5d3506e2015-03-30 11:01:29 -0400579
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500580 return retval;
581}
582
583static gint
584gtk_q_tree_model_length(GtkQTreeModel *q_tree_model)
585{
586 GtkQTreeModelPrivate *priv = q_tree_model->priv;
587 return priv->model->rowCount();
588}
589
590static void
591gtk_q_tree_model_set_n_columns(GtkQTreeModel *q_tree_model,
592 gint n_columns)
593{
594 GtkQTreeModelPrivate *priv = q_tree_model->priv;
595 int i;
596
597 if (priv->n_columns == n_columns)
598 return;
599
600 priv->column_headers = g_renew (GType, priv->column_headers, n_columns);
601 for (i = priv->n_columns; i < n_columns; i++)
602 priv->column_headers[i] = G_TYPE_INVALID;
603 priv->n_columns = n_columns;
604
605 priv->column_roles = g_renew (gint, priv->column_roles, n_columns);
606 for (i = priv->n_columns; i < n_columns; i++)
607 priv->column_roles[i] = -1;
aviau9bbb19b2016-05-16 15:53:44 -0400608
609 priv->column_model_col = g_renew (gint, priv->column_model_col, n_columns);
610 for (i = priv->n_columns; i < n_columns; i++)
611 priv->column_model_col[i] = 0;
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500612}
613
614static void
615gtk_q_tree_model_set_column_type(GtkQTreeModel *q_tree_model,
616 gint column,
aviau9bbb19b2016-05-16 15:53:44 -0400617 gint model_col,
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500618 gint role,
619 GType type)
620{
621 GtkQTreeModelPrivate *priv = q_tree_model->priv;
622
623 /* TODO: check if its a type supported by our model
624 * if (!_gtk_tree_data_list_check_type (type))
625 * {
626 * g_warning ("%s: Invalid type %s\n", G_STRLOC, g_type_name (type));
627 * return;
628 * }
629 */
630
aviau9bbb19b2016-05-16 15:53:44 -0400631 priv->column_model_col[column] = model_col;
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500632 priv->column_headers[column] = type;
633 priv->column_roles[column] = role;
634}
635
636
637static void
638gtk_q_tree_model_finalize(GObject *object)
639{
640 GtkQTreeModel *q_tree_model = GTK_Q_TREE_MODEL (object);
641 GtkQTreeModelPrivate *priv = q_tree_model->priv;
642
aviau9bbb19b2016-05-16 15:53:44 -0400643 g_free(priv->column_model_col);
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500644 g_free(priv->column_headers);
645 g_free(priv->column_roles);
646
647 /* delete the created proxy model */
648 delete priv->model;
649
650 G_OBJECT_CLASS(gtk_q_tree_model_parent_class)->finalize (object);
651}
652
653static gboolean
654iter_is_valid(GtkTreeIter *iter,
655 GtkQTreeModel *q_tree_model)
656{
657 gboolean retval;
658 g_return_val_if_fail(iter != NULL, FALSE);
659 QIter *qiter = Q_ITER(iter);
660 retval = q_tree_model->priv->model->indexFromId(qiter->row.value, qiter->column.value, qiter->id).isValid();
661 return retval;
662}
663
664static void
665qmodelindex_to_iter(const QModelIndex &idx, GtkTreeIter *iter)
666{
667 Q_ITER(iter)->row.value = idx.row();
Stepan Salenikovich7ff47962015-03-16 15:10:14 -0400668 Q_ITER(iter)->column.value = idx.column();
Stepan Salenikovichd2dbcee2015-02-27 16:52:28 -0500669 Q_ITER(iter)->id = idx.internalPointer();
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500670}
671
672static gboolean
673validate_index(gint stamp, const QModelIndex &idx, GtkTreeIter *iter)
674{
675 if (idx.isValid()) {
676 iter->stamp = stamp;
677 qmodelindex_to_iter(idx, iter);
678 } else {
679 iter->stamp = 0;
680 return FALSE;
681 }
682 return TRUE;
683}
684
685/* Start Fulfill the GtkTreeModel requirements */
686
687/* flags supported by this interface */
688static GtkTreeModelFlags
689gtk_q_tree_model_get_flags(G_GNUC_UNUSED GtkTreeModel *tree_model)
690{
691 // TODO: possibly return based on the model?
692 return (GtkTreeModelFlags)(GTK_TREE_MODEL_ITERS_PERSIST);
693}
694
695/* number of columns supported by this tree model */
696static gint
697gtk_q_tree_model_get_n_columns(GtkTreeModel *tree_model)
698{
699 GtkQTreeModel *q_tree_model = GTK_Q_TREE_MODEL(tree_model);
700 GtkQTreeModelPrivate *priv = q_tree_model->priv;
701
702 return priv->model->columnCount();
703}
704
705/* get given column type */
706static GType
707gtk_q_tree_model_get_column_type(GtkTreeModel *tree_model,
708 gint index)
709{
710 GtkQTreeModel *q_tree_model = GTK_Q_TREE_MODEL (tree_model);
711 GtkQTreeModelPrivate *priv = q_tree_model->priv;
712
713 g_return_val_if_fail (index < gtk_q_tree_model_get_n_columns(tree_model), G_TYPE_INVALID);
714
715 return priv->column_headers[index];
716}
717
718/* Sets @iter to a valid iterator pointing to @path. If @path does
719 * not exist, @iter is set to an invalid iterator and %FALSE is returned.
720 */
721static gboolean
722gtk_q_tree_model_get_iter(GtkTreeModel *tree_model,
723 GtkTreeIter *iter,
724 GtkTreePath *path)
725{
726 GtkQTreeModel *q_tree_model = GTK_Q_TREE_MODEL (tree_model);
727 GtkQTreeModelPrivate *priv = q_tree_model->priv;
728
729 /* GtkTreePath is a list of indices, each one indicates
730 * the child of the previous one.
731 * Since GtkTreeModel only has rows (columns are only used to
732 * store data in each item), each index is basically the row
733 * at the given tree level.
734 * To get the iter, we want to get the QModelIndex. To get
735 * the QModelIndex we simply start at the first level and
736 * traverse the model the number of layers equal to the number
737 * of indices in the path.
738 */
739 gint depth;
740 gint* indices = gtk_tree_path_get_indices_with_depth(path, &depth);
741 QModelIndex idx = priv->model->index(indices[0], 0);
742 for(int layer = 1; layer < depth; layer++ ) {
743 /* check if previous iter is valid */
744 if (!idx.isValid()) {
745 break;
746 } else {
747 idx = idx.child(indices[layer], 0);
748 }
749 }
750
751 if (!idx.isValid()) {
752 iter->stamp = 0;
753 return FALSE;
754 } else {
755 /* we have a valid QModelIndex; turn it into an iter */
756 iter->stamp = priv->stamp;
757 qmodelindex_to_iter(idx, iter);
758 }
759
760 return TRUE;
761}
762
763/* Returns a newly-created #GtkTreePath referenced by @iter.
764 *
765 * This path should be freed with gtk_tree_path_free().
766 */
767static GtkTreePath *
768gtk_q_tree_model_get_path(GtkTreeModel *tree_model,
769 GtkTreeIter *iter)
770{
771 GtkQTreeModel *q_tree_model = GTK_Q_TREE_MODEL (tree_model);
772 GtkQTreeModelPrivate *priv = q_tree_model->priv;
773 GtkTreePath *path;
774
775 g_return_val_if_fail (iter->stamp == priv->stamp, NULL);
776 g_return_val_if_fail (iter_is_valid(iter, q_tree_model), NULL);
777
778 /* To get the path, we have to traverse from the child all the way up */
779 path = gtk_tree_path_new();
780 QIter *qiter = Q_ITER(iter);
781 QModelIndex idx = priv->model->indexFromId(qiter->row.value, qiter->column.value, qiter->id);
782 while( idx.isValid() ){
783 gtk_tree_path_prepend_index(path, idx.row());
784 idx = idx.parent();
785 }
786 return path;
787}
788
789static inline GType
790get_fundamental_type(GType type)
791{
792 GType result;
793
794 result = G_TYPE_FUNDAMENTAL (type);
795
796 if (result == G_TYPE_INTERFACE) {
797 if (g_type_is_a (type, G_TYPE_OBJECT))
798 result = G_TYPE_OBJECT;
799 }
800
801 return result;
802}
803
804/* Initializes and sets @value to that at @column. */
805static void
806gtk_q_tree_model_get_value(GtkTreeModel *tree_model,
807 GtkTreeIter *iter,
808 gint column,
809 GValue *value)
810{
811 GtkQTreeModel *q_tree_model = GTK_Q_TREE_MODEL (tree_model);
812 GtkQTreeModelPrivate *priv = q_tree_model->priv;
813
814 g_return_if_fail (column < priv->n_columns);
815 g_return_if_fail (iter_is_valid(iter, q_tree_model));
816
817 /* get the data */
818 QIter *qiter = Q_ITER(iter);
aviau9bbb19b2016-05-16 15:53:44 -0400819 int model_col = priv->column_model_col[column];
820 QModelIndex idx = priv->model->indexFromId(qiter->row.value, model_col, qiter->id);
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500821 int role = priv->column_roles[column];
822 QVariant var = priv->model->data(idx, role);
823 GType type = priv->column_headers[column];
824 g_value_init (value, type);
825 switch (get_fundamental_type (type))
826 {
827 /* TODO: implement the rest of the data types the we plan to support
828 * otherwise get rid of the code and make sure un-implemented types
829 * are not allowed
830 */
831 case G_TYPE_BOOLEAN:
832 g_value_set_boolean (value, (gboolean) var.toBool());
833 break;
834 // case G_TYPE_CHAR:
835 // g_value_set_schar (value, (gchar) list->data.v_char);
836 // break;
837 // case G_TYPE_UCHAR:
838 // g_value_set_uchar (value, (guchar) list->data.v_uchar);
839 // break;
840 case G_TYPE_INT:
841 g_value_set_int (value, (gint) var.toInt());
842 break;
843 case G_TYPE_UINT:
844 g_value_set_uint (value, (guint) var.toUInt());
845 break;
846 // case G_TYPE_LONG:
847 // g_value_set_long (value, list->data.v_long);
848 // break;
849 // case G_TYPE_ULONG:
850 // g_value_set_ulong (value, list->data.v_ulong);
851 // break;
852 // case G_TYPE_INT64:
853 // g_value_set_int64 (value, list->data.v_int64);
854 // break;
855 // case G_TYPE_UINT64:
856 // g_value_set_uint64 (value, list->data.v_uint64);
857 // break;
858 // case G_TYPE_ENUM:
859 // g_value_set_enum (value, list->data.v_int);
860 // break;
861 // case G_TYPE_FLAGS:
862 // g_value_set_flags (value, list->data.v_uint);
863 // break;
864 // case G_TYPE_FLOAT:
865 // g_value_set_float (value, (gfloat) list->data.v_float);
866 // break;
867 // case G_TYPE_DOUBLE:
868 // g_value_set_double (value, (gdouble) list->data.v_double);
869 // break;
870 case G_TYPE_STRING:
Stepan Salenikovich69771842015-02-24 18:11:45 -0500871 {
Stepan Salenikovich6f687072015-03-26 10:43:37 -0400872 QByteArray ba = var.toString().toUtf8();
873 g_value_set_string(value, (gchar *)var.toByteArray().constData());
Stepan Salenikovich69771842015-02-24 18:11:45 -0500874 }
875 break;
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500876 // case G_TYPE_POINTER:
877 // g_value_set_pointer (value, (gpointer) list->data.v_pointer);
878 // break;
879 // case G_TYPE_BOXED:
880 // g_value_set_boxed (value, (gpointer) list->data.v_pointer);
881 // break;
882 // case G_TYPE_VARIANT:
883 // g_value_set_variant (value, (gpointer) list->data.v_pointer);
884 // break;
885 // case G_TYPE_OBJECT:
886 // g_value_set_object (value, (GObject *) list->data.v_pointer);
887 // break;
888 default:
889 g_warning ("%s: Unsupported type (%s) retrieved.", G_STRLOC, g_type_name (value->g_type));
890 break;
891 }
892
893 return;
894}
895
896/* Sets @iter to point to the node following it at the current level.
897 *
898 * If there is no next @iter, %FALSE is returned and @iter is set
899 * to be invalid.
900 */
901static gboolean
902gtk_q_tree_model_iter_next(GtkTreeModel *tree_model,
903 GtkTreeIter *iter)
904{
905 GtkQTreeModel *q_tree_model = GTK_Q_TREE_MODEL (tree_model);
906 GtkQTreeModelPrivate *priv = q_tree_model->priv;
907
908 g_return_val_if_fail (iter_is_valid(iter, q_tree_model), FALSE);
909
910 QIter *qiter = Q_ITER(iter);
911 QModelIndex idx = priv->model->indexFromId(qiter->row.value, qiter->column.value, qiter->id);
912 idx = idx.sibling(idx.row()+1, idx.column());
913
914 /* validate */
915 if (validate_index(priv->stamp, idx, iter) ) {
916 GtkTreePath *path = gtk_q_tree_model_get_path(tree_model, iter);
917 gtk_tree_path_free(path);
918 return TRUE;
919 } else {
920 return FALSE;
921 }
922}
923
924/* Sets @iter to point to the previous node at the current level.
925 *
926 * If there is no previous @iter, %FALSE is returned and @iter is
927 * set to be invalid.
928 */
929static gboolean
930gtk_q_tree_model_iter_previous(GtkTreeModel *tree_model,
931 GtkTreeIter *iter)
932{
933 GtkQTreeModel *q_tree_model = GTK_Q_TREE_MODEL (tree_model);
934 GtkQTreeModelPrivate *priv = q_tree_model->priv;
935
936 g_return_val_if_fail (iter_is_valid(iter, q_tree_model), FALSE);
937
938 QIter *qiter = Q_ITER(iter);
939 QModelIndex idx = priv->model->indexFromId(qiter->row.value, qiter->column.value, qiter->id);
940 idx = idx.sibling(idx.row()-1, idx.column());
941
942 /* validate */
943 return validate_index(priv->stamp, idx, iter);
944}
945
946/* Sets @iter to point to the first child of @parent.
947 *
948 * If @parent has no children, %FALSE is returned and @iter is
949 * set to be invalid. @parent will remain a valid node after this
950 * function has been called.
951 *
952 * If @parent is %NULL returns the first node
953 */
954static gboolean
955gtk_q_tree_model_iter_children(GtkTreeModel *tree_model,
956 GtkTreeIter *iter,
957 GtkTreeIter *parent)
958{
959 GtkQTreeModel *q_tree_model = GTK_Q_TREE_MODEL (tree_model);
960 GtkQTreeModelPrivate *priv = q_tree_model->priv;
961 QModelIndex idx;
962
963 /* make sure parent if valid node if its not NULL */
964 if (parent)
965 g_return_val_if_fail(iter_is_valid(parent, q_tree_model), FALSE);
966
967 if (parent) {
968 /* get first child */
969 QIter *qparent = Q_ITER(parent);
970 idx = priv->model->indexFromId(qparent->row.value, qparent->column.value, qparent->id);
971 idx = idx.child(0, 0);
972 } else {
973 /* parent is NULL, get the first node */
974 idx = priv->model->index(0, 0);
975 }
976
977 /* validate child */
978 return validate_index(priv->stamp, idx, iter);
979}
980
981/* Returns %TRUE if @iter has children, %FALSE otherwise. */
982static gboolean
983gtk_q_tree_model_iter_has_child (GtkTreeModel *tree_model,
984 GtkTreeIter *iter)
985{
986 GtkQTreeModel *q_tree_model = GTK_Q_TREE_MODEL (tree_model);
987 GtkQTreeModelPrivate *priv = q_tree_model->priv;
988 g_return_val_if_fail(iter_is_valid(iter, q_tree_model), FALSE);
989
990 QIter *qiter = Q_ITER(iter);
991 QModelIndex idx = priv->model->indexFromId(qiter->row.value, qiter->column.value, qiter->id);
992 return priv->model->hasChildren(idx);
993}
994
995/* Returns the number of children that @iter has.
996 *
997 * As a special case, if @iter is %NULL, then the number
998 * of toplevel nodes is returned.
999 */
1000static gint
1001gtk_q_tree_model_iter_n_children (GtkTreeModel *tree_model,
1002 GtkTreeIter *iter)
1003{
1004 GtkQTreeModel *q_tree_model = GTK_Q_TREE_MODEL (tree_model);
1005 GtkQTreeModelPrivate *priv = q_tree_model->priv;
1006
1007 if (iter == NULL)
1008 return gtk_q_tree_model_length(q_tree_model);
1009
1010 g_return_val_if_fail(iter_is_valid(iter, q_tree_model), -1);
1011 QIter *qiter = Q_ITER(iter);
1012 QModelIndex idx = priv->model->indexFromId(qiter->row.value, qiter->column.value, qiter->id);
1013 return priv->model->rowCount(idx);
1014}
1015
1016/* Sets @iter to be the child of @parent, using the given index.
1017 *
1018 * The first index is 0. If @n is too big, or @parent has no children,
1019 * @iter is set to an invalid iterator and %FALSE is returned. @parent
1020 * will remain a valid node after this function has been called. As a
1021 * special case, if @parent is %NULL, then the @n<!-- -->th root node
1022 * is set.
1023 */
1024static gboolean
1025gtk_q_tree_model_iter_nth_child(GtkTreeModel *tree_model,
1026 GtkTreeIter *iter,
1027 GtkTreeIter *parent,
1028 gint n)
1029{
1030 GtkQTreeModel *q_tree_model = GTK_Q_TREE_MODEL (tree_model);
1031 GtkQTreeModelPrivate *priv = q_tree_model->priv;
1032 QModelIndex idx;
1033
1034 if (parent) {
1035 g_return_val_if_fail(iter_is_valid(parent, q_tree_model), FALSE);
1036
1037 /* get the nth child */
1038 QIter *qparent = Q_ITER(parent);
1039 idx = priv->model->indexFromId(qparent->row.value, qparent->column.value, qparent->id);
1040 idx = idx.child(n, 0);
1041 } else {
1042 idx = priv->model->index(n, 0);
1043 }
1044
1045 /* validate */
1046 return validate_index(priv->stamp, idx, iter);
1047}
1048
1049/* Sets @iter to be the parent of @child.
1050 *
1051 * If @child is at the toplevel, and doesn't have a parent, then
1052 * @iter is set to an invalid iterator and %FALSE is returned.
1053 * @child will remain a valid node after this function has been
1054 * called.
1055 */
1056static gboolean
1057gtk_q_tree_model_iter_parent(GtkTreeModel *tree_model,
1058 GtkTreeIter *iter,
1059 GtkTreeIter *child)
1060{
1061 GtkQTreeModel *q_tree_model = GTK_Q_TREE_MODEL (tree_model);
1062 GtkQTreeModelPrivate *priv = q_tree_model->priv;
1063 QModelIndex idx;
1064
1065 g_return_val_if_fail(iter_is_valid(child, q_tree_model), FALSE);
1066
1067 QIter *qchild = Q_ITER(child);
1068 idx = priv->model->indexFromId(qchild->row.value, qchild->column.value, qchild->id);
1069 idx = idx.parent();
1070
1071 /* validate */
1072 return validate_index(priv->stamp, idx, iter);
1073}
1074
Stepan Salenikovich69771842015-02-24 18:11:45 -05001075/* End Fulfill the GtkTreeModel requirements */
Stepan Salenikovich1e738fe2016-10-25 10:48:09 -04001076
1077/* Drag and Drop Source Interface */
1078static gboolean
1079gtk_q_tree_model_row_draggable(GtkTreeDragSource *, GtkTreePath *)
1080{
1081 // g_debug("row draggable");
1082 return TRUE;
1083}
1084static gboolean
1085gtk_q_tree_model_drag_data_delete(GtkTreeDragSource *, GtkTreePath *)
1086{
1087 /* we don't want to delete any data by dragging for now */
1088 // g_debug("drag data delete");
1089 return FALSE;
1090}
1091
1092static gboolean
1093gtk_q_tree_model_drag_data_get(GtkTreeDragSource *, GtkTreePath *, GtkSelectionData *)
1094{
1095 // g_debug("drag data get");
1096 return FALSE;
1097}
1098
1099static gboolean
1100gtk_q_tree_model_drag_data_received(GtkTreeDragDest *, GtkTreePath *, GtkSelectionData *)
1101{
1102 // g_debug("drag data received");
1103 return FALSE;
1104}
1105
1106static gboolean
1107gtk_q_tree_model_row_drop_possible(GtkTreeDragDest *, GtkTreePath *, GtkSelectionData *)
1108{
1109 // g_debug("row drop possible");
1110 return FALSE;
1111}