gnome: fix bindings for QSortFilterProxyModel
This is a work in progress,
debug statements left in but commented out
as they are very verbose.
Refs #68596
Change-Id: Id0a4a462b25074a6f97c7ca1b5709f96bc4461cb
diff --git a/src/models/gtkqsortfiltertreemodel.cpp b/src/models/gtkqsortfiltertreemodel.cpp
new file mode 100644
index 0000000..dfd373b
--- /dev/null
+++ b/src/models/gtkqsortfiltertreemodel.cpp
@@ -0,0 +1,1022 @@
+/*
+ * Copyright (C) 2015 Savoir-Faire Linux Inc.
+ * Author: Stepan Salenikovich <stepan.salenikovich@savoirfairelinux.com>
+ *
+ * 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.
+ *
+ * Additional permission under GNU GPL version 3 section 7:
+ *
+ * If you modify this program, or any covered work, by linking or
+ * combining it with the OpenSSL project's OpenSSL library (or a
+ * modified version of that library), containing parts covered by the
+ * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
+ * grants you additional permission to convey the resulting work.
+ * Corresponding Source for a non-source form of such a combination
+ * shall include the source code for the parts of OpenSSL used as well
+ * as that of the covered work.
+ */
+
+#include "gtkqsortfiltertreemodel.h"
+#include <gtk/gtk.h>
+#include <QtCore/QAbstractItemModel>
+#include <QtCore/QSortFilterProxyModel>
+#include <QtCore/QDebug>
+#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 _GtkQSortFilterTreeModelPrivate GtkQSortFilterTreeModelPrivate;
+
+struct _GtkQSortFilterTreeModel
+{
+ GObject parent;
+
+ /* private */
+ GtkQSortFilterTreeModelPrivate *priv;
+};
+
+struct _GtkQSortFilterTreeModelClass
+{
+ GObjectClass parent_class;
+};
+
+struct _GtkQSortFilterTreeModelPrivate
+{
+ GType *column_headers;
+ gint *column_roles;
+
+ gint stamp;
+ gint n_columns;
+
+ QSortFilterProxyModel *given_model;
+ QAbstractItemModel *original_model;
+ GtkAccessProxyModel *access_model;
+};
+
+/* static prototypes */
+
+/* GtkTreeModel prototypes */
+static void gtk_q_sort_filter_tree_model_tree_model_init (GtkTreeModelIface * );
+static void gtk_q_sort_filter_tree_model_finalize (GObject * );
+static GtkTreeModelFlags gtk_q_sort_filter_tree_model_get_flags (GtkTreeModel * );
+static gint gtk_q_sort_filter_tree_model_get_n_columns (GtkTreeModel * );
+static GType gtk_q_sort_filter_tree_model_get_column_type (GtkTreeModel *,
+ gint );
+static gboolean gtk_q_sort_filter_tree_model_get_iter (GtkTreeModel *,
+ GtkTreeIter *,
+ GtkTreePath * );
+static GtkTreePath * gtk_q_sort_filter_tree_model_get_path (GtkTreeModel *,
+ GtkTreeIter * );
+static void gtk_q_sort_filter_tree_model_get_value (GtkTreeModel *,
+ GtkTreeIter *,
+ gint,
+ GValue * );
+static gboolean gtk_q_sort_filter_tree_model_iter_next (GtkTreeModel *,
+ GtkTreeIter * );
+static gboolean gtk_q_sort_filter_tree_model_iter_previous (GtkTreeModel *,
+ GtkTreeIter * );
+static gboolean gtk_q_sort_filter_tree_model_iter_children (GtkTreeModel *,
+ GtkTreeIter *,
+ GtkTreeIter * );
+static gboolean gtk_q_sort_filter_tree_model_iter_has_child (GtkTreeModel *,
+ GtkTreeIter * );
+static gint gtk_q_sort_filter_tree_model_iter_n_children (GtkTreeModel *,
+ GtkTreeIter * );
+static gboolean gtk_q_sort_filter_tree_model_iter_nth_child (GtkTreeModel *,
+ GtkTreeIter *,
+ GtkTreeIter *,
+ gint );
+static gboolean gtk_q_sort_filter_tree_model_iter_parent (GtkTreeModel *,
+ GtkTreeIter *,
+ GtkTreeIter * );
+
+/* implementation prototypes */
+static void qmodelindex_to_iter (const QModelIndex &,
+ GtkTreeIter * );
+// static void gtk_q_sort_filter_tree_model_increment_stamp (GtkQSortFilterTreeModel * );
+
+static gint gtk_q_sort_filter_tree_model_length (GtkQSortFilterTreeModel * );
+
+static void gtk_q_sort_filter_tree_model_set_n_columns (GtkQSortFilterTreeModel *,
+ gint );
+static void gtk_q_sort_filter_tree_model_set_column_type (GtkQSortFilterTreeModel *,
+ gint,
+ gint,
+ GType );
+
+/* End private prototypes */
+
+/* define type, inherit from GObject, define implemented interface(s) */
+G_DEFINE_TYPE_WITH_CODE (GtkQSortFilterTreeModel, gtk_q_sort_filter_tree_model, G_TYPE_OBJECT,
+ G_ADD_PRIVATE (GtkQSortFilterTreeModel)
+ G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL,
+ gtk_q_sort_filter_tree_model_tree_model_init))
+
+#define GTK_Q_SORT_FILTER_TREE_MODEL_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_Q_SORT_FILTER_TREE_MODEL, GtkQSortFilterTreeModelPrivate))
+
+#define Q_ITER(iter) ((QIter *)iter)
+
+static void
+gtk_q_sort_filter_tree_model_class_init(GtkQSortFilterTreeModelClass *klass)
+{
+ G_OBJECT_CLASS(klass)->finalize = gtk_q_sort_filter_tree_model_finalize;
+}
+
+static void
+gtk_q_sort_filter_tree_model_tree_model_init(GtkTreeModelIface *iface)
+{
+ iface->get_flags = gtk_q_sort_filter_tree_model_get_flags;
+ iface->get_n_columns = gtk_q_sort_filter_tree_model_get_n_columns;
+ iface->get_column_type = gtk_q_sort_filter_tree_model_get_column_type;
+ iface->get_iter = gtk_q_sort_filter_tree_model_get_iter;
+ iface->get_path = gtk_q_sort_filter_tree_model_get_path;
+ iface->get_value = gtk_q_sort_filter_tree_model_get_value;
+ iface->iter_next = gtk_q_sort_filter_tree_model_iter_next;
+ iface->iter_previous = gtk_q_sort_filter_tree_model_iter_previous;
+ iface->iter_children = gtk_q_sort_filter_tree_model_iter_children;
+ iface->iter_has_child = gtk_q_sort_filter_tree_model_iter_has_child;
+ iface->iter_n_children = gtk_q_sort_filter_tree_model_iter_n_children;
+ iface->iter_nth_child = gtk_q_sort_filter_tree_model_iter_nth_child;
+ iface->iter_parent = gtk_q_sort_filter_tree_model_iter_parent;
+}
+
+static void
+gtk_q_sort_filter_tree_model_init(GtkQSortFilterTreeModel *q_tree_model)
+{
+ GtkQSortFilterTreeModelPrivate *priv = GTK_Q_SORT_FILTER_TREE_MODEL_GET_PRIVATE(q_tree_model);
+ q_tree_model->priv = priv;
+
+ priv->stamp = g_random_int();
+ priv->access_model = NULL;
+ priv->original_model = NULL;
+ priv->given_model = NULL;
+}
+
+/**
+ * gtk_q_sort_filter_tree_model_get_qmodel
+ * returns the original model from which this GtkQSortFilterTreeModel is created
+ */
+QSortFilterProxyModel *
+gtk_q_sort_filter_tree_model_get_qmodel(GtkQSortFilterTreeModel *q_tree_model)
+{
+ GtkQSortFilterTreeModelPrivate *priv = GTK_Q_SORT_FILTER_TREE_MODEL_GET_PRIVATE(q_tree_model);
+ return priv->given_model;
+}
+
+/**
+ * gtk_q_sort_filter_tree_model_get_source_idx
+ * Returns the index of the original model used to create this GtkQSortFilterTreeModel from
+ * the given iter, if there is one.
+ */
+QModelIndex
+gtk_q_sort_filter_tree_model_get_source_idx(GtkQSortFilterTreeModel *q_tree_model, GtkTreeIter *iter)
+{
+ GtkQSortFilterTreeModelPrivate *priv = GTK_Q_SORT_FILTER_TREE_MODEL_GET_PRIVATE(q_tree_model);
+ /* get the call */
+ QIter *qiter = Q_ITER(iter);
+ QModelIndex retval = QModelIndex();
+ QModelIndex access_idx = priv->access_model->indexFromId(qiter->row.value, qiter->column.value, qiter->id);
+ if (access_idx.isValid()) {
+ QModelIndex original_idx = priv->access_model->mapToSource(access_idx);
+ if (original_idx.isValid())
+ retval = priv->given_model->mapFromSource(original_idx);
+ }
+ if (!retval.isValid())
+ g_warning("could not get valid QModelIndex from given GtkTreeIter");
+
+ return retval;
+}
+
+/**
+ * Takes a QModelIndex from the original QAbstractItemModel and returns a valid GtkTreeIter in the corresponding
+ * GtkQSortFilterTreeModel
+ */
+gboolean
+gtk_q_sort_filter_tree_model_source_index_to_iter(GtkQSortFilterTreeModel *q_tree_model, const QModelIndex &idx, GtkTreeIter *iter)
+{
+ GtkQSortFilterTreeModelPrivate *priv = GTK_Q_SORT_FILTER_TREE_MODEL_GET_PRIVATE(q_tree_model);
+
+ /* the the proxy_idx from the source idx */
+ QModelIndex original_idx = priv->given_model->mapToSource(idx);
+ QModelIndex access_idx = priv->access_model->mapFromSource(original_idx);
+ if (!access_idx.isValid())
+ return FALSE;
+
+ /* make sure iter is valid */
+ iter->stamp = priv->stamp;
+
+ /* map the proxy idx to iter */
+ Q_ITER(iter)->row.value = access_idx.row();
+ Q_ITER(iter)->column.value = access_idx.column();
+ Q_ITER(iter)->id = access_idx.internalPointer();
+ return TRUE;
+}
+
+/**
+ * gtk_q_sort_filter_tree_model_new:
+ * @model: QAbstractItemModel to which this model will bind.
+ * @n_columns: number of columns in the list store
+ * @...: all #GType follwed by the #Role pair for each column.
+ *
+ * Return value: a new #GtkQSortFilterTreeModel
+ */
+GtkQSortFilterTreeModel *
+gtk_q_sort_filter_tree_model_new(QSortFilterProxyModel *model, size_t n_columns, ...)
+{
+ GtkQSortFilterTreeModel *retval;
+ va_list args;
+ gint i;
+
+ g_return_val_if_fail(n_columns > 0, NULL);
+
+ retval = (GtkQSortFilterTreeModel *)g_object_new(GTK_TYPE_Q_SORT_FILTER_TREE_MODEL, NULL);
+ gtk_q_sort_filter_tree_model_set_n_columns(retval, n_columns);
+
+ /* get original model */
+ retval->priv->given_model = model;
+ retval->priv->original_model = model->sourceModel();
+
+ /* TODO: for now, assume one level of proxy model, later make it work for any
+ * number of proxy models */
+ // QAbstractItemModel *original = model;
+ // QAbstractProxyModel *proxy = qobject_cast<QAbstractProxyModel*>(model);
+ // while (proxy) {
+ // g_debug("got a proxy!");
+ // original = proxy->sourceModel();
+ // proxy = qobject_cast<QAbstractProxyModel*>(original);
+ // }
+ // retval->priv->original_model = original;
+ // retval->priv->given_model = model;
+
+ /* get access model from original model */
+ retval->priv->access_model = new GtkAccessProxyModel();
+ retval->priv->access_model->setSourceModel(retval->priv->original_model);
+ gint stamp = retval->priv->stamp;
+
+ n_columns = 2*n_columns;
+ va_start (args, n_columns);
+
+ for (i = 0; i < (gint)(n_columns/2); i++) {
+ /* first 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_sort_filter_tree_model_set_column_type (retval, i, role, type);
+ }
+
+ va_end (args);
+
+ gtk_q_sort_filter_tree_model_length(retval);
+
+ /* connect signals */
+ QObject::connect(
+ retval->priv->given_model,
+ &QAbstractItemModel::rowsInserted,
+ [=](const QModelIndex & parent, int first, int last) {
+ for( int row = first; row <= last; row++) {
+ // g_debug("inserted row %d", row);
+ GtkTreeIter *iter = g_new0(GtkTreeIter, 1);
+ QModelIndex idx_given = retval->priv->given_model->index(row, 0, parent);
+ QModelIndex idx_original = retval->priv->given_model->mapToSource(idx_given);
+ QModelIndex idx_access = retval->priv->access_model->mapFromSource(idx_original);
+ iter->stamp = stamp;
+ qmodelindex_to_iter(idx_access, iter);
+ GtkTreePath *path = gtk_q_sort_filter_tree_model_get_path(GTK_TREE_MODEL(retval), iter);
+ gtk_tree_model_row_inserted(GTK_TREE_MODEL(retval), path, iter);
+ }
+ }
+ );
+
+ QObject::connect(
+ retval->priv->given_model,
+ &QAbstractItemModel::rowsAboutToBeMoved,
+ [=](const QModelIndex & sourceParent, int sourceStart, int sourceEnd,
+ G_GNUC_UNUSED const QModelIndex & destinationParent, G_GNUC_UNUSED int destinationRow) {
+ /* 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++) {
+ // g_debug("row about to be moved: %d", row);
+ QModelIndex idx_given = retval->priv->given_model->index(row, 0, sourceParent);
+ QModelIndex idx_original = retval->priv->given_model->mapToSource(idx_given);
+ QModelIndex idx_access = retval->priv->access_model->mapFromSource(idx_original);
+ GtkTreeIter iter_old;
+ qmodelindex_to_iter(idx_access, &iter_old);
+ GtkTreePath *path_old = gtk_q_sort_filter_tree_model_get_path(GTK_TREE_MODEL(retval), &iter_old);
+ gtk_tree_model_row_deleted(GTK_TREE_MODEL(retval), path_old);
+ }
+ }
+ );
+
+ QObject::connect(
+ retval->priv->given_model,
+ &QAbstractItemModel::rowsMoved,
+ [=](G_GNUC_UNUSED const QModelIndex & sourceParent, int sourceStart, int sourceEnd,
+ const QModelIndex & destinationParent, int destinationRow) {
+ /* these rows should have been removed in the "rowsAboutToBeMoved" handler
+ * now insert them in the new location */
+ for( int row = sourceStart; row <= sourceEnd; row++) {
+ // g_debug("row moved %d", row);
+ GtkTreeIter *iter_new = g_new0(GtkTreeIter, 1);
+ QModelIndex idx_given = retval->priv->given_model->index(destinationRow, 0, destinationParent);
+ QModelIndex idx_original = retval->priv->given_model->mapToSource(idx_given);
+ QModelIndex idx_access = retval->priv->access_model->mapFromSource(idx_original);
+ iter_new->stamp = stamp;
+ qmodelindex_to_iter(idx_access, iter_new);
+ GtkTreePath *path_new = gtk_q_sort_filter_tree_model_get_path(GTK_TREE_MODEL(retval), iter_new);
+ gtk_tree_model_row_inserted(GTK_TREE_MODEL(retval), path_new, iter_new);
+ destinationRow++;
+ }
+ }
+ );
+
+ QObject::connect(
+ retval->priv->given_model,
+ &QAbstractItemModel::rowsAboutToBeRemoved,
+ [=](const QModelIndex & parent, int first, int last) {
+ for( int row = first; row <= last; row++) {
+ // g_debug("row about to be deleted %d", row);
+ QModelIndex idx_given = retval->priv->given_model->index(row, 0, parent);
+ QModelIndex idx_original = retval->priv->given_model->mapToSource(idx_given);
+ QModelIndex idx_access = retval->priv->access_model->mapFromSource(idx_original);
+ GtkTreeIter iter;
+ iter.stamp = stamp;
+ qmodelindex_to_iter(idx_access, &iter);
+ GtkTreePath *path = gtk_q_sort_filter_tree_model_get_path(GTK_TREE_MODEL(retval), &iter);
+ gtk_tree_model_row_deleted(GTK_TREE_MODEL(retval), path);
+ }
+ }
+ );
+
+ QObject::connect(
+ retval->priv->given_model,
+ &QAbstractItemModel::dataChanged,
+ [=](const QModelIndex & topLeft, const QModelIndex & bottomRight,
+ G_GNUC_UNUSED const QVector<int> & roles = QVector<int> ()) {
+ /* we have to assume only one column */
+ int first = topLeft.row();
+ // g_debug("data changed on row: %d", first);
+ int last = bottomRight.row();
+ if (topLeft.column() != bottomRight.column() ) {
+ // g_warning("more than one column is not supported!");
+ }
+ /* the first idx IS topLeft, the reset are his siblings */
+ GtkTreeIter *iter = g_new0(GtkTreeIter, 1);
+ QModelIndex idx_given = topLeft;
+ QModelIndex idx_original = retval->priv->given_model->mapToSource(idx_given);
+ QModelIndex idx_access = retval->priv->access_model->mapFromSource(idx_original);
+ iter->stamp = stamp;
+ qmodelindex_to_iter(idx_access, iter);
+ GtkTreePath *path = gtk_q_sort_filter_tree_model_get_path(GTK_TREE_MODEL(retval), iter);
+ gtk_tree_model_row_changed(GTK_TREE_MODEL(retval), path, iter);
+ for( int row = first + 1; row <= last; row++) {
+ // g_debug("data changed on row: %d", row);
+ iter = g_new0(GtkTreeIter, 1);
+ idx_given = topLeft.sibling(row, 0);
+ idx_original = retval->priv->given_model->mapToSource(idx_given);
+ idx_access = retval->priv->access_model->mapFromSource(idx_original);
+ iter->stamp = stamp;
+ qmodelindex_to_iter(idx_access, iter);
+ path = gtk_q_sort_filter_tree_model_get_path(GTK_TREE_MODEL(retval), iter);
+ gtk_tree_model_row_changed(GTK_TREE_MODEL(retval), path, iter);
+ }
+ }
+ );
+
+ return retval;
+}
+
+static gint
+gtk_q_sort_filter_tree_model_length(GtkQSortFilterTreeModel *q_tree_model)
+{
+ GtkQSortFilterTreeModelPrivate *priv = q_tree_model->priv;
+ // g_debug("row cout: %d", priv->access_model->rowCount());
+ return priv->access_model->rowCount();
+}
+
+static void
+gtk_q_sort_filter_tree_model_set_n_columns(GtkQSortFilterTreeModel *q_tree_model,
+ gint n_columns)
+{
+ GtkQSortFilterTreeModelPrivate *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;
+}
+
+static void
+gtk_q_sort_filter_tree_model_set_column_type(GtkQSortFilterTreeModel *q_tree_model,
+ gint column,
+ gint role,
+ GType type)
+{
+ GtkQSortFilterTreeModelPrivate *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_headers[column] = type;
+ priv->column_roles[column] = role;
+}
+
+
+static void
+gtk_q_sort_filter_tree_model_finalize(GObject *object)
+{
+ GtkQSortFilterTreeModel *q_tree_model = GTK_Q_SORT_FILTER_TREE_MODEL (object);
+ GtkQSortFilterTreeModelPrivate *priv = q_tree_model->priv;
+
+ g_free(priv->column_headers);
+ g_free(priv->column_roles);
+
+ /* delete the created proxy model */
+ delete priv->access_model;
+
+ G_OBJECT_CLASS(gtk_q_sort_filter_tree_model_parent_class)->finalize (object);
+}
+
+static gboolean
+iter_is_valid(GtkTreeIter *iter,
+ GtkQSortFilterTreeModel *q_tree_model)
+{
+ // g_debug("\titer is valid");
+ g_return_val_if_fail(iter != NULL, FALSE);
+ QIter *qiter = Q_ITER(iter);
+ // g_debug("\t\tr: %d, p: %p", qiter->row.value, qiter->id);
+ QModelIndex idx_access = q_tree_model->priv->access_model->indexFromId(qiter->row.value, qiter->column.value, qiter->id);
+ // if (!idx_access.isValid())
+ // g_debug("access idx not valid");
+ QModelIndex idx_original = q_tree_model->priv->access_model->mapToSource(idx_access);
+ // if (!idx_original.isValid())
+ // g_debug("original idx not valid");
+ QModelIndex idx_given = q_tree_model->priv->given_model->mapFromSource(idx_original);
+ // if (!idx_given.isValid())
+ // g_debug("given idx not valid");
+ return idx_given.isValid();
+}
+
+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_sort_filter_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_sort_filter_tree_model_get_n_columns(GtkTreeModel *tree_model)
+{
+ GtkQSortFilterTreeModel *q_tree_model = GTK_Q_SORT_FILTER_TREE_MODEL(tree_model);
+ GtkQSortFilterTreeModelPrivate *priv = q_tree_model->priv;
+
+ return priv->given_model->columnCount();
+}
+
+/* get given column type */
+static GType
+gtk_q_sort_filter_tree_model_get_column_type(GtkTreeModel *tree_model,
+ gint index)
+{
+ GtkQSortFilterTreeModel *q_tree_model = GTK_Q_SORT_FILTER_TREE_MODEL (tree_model);
+ GtkQSortFilterTreeModelPrivate *priv = q_tree_model->priv;
+
+ g_return_val_if_fail (index < gtk_q_sort_filter_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_sort_filter_tree_model_get_iter(GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ GtkTreePath *path)
+{
+ // g_debug("get iter from path");
+ GtkQSortFilterTreeModel *q_tree_model = GTK_Q_SORT_FILTER_TREE_MODEL (tree_model);
+ GtkQSortFilterTreeModelPrivate *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_given = priv->given_model->index(indices[0], 0);
+ for(int layer = 1; layer < depth; layer++ ) {
+ /* check if previous iter is valid */
+ if (!idx_given.isValid()) {
+ break;
+ } else {
+ idx_given = idx_given.child(indices[layer], 0);
+ }
+ }
+
+ if (!idx_given.isValid()) {
+ iter->stamp = 0;
+ return FALSE;
+ } else {
+ /* we have a valid QModelIndex; turn it into an iter */
+ iter->stamp = priv->stamp;
+ QModelIndex idx_original = priv->given_model->mapToSource(idx_given);
+ // if (!idx_original.isValid())
+ // g_debug("\toriginal idx not valid");
+ QModelIndex idx_access = priv->access_model->mapFromSource(idx_original);
+ // if (!idx_access.isValid())
+ // g_debug("\taccess idx not valid");
+ qmodelindex_to_iter(idx_access, 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_sort_filter_tree_model_get_path(GtkTreeModel *tree_model,
+ GtkTreeIter *iter)
+{
+ // g_debug("get path");
+ GtkQSortFilterTreeModel *q_tree_model = GTK_Q_SORT_FILTER_TREE_MODEL (tree_model);
+ GtkQSortFilterTreeModelPrivate *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_access = priv->access_model->indexFromId(qiter->row.value, qiter->column.value, qiter->id);
+ QModelIndex idx_original = priv->access_model->mapToSource(idx_access);
+ QModelIndex idx_given = priv->given_model->mapFromSource(idx_original);
+ while( idx_given.isValid() ){
+ gtk_tree_path_prepend_index(path, idx_given.row());
+ idx_given = idx_given.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_sort_filter_tree_model_get_value(GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ gint column,
+ GValue *value)
+{
+ GtkQSortFilterTreeModel *q_tree_model = GTK_Q_SORT_FILTER_TREE_MODEL (tree_model);
+ GtkQSortFilterTreeModelPrivate *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);
+ QModelIndex idx_access = priv->access_model->indexFromId(qiter->row.value, qiter->column.value, qiter->id);
+ QModelIndex idx_original = priv->access_model->mapToSource(idx_access);
+ QModelIndex idx_given = priv->given_model->mapFromSource(idx_original);
+ int role = priv->column_roles[column];
+ QVariant var = idx_given.data(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().toLocal8Bit();
+ g_value_set_string(value, (gchar *)ba.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_sort_filter_tree_model_iter_next(GtkTreeModel *tree_model,
+ GtkTreeIter *iter)
+{
+ // g_debug("iter next");
+ GtkQSortFilterTreeModel *q_tree_model = GTK_Q_SORT_FILTER_TREE_MODEL (tree_model);
+ GtkQSortFilterTreeModelPrivate *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_access = priv->access_model->indexFromId(qiter->row.value, qiter->column.value, qiter->id);
+ // if (!idx_access.isValid())
+ // g_debug("\taccess not valid");
+ QModelIndex idx_original = priv->access_model->mapToSource(idx_access);
+ // if (!idx_original.isValid())
+ // g_debug("\toriginal not valid");
+ QModelIndex idx_given = priv->given_model->mapFromSource(idx_original);
+ // if (!idx_given.isValid())
+ // g_debug("\tgiven not valid");
+ idx_given = idx_given.sibling(idx_given.row()+1, idx_given.column());
+ // if (!idx_given.isValid())
+ // g_debug("\tgiven sibling not valid");
+
+ /* validate */
+ idx_original = priv->given_model->mapToSource(idx_given);
+ // if (!idx_original.isValid())
+ // g_debug("\toriginal sibling not valid");
+ idx_access = priv->access_model->mapFromSource(idx_original);
+ // if (!idx_access.isValid())
+ // g_debug("\tacess sibling not valid");
+ if (validate_index(priv->stamp, idx_access, iter) ) {
+ // QIter *qiter = Q_ITER(iter);
+ // g_debug("\tr: %d, p: %p", qiter->row.value, qiter->id);
+ // g_debug("\ttrue");
+ return TRUE;
+ } else {
+ // g_debug("\tfalse");
+ 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_sort_filter_tree_model_iter_previous(GtkTreeModel *tree_model,
+ GtkTreeIter *iter)
+{
+ // g_debug("iter prev");
+ GtkQSortFilterTreeModel *q_tree_model = GTK_Q_SORT_FILTER_TREE_MODEL (tree_model);
+ GtkQSortFilterTreeModelPrivate *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_access = priv->access_model->indexFromId(qiter->row.value, qiter->column.value, qiter->id);
+ QModelIndex idx_original = priv->access_model->mapToSource(idx_access);
+ QModelIndex idx_given = priv->given_model->mapFromSource(idx_original);
+ idx_given = idx_given.sibling(idx_given.row()-1, idx_given.column());
+
+ /* validate */
+ // return validate_index(priv->stamp, idx, iter);
+ idx_original = priv->given_model->mapToSource(idx_given);
+ idx_access = priv->access_model->mapFromSource(idx_original);
+ if (validate_index(priv->stamp, idx_access, iter)) {
+ // g_debug("\ttrue");
+ return TRUE;
+ } else {
+ // g_debug("\tfalse");
+ return FALSE;
+ }
+}
+
+/* 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_sort_filter_tree_model_iter_children(GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ GtkTreeIter *parent)
+{
+ // g_debug("iter return first child");
+ GtkQSortFilterTreeModel *q_tree_model = GTK_Q_SORT_FILTER_TREE_MODEL (tree_model);
+ GtkQSortFilterTreeModelPrivate *priv = q_tree_model->priv;
+ QModelIndex idx_access;
+ QModelIndex idx_original;
+ QModelIndex idx_given;
+
+ /* 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_access = priv->access_model->indexFromId(qparent->row.value, qparent->column.value, qparent->id);
+ idx_original = priv->access_model->mapToSource(idx_access);
+ idx_given = priv->given_model->mapFromSource(idx_original);
+ idx_given = idx_given.child(0, 0);
+ } else {
+ /* parent is NULL, get the first node */
+ idx_given = priv->given_model->index(0, 0);
+ // g_debug("\treturning the first node");
+ }
+
+ /* validate child */
+ // return validate_index(priv->stamp, idx, iter);
+ idx_original = priv->given_model->mapToSource(idx_given);
+ idx_access = priv->access_model->mapFromSource(idx_original);
+ if (validate_index(priv->stamp, idx_access, iter)) {
+ // g_debug("\ttrue");
+ return TRUE;
+ } else {
+ // g_debug("\tfalse");
+ return FALSE;
+ }
+}
+
+/* Returns %TRUE if @iter has children, %FALSE otherwise. */
+static gboolean
+gtk_q_sort_filter_tree_model_iter_has_child (GtkTreeModel *tree_model,
+ GtkTreeIter *iter)
+{
+ // g_debug("iter has child");
+ GtkQSortFilterTreeModel *q_tree_model = GTK_Q_SORT_FILTER_TREE_MODEL (tree_model);
+ GtkQSortFilterTreeModelPrivate *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_access = priv->access_model->indexFromId(qiter->row.value, qiter->column.value, qiter->id);
+ QModelIndex idx_original = priv->access_model->mapToSource(idx_access);
+ QModelIndex idx_given = priv->given_model->mapFromSource(idx_original);
+ // return priv->access_model->hasChildren(idx);
+ if (priv->given_model->hasChildren(idx_given)) {
+ // g_debug("\ttrue");
+ return TRUE;
+ } else {
+ // g_debug("\tfalse");
+ return FALSE;
+ }
+}
+
+/* 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_sort_filter_tree_model_iter_n_children (GtkTreeModel *tree_model,
+ GtkTreeIter *iter)
+{
+ // g_debug("get num children");
+ GtkQSortFilterTreeModel *q_tree_model = GTK_Q_SORT_FILTER_TREE_MODEL (tree_model);
+ GtkQSortFilterTreeModelPrivate *priv = q_tree_model->priv;
+
+ if (iter == NULL) {
+ // g_debug("\ttop level");
+ return gtk_q_sort_filter_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_access = priv->access_model->indexFromId(qiter->row.value, qiter->column.value, qiter->id);
+ QModelIndex idx_original = priv->access_model->mapToSource(idx_access);
+ QModelIndex idx_given = priv->given_model->mapFromSource(idx_original);
+ // g_debug("\t%d", priv->given_model->rowCount(idx_given));
+ return priv->given_model->rowCount(idx_given);
+}
+
+/* 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 @n<!-- -->th root node
+ * is set.
+ */
+static gboolean
+gtk_q_sort_filter_tree_model_iter_nth_child(GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ GtkTreeIter *parent,
+ gint n)
+{
+ // g_debug("get nth child");
+ GtkQSortFilterTreeModel *q_tree_model = GTK_Q_SORT_FILTER_TREE_MODEL (tree_model);
+ GtkQSortFilterTreeModelPrivate *priv = q_tree_model->priv;
+ QModelIndex idx_access;
+ QModelIndex idx_original;
+ QModelIndex idx_given;
+
+ 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_access = priv->access_model->indexFromId(qparent->row.value, qparent->column.value, qparent->id);
+ idx_original = priv->access_model->mapToSource(idx_access);
+ idx_given = priv->given_model->mapFromSource(idx_original);
+ idx_given = idx_given.child(n, 0);
+ } else {
+ idx_given = priv->given_model->index(n, 0);
+ // g_debug("\tof root node");
+ }
+
+ /* validate */
+ // return validate_index(priv->stamp, idx, iter);
+ idx_original = priv->given_model->mapToSource(idx_given);
+ idx_access = priv->access_model->mapFromSource(idx_original);
+ if (validate_index(priv->stamp, idx_access, iter)) {
+ // g_debug("\ttrue");
+ return TRUE;
+ } else {
+ // g_debug("\tfalse");
+ return FALSE;
+ }
+}
+
+/* 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_sort_filter_tree_model_iter_parent(GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ GtkTreeIter *child)
+{
+ // g_debug("get parent");
+ GtkQSortFilterTreeModel *q_tree_model = GTK_Q_SORT_FILTER_TREE_MODEL (tree_model);
+ GtkQSortFilterTreeModelPrivate *priv = q_tree_model->priv;
+ QModelIndex idx_access;
+ QModelIndex idx_original;
+ QModelIndex idx_given;
+
+ g_return_val_if_fail(iter_is_valid(child, q_tree_model), FALSE);
+
+ QIter *qchild = Q_ITER(child);
+ idx_access = priv->access_model->indexFromId(qchild->row.value, qchild->column.value, qchild->id);
+ idx_original = priv->access_model->mapToSource(idx_access);
+ idx_given = priv->given_model->mapFromSource(idx_original);
+ idx_given = idx_given.parent();
+
+ /* validate */
+ // return validate_index(priv->stamp, idx, iter);
+ idx_original = priv->given_model->mapToSource(idx_given);
+ idx_access = priv->access_model->mapFromSource(idx_original);
+ if (validate_index(priv->stamp, idx_access, iter)) {
+ // g_debug("\ttrue");
+ return TRUE;
+ } else {
+ // g_debug("\tfalse");
+ return FALSE;
+ }
+}
+
+/* End Fulfill the GtkTreeModel requirements */
diff --git a/src/models/gtkqsortfiltertreemodel.h b/src/models/gtkqsortfiltertreemodel.h
new file mode 100644
index 0000000..baed911
--- /dev/null
+++ b/src/models/gtkqsortfiltertreemodel.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2015 Savoir-Faire Linux Inc.
+ * Author: Stepan Salenikovich <stepan.salenikovich@savoirfairelinux.com>
+ *
+ * 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.
+ *
+ * Additional permission under GNU GPL version 3 section 7:
+ *
+ * If you modify this program, or any covered work, by linking or
+ * combining it with the OpenSSL project's OpenSSL library (or a
+ * modified version of that library), containing parts covered by the
+ * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
+ * grants you additional permission to convey the resulting work.
+ * Corresponding Source for a non-source form of such a combination
+ * shall include the source code for the parts of OpenSSL used as well
+ * as that of the covered work.
+ */
+
+#ifndef GTK_Q_SORT_FILTER_TREE_MODEL_H_
+#define GTK_Q_SORT_FILTER_TREE_MODEL_H_
+
+#include <gtk/gtk.h>
+#include <QtCore/QSortFilterProxyModel>
+#include "gtkaccessproxymodel.h"
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_Q_SORT_FILTER_TREE_MODEL (gtk_q_sort_filter_tree_model_get_type ())
+#define GTK_Q_SORT_FILTER_TREE_MODEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_Q_SORT_FILTER_TREE_MODEL, GtkQSortFilterTreeModel))
+#define GTK_Q_SORT_FILTER_TREE_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_Q_SORT_FILTER_TREE_MODEL, GtkQSortFilterTreeModelClass))
+#define GTK_IS_Q_SORT_FILTER_TREE_MODEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_Q_SORT_FILTER_TREE_MODEL))
+#define GTK_IS_Q_SORT_FILTER_TREE_MODEL_CLASS(klass)(G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_Q_SORT_FILTER_TREE_MODEL))
+#define GTK_Q_SORT_FILTER_TREE_MODEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_Q_SORT_FILTER_TREE_MODEL, GtkQSortFilterTreeModelClass))
+
+typedef struct _GtkQSortFilterTreeModel GtkQSortFilterTreeModel;
+typedef struct _GtkQSortFilterTreeModelClass GtkQSortFilterTreeModelClass;
+
+GType gtk_q_sort_filter_tree_model_get_type (void) G_GNUC_CONST;
+GtkQSortFilterTreeModel *gtk_q_sort_filter_tree_model_new (QSortFilterProxyModel *, size_t, ...);
+QSortFilterProxyModel *gtk_q_sort_filter_tree_model_get_qmodel (GtkQSortFilterTreeModel *);
+QModelIndex gtk_q_sort_filter_tree_model_get_source_idx (GtkQSortFilterTreeModel *, GtkTreeIter *);
+gboolean gtk_q_sort_filter_tree_model_source_index_to_iter(GtkQSortFilterTreeModel *, const QModelIndex &, GtkTreeIter *);
+
+G_END_DECLS
+
+#endif /* GTK_Q_SORT_FILTER_TREE_MODEL_H_ */
diff --git a/src/models/gtkqtreemodel.cpp b/src/models/gtkqtreemodel.cpp
index 6b0d51a..6ba1fe9 100644
--- a/src/models/gtkqtreemodel.cpp
+++ b/src/models/gtkqtreemodel.cpp
@@ -223,7 +223,7 @@
/* map the proxy idx to iter */
Q_ITER(iter)->row.value = proxy_idx.row();
- Q_ITER(iter)->column.value = proxy_idx.row();
+ Q_ITER(iter)->column.value = proxy_idx.column();
Q_ITER(iter)->id = proxy_idx.internalPointer();
return TRUE;
}
@@ -459,7 +459,7 @@
qmodelindex_to_iter(const QModelIndex &idx, GtkTreeIter *iter)
{
Q_ITER(iter)->row.value = idx.row();
- Q_ITER(iter)->column.value = idx.row();
+ Q_ITER(iter)->column.value = idx.column();
Q_ITER(iter)->id = idx.internalPointer();
}
diff --git a/src/ringmainwindow.cpp b/src/ringmainwindow.cpp
index bc123cb..274efee 100644
--- a/src/ringmainwindow.cpp
+++ b/src/ringmainwindow.cpp
@@ -40,6 +40,8 @@
#include <string.h>
#include <historymodel.h>
#include <contactmethod.h>
+#include <QtCore/QSortFilterProxyModel>
+#include "models/gtkqsortfiltertreemodel.h"
#define DEFAULT_VIEW_NAME "placeholder"
#define VIEW_CONTACTS "contacts"
@@ -256,7 +258,7 @@
/* get iter */
GtkTreeIter iter;
if (gtk_tree_model_get_iter(model, &iter, path)) {
- QModelIndex idx = gtk_q_tree_model_get_source_idx(GTK_Q_TREE_MODEL(model), &iter);
+ QModelIndex idx = gtk_q_sort_filter_tree_model_get_source_idx(GTK_Q_SORT_FILTER_TREE_MODEL(model), &iter);
QVariant contact_method = idx.data(static_cast<int>(Call::Role::ContactMethod));
/* create new call */
@@ -459,9 +461,14 @@
gtk_stack_set_visible_child(GTK_STACK(priv->stack_contacts_history_presence),
scrolled_window);
- GtkQTreeModel *history_model;
- history_model = gtk_q_tree_model_new(HistoryModel::instance(), 4,
+ /* sort the history in descending order by date */
+ QSortFilterProxyModel *proxyModel = new QSortFilterProxyModel(HistoryModel::instance());
+ proxyModel->setSourceModel(HistoryModel::instance());
+ proxyModel->setSortRole(static_cast<int>(Call::Role::Date));
+ proxyModel->sort(0,Qt::DescendingOrder);
+
+ GtkQSortFilterTreeModel *history_model = gtk_q_sort_filter_tree_model_new((QSortFilterProxyModel *)proxyModel, 4,
Qt::DisplayRole, G_TYPE_STRING,
Call::Role::Number, G_TYPE_STRING,
Call::Role::FormattedDate, G_TYPE_STRING,