/* * Copyright (C) 2015-2016 Savoir-faire Linux Inc. * Author: Stepan Salenikovich * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "gtkqtreemodel.h" #include #include #include #include "gtkaccessproxymodel.h" typedef union _int_ptr_t { gint value; void *ptr; } int_ptr_t; typedef struct _QIter { gint stamp; int_ptr_t row; int_ptr_t column; void *id; gpointer user_data; } QIter; typedef struct _GtkQTreeModelPrivate GtkQTreeModelPrivate; struct _GtkQTreeModel { GObject parent; /* private */ GtkQTreeModelPrivate *priv; }; struct _GtkQTreeModelClass { GObjectClass parent_class; }; struct _GtkQTreeModelPrivate { GType *column_headers; gint *column_roles; gint *column_model_col; gint stamp; gint n_columns; GtkAccessProxyModel *model; }; /* static prototypes */ /* GtkTreeModel prototypes */ static void gtk_q_tree_model_tree_model_init (GtkTreeModelIface * ); static void gtk_q_tree_model_finalize (GObject * ); static GtkTreeModelFlags gtk_q_tree_model_get_flags (GtkTreeModel * ); static gint gtk_q_tree_model_get_n_columns (GtkTreeModel * ); static GType gtk_q_tree_model_get_column_type (GtkTreeModel *, gint ); static gboolean gtk_q_tree_model_get_iter (GtkTreeModel *, GtkTreeIter *, GtkTreePath * ); static GtkTreePath * gtk_q_tree_model_get_path (GtkTreeModel *, GtkTreeIter * ); static void gtk_q_tree_model_get_value (GtkTreeModel *, GtkTreeIter *, gint, GValue * ); static gboolean gtk_q_tree_model_iter_next (GtkTreeModel *, GtkTreeIter * ); static gboolean gtk_q_tree_model_iter_previous (GtkTreeModel *, GtkTreeIter * ); static gboolean gtk_q_tree_model_iter_children (GtkTreeModel *, GtkTreeIter *, GtkTreeIter * ); static gboolean gtk_q_tree_model_iter_has_child (GtkTreeModel *, GtkTreeIter * ); static gint gtk_q_tree_model_iter_n_children (GtkTreeModel *, GtkTreeIter * ); static gboolean gtk_q_tree_model_iter_nth_child (GtkTreeModel *, GtkTreeIter *, GtkTreeIter *, gint ); static gboolean gtk_q_tree_model_iter_parent (GtkTreeModel *, GtkTreeIter *, GtkTreeIter * ); /* implementation prototypes */ static void qmodelindex_to_iter (const QModelIndex &, GtkTreeIter * ); // static void gtk_q_tree_model_increment_stamp (GtkQTreeModel * ); static gint gtk_q_tree_model_length (GtkQTreeModel * ); static void gtk_q_tree_model_set_n_columns (GtkQTreeModel *, gint ); static void gtk_q_tree_model_set_column_type (GtkQTreeModel *, gint, gint, gint, GType ); /* End private prototypes */ /* define type, inherit from GObject, define implemented interface(s) */ G_DEFINE_TYPE_WITH_CODE (GtkQTreeModel, gtk_q_tree_model, G_TYPE_OBJECT, G_ADD_PRIVATE (GtkQTreeModel) G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL, gtk_q_tree_model_tree_model_init)) #define GTK_Q_TREEMODEL_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_Q_TREE_MODEL, GtkQTreeModelPrivate)) #define Q_ITER(iter) ((QIter *)iter) static void gtk_q_tree_model_class_init(GtkQTreeModelClass *klass) { G_OBJECT_CLASS(klass)->finalize = gtk_q_tree_model_finalize; } static void gtk_q_tree_model_tree_model_init(GtkTreeModelIface *iface) { iface->get_flags = gtk_q_tree_model_get_flags; iface->get_n_columns = gtk_q_tree_model_get_n_columns; iface->get_column_type = gtk_q_tree_model_get_column_type; iface->get_iter = gtk_q_tree_model_get_iter; iface->get_path = gtk_q_tree_model_get_path; iface->get_value = gtk_q_tree_model_get_value; iface->iter_next = gtk_q_tree_model_iter_next; iface->iter_previous = gtk_q_tree_model_iter_previous; iface->iter_children = gtk_q_tree_model_iter_children; iface->iter_has_child = gtk_q_tree_model_iter_has_child; iface->iter_n_children = gtk_q_tree_model_iter_n_children; iface->iter_nth_child = gtk_q_tree_model_iter_nth_child; iface->iter_parent = gtk_q_tree_model_iter_parent; } static void gtk_q_tree_model_init(GtkQTreeModel *q_tree_model) { GtkQTreeModelPrivate *priv = GTK_Q_TREEMODEL_GET_PRIVATE(q_tree_model); q_tree_model->priv = priv; priv->stamp = g_random_int(); priv->model = NULL; } /** * gtk_q_tree_model_get_qmodel * returns the original model from which this GtkQTreeModel is created */ QAbstractItemModel * gtk_q_tree_model_get_qmodel(GtkQTreeModel *q_tree_model) { GtkQTreeModelPrivate *priv = GTK_Q_TREEMODEL_GET_PRIVATE(q_tree_model); return priv->model->sourceModel(); } /** * gtk_q_tree_model_get_source_idx * Returns the index of the original model used to create this GtkQTreeModel from * the given iter, if there is one. */ QModelIndex gtk_q_tree_model_get_source_idx(GtkQTreeModel *q_tree_model, GtkTreeIter *iter) { GtkQTreeModelPrivate *priv = GTK_Q_TREEMODEL_GET_PRIVATE(q_tree_model); QIter *qiter = Q_ITER(iter); GtkAccessProxyModel *proxy_model = priv->model; QModelIndex proxy_idx = proxy_model->indexFromId(qiter->row.value, qiter->column.value, qiter->id); if (proxy_idx.isValid()) { return proxy_model->mapToSource(proxy_idx); } else { g_warning("could not get valid QModelIndex from given GtkTreeIter"); return QModelIndex(); } } /** * Takes a QModelIndex from the original QAbstractItemModel and returns a valid GtkTreeIter in the corresponding * GtkQTreeModel */ gboolean gtk_q_tree_model_source_index_to_iter(GtkQTreeModel *q_tree_model, const QModelIndex &idx, GtkTreeIter *iter) { GtkQTreeModelPrivate *priv = GTK_Q_TREEMODEL_GET_PRIVATE(q_tree_model); /* the the proxy_idx from the source idx */ QModelIndex proxy_idx = priv->model->mapFromSource(idx); if (!proxy_idx.isValid()) return FALSE; /* make sure iter is valid */ iter->stamp = priv->stamp; /* map the proxy idx to iter */ Q_ITER(iter)->row.value = proxy_idx.row(); Q_ITER(iter)->column.value = proxy_idx.column(); Q_ITER(iter)->id = proxy_idx.internalPointer(); return TRUE; } /** * helper method which recursively adds all the children of the given QModelIndex */ static void insert_children(const QModelIndex &idx, GtkQTreeModel *gtk_model) { const auto children = gtk_model->priv->model->rowCount(idx); for (int i = 0; i < children; ++i) { GtkTreeIter iter_child; auto idx_child = gtk_model->priv->model->index(i, 0, idx); if (idx_child.isValid()) { iter_child.stamp = gtk_model->priv->stamp; qmodelindex_to_iter(idx_child, &iter_child); if (auto path_child = gtk_q_tree_model_get_path(GTK_TREE_MODEL(gtk_model), &iter_child)) { gtk_tree_model_row_inserted(GTK_TREE_MODEL(gtk_model), path_child, &iter_child); gtk_tree_path_free(path_child); } insert_children(idx_child, gtk_model); } } } /** * gtk_q_tree_model_new: * @model: QAbstractItemModel to which this model will bind. * @n_columns: number of columns in the list store * @...: model #Column, #GType and #Role for each column. * * Return value: a new #GtkQTreeModel */ GtkQTreeModel * gtk_q_tree_model_new(QAbstractItemModel *model, size_t n_columns, ...) { GtkQTreeModel *retval; va_list args; gint i; g_return_val_if_fail(n_columns > 0, NULL); retval = (GtkQTreeModel *)g_object_new(GTK_TYPE_Q_TREE_MODEL, NULL); gtk_q_tree_model_set_n_columns(retval, n_columns); /* get proxy model from given model */ GtkAccessProxyModel* proxy_model = new GtkAccessProxyModel(); proxy_model->setSourceModel(model); retval->priv->model = proxy_model; gint stamp = retval->priv->stamp; n_columns = 3*n_columns; va_start (args, n_columns); for (i = 0; i < (gint)(n_columns/3); i++) { /* first get the model column */ gint model_col = va_arg(args, gint); /* then get the role of the QModel */ gint role = va_arg(args, gint); /* then get the type the role will be interpreted as */ GType type = va_arg(args, GType); /* TODO: check if its a type supported by our model * if (! _gtk_tree_data_list_check_type (type)) * { * g_warning ("%s: Invalid type %s\n", G_STRLOC, g_type_name (type)); * g_object_unref (retval); * va_end (args); * * return NULL; * } */ gtk_q_tree_model_set_column_type (retval, i, model_col, role, type); } va_end (args); gtk_q_tree_model_length(retval); /* connect signals */ QObject::connect( proxy_model, &QAbstractItemModel::rowsInserted, [=](const QModelIndex & parent, int first, int last) { for( int row = first; row <= last; row++) { GtkTreeIter iter; QModelIndex idx = proxy_model->index(row, 0, parent); iter.stamp = stamp; qmodelindex_to_iter(idx, &iter); GtkTreePath *path = gtk_q_tree_model_get_path(GTK_TREE_MODEL(retval), &iter); gtk_tree_model_row_inserted(GTK_TREE_MODEL(retval), path, &iter); gtk_tree_path_free(path); } } ); QObject::connect( proxy_model, &QAbstractItemModel::rowsAboutToBeMoved, [=](const QModelIndex & sourceParent, int sourceStart, int sourceEnd, const QModelIndex & destinationParent, G_GNUC_UNUSED int destinationRow) { /* if the sourceParent and the destinationParent are the same, then we can use the * GtkTreeModel API to move the rows, otherwise we must first delete them and then * re-insert them under the new parent */ if (sourceParent == destinationParent) return; /* first remove the row from old location * then insert them at the new location on the "rowsMoved signal */ for( int row = sourceStart; row <= sourceEnd; row++) { QModelIndex idx = proxy_model->index(row, 0, sourceParent); GtkTreeIter iter_old; iter_old.stamp = stamp; qmodelindex_to_iter(idx, &iter_old); GtkTreePath *path_old = gtk_q_tree_model_get_path(GTK_TREE_MODEL(retval), &iter_old); gtk_tree_model_row_deleted(GTK_TREE_MODEL(retval), path_old); gtk_tree_path_free(path_old); } } ); QObject::connect( proxy_model, &QAbstractItemModel::rowsMoved, [=](const QModelIndex & sourceParent, int sourceStart, int sourceEnd, const QModelIndex & destinationParent, int destinationRow) { /* if the sourceParent and the destinationParent are the same, then we can use the * GtkTreeModel API to move the rows, otherwise we must first delete them and then * re-insert them under the new parent */ if (sourceParent == destinationParent) { GtkTreeIter *iter_parent = nullptr; GtkTreePath *path_parent = nullptr; if (sourceParent.isValid()) { iter_parent = g_new0(GtkTreeIter, 1); iter_parent->stamp = stamp; qmodelindex_to_iter(sourceParent, iter_parent); path_parent = gtk_q_tree_model_get_path(GTK_TREE_MODEL(retval), iter_parent); } else { path_parent = gtk_tree_path_new(); } /* gtk_tree_model_rows_reordered takes an array of integers mapping the current * position of each child to its old position before the re-ordering, * i.e. new_order [newpos] = oldpos */ const auto rows = proxy_model->rowCount(sourceParent); gint new_order[rows]; const auto destinationRowLast = destinationRow + (sourceEnd - sourceStart); const auto totalMoved = sourceEnd - sourceStart + 1; for (int row = 0; row < rows; ++row ) { if ( (row < sourceStart && row < destinationRow) || (row > sourceEnd && row > destinationRowLast) ) { // if the row is outside of the bounds of change, then it stayed in the same place new_order[row] = row; } else { if (row < destinationRow) { // in front of the destination, so it used to be behind the rows that moved new_order[row] = row + totalMoved; } else if (row >= destinationRow && row <= destinationRowLast) { // within the destination, so it was within the rows that moved new_order[row] = sourceStart + (row - destinationRow); } else { // behind the destination, so it used to be in front of the rows that moved new_order[row] = row - totalMoved; } } } gtk_tree_model_rows_reordered(GTK_TREE_MODEL(retval), path_parent, iter_parent, new_order); g_free(iter_parent); gtk_tree_path_free(path_parent); } else { /* these rows should have been removed in the "rowsAboutToBeMoved" handler * now insert them in the new location */ for( int row = sourceStart; row <= sourceEnd; row++) { GtkTreeIter iter_new; QModelIndex idx = proxy_model->index(destinationRow, 0, destinationParent); iter_new.stamp = stamp; qmodelindex_to_iter(idx, &iter_new); GtkTreePath *path_new = gtk_q_tree_model_get_path(GTK_TREE_MODEL(retval), &iter_new); gtk_tree_model_row_inserted(GTK_TREE_MODEL(retval), path_new, &iter_new); gtk_tree_path_free(path_new); insert_children(idx, retval); destinationRow++; } } } ); QObject::connect( proxy_model, &QAbstractItemModel::rowsRemoved, [=](const QModelIndex & parent, int first, int last) { GtkTreePath *parent_path = NULL; if (parent.isValid()) { GtkTreeIter iter_parent; iter_parent.stamp = stamp; qmodelindex_to_iter(parent, &iter_parent); parent_path = gtk_q_tree_model_get_path(GTK_TREE_MODEL(retval), &iter_parent); } else { parent_path = gtk_tree_path_new(); } /* go last to first, since the rows are being deleted */ for( int row = last; row >= first; --row) { // g_debug("deleting row: %d", row); GtkTreePath *path = gtk_tree_path_copy(parent_path); gtk_tree_path_append_index(path, row); gtk_tree_model_row_deleted(GTK_TREE_MODEL(retval), path); gtk_tree_path_free(path); } gtk_tree_path_free(parent_path); } ); QObject::connect( proxy_model, &QAbstractItemModel::dataChanged, [=](const QModelIndex & topLeft, const QModelIndex & bottomRight, G_GNUC_UNUSED const QVector & roles = QVector ()) { /* we have to assume only one column */ int first = topLeft.row(); int last = bottomRight.row(); /* the first idx IS topLeft, the rest are his siblings */ GtkTreeIter iter; QModelIndex idx = topLeft; iter.stamp = stamp; qmodelindex_to_iter(idx, &iter); GtkTreePath *path = gtk_q_tree_model_get_path(GTK_TREE_MODEL(retval), &iter); gtk_tree_model_row_changed(GTK_TREE_MODEL(retval), path, &iter); gtk_tree_path_free(path); for( int row = first + 1; row <= last; row++) { idx = topLeft.sibling(row, 0); qmodelindex_to_iter(idx, &iter); path = gtk_q_tree_model_get_path(GTK_TREE_MODEL(retval), &iter); gtk_tree_model_row_changed(GTK_TREE_MODEL(retval), path, &iter); gtk_tree_path_free(path); } } ); QObject::connect( proxy_model, &QAbstractItemModel::modelAboutToBeReset, [=] () { // g_debug("model about to be reset"); /* nothing equvivalent eixists in GtkTreeModel, so simply delete all * rows, and add all rows when the model is reset; * we must delete the rows in ascending order */ int row_count = proxy_model->rowCount(); for (int row = row_count; row > 0; --row) { // g_debug("deleting row %d", row -1); QModelIndex idx = proxy_model->index(row - 1, 0); GtkTreeIter iter; iter.stamp = stamp; qmodelindex_to_iter(idx, &iter); GtkTreePath *path = gtk_q_tree_model_get_path(GTK_TREE_MODEL(retval), &iter); gtk_tree_model_row_deleted(GTK_TREE_MODEL(retval), path); gtk_tree_path_free(path); } } ); QObject::connect( proxy_model, &QAbstractItemModel::modelReset, [=] () { // g_debug("model reset"); /* now recursively add all the (new) rows */ insert_children(QModelIndex(), retval); } ); // QObject::connect( // proxy_model, // &QAbstractItemModel::layoutAboutToBeChanged, // [=] () { // // g_debug("layout about to be changed"); // /* nothing equvivalent eixists in GtkTreeModel, so simply delete all // * rows, and add all rows when the model is reset; // * we must delete the rows in ascending order */ // int row_count = proxy_model->rowCount(); // for (int row = row_count; row > 0; --row) { // // g_debug("deleting row %d", row -1); // QModelIndex idx = proxy_model->index(row - 1, 0); // GtkTreeIter iter; // iter.stamp = stamp; // qmodelindex_to_iter(idx, &iter); // GtkTreePath *path = gtk_q_tree_model_get_path(GTK_TREE_MODEL(retval), &iter); // gtk_tree_model_row_deleted(GTK_TREE_MODEL(retval), path); // } // } // ); // // QObject::connect( // proxy_model, // &QAbstractItemModel::layoutChanged, // [=] () { // // g_debug("layout changed"); // /* now add all the (new) rows */ // int row_count = proxy_model->rowCount(); // for (int row = 0; row < row_count; row++) { // // g_debug("adding row %d", row); // GtkTreeIter *iter_new = g_new0(GtkTreeIter, 1); // QModelIndex idx = proxy_model->index(row, 0); // iter_new->stamp = stamp; // qmodelindex_to_iter(idx, iter_new); // GtkTreePath *path_new = gtk_q_tree_model_get_path(GTK_TREE_MODEL(retval), iter_new); // gtk_tree_model_row_inserted(GTK_TREE_MODEL(retval), path_new, iter_new); // } // } // ); return retval; } static gint gtk_q_tree_model_length(GtkQTreeModel *q_tree_model) { GtkQTreeModelPrivate *priv = q_tree_model->priv; return priv->model->rowCount(); } static void gtk_q_tree_model_set_n_columns(GtkQTreeModel *q_tree_model, gint n_columns) { GtkQTreeModelPrivate *priv = q_tree_model->priv; int i; if (priv->n_columns == n_columns) return; priv->column_headers = g_renew (GType, priv->column_headers, n_columns); for (i = priv->n_columns; i < n_columns; i++) priv->column_headers[i] = G_TYPE_INVALID; priv->n_columns = n_columns; priv->column_roles = g_renew (gint, priv->column_roles, n_columns); for (i = priv->n_columns; i < n_columns; i++) priv->column_roles[i] = -1; priv->column_model_col = g_renew (gint, priv->column_model_col, n_columns); for (i = priv->n_columns; i < n_columns; i++) priv->column_model_col[i] = 0; } static void gtk_q_tree_model_set_column_type(GtkQTreeModel *q_tree_model, gint column, gint model_col, gint role, GType type) { GtkQTreeModelPrivate *priv = q_tree_model->priv; /* TODO: check if its a type supported by our model * if (!_gtk_tree_data_list_check_type (type)) * { * g_warning ("%s: Invalid type %s\n", G_STRLOC, g_type_name (type)); * return; * } */ priv->column_model_col[column] = model_col; priv->column_headers[column] = type; priv->column_roles[column] = role; } static void gtk_q_tree_model_finalize(GObject *object) { GtkQTreeModel *q_tree_model = GTK_Q_TREE_MODEL (object); GtkQTreeModelPrivate *priv = q_tree_model->priv; g_free(priv->column_model_col); g_free(priv->column_headers); g_free(priv->column_roles); /* delete the created proxy model */ delete priv->model; G_OBJECT_CLASS(gtk_q_tree_model_parent_class)->finalize (object); } static gboolean iter_is_valid(GtkTreeIter *iter, GtkQTreeModel *q_tree_model) { gboolean retval; g_return_val_if_fail(iter != NULL, FALSE); QIter *qiter = Q_ITER(iter); retval = q_tree_model->priv->model->indexFromId(qiter->row.value, qiter->column.value, qiter->id).isValid(); return retval; } static void qmodelindex_to_iter(const QModelIndex &idx, GtkTreeIter *iter) { Q_ITER(iter)->row.value = idx.row(); Q_ITER(iter)->column.value = idx.column(); Q_ITER(iter)->id = idx.internalPointer(); } static gboolean validate_index(gint stamp, const QModelIndex &idx, GtkTreeIter *iter) { if (idx.isValid()) { iter->stamp = stamp; qmodelindex_to_iter(idx, iter); } else { iter->stamp = 0; return FALSE; } return TRUE; } /* Start Fulfill the GtkTreeModel requirements */ /* flags supported by this interface */ static GtkTreeModelFlags gtk_q_tree_model_get_flags(G_GNUC_UNUSED GtkTreeModel *tree_model) { // TODO: possibly return based on the model? return (GtkTreeModelFlags)(GTK_TREE_MODEL_ITERS_PERSIST); } /* number of columns supported by this tree model */ static gint gtk_q_tree_model_get_n_columns(GtkTreeModel *tree_model) { GtkQTreeModel *q_tree_model = GTK_Q_TREE_MODEL(tree_model); GtkQTreeModelPrivate *priv = q_tree_model->priv; return priv->model->columnCount(); } /* get given column type */ static GType gtk_q_tree_model_get_column_type(GtkTreeModel *tree_model, gint index) { GtkQTreeModel *q_tree_model = GTK_Q_TREE_MODEL (tree_model); GtkQTreeModelPrivate *priv = q_tree_model->priv; g_return_val_if_fail (index < gtk_q_tree_model_get_n_columns(tree_model), G_TYPE_INVALID); return priv->column_headers[index]; } /* Sets @iter to a valid iterator pointing to @path. If @path does * not exist, @iter is set to an invalid iterator and %FALSE is returned. */ static gboolean gtk_q_tree_model_get_iter(GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreePath *path) { GtkQTreeModel *q_tree_model = GTK_Q_TREE_MODEL (tree_model); GtkQTreeModelPrivate *priv = q_tree_model->priv; /* GtkTreePath is a list of indices, each one indicates * the child of the previous one. * Since GtkTreeModel only has rows (columns are only used to * store data in each item), each index is basically the row * at the given tree level. * To get the iter, we want to get the QModelIndex. To get * the QModelIndex we simply start at the first level and * traverse the model the number of layers equal to the number * of indices in the path. */ gint depth; gint* indices = gtk_tree_path_get_indices_with_depth(path, &depth); QModelIndex idx = priv->model->index(indices[0], 0); for(int layer = 1; layer < depth; layer++ ) { /* check if previous iter is valid */ if (!idx.isValid()) { break; } else { idx = idx.child(indices[layer], 0); } } if (!idx.isValid()) { iter->stamp = 0; return FALSE; } else { /* we have a valid QModelIndex; turn it into an iter */ iter->stamp = priv->stamp; qmodelindex_to_iter(idx, iter); } return TRUE; } /* Returns a newly-created #GtkTreePath referenced by @iter. * * This path should be freed with gtk_tree_path_free(). */ static GtkTreePath * gtk_q_tree_model_get_path(GtkTreeModel *tree_model, GtkTreeIter *iter) { GtkQTreeModel *q_tree_model = GTK_Q_TREE_MODEL (tree_model); GtkQTreeModelPrivate *priv = q_tree_model->priv; GtkTreePath *path; g_return_val_if_fail (iter->stamp == priv->stamp, NULL); g_return_val_if_fail (iter_is_valid(iter, q_tree_model), NULL); /* To get the path, we have to traverse from the child all the way up */ path = gtk_tree_path_new(); QIter *qiter = Q_ITER(iter); QModelIndex idx = priv->model->indexFromId(qiter->row.value, qiter->column.value, qiter->id); while( idx.isValid() ){ gtk_tree_path_prepend_index(path, idx.row()); idx = idx.parent(); } return path; } static inline GType get_fundamental_type(GType type) { GType result; result = G_TYPE_FUNDAMENTAL (type); if (result == G_TYPE_INTERFACE) { if (g_type_is_a (type, G_TYPE_OBJECT)) result = G_TYPE_OBJECT; } return result; } /* Initializes and sets @value to that at @column. */ static void gtk_q_tree_model_get_value(GtkTreeModel *tree_model, GtkTreeIter *iter, gint column, GValue *value) { GtkQTreeModel *q_tree_model = GTK_Q_TREE_MODEL (tree_model); GtkQTreeModelPrivate *priv = q_tree_model->priv; g_return_if_fail (column < priv->n_columns); g_return_if_fail (iter_is_valid(iter, q_tree_model)); /* get the data */ QIter *qiter = Q_ITER(iter); int model_col = priv->column_model_col[column]; QModelIndex idx = priv->model->indexFromId(qiter->row.value, model_col, qiter->id); int role = priv->column_roles[column]; QVariant var = priv->model->data(idx, role); GType type = priv->column_headers[column]; g_value_init (value, type); switch (get_fundamental_type (type)) { /* TODO: implement the rest of the data types the we plan to support * otherwise get rid of the code and make sure un-implemented types * are not allowed */ case G_TYPE_BOOLEAN: g_value_set_boolean (value, (gboolean) var.toBool()); break; // case G_TYPE_CHAR: // g_value_set_schar (value, (gchar) list->data.v_char); // break; // case G_TYPE_UCHAR: // g_value_set_uchar (value, (guchar) list->data.v_uchar); // break; case G_TYPE_INT: g_value_set_int (value, (gint) var.toInt()); break; case G_TYPE_UINT: g_value_set_uint (value, (guint) var.toUInt()); break; // case G_TYPE_LONG: // g_value_set_long (value, list->data.v_long); // break; // case G_TYPE_ULONG: // g_value_set_ulong (value, list->data.v_ulong); // break; // case G_TYPE_INT64: // g_value_set_int64 (value, list->data.v_int64); // break; // case G_TYPE_UINT64: // g_value_set_uint64 (value, list->data.v_uint64); // break; // case G_TYPE_ENUM: // g_value_set_enum (value, list->data.v_int); // break; // case G_TYPE_FLAGS: // g_value_set_flags (value, list->data.v_uint); // break; // case G_TYPE_FLOAT: // g_value_set_float (value, (gfloat) list->data.v_float); // break; // case G_TYPE_DOUBLE: // g_value_set_double (value, (gdouble) list->data.v_double); // break; case G_TYPE_STRING: { QByteArray ba = var.toString().toUtf8(); g_value_set_string(value, (gchar *)var.toByteArray().constData()); } break; // case G_TYPE_POINTER: // g_value_set_pointer (value, (gpointer) list->data.v_pointer); // break; // case G_TYPE_BOXED: // g_value_set_boxed (value, (gpointer) list->data.v_pointer); // break; // case G_TYPE_VARIANT: // g_value_set_variant (value, (gpointer) list->data.v_pointer); // break; // case G_TYPE_OBJECT: // g_value_set_object (value, (GObject *) list->data.v_pointer); // break; default: g_warning ("%s: Unsupported type (%s) retrieved.", G_STRLOC, g_type_name (value->g_type)); break; } return; } /* Sets @iter to point to the node following it at the current level. * * If there is no next @iter, %FALSE is returned and @iter is set * to be invalid. */ static gboolean gtk_q_tree_model_iter_next(GtkTreeModel *tree_model, GtkTreeIter *iter) { GtkQTreeModel *q_tree_model = GTK_Q_TREE_MODEL (tree_model); GtkQTreeModelPrivate *priv = q_tree_model->priv; g_return_val_if_fail (iter_is_valid(iter, q_tree_model), FALSE); QIter *qiter = Q_ITER(iter); QModelIndex idx = priv->model->indexFromId(qiter->row.value, qiter->column.value, qiter->id); idx = idx.sibling(idx.row()+1, idx.column()); /* validate */ if (validate_index(priv->stamp, idx, iter) ) { GtkTreePath *path = gtk_q_tree_model_get_path(tree_model, iter); gtk_tree_path_free(path); return TRUE; } else { return FALSE; } } /* Sets @iter to point to the previous node at the current level. * * If there is no previous @iter, %FALSE is returned and @iter is * set to be invalid. */ static gboolean gtk_q_tree_model_iter_previous(GtkTreeModel *tree_model, GtkTreeIter *iter) { GtkQTreeModel *q_tree_model = GTK_Q_TREE_MODEL (tree_model); GtkQTreeModelPrivate *priv = q_tree_model->priv; g_return_val_if_fail (iter_is_valid(iter, q_tree_model), FALSE); QIter *qiter = Q_ITER(iter); QModelIndex idx = priv->model->indexFromId(qiter->row.value, qiter->column.value, qiter->id); idx = idx.sibling(idx.row()-1, idx.column()); /* validate */ return validate_index(priv->stamp, idx, iter); } /* Sets @iter to point to the first child of @parent. * * If @parent has no children, %FALSE is returned and @iter is * set to be invalid. @parent will remain a valid node after this * function has been called. * * If @parent is %NULL returns the first node */ static gboolean gtk_q_tree_model_iter_children(GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreeIter *parent) { GtkQTreeModel *q_tree_model = GTK_Q_TREE_MODEL (tree_model); GtkQTreeModelPrivate *priv = q_tree_model->priv; QModelIndex idx; /* make sure parent if valid node if its not NULL */ if (parent) g_return_val_if_fail(iter_is_valid(parent, q_tree_model), FALSE); if (parent) { /* get first child */ QIter *qparent = Q_ITER(parent); idx = priv->model->indexFromId(qparent->row.value, qparent->column.value, qparent->id); idx = idx.child(0, 0); } else { /* parent is NULL, get the first node */ idx = priv->model->index(0, 0); } /* validate child */ return validate_index(priv->stamp, idx, iter); } /* Returns %TRUE if @iter has children, %FALSE otherwise. */ static gboolean gtk_q_tree_model_iter_has_child (GtkTreeModel *tree_model, GtkTreeIter *iter) { GtkQTreeModel *q_tree_model = GTK_Q_TREE_MODEL (tree_model); GtkQTreeModelPrivate *priv = q_tree_model->priv; g_return_val_if_fail(iter_is_valid(iter, q_tree_model), FALSE); QIter *qiter = Q_ITER(iter); QModelIndex idx = priv->model->indexFromId(qiter->row.value, qiter->column.value, qiter->id); return priv->model->hasChildren(idx); } /* Returns the number of children that @iter has. * * As a special case, if @iter is %NULL, then the number * of toplevel nodes is returned. */ static gint gtk_q_tree_model_iter_n_children (GtkTreeModel *tree_model, GtkTreeIter *iter) { GtkQTreeModel *q_tree_model = GTK_Q_TREE_MODEL (tree_model); GtkQTreeModelPrivate *priv = q_tree_model->priv; if (iter == NULL) return gtk_q_tree_model_length(q_tree_model); g_return_val_if_fail(iter_is_valid(iter, q_tree_model), -1); QIter *qiter = Q_ITER(iter); QModelIndex idx = priv->model->indexFromId(qiter->row.value, qiter->column.value, qiter->id); return priv->model->rowCount(idx); } /* Sets @iter to be the child of @parent, using the given index. * * The first index is 0. If @n is too big, or @parent has no children, * @iter is set to an invalid iterator and %FALSE is returned. @parent * will remain a valid node after this function has been called. As a * special case, if @parent is %NULL, then the @nth root node * is set. */ static gboolean gtk_q_tree_model_iter_nth_child(GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreeIter *parent, gint n) { GtkQTreeModel *q_tree_model = GTK_Q_TREE_MODEL (tree_model); GtkQTreeModelPrivate *priv = q_tree_model->priv; QModelIndex idx; if (parent) { g_return_val_if_fail(iter_is_valid(parent, q_tree_model), FALSE); /* get the nth child */ QIter *qparent = Q_ITER(parent); idx = priv->model->indexFromId(qparent->row.value, qparent->column.value, qparent->id); idx = idx.child(n, 0); } else { idx = priv->model->index(n, 0); } /* validate */ return validate_index(priv->stamp, idx, iter); } /* Sets @iter to be the parent of @child. * * If @child is at the toplevel, and doesn't have a parent, then * @iter is set to an invalid iterator and %FALSE is returned. * @child will remain a valid node after this function has been * called. */ static gboolean gtk_q_tree_model_iter_parent(GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreeIter *child) { GtkQTreeModel *q_tree_model = GTK_Q_TREE_MODEL (tree_model); GtkQTreeModelPrivate *priv = q_tree_model->priv; QModelIndex idx; g_return_val_if_fail(iter_is_valid(child, q_tree_model), FALSE); QIter *qchild = Q_ITER(child); idx = priv->model->indexFromId(qchild->row.value, qchild->column.value, qchild->id); idx = idx.parent(); /* validate */ return validate_index(priv->stamp, idx, iter); } /* End Fulfill the GtkTreeModel requirements */