blob: 7a8574cadfd544fd3ad67edbd4cf4bc91ffa2d55 [file] [log] [blame]
Stepan Salenikovicha3557452015-02-20 14:14:12 -05001/*
Guillaume Roguez2a6150d2017-07-19 18:24:47 -04002 * Copyright (C) 2015-2017 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;
Stepan Salenikovich3026bb32017-04-27 13:31:48 -040065
66 gboolean layout_changing;
Stepan Salenikovicha3557452015-02-20 14:14:12 -050067};
68
69/* static prototypes */
70
71/* GtkTreeModel prototypes */
72static void gtk_q_tree_model_tree_model_init (GtkTreeModelIface * );
73static void gtk_q_tree_model_finalize (GObject * );
74static GtkTreeModelFlags gtk_q_tree_model_get_flags (GtkTreeModel * );
75static gint gtk_q_tree_model_get_n_columns (GtkTreeModel * );
76static GType gtk_q_tree_model_get_column_type (GtkTreeModel *,
77 gint );
78static gboolean gtk_q_tree_model_get_iter (GtkTreeModel *,
79 GtkTreeIter *,
80 GtkTreePath * );
81static GtkTreePath * gtk_q_tree_model_get_path (GtkTreeModel *,
82 GtkTreeIter * );
83static void gtk_q_tree_model_get_value (GtkTreeModel *,
84 GtkTreeIter *,
85 gint,
86 GValue * );
87static gboolean gtk_q_tree_model_iter_next (GtkTreeModel *,
88 GtkTreeIter * );
89static gboolean gtk_q_tree_model_iter_previous (GtkTreeModel *,
90 GtkTreeIter * );
91static gboolean gtk_q_tree_model_iter_children (GtkTreeModel *,
92 GtkTreeIter *,
93 GtkTreeIter * );
94static gboolean gtk_q_tree_model_iter_has_child (GtkTreeModel *,
95 GtkTreeIter * );
96static gint gtk_q_tree_model_iter_n_children (GtkTreeModel *,
97 GtkTreeIter * );
98static gboolean gtk_q_tree_model_iter_nth_child (GtkTreeModel *,
99 GtkTreeIter *,
100 GtkTreeIter *,
101 gint );
102static gboolean gtk_q_tree_model_iter_parent (GtkTreeModel *,
103 GtkTreeIter *,
104 GtkTreeIter * );
105
Stepan Salenikovich1e738fe2016-10-25 10:48:09 -0400106/* Drag and Drop */
107static void gtk_q_tree_model_drag_source_init (GtkTreeDragSourceIface *);
108static void gtk_q_tree_model_drag_dest_init (GtkTreeDragDestIface * );
109static gboolean gtk_q_tree_model_row_draggable (GtkTreeDragSource *,
110 GtkTreePath * );
111static gboolean gtk_q_tree_model_drag_data_delete (GtkTreeDragSource *,
112 GtkTreePath * );
113static gboolean gtk_q_tree_model_drag_data_get (GtkTreeDragSource *,
114 GtkTreePath *,
115 GtkSelectionData * );
116static gboolean gtk_q_tree_model_drag_data_received(GtkTreeDragDest *,
117 GtkTreePath *,
118 GtkSelectionData * );
119static gboolean gtk_q_tree_model_row_drop_possible (GtkTreeDragDest *,
120 GtkTreePath *,
121 GtkSelectionData * );
122
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500123/* implementation prototypes */
124static void qmodelindex_to_iter (const QModelIndex &,
125 GtkTreeIter * );
126// static void gtk_q_tree_model_increment_stamp (GtkQTreeModel * );
127
128static gint gtk_q_tree_model_length (GtkQTreeModel * );
129
130static void gtk_q_tree_model_set_n_columns (GtkQTreeModel *,
131 gint );
132static void gtk_q_tree_model_set_column_type (GtkQTreeModel *,
133 gint,
134 gint,
aviau9bbb19b2016-05-16 15:53:44 -0400135 gint,
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500136 GType );
137
138/* End private prototypes */
139
140/* define type, inherit from GObject, define implemented interface(s) */
141G_DEFINE_TYPE_WITH_CODE (GtkQTreeModel, gtk_q_tree_model, G_TYPE_OBJECT,
142 G_ADD_PRIVATE (GtkQTreeModel)
Stepan Salenikovich1e738fe2016-10-25 10:48:09 -0400143 G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL,
144 gtk_q_tree_model_tree_model_init)
145 G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_SOURCE,
146 gtk_q_tree_model_drag_source_init)
147 G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_DEST,
148 gtk_q_tree_model_drag_dest_init))
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500149
150#define GTK_Q_TREEMODEL_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_Q_TREE_MODEL, GtkQTreeModelPrivate))
151
152#define Q_ITER(iter) ((QIter *)iter)
153
154static void
155gtk_q_tree_model_class_init(GtkQTreeModelClass *klass)
156{
157 G_OBJECT_CLASS(klass)->finalize = gtk_q_tree_model_finalize;
158}
159
160static void
161gtk_q_tree_model_tree_model_init(GtkTreeModelIface *iface)
162{
163 iface->get_flags = gtk_q_tree_model_get_flags;
164 iface->get_n_columns = gtk_q_tree_model_get_n_columns;
165 iface->get_column_type = gtk_q_tree_model_get_column_type;
166 iface->get_iter = gtk_q_tree_model_get_iter;
167 iface->get_path = gtk_q_tree_model_get_path;
168 iface->get_value = gtk_q_tree_model_get_value;
169 iface->iter_next = gtk_q_tree_model_iter_next;
170 iface->iter_previous = gtk_q_tree_model_iter_previous;
171 iface->iter_children = gtk_q_tree_model_iter_children;
172 iface->iter_has_child = gtk_q_tree_model_iter_has_child;
173 iface->iter_n_children = gtk_q_tree_model_iter_n_children;
174 iface->iter_nth_child = gtk_q_tree_model_iter_nth_child;
175 iface->iter_parent = gtk_q_tree_model_iter_parent;
176}
177
178static void
Stepan Salenikovich1e738fe2016-10-25 10:48:09 -0400179gtk_q_tree_model_drag_source_init(GtkTreeDragSourceIface *iface)
180{
181 iface->row_draggable = gtk_q_tree_model_row_draggable;
182 iface->drag_data_delete = gtk_q_tree_model_drag_data_delete;
183 iface->drag_data_get = gtk_q_tree_model_drag_data_get;
184}
185
186static void
187gtk_q_tree_model_drag_dest_init(GtkTreeDragDestIface *iface)
188{
189 iface->drag_data_received = gtk_q_tree_model_drag_data_received;
190 iface->row_drop_possible = gtk_q_tree_model_row_drop_possible;
191}
192
193static void
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500194gtk_q_tree_model_init(GtkQTreeModel *q_tree_model)
195{
196 GtkQTreeModelPrivate *priv = GTK_Q_TREEMODEL_GET_PRIVATE(q_tree_model);
197 q_tree_model->priv = priv;
198
199 priv->stamp = g_random_int();
200 priv->model = NULL;
201}
202
203/**
204 * gtk_q_tree_model_get_qmodel
205 * returns the original model from which this GtkQTreeModel is created
206 */
207QAbstractItemModel *
208gtk_q_tree_model_get_qmodel(GtkQTreeModel *q_tree_model)
209{
210 GtkQTreeModelPrivate *priv = GTK_Q_TREEMODEL_GET_PRIVATE(q_tree_model);
211 return priv->model->sourceModel();
212}
213
214/**
215 * gtk_q_tree_model_get_source_idx
216 * Returns the index of the original model used to create this GtkQTreeModel from
217 * the given iter, if there is one.
218 */
219QModelIndex
220gtk_q_tree_model_get_source_idx(GtkQTreeModel *q_tree_model, GtkTreeIter *iter)
221{
222 GtkQTreeModelPrivate *priv = GTK_Q_TREEMODEL_GET_PRIVATE(q_tree_model);
Stepan Salenikovichf842c8b2017-04-19 18:49:19 -0400223
224 g_return_val_if_fail (iter->stamp == priv->stamp, QModelIndex());
225
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500226 QIter *qiter = Q_ITER(iter);
227 GtkAccessProxyModel *proxy_model = priv->model;
228 QModelIndex proxy_idx = proxy_model->indexFromId(qiter->row.value, qiter->column.value, qiter->id);
229 if (proxy_idx.isValid()) {
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500230 return proxy_model->mapToSource(proxy_idx);
231 } else {
Stepan Salenikovich69771842015-02-24 18:11:45 -0500232 g_warning("could not get valid QModelIndex from given GtkTreeIter");
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500233 return QModelIndex();
234 }
235}
236
237/**
238 * Takes a QModelIndex from the original QAbstractItemModel and returns a valid GtkTreeIter in the corresponding
239 * GtkQTreeModel
240 */
241gboolean
242gtk_q_tree_model_source_index_to_iter(GtkQTreeModel *q_tree_model, const QModelIndex &idx, GtkTreeIter *iter)
243{
244 GtkQTreeModelPrivate *priv = GTK_Q_TREEMODEL_GET_PRIVATE(q_tree_model);
245
Stepan Salenikovich69771842015-02-24 18:11:45 -0500246 /* the the proxy_idx from the source idx */
247 QModelIndex proxy_idx = priv->model->mapFromSource(idx);
248 if (!proxy_idx.isValid())
249 return FALSE;
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500250
251 /* make sure iter is valid */
252 iter->stamp = priv->stamp;
253
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500254 /* map the proxy idx to iter */
255 Q_ITER(iter)->row.value = proxy_idx.row();
Stepan Salenikovich7ff47962015-03-16 15:10:14 -0400256 Q_ITER(iter)->column.value = proxy_idx.column();
Stepan Salenikovichd2dbcee2015-02-27 16:52:28 -0500257 Q_ITER(iter)->id = proxy_idx.internalPointer();
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500258 return TRUE;
259}
260
261/**
Stepan Salenikovich22de0592015-10-21 10:27:20 -0400262 * helper method which recursively adds all the children of the given QModelIndex
263 */
264static void
265insert_children(const QModelIndex &idx, GtkQTreeModel *gtk_model)
266{
267 const auto children = gtk_model->priv->model->rowCount(idx);
268 for (int i = 0; i < children; ++i) {
269 GtkTreeIter iter_child;
270 auto idx_child = gtk_model->priv->model->index(i, 0, idx);
271 if (idx_child.isValid()) {
272 iter_child.stamp = gtk_model->priv->stamp;
273 qmodelindex_to_iter(idx_child, &iter_child);
274 if (auto path_child = gtk_q_tree_model_get_path(GTK_TREE_MODEL(gtk_model), &iter_child)) {
275 gtk_tree_model_row_inserted(GTK_TREE_MODEL(gtk_model), path_child, &iter_child);
276 gtk_tree_path_free(path_child);
277 }
278 insert_children(idx_child, gtk_model);
279 }
280 }
281}
282
283/**
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500284 * gtk_q_tree_model_new:
285 * @model: QAbstractItemModel to which this model will bind.
286 * @n_columns: number of columns in the list store
aviau271bcc22016-05-27 17:25:19 -0400287 * @...: model #Column, #GType and #Role for each column.
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500288 *
289 * Return value: a new #GtkQTreeModel
290 */
291GtkQTreeModel *
292gtk_q_tree_model_new(QAbstractItemModel *model, size_t n_columns, ...)
293{
294 GtkQTreeModel *retval;
295 va_list args;
296 gint i;
297
298 g_return_val_if_fail(n_columns > 0, NULL);
299
300 retval = (GtkQTreeModel *)g_object_new(GTK_TYPE_Q_TREE_MODEL, NULL);
301 gtk_q_tree_model_set_n_columns(retval, n_columns);
302
303 /* get proxy model from given model */
304 GtkAccessProxyModel* proxy_model = new GtkAccessProxyModel();
305 proxy_model->setSourceModel(model);
306 retval->priv->model = proxy_model;
307 gint stamp = retval->priv->stamp;
308
aviau9bbb19b2016-05-16 15:53:44 -0400309 n_columns = 3*n_columns;
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500310 va_start (args, n_columns);
311
aviau9bbb19b2016-05-16 15:53:44 -0400312 for (i = 0; i < (gint)(n_columns/3); i++) {
313 /* first get the model column */
314 gint model_col = va_arg(args, gint);
315
aviau271bcc22016-05-27 17:25:19 -0400316 /* then get the role of the QModel */
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500317 gint role = va_arg(args, gint);
318
319 /* then get the type the role will be interpreted as */
320 GType type = va_arg(args, GType);
321
322 /* TODO: check if its a type supported by our model
323 * if (! _gtk_tree_data_list_check_type (type))
324 * {
325 * g_warning ("%s: Invalid type %s\n", G_STRLOC, g_type_name (type));
326 * g_object_unref (retval);
327 * va_end (args);
328 *
329 * return NULL;
330 * }
331 */
332
aviau9bbb19b2016-05-16 15:53:44 -0400333 gtk_q_tree_model_set_column_type (retval, i, model_col, role, type);
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500334 }
335
336 va_end (args);
337
338 gtk_q_tree_model_length(retval);
339
340 /* connect signals */
341 QObject::connect(
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400342 proxy_model,
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500343 &QAbstractItemModel::rowsInserted,
344 [=](const QModelIndex & parent, int first, int last) {
345 for( int row = first; row <= last; row++) {
Stepan Salenikovich8573f862015-08-17 16:54:50 -0400346 GtkTreeIter iter;
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400347 QModelIndex idx = proxy_model->index(row, 0, parent);
Stepan Salenikovich8573f862015-08-17 16:54:50 -0400348 iter.stamp = stamp;
349 qmodelindex_to_iter(idx, &iter);
350 GtkTreePath *path = gtk_q_tree_model_get_path(GTK_TREE_MODEL(retval), &iter);
351 gtk_tree_model_row_inserted(GTK_TREE_MODEL(retval), path, &iter);
Stepan Salenikovichd2dfc162015-08-17 16:40:28 -0400352 gtk_tree_path_free(path);
Stepan Salenikovich4f922f72016-10-27 13:24:31 -0400353
354 // in certain cases (eg: proxy models), its possible for rows to be inserted that
355 // already have children; however no rowsInserted will be emitted for the children,
356 // in these cases we check for the existence of chilren and insert them
357 insert_children(idx, retval);
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500358 }
359 }
360 );
361
362 QObject::connect(
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400363 proxy_model,
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500364 &QAbstractItemModel::rowsAboutToBeMoved,
Stepan Salenikovich69771842015-02-24 18:11:45 -0500365 [=](const QModelIndex & sourceParent, int sourceStart, int sourceEnd,
Stepan Salenikovichede936f2016-10-26 14:42:16 -0400366 const QModelIndex & destinationParent, int destinationRow) {
367
368 /* detect if the source or destination rows are out of bounds (due to some incosistency)
369 * in order to prevent crash */
370 auto max_row = proxy_model->rowCount(sourceParent) - 1;
371 auto destinationEndRow = destinationRow + (sourceEnd - sourceStart);
372 if (sourceStart > max_row || sourceEnd > max_row || destinationRow > max_row || destinationEndRow > max_row) {
373 g_critical("rowsMoved: rows being moved inconsistent with number of rows in the model");
374 return;
375 }
376
Stepan Salenikovich0ba98102015-10-21 15:07:52 -0400377 /* if the sourceParent and the destinationParent are the same, then we can use the
378 * GtkTreeModel API to move the rows, otherwise we must first delete them and then
379 * re-insert them under the new parent */
380 if (sourceParent == destinationParent)
381 return;
382
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500383 /* first remove the row from old location
384 * then insert them at the new location on the "rowsMoved signal */
385 for( int row = sourceStart; row <= sourceEnd; row++) {
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400386 QModelIndex idx = proxy_model->index(row, 0, sourceParent);
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500387 GtkTreeIter iter_old;
Stepan Salenikovichfcf8fc62015-10-21 11:08:08 -0400388 iter_old.stamp = stamp;
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500389 qmodelindex_to_iter(idx, &iter_old);
390 GtkTreePath *path_old = gtk_q_tree_model_get_path(GTK_TREE_MODEL(retval), &iter_old);
391 gtk_tree_model_row_deleted(GTK_TREE_MODEL(retval), path_old);
Stepan Salenikovichd2dfc162015-08-17 16:40:28 -0400392 gtk_tree_path_free(path_old);
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500393 }
394 }
395 );
396
397 QObject::connect(
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400398 proxy_model,
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500399 &QAbstractItemModel::rowsMoved,
Stepan Salenikovich0ba98102015-10-21 15:07:52 -0400400 [=](const QModelIndex & sourceParent, int sourceStart, int sourceEnd,
Stepan Salenikovich69771842015-02-24 18:11:45 -0500401 const QModelIndex & destinationParent, int destinationRow) {
Stepan Salenikovichede936f2016-10-26 14:42:16 -0400402
403 /* detect if the source or destination rows are out of bounds (due to some incosistency)
404 * in order to prevent crash */
405 auto max_row = proxy_model->rowCount(sourceParent) - 1;
406 auto destinationEndRow = destinationRow + (sourceEnd - sourceStart);
407 if (sourceStart > max_row || sourceEnd > max_row || destinationRow > max_row || destinationEndRow > max_row) {
408 g_critical("rowsMoved: rows being moved inconsistent with number of rows in the model");
409 return;
410 }
411
Stepan Salenikovich0ba98102015-10-21 15:07:52 -0400412 /* if the sourceParent and the destinationParent are the same, then we can use the
413 * GtkTreeModel API to move the rows, otherwise we must first delete them and then
414 * re-insert them under the new parent */
415 if (sourceParent == destinationParent) {
416 GtkTreeIter *iter_parent = nullptr;
417 GtkTreePath *path_parent = nullptr;
418 if (sourceParent.isValid()) {
419 iter_parent = g_new0(GtkTreeIter, 1);
420 iter_parent->stamp = stamp;
421 qmodelindex_to_iter(sourceParent, iter_parent);
422 path_parent = gtk_q_tree_model_get_path(GTK_TREE_MODEL(retval), iter_parent);
423 } else {
424 path_parent = gtk_tree_path_new();
425 }
426
427 /* gtk_tree_model_rows_reordered takes an array of integers mapping the current
428 * position of each child to its old position before the re-ordering,
429 * i.e. new_order [newpos] = oldpos
430 */
431 const auto rows = proxy_model->rowCount(sourceParent);
Stepan Salenikovich365dfd12015-10-21 18:52:38 -0400432 gint new_order[rows];
Stepan Salenikovich0ba98102015-10-21 15:07:52 -0400433 const auto destinationRowLast = destinationRow + (sourceEnd - sourceStart);
Stepan Salenikovichcaa182a2015-10-22 11:50:09 -0400434 const auto totalMoved = sourceEnd - sourceStart + 1;
Stepan Salenikovich0ba98102015-10-21 15:07:52 -0400435 for (int row = 0; row < rows; ++row ) {
436 if ( (row < sourceStart && row < destinationRow)
437 || (row > sourceEnd && row > destinationRowLast) ) {
438 // if the row is outside of the bounds of change, then it stayed in the same place
439 new_order[row] = row;
440 } else {
441 if (row < destinationRow) {
442 // in front of the destination, so it used to be behind the rows that moved
Stepan Salenikovichcaa182a2015-10-22 11:50:09 -0400443 new_order[row] = row + totalMoved;
Stepan Salenikovich0ba98102015-10-21 15:07:52 -0400444 } else if (row >= destinationRow && row <= destinationRowLast) {
445 // within the destination, so it was within the rows that moved
446 new_order[row] = sourceStart + (row - destinationRow);
447 } else {
448 // behind the destination, so it used to be in front of the rows that moved
Stepan Salenikovichcaa182a2015-10-22 11:50:09 -0400449 new_order[row] = row - totalMoved;
Stepan Salenikovich0ba98102015-10-21 15:07:52 -0400450 }
451 }
452 }
453 gtk_tree_model_rows_reordered(GTK_TREE_MODEL(retval), path_parent, iter_parent, new_order);
454
455 g_free(iter_parent);
456 gtk_tree_path_free(path_parent);
457 } else {
458 /* these rows should have been removed in the "rowsAboutToBeMoved" handler
459 * now insert them in the new location */
460 for( int row = sourceStart; row <= sourceEnd; row++) {
461 GtkTreeIter iter_new;
462 QModelIndex idx = proxy_model->index(destinationRow, 0, destinationParent);
463 iter_new.stamp = stamp;
464 qmodelindex_to_iter(idx, &iter_new);
465 GtkTreePath *path_new = gtk_q_tree_model_get_path(GTK_TREE_MODEL(retval), &iter_new);
466 gtk_tree_model_row_inserted(GTK_TREE_MODEL(retval), path_new, &iter_new);
467 gtk_tree_path_free(path_new);
468 insert_children(idx, retval);
469 destinationRow++;
470 }
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500471 }
472 }
473 );
474
475 QObject::connect(
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400476 proxy_model,
Stepan Salenikoviche17a2932015-06-11 14:50:02 -0400477 &QAbstractItemModel::rowsRemoved,
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500478 [=](const QModelIndex & parent, int first, int last) {
Stepan Salenikoviche17a2932015-06-11 14:50:02 -0400479 GtkTreePath *parent_path = NULL;
480 if (parent.isValid()) {
481 GtkTreeIter iter_parent;
482 iter_parent.stamp = stamp;
483 qmodelindex_to_iter(parent, &iter_parent);
484 parent_path = gtk_q_tree_model_get_path(GTK_TREE_MODEL(retval), &iter_parent);
485 } else {
486 parent_path = gtk_tree_path_new();
487 }
488
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400489 /* go last to first, since the rows are being deleted */
490 for( int row = last; row >= first; --row) {
491 // g_debug("deleting row: %d", row);
Stepan Salenikoviche17a2932015-06-11 14:50:02 -0400492 GtkTreePath *path = gtk_tree_path_copy(parent_path);
493 gtk_tree_path_append_index(path, row);
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500494 gtk_tree_model_row_deleted(GTK_TREE_MODEL(retval), path);
Stepan Salenikovichd2dfc162015-08-17 16:40:28 -0400495 gtk_tree_path_free(path);
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500496 }
Stepan Salenikoviche17a2932015-06-11 14:50:02 -0400497
498 gtk_tree_path_free(parent_path);
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500499 }
500 );
501
502 QObject::connect(
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400503 proxy_model,
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500504 &QAbstractItemModel::dataChanged,
Stepan Salenikovich69771842015-02-24 18:11:45 -0500505 [=](const QModelIndex & topLeft, const QModelIndex & bottomRight,
506 G_GNUC_UNUSED const QVector<int> & roles = QVector<int> ()) {
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500507 /* we have to assume only one column */
508 int first = topLeft.row();
509 int last = bottomRight.row();
Stepan Salenikovicha647bcf2016-10-04 17:31:34 -0400510
511 /* the first idx IS topLeft, the rest are his siblings */
Stepan Salenikovich8573f862015-08-17 16:54:50 -0400512 GtkTreeIter iter;
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500513 QModelIndex idx = topLeft;
Stepan Salenikovich8573f862015-08-17 16:54:50 -0400514 iter.stamp = stamp;
515 qmodelindex_to_iter(idx, &iter);
516 GtkTreePath *path = gtk_q_tree_model_get_path(GTK_TREE_MODEL(retval), &iter);
517 gtk_tree_model_row_changed(GTK_TREE_MODEL(retval), path, &iter);
Stepan Salenikovichd2dfc162015-08-17 16:40:28 -0400518 gtk_tree_path_free(path);
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500519 for( int row = first + 1; row <= last; row++) {
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500520 idx = topLeft.sibling(row, 0);
Stepan Salenikovich8573f862015-08-17 16:54:50 -0400521 qmodelindex_to_iter(idx, &iter);
522 path = gtk_q_tree_model_get_path(GTK_TREE_MODEL(retval), &iter);
523 gtk_tree_model_row_changed(GTK_TREE_MODEL(retval), path, &iter);
Stepan Salenikovichd2dfc162015-08-17 16:40:28 -0400524 gtk_tree_path_free(path);
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500525 }
526 }
527 );
528
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400529 QObject::connect(
530 proxy_model,
531 &QAbstractItemModel::modelAboutToBeReset,
532 [=] () {
533 // g_debug("model about to be reset");
534 /* nothing equvivalent eixists in GtkTreeModel, so simply delete all
535 * rows, and add all rows when the model is reset;
536 * we must delete the rows in ascending order */
537 int row_count = proxy_model->rowCount();
538 for (int row = row_count; row > 0; --row) {
539 // g_debug("deleting row %d", row -1);
540 QModelIndex idx = proxy_model->index(row - 1, 0);
541 GtkTreeIter iter;
542 iter.stamp = stamp;
543 qmodelindex_to_iter(idx, &iter);
544 GtkTreePath *path = gtk_q_tree_model_get_path(GTK_TREE_MODEL(retval), &iter);
545 gtk_tree_model_row_deleted(GTK_TREE_MODEL(retval), path);
Stepan Salenikovichd2dfc162015-08-17 16:40:28 -0400546 gtk_tree_path_free(path);
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400547 }
548 }
549 );
550
551 QObject::connect(
552 proxy_model,
553 &QAbstractItemModel::modelReset,
554 [=] () {
555 // g_debug("model reset");
Stepan Salenikovich22de0592015-10-21 10:27:20 -0400556 /* now recursively add all the (new) rows */
557 insert_children(QModelIndex(), retval);
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400558 }
559 );
560
Stepan Salenikovich2d4b4722016-10-26 15:33:39 -0400561 QObject::connect(
562 proxy_model,
Stepan Salenikovich40d63252017-04-26 16:37:26 -0400563 &QAbstractItemModel::layoutAboutToBeChanged,
Stepan Salenikovich2d4b4722016-10-26 15:33:39 -0400564 [=] () {
Stepan Salenikovich40d63252017-04-26 16:37:26 -0400565 // g_debug("layout aboout to change");
Stepan Salenikovich3026bb32017-04-27 13:31:48 -0400566
567 if (retval->priv->layout_changing)
568 g_warning("GtkQTreeModel: handling layoutAboutToBeChanged, but didn't finish layoutChanged");
569
570 retval->priv->layout_changing = TRUE;
Stepan Salenikovich40d63252017-04-26 16:37:26 -0400571 /* nothing equvivalent eixists in GtkTreeModel, so simply delete all
572 * rows, and add all rows when the layout is changed;
573 * we must delete the rows in ascending order */
Stepan Salenikovich2d4b4722016-10-26 15:33:39 -0400574 int row_count = proxy_model->rowCount();
Stepan Salenikovich40d63252017-04-26 16:37:26 -0400575 for (int row = row_count; row > 0; --row) {
576 // g_debug("deleting row %d", row -1);
577 QModelIndex idx = proxy_model->index(row - 1, 0);
Stepan Salenikovich2d4b4722016-10-26 15:33:39 -0400578 GtkTreeIter iter;
Stepan Salenikovich2d4b4722016-10-26 15:33:39 -0400579 iter.stamp = stamp;
580 qmodelindex_to_iter(idx, &iter);
581 GtkTreePath *path = gtk_q_tree_model_get_path(GTK_TREE_MODEL(retval), &iter);
Stepan Salenikovich40d63252017-04-26 16:37:26 -0400582 gtk_tree_model_row_deleted(GTK_TREE_MODEL(retval), path);
Stepan Salenikovich2d4b4722016-10-26 15:33:39 -0400583 gtk_tree_path_free(path);
584 }
585 }
586 );
Stepan Salenikovich5d3506e2015-03-30 11:01:29 -0400587
Stepan Salenikovich40d63252017-04-26 16:37:26 -0400588 QObject::connect(
589 proxy_model,
590 &QAbstractItemModel::layoutChanged,
591 [=] () {
592 // g_debug("layout changed");
Stepan Salenikovich3026bb32017-04-27 13:31:48 -0400593
594 if (!retval->priv->layout_changing)
595 g_warning("GtkQTreeModel: handling layoutChanged, but didn't get a layoutAboutToBeChanged");
596
Stepan Salenikovich40d63252017-04-26 16:37:26 -0400597 /* after layoutAboutToBeChanged has been handled by removing all the rows, we add all
598 * the rows back;
599 * NOTE: this will lose the selection in the GtkTreeView, if it needs to be kept, then
600 * in the view code we need to connect to this layoutChanged signal and re-sync
601 * the selection */
602 insert_children(QModelIndex(), retval);
Stepan Salenikovich3026bb32017-04-27 13:31:48 -0400603
604 retval->priv->layout_changing = FALSE;
Stepan Salenikovich40d63252017-04-26 16:37:26 -0400605 }
606 );
607
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500608 return retval;
609}
610
611static gint
612gtk_q_tree_model_length(GtkQTreeModel *q_tree_model)
613{
614 GtkQTreeModelPrivate *priv = q_tree_model->priv;
615 return priv->model->rowCount();
616}
617
618static void
619gtk_q_tree_model_set_n_columns(GtkQTreeModel *q_tree_model,
620 gint n_columns)
621{
622 GtkQTreeModelPrivate *priv = q_tree_model->priv;
623 int i;
624
625 if (priv->n_columns == n_columns)
626 return;
627
628 priv->column_headers = g_renew (GType, priv->column_headers, n_columns);
629 for (i = priv->n_columns; i < n_columns; i++)
630 priv->column_headers[i] = G_TYPE_INVALID;
631 priv->n_columns = n_columns;
632
633 priv->column_roles = g_renew (gint, priv->column_roles, n_columns);
634 for (i = priv->n_columns; i < n_columns; i++)
635 priv->column_roles[i] = -1;
aviau9bbb19b2016-05-16 15:53:44 -0400636
637 priv->column_model_col = g_renew (gint, priv->column_model_col, n_columns);
638 for (i = priv->n_columns; i < n_columns; i++)
639 priv->column_model_col[i] = 0;
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500640}
641
642static void
643gtk_q_tree_model_set_column_type(GtkQTreeModel *q_tree_model,
644 gint column,
aviau9bbb19b2016-05-16 15:53:44 -0400645 gint model_col,
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500646 gint role,
647 GType type)
648{
649 GtkQTreeModelPrivate *priv = q_tree_model->priv;
650
651 /* TODO: check if its a type supported by our model
652 * if (!_gtk_tree_data_list_check_type (type))
653 * {
654 * g_warning ("%s: Invalid type %s\n", G_STRLOC, g_type_name (type));
655 * return;
656 * }
657 */
658
aviau9bbb19b2016-05-16 15:53:44 -0400659 priv->column_model_col[column] = model_col;
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500660 priv->column_headers[column] = type;
661 priv->column_roles[column] = role;
662}
663
664
665static void
666gtk_q_tree_model_finalize(GObject *object)
667{
668 GtkQTreeModel *q_tree_model = GTK_Q_TREE_MODEL (object);
669 GtkQTreeModelPrivate *priv = q_tree_model->priv;
670
aviau9bbb19b2016-05-16 15:53:44 -0400671 g_free(priv->column_model_col);
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500672 g_free(priv->column_headers);
673 g_free(priv->column_roles);
674
675 /* delete the created proxy model */
676 delete priv->model;
677
678 G_OBJECT_CLASS(gtk_q_tree_model_parent_class)->finalize (object);
679}
680
681static gboolean
682iter_is_valid(GtkTreeIter *iter,
683 GtkQTreeModel *q_tree_model)
684{
Stepan Salenikovichc8402f02017-04-26 15:00:42 -0400685 auto priv = GTK_Q_TREEMODEL_GET_PRIVATE(q_tree_model);
686
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500687 g_return_val_if_fail(iter != NULL, FALSE);
Stepan Salenikovichc8402f02017-04-26 15:00:42 -0400688 g_return_val_if_fail(iter->stamp == priv->stamp, FALSE);
689
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500690 QIter *qiter = Q_ITER(iter);
Stepan Salenikovichc8402f02017-04-26 15:00:42 -0400691 return q_tree_model->priv->model->indexFromId(qiter->row.value, qiter->column.value, qiter->id).isValid();
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500692}
693
694static void
695qmodelindex_to_iter(const QModelIndex &idx, GtkTreeIter *iter)
696{
697 Q_ITER(iter)->row.value = idx.row();
Stepan Salenikovich7ff47962015-03-16 15:10:14 -0400698 Q_ITER(iter)->column.value = idx.column();
Stepan Salenikovichd2dbcee2015-02-27 16:52:28 -0500699 Q_ITER(iter)->id = idx.internalPointer();
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500700}
701
702static gboolean
703validate_index(gint stamp, const QModelIndex &idx, GtkTreeIter *iter)
704{
705 if (idx.isValid()) {
706 iter->stamp = stamp;
707 qmodelindex_to_iter(idx, iter);
708 } else {
709 iter->stamp = 0;
710 return FALSE;
711 }
712 return TRUE;
713}
714
715/* Start Fulfill the GtkTreeModel requirements */
716
717/* flags supported by this interface */
718static GtkTreeModelFlags
719gtk_q_tree_model_get_flags(G_GNUC_UNUSED GtkTreeModel *tree_model)
720{
721 // TODO: possibly return based on the model?
722 return (GtkTreeModelFlags)(GTK_TREE_MODEL_ITERS_PERSIST);
723}
724
725/* number of columns supported by this tree model */
726static gint
727gtk_q_tree_model_get_n_columns(GtkTreeModel *tree_model)
728{
729 GtkQTreeModel *q_tree_model = GTK_Q_TREE_MODEL(tree_model);
730 GtkQTreeModelPrivate *priv = q_tree_model->priv;
731
732 return priv->model->columnCount();
733}
734
735/* get given column type */
736static GType
737gtk_q_tree_model_get_column_type(GtkTreeModel *tree_model,
738 gint index)
739{
740 GtkQTreeModel *q_tree_model = GTK_Q_TREE_MODEL (tree_model);
741 GtkQTreeModelPrivate *priv = q_tree_model->priv;
742
743 g_return_val_if_fail (index < gtk_q_tree_model_get_n_columns(tree_model), G_TYPE_INVALID);
744
745 return priv->column_headers[index];
746}
747
748/* Sets @iter to a valid iterator pointing to @path. If @path does
749 * not exist, @iter is set to an invalid iterator and %FALSE is returned.
750 */
751static gboolean
752gtk_q_tree_model_get_iter(GtkTreeModel *tree_model,
753 GtkTreeIter *iter,
754 GtkTreePath *path)
755{
756 GtkQTreeModel *q_tree_model = GTK_Q_TREE_MODEL (tree_model);
757 GtkQTreeModelPrivate *priv = q_tree_model->priv;
758
759 /* GtkTreePath is a list of indices, each one indicates
760 * the child of the previous one.
761 * Since GtkTreeModel only has rows (columns are only used to
762 * store data in each item), each index is basically the row
763 * at the given tree level.
764 * To get the iter, we want to get the QModelIndex. To get
765 * the QModelIndex we simply start at the first level and
766 * traverse the model the number of layers equal to the number
767 * of indices in the path.
768 */
769 gint depth;
770 gint* indices = gtk_tree_path_get_indices_with_depth(path, &depth);
771 QModelIndex idx = priv->model->index(indices[0], 0);
772 for(int layer = 1; layer < depth; layer++ ) {
773 /* check if previous iter is valid */
774 if (!idx.isValid()) {
775 break;
776 } else {
777 idx = idx.child(indices[layer], 0);
778 }
779 }
780
781 if (!idx.isValid()) {
782 iter->stamp = 0;
783 return FALSE;
784 } else {
785 /* we have a valid QModelIndex; turn it into an iter */
786 iter->stamp = priv->stamp;
787 qmodelindex_to_iter(idx, iter);
788 }
789
790 return TRUE;
791}
792
793/* Returns a newly-created #GtkTreePath referenced by @iter.
794 *
795 * This path should be freed with gtk_tree_path_free().
796 */
797static GtkTreePath *
798gtk_q_tree_model_get_path(GtkTreeModel *tree_model,
799 GtkTreeIter *iter)
800{
801 GtkQTreeModel *q_tree_model = GTK_Q_TREE_MODEL (tree_model);
802 GtkQTreeModelPrivate *priv = q_tree_model->priv;
803 GtkTreePath *path;
804
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500805 g_return_val_if_fail (iter_is_valid(iter, q_tree_model), NULL);
806
807 /* To get the path, we have to traverse from the child all the way up */
808 path = gtk_tree_path_new();
809 QIter *qiter = Q_ITER(iter);
810 QModelIndex idx = priv->model->indexFromId(qiter->row.value, qiter->column.value, qiter->id);
811 while( idx.isValid() ){
812 gtk_tree_path_prepend_index(path, idx.row());
813 idx = idx.parent();
814 }
815 return path;
816}
817
818static inline GType
819get_fundamental_type(GType type)
820{
821 GType result;
822
823 result = G_TYPE_FUNDAMENTAL (type);
824
825 if (result == G_TYPE_INTERFACE) {
826 if (g_type_is_a (type, G_TYPE_OBJECT))
827 result = G_TYPE_OBJECT;
828 }
829
830 return result;
831}
832
833/* Initializes and sets @value to that at @column. */
834static void
835gtk_q_tree_model_get_value(GtkTreeModel *tree_model,
836 GtkTreeIter *iter,
837 gint column,
838 GValue *value)
839{
840 GtkQTreeModel *q_tree_model = GTK_Q_TREE_MODEL (tree_model);
841 GtkQTreeModelPrivate *priv = q_tree_model->priv;
842
843 g_return_if_fail (column < priv->n_columns);
844 g_return_if_fail (iter_is_valid(iter, q_tree_model));
845
846 /* get the data */
847 QIter *qiter = Q_ITER(iter);
aviau9bbb19b2016-05-16 15:53:44 -0400848 int model_col = priv->column_model_col[column];
849 QModelIndex idx = priv->model->indexFromId(qiter->row.value, model_col, qiter->id);
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500850 int role = priv->column_roles[column];
851 QVariant var = priv->model->data(idx, role);
852 GType type = priv->column_headers[column];
853 g_value_init (value, type);
854 switch (get_fundamental_type (type))
855 {
856 /* TODO: implement the rest of the data types the we plan to support
857 * otherwise get rid of the code and make sure un-implemented types
858 * are not allowed
859 */
860 case G_TYPE_BOOLEAN:
861 g_value_set_boolean (value, (gboolean) var.toBool());
862 break;
863 // case G_TYPE_CHAR:
864 // g_value_set_schar (value, (gchar) list->data.v_char);
865 // break;
866 // case G_TYPE_UCHAR:
867 // g_value_set_uchar (value, (guchar) list->data.v_uchar);
868 // break;
869 case G_TYPE_INT:
870 g_value_set_int (value, (gint) var.toInt());
871 break;
872 case G_TYPE_UINT:
873 g_value_set_uint (value, (guint) var.toUInt());
874 break;
875 // case G_TYPE_LONG:
876 // g_value_set_long (value, list->data.v_long);
877 // break;
878 // case G_TYPE_ULONG:
879 // g_value_set_ulong (value, list->data.v_ulong);
880 // break;
881 // case G_TYPE_INT64:
882 // g_value_set_int64 (value, list->data.v_int64);
883 // break;
884 // case G_TYPE_UINT64:
885 // g_value_set_uint64 (value, list->data.v_uint64);
886 // break;
887 // case G_TYPE_ENUM:
888 // g_value_set_enum (value, list->data.v_int);
889 // break;
890 // case G_TYPE_FLAGS:
891 // g_value_set_flags (value, list->data.v_uint);
892 // break;
893 // case G_TYPE_FLOAT:
894 // g_value_set_float (value, (gfloat) list->data.v_float);
895 // break;
896 // case G_TYPE_DOUBLE:
897 // g_value_set_double (value, (gdouble) list->data.v_double);
898 // break;
899 case G_TYPE_STRING:
Stepan Salenikovich69771842015-02-24 18:11:45 -0500900 {
Stepan Salenikovich6f687072015-03-26 10:43:37 -0400901 QByteArray ba = var.toString().toUtf8();
902 g_value_set_string(value, (gchar *)var.toByteArray().constData());
Stepan Salenikovich69771842015-02-24 18:11:45 -0500903 }
904 break;
Stepan Salenikovicha3557452015-02-20 14:14:12 -0500905 // case G_TYPE_POINTER:
906 // g_value_set_pointer (value, (gpointer) list->data.v_pointer);
907 // break;
908 // case G_TYPE_BOXED:
909 // g_value_set_boxed (value, (gpointer) list->data.v_pointer);
910 // break;
911 // case G_TYPE_VARIANT:
912 // g_value_set_variant (value, (gpointer) list->data.v_pointer);
913 // break;
914 // case G_TYPE_OBJECT:
915 // g_value_set_object (value, (GObject *) list->data.v_pointer);
916 // break;
917 default:
918 g_warning ("%s: Unsupported type (%s) retrieved.", G_STRLOC, g_type_name (value->g_type));
919 break;
920 }
921
922 return;
923}
924
925/* Sets @iter to point to the node following it at the current level.
926 *
927 * If there is no next @iter, %FALSE is returned and @iter is set
928 * to be invalid.
929 */
930static gboolean
931gtk_q_tree_model_iter_next(GtkTreeModel *tree_model,
932 GtkTreeIter *iter)
933{
934 GtkQTreeModel *q_tree_model = GTK_Q_TREE_MODEL (tree_model);
935 GtkQTreeModelPrivate *priv = q_tree_model->priv;
936
937 g_return_val_if_fail (iter_is_valid(iter, q_tree_model), FALSE);
938
939 QIter *qiter = Q_ITER(iter);
940 QModelIndex idx = priv->model->indexFromId(qiter->row.value, qiter->column.value, qiter->id);
941 idx = idx.sibling(idx.row()+1, idx.column());
942
943 /* validate */
944 if (validate_index(priv->stamp, idx, iter) ) {
945 GtkTreePath *path = gtk_q_tree_model_get_path(tree_model, iter);
946 gtk_tree_path_free(path);
947 return TRUE;
948 } else {
949 return FALSE;
950 }
951}
952
953/* Sets @iter to point to the previous node at the current level.
954 *
955 * If there is no previous @iter, %FALSE is returned and @iter is
956 * set to be invalid.
957 */
958static gboolean
959gtk_q_tree_model_iter_previous(GtkTreeModel *tree_model,
960 GtkTreeIter *iter)
961{
962 GtkQTreeModel *q_tree_model = GTK_Q_TREE_MODEL (tree_model);
963 GtkQTreeModelPrivate *priv = q_tree_model->priv;
964
965 g_return_val_if_fail (iter_is_valid(iter, q_tree_model), FALSE);
966
967 QIter *qiter = Q_ITER(iter);
968 QModelIndex idx = priv->model->indexFromId(qiter->row.value, qiter->column.value, qiter->id);
969 idx = idx.sibling(idx.row()-1, idx.column());
970
971 /* validate */
972 return validate_index(priv->stamp, idx, iter);
973}
974
975/* Sets @iter to point to the first child of @parent.
976 *
977 * If @parent has no children, %FALSE is returned and @iter is
978 * set to be invalid. @parent will remain a valid node after this
979 * function has been called.
980 *
981 * If @parent is %NULL returns the first node
982 */
983static gboolean
984gtk_q_tree_model_iter_children(GtkTreeModel *tree_model,
985 GtkTreeIter *iter,
986 GtkTreeIter *parent)
987{
988 GtkQTreeModel *q_tree_model = GTK_Q_TREE_MODEL (tree_model);
989 GtkQTreeModelPrivate *priv = q_tree_model->priv;
990 QModelIndex idx;
991
992 /* make sure parent if valid node if its not NULL */
993 if (parent)
994 g_return_val_if_fail(iter_is_valid(parent, q_tree_model), FALSE);
995
996 if (parent) {
997 /* get first child */
998 QIter *qparent = Q_ITER(parent);
999 idx = priv->model->indexFromId(qparent->row.value, qparent->column.value, qparent->id);
1000 idx = idx.child(0, 0);
1001 } else {
1002 /* parent is NULL, get the first node */
1003 idx = priv->model->index(0, 0);
1004 }
1005
1006 /* validate child */
1007 return validate_index(priv->stamp, idx, iter);
1008}
1009
1010/* Returns %TRUE if @iter has children, %FALSE otherwise. */
1011static gboolean
1012gtk_q_tree_model_iter_has_child (GtkTreeModel *tree_model,
1013 GtkTreeIter *iter)
1014{
1015 GtkQTreeModel *q_tree_model = GTK_Q_TREE_MODEL (tree_model);
1016 GtkQTreeModelPrivate *priv = q_tree_model->priv;
1017 g_return_val_if_fail(iter_is_valid(iter, q_tree_model), FALSE);
1018
1019 QIter *qiter = Q_ITER(iter);
1020 QModelIndex idx = priv->model->indexFromId(qiter->row.value, qiter->column.value, qiter->id);
1021 return priv->model->hasChildren(idx);
1022}
1023
1024/* Returns the number of children that @iter has.
1025 *
1026 * As a special case, if @iter is %NULL, then the number
1027 * of toplevel nodes is returned.
1028 */
1029static gint
1030gtk_q_tree_model_iter_n_children (GtkTreeModel *tree_model,
1031 GtkTreeIter *iter)
1032{
1033 GtkQTreeModel *q_tree_model = GTK_Q_TREE_MODEL (tree_model);
1034 GtkQTreeModelPrivate *priv = q_tree_model->priv;
1035
1036 if (iter == NULL)
1037 return gtk_q_tree_model_length(q_tree_model);
1038
1039 g_return_val_if_fail(iter_is_valid(iter, q_tree_model), -1);
1040 QIter *qiter = Q_ITER(iter);
1041 QModelIndex idx = priv->model->indexFromId(qiter->row.value, qiter->column.value, qiter->id);
1042 return priv->model->rowCount(idx);
1043}
1044
1045/* Sets @iter to be the child of @parent, using the given index.
1046 *
1047 * The first index is 0. If @n is too big, or @parent has no children,
1048 * @iter is set to an invalid iterator and %FALSE is returned. @parent
1049 * will remain a valid node after this function has been called. As a
1050 * special case, if @parent is %NULL, then the @n<!-- -->th root node
1051 * is set.
1052 */
1053static gboolean
1054gtk_q_tree_model_iter_nth_child(GtkTreeModel *tree_model,
1055 GtkTreeIter *iter,
1056 GtkTreeIter *parent,
1057 gint n)
1058{
1059 GtkQTreeModel *q_tree_model = GTK_Q_TREE_MODEL (tree_model);
1060 GtkQTreeModelPrivate *priv = q_tree_model->priv;
1061 QModelIndex idx;
1062
1063 if (parent) {
1064 g_return_val_if_fail(iter_is_valid(parent, q_tree_model), FALSE);
1065
1066 /* get the nth child */
1067 QIter *qparent = Q_ITER(parent);
1068 idx = priv->model->indexFromId(qparent->row.value, qparent->column.value, qparent->id);
1069 idx = idx.child(n, 0);
1070 } else {
1071 idx = priv->model->index(n, 0);
1072 }
1073
1074 /* validate */
1075 return validate_index(priv->stamp, idx, iter);
1076}
1077
1078/* Sets @iter to be the parent of @child.
1079 *
1080 * If @child is at the toplevel, and doesn't have a parent, then
1081 * @iter is set to an invalid iterator and %FALSE is returned.
1082 * @child will remain a valid node after this function has been
1083 * called.
1084 */
1085static gboolean
1086gtk_q_tree_model_iter_parent(GtkTreeModel *tree_model,
1087 GtkTreeIter *iter,
1088 GtkTreeIter *child)
1089{
1090 GtkQTreeModel *q_tree_model = GTK_Q_TREE_MODEL (tree_model);
1091 GtkQTreeModelPrivate *priv = q_tree_model->priv;
1092 QModelIndex idx;
1093
1094 g_return_val_if_fail(iter_is_valid(child, q_tree_model), FALSE);
1095
1096 QIter *qchild = Q_ITER(child);
1097 idx = priv->model->indexFromId(qchild->row.value, qchild->column.value, qchild->id);
1098 idx = idx.parent();
1099
1100 /* validate */
1101 return validate_index(priv->stamp, idx, iter);
1102}
1103
Stepan Salenikovich69771842015-02-24 18:11:45 -05001104/* End Fulfill the GtkTreeModel requirements */
Stepan Salenikovich1e738fe2016-10-25 10:48:09 -04001105
1106/* Drag and Drop Source Interface */
1107static gboolean
1108gtk_q_tree_model_row_draggable(GtkTreeDragSource *, GtkTreePath *)
1109{
1110 // g_debug("row draggable");
1111 return TRUE;
1112}
1113static gboolean
1114gtk_q_tree_model_drag_data_delete(GtkTreeDragSource *, GtkTreePath *)
1115{
1116 /* we don't want to delete any data by dragging for now */
1117 // g_debug("drag data delete");
1118 return FALSE;
1119}
1120
1121static gboolean
1122gtk_q_tree_model_drag_data_get(GtkTreeDragSource *, GtkTreePath *, GtkSelectionData *)
1123{
1124 // g_debug("drag data get");
1125 return FALSE;
1126}
1127
1128static gboolean
1129gtk_q_tree_model_drag_data_received(GtkTreeDragDest *, GtkTreePath *, GtkSelectionData *)
1130{
1131 // g_debug("drag data received");
1132 return FALSE;
1133}
1134
1135static gboolean
1136gtk_q_tree_model_row_drop_possible(GtkTreeDragDest *, GtkTreePath *, GtkSelectionData *)
1137{
1138 // g_debug("row drop possible");
1139 return FALSE;
1140}
Stepan Salenikovich3026bb32017-04-27 13:31:48 -04001141
1142gboolean
1143gtk_q_tree_model_is_layout_changing(GtkQTreeModel *self)
1144{
1145 return self->priv->layout_changing;
1146}
1147
1148
1149gboolean
1150gtk_q_tree_model_ignore_selection_change(GtkTreeSelection *selection)
1151{
1152 auto treeview = gtk_tree_selection_get_tree_view(selection);
1153 auto model = gtk_tree_view_get_model(treeview);
1154
1155 g_return_val_if_fail(GTK_IS_Q_TREE_MODEL(model), FALSE);
1156
1157 return gtk_q_tree_model_is_layout_changing(GTK_Q_TREE_MODEL(model));
1158}