* we warned you and we're not liable for any head injuries.
*/
+#include <config.h>
#include <string.h>
#include "gtktreemodelsort.h"
#include "gtktreestore.h"
#include "gtktreedatalist.h"
#include "gtkintl.h"
+#include "gtkprivate.h"
+#include "gtktreednd.h"
+#include "gtkalias.h"
typedef struct _SortElt SortElt;
typedef struct _SortLevel SortLevel;
#define SORT_ELT(sort_elt) ((SortElt *)sort_elt)
#define SORT_LEVEL(sort_level) ((SortLevel *)sort_level)
-#define GET_CHILD_ITER(tree_model_sort,child_iter,sort_iter) gtk_tree_model_sort_convert_iter_to_child_iter(GTK_TREE_MODEL_SORT (tree_model_sort), child_iter, sort_iter);
+#define GET_CHILD_ITER(tree_model_sort,ch_iter,so_iter) gtk_tree_model_sort_convert_iter_to_child_iter(GTK_TREE_MODEL_SORT (tree_model_sort), ch_iter, so_iter);
#define NO_SORT_FUNC ((GtkTreeIterCompareFunc) 0x1)
static void gtk_tree_model_sort_class_init (GtkTreeModelSortClass *tree_model_sort_class);
static void gtk_tree_model_sort_tree_model_init (GtkTreeModelIface *iface);
static void gtk_tree_model_sort_tree_sortable_init (GtkTreeSortableIface *iface);
+static void gtk_tree_model_sort_drag_source_init (GtkTreeDragSourceIface*iface);
static void gtk_tree_model_sort_finalize (GObject *object);
static void gtk_tree_model_sort_set_property (GObject *object,
guint prop_id,
static void gtk_tree_model_sort_unref_node (GtkTreeModel *tree_model,
GtkTreeIter *iter);
+/* TreeDragSource interface */
+static gboolean gtk_tree_model_sort_row_draggable (GtkTreeDragSource *drag_source,
+ GtkTreePath *path);
+static gboolean gtk_tree_model_sort_drag_data_get (GtkTreeDragSource *drag_source,
+ GtkTreePath *path,
+ GtkSelectionData *selection_data);
+static gboolean gtk_tree_model_sort_drag_data_delete (GtkTreeDragSource *drag_source,
+ GtkTreePath *path);
+
/* TreeSortable interface */
static gboolean gtk_tree_model_sort_get_sort_column_id (GtkTreeSortable *sortable,
gint *sort_column_id,
static gint gtk_tree_model_sort_level_find_insert (GtkTreeModelSort *tree_model_sort,
SortLevel *level,
GtkTreeIter *iter,
- gboolean skip_sort_elt);
+ gint skip_index);
static gboolean gtk_tree_model_sort_insert_value (GtkTreeModelSort *tree_model_sort,
SortLevel *level,
GtkTreePath *s_path,
NULL
};
+ static const GInterfaceInfo drag_source_info =
+ {
+ (GInterfaceInitFunc) gtk_tree_model_sort_drag_source_init,
+ NULL,
+ NULL
+ };
+
tree_model_sort_type =
g_type_register_static (G_TYPE_OBJECT, "GtkTreeModelSort",
&tree_model_sort_info, 0);
g_type_add_interface_static (tree_model_sort_type,
GTK_TYPE_TREE_SORTABLE,
&sortable_info);
+
+ g_type_add_interface_static (tree_model_sort_type,
+ GTK_TYPE_TREE_DRAG_SOURCE,
+ &drag_source_info);
}
return tree_model_sort_type;
g_object_class_install_property (object_class,
PROP_MODEL,
g_param_spec_object ("model",
- _("TreeModelSort Model"),
- _("The model for the TreeModelSort to sort"),
+ P_("TreeModelSort Model"),
+ P_("The model for the TreeModelSort to sort"),
GTK_TYPE_TREE_MODEL,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+ GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
}
static void
iface->has_default_sort_func = gtk_tree_model_sort_has_default_sort_func;
}
+static void
+gtk_tree_model_sort_drag_source_init (GtkTreeDragSourceIface *iface)
+{
+ iface->row_draggable = gtk_tree_model_sort_row_draggable;
+ iface->drag_data_delete = gtk_tree_model_sort_drag_data_delete;
+ iface->drag_data_get = gtk_tree_model_sort_drag_data_get;
+}
+
/**
* gtk_tree_model_sort_new_with_model:
* @child_model: A #GtkTreeModel
*
- * Creates a new #GtkTreeModel, with @child_model as the child_model.
+ * Creates a new #GtkTreeModel, with @child_model as the child model.
*
* Return value: A new #GtkTreeModel.
*/
gboolean free_s_path = FALSE;
- gint offset, index = 0, old_index, i;
+ gint index = 0, old_index, i;
g_return_if_fail (start_s_path != NULL || start_s_iter != NULL);
level = iter.user_data;
elt = iter.user_data2;
+ level->ref_count++;
+
if (level->array->len < 2 ||
(tree_model_sort->sort_column_id == GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID &&
tree_model_sort->default_sort_func == NO_SORT_FUNC))
gtk_tree_path_free (path);
+ level->ref_count--;
+
return;
}
-
+
if (!GTK_TREE_MODEL_SORT_CACHE_CHILD_ITERS (tree_model_sort))
{
gtk_tree_model_get_iter (tree_model_sort->child_model,
&tmpiter, start_s_path);
}
- offset = elt->offset;
-
- for (i = 0; i < level->array->len; i++)
- if (elt->offset == g_array_index (level->array, SortElt, i).offset)
- index = i;
+ old_index = elt - SORT_ELT (level->array->data);
memcpy (&tmp, elt, sizeof (SortElt));
- g_array_remove_index (level->array, index);
-
- old_index = index;
if (GTK_TREE_MODEL_SORT_CACHE_CHILD_ITERS (tree_model_sort))
index = gtk_tree_model_sort_level_find_insert (tree_model_sort,
level,
&tmp.iter,
- TRUE);
+ old_index);
else
index = gtk_tree_model_sort_level_find_insert (tree_model_sort,
level,
&tmpiter,
- TRUE);
+ old_index);
- g_array_insert_val (level->array, index, tmp);
+ if (index < old_index)
+ {
+ g_memmove (level->array->data + ((index + 1)*sizeof (SortElt)),
+ level->array->data + ((index)*sizeof (SortElt)),
+ (old_index - index)* sizeof(SortElt));
+ }
+ else if (index > old_index)
+ {
+ g_memmove (level->array->data + ((old_index)*sizeof (SortElt)),
+ level->array->data + ((old_index + 1)*sizeof (SortElt)),
+ (index - old_index)* sizeof(SortElt));
+ }
+ memcpy (level->array->data + ((index)*sizeof (SortElt)),
+ &tmp, sizeof (SortElt));
for (i = 0; i < level->array->len; i++)
if (g_array_index (level->array, SortElt, i).children)
g_free (new_order);
}
+ level->ref_count--;
+
/* emit row_changed signal (at new location) */
gtk_tree_model_get_iter (GTK_TREE_MODEL (data), &iter, path);
gtk_tree_model_row_changed (GTK_TREE_MODEL (data), path, &iter);
if (!parent_level)
goto done;
+ if (level->ref_count == 0 && level != tree_model_sort->root)
+ {
+ gtk_tree_model_sort_free_level (tree_model_sort, level);
+ goto done;
+ }
+
if (!gtk_tree_model_sort_insert_value (tree_model_sort,
parent_level,
s_path,
while (elt->ref_count > 0)
gtk_tree_model_sort_real_unref_node (GTK_TREE_MODEL (data), &iter, FALSE);
- if (level->ref_count == 0 && level != tree_model_sort->root)
+ if (level->ref_count == 0)
{
- /* This will prune the level, so I can just emit the signal and not worry
- * about cleaning this level up. */
+ /* This will prune the level, so I can just emit the signal and
+ * not worry about cleaning this level up.
+ * Careful, root level is not cleaned up in increment stamp.
+ */
gtk_tree_model_sort_increment_stamp (tree_model_sort);
gtk_tree_path_free (path);
+ if (level == tree_model_sort->root)
+ {
+ gtk_tree_model_sort_free_level (tree_model_sort,
+ tree_model_sort->root);
+ tree_model_sort->root = NULL;
+ }
return;
}
static GtkTreeModelFlags
gtk_tree_model_sort_get_flags (GtkTreeModel *tree_model)
{
+ GtkTreeModelFlags flags;
+
g_return_val_if_fail (GTK_IS_TREE_MODEL_SORT (tree_model), 0);
+ g_return_val_if_fail (GTK_TREE_MODEL_SORT (tree_model)->child_model != NULL, 0);
+
+ flags = gtk_tree_model_get_flags (GTK_TREE_MODEL_SORT (tree_model)->child_model);
+
+ if ((flags & GTK_TREE_MODEL_LIST_ONLY) == GTK_TREE_MODEL_LIST_ONLY)
+ return GTK_TREE_MODEL_LIST_ONLY;
return 0;
}
level = g_array_index (level->array, SortElt, indices[i]).children;
}
- if (level == NULL)
- return FALSE;
+ if (!level || indices[i] >= level->array->len)
+ {
+ iter->stamp = 0;
+ return FALSE;
+ }
+
iter->stamp = tree_model_sort->stamp;
iter->user_data = level;
iter->user_data2 = &g_array_index (level->array, SortElt, indices[depth - 1]);
g_return_val_if_fail (GTK_IS_TREE_MODEL_SORT (sortable), FALSE);
- if (tree_model_sort->sort_column_id == GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID)
- return FALSE;
-
if (sort_column_id)
*sort_column_id = tree_model_sort->sort_column_id;
if (order)
*order = tree_model_sort->order;
+ if (tree_model_sort->sort_column_id == GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID ||
+ tree_model_sort->sort_column_id == GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID)
+ return FALSE;
+
return TRUE;
}
tree_model_sort->sort_column_id = sort_column_id;
tree_model_sort->order = order;
- gtk_tree_model_sort_sort (tree_model_sort);
-
gtk_tree_sortable_sort_column_changed (sortable);
+
+ gtk_tree_model_sort_sort (tree_model_sort);
}
static void
header->func = func;
header->data = data;
header->destroy = destroy;
+
+ if (tree_model_sort->sort_column_id == sort_column_id)
+ gtk_tree_model_sort_sort (tree_model_sort);
}
static void
tree_model_sort->default_sort_func = func;
tree_model_sort->default_sort_data = data;
tree_model_sort->default_sort_destroy = destroy;
+
+ if (tree_model_sort->sort_column_id == GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID)
+ gtk_tree_model_sort_sort (tree_model_sort);
}
static gboolean
return (tree_model_sort->default_sort_func != NULL);
}
+/* DragSource interface */
+static gboolean
+gtk_tree_model_sort_row_draggable (GtkTreeDragSource *drag_source,
+ GtkTreePath *path)
+{
+ GtkTreeModelSort *tree_model_sort = (GtkTreeModelSort *)drag_source;
+ GtkTreePath *child_path;
+ gboolean draggable;
+
+ g_return_val_if_fail (GTK_IS_TREE_MODEL_SORT (drag_source), FALSE);
+ g_return_val_if_fail (path != NULL, FALSE);
+
+ child_path = gtk_tree_model_sort_convert_path_to_child_path (tree_model_sort,
+ path);
+ draggable = gtk_tree_drag_source_row_draggable (GTK_TREE_DRAG_SOURCE (tree_model_sort->child_model), child_path);
+ gtk_tree_path_free (child_path);
+
+ return draggable;
+}
+
+static gboolean
+gtk_tree_model_sort_drag_data_get (GtkTreeDragSource *drag_source,
+ GtkTreePath *path,
+ GtkSelectionData *selection_data)
+{
+ GtkTreeModelSort *tree_model_sort = (GtkTreeModelSort *)drag_source;
+ GtkTreePath *child_path;
+ gboolean gotten;
+
+ g_return_val_if_fail (GTK_IS_TREE_MODEL_SORT (drag_source), FALSE);
+ g_return_val_if_fail (path != NULL, FALSE);
+
+ child_path = gtk_tree_model_sort_convert_path_to_child_path (tree_model_sort,
+ path);
+ gotten = gtk_tree_drag_source_drag_data_get (GTK_TREE_DRAG_SOURCE (tree_model_sort->child_model), child_path, selection_data);
+ gtk_tree_path_free (child_path);
+
+ return gotten;
+}
+
+static gboolean
+gtk_tree_model_sort_drag_data_delete (GtkTreeDragSource *drag_source,
+ GtkTreePath *path)
+{
+ GtkTreeModelSort *tree_model_sort = (GtkTreeModelSort *)drag_source;
+ GtkTreePath *child_path;
+ gboolean deleted;
+
+ g_return_val_if_fail (GTK_IS_TREE_MODEL_SORT (drag_source), FALSE);
+ g_return_val_if_fail (path != NULL, FALSE);
+
+ child_path = gtk_tree_model_sort_convert_path_to_child_path (tree_model_sort,
+ path);
+ deleted = gtk_tree_drag_source_drag_data_delete (GTK_TREE_DRAG_SOURCE (tree_model_sort->child_model), child_path);
+ gtk_tree_path_free (child_path);
+
+ return deleted;
+}
+
/* sorting code - private */
static gint
gtk_tree_model_sort_compare_func (gconstpointer a,
if (level->array->len < 1 && !((SortElt *)level->array->data)->children)
return;
+ level->ref_count++;
+
/* Set up data */
data.tree_model_sort = tree_model_sort;
if (level->parent_elt)
}
g_free (new_order);
+
+ level->ref_count--;
}
static void
gtk_tree_model_sort_level_find_insert (GtkTreeModelSort *tree_model_sort,
SortLevel *level,
GtkTreeIter *iter,
- gboolean skip_sort_elt)
+ gint skip_index)
{
- gint middle;
+ gint start, middle, end;
gint cmp;
SortElt *tmp_elt;
GtkTreeIter tmp_iter;
GtkTreeIterCompareFunc func;
gpointer data;
- GtkTreePath *path;
-
if (tree_model_sort->sort_column_id != GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID)
{
GtkTreeDataSortHeader *header;
}
g_return_val_if_fail (func != NULL, 0);
-
- for (middle = 0; middle < level->array->len; middle++)
- {
- tmp_elt = &(g_array_index (level->array, SortElt, middle));
- if (!skip_sort_elt && SORT_ELT (iter->user_data2) == tmp_elt)
- continue;
+ start = 0;
+ end = level->array->len;
+ if (skip_index < 0)
+ skip_index = end;
+ else
+ end--;
- path = gtk_tree_model_sort_elt_get_path (level, tmp_elt);
- gtk_tree_model_get_iter (tree_model_sort->child_model,
- &tmp_iter, path);
- gtk_tree_path_free (path);
+ if (start == end)
+ return 0;
+
+ while (start != end)
+ {
+ middle = (start + end) / 2;
+ if (middle < skip_index)
+ tmp_elt = &(g_array_index (level->array, SortElt, middle));
+ else
+ tmp_elt = &(g_array_index (level->array, SortElt, middle + 1));
+
+ if (!GTK_TREE_MODEL_SORT_CACHE_CHILD_ITERS (tree_model_sort))
+ {
+ GtkTreePath *path = gtk_tree_model_sort_elt_get_path (level, tmp_elt);
+ gtk_tree_model_get_iter (tree_model_sort->child_model,
+ &tmp_iter, path);
+ gtk_tree_path_free (path);
+ }
+ else
+ tmp_iter = tmp_elt->iter;
+
if (tree_model_sort->order == GTK_SORT_ASCENDING)
cmp = (* func) (GTK_TREE_MODEL (tree_model_sort->child_model),
&tmp_iter, iter, data);
cmp = (* func) (GTK_TREE_MODEL (tree_model_sort->child_model),
iter, &tmp_iter, data);
- if (cmp > 0)
- break;
+ if (cmp <= 0)
+ start = middle + 1;
+ else
+ end = middle;
}
- return middle;
+ if (cmp <= 0)
+ return middle + 1;
+ else
+ return middle;
}
static gboolean
else
index = gtk_tree_model_sort_level_find_insert (tree_model_sort,
level, s_iter,
- FALSE);
+ -1);
g_array_insert_vals (level->array, index, &elt, 1);
tmp_elt = SORT_ELT (level->array->data);
/**
* gtk_tree_model_sort_set_model:
* @tree_model_sort: The #GtkTreeModelSort.
- * @child_model: A #GtkTreeModel, or NULL.
+ * @child_model: A #GtkTreeModel, or %NULL.
*
- * Sets the model of @tree_model_sort to be @model. If @model is NULL, then the
- * old model is unset. The sort function is unset as a result of this call.
- * The model will be in an unsorted state until a sort function is set.
+ * Sets the model of @tree_model_sort to be @model. If @model is %NULL,
+ * then the old model is unset. The sort function is unset as a result
+ * of this call. The model will be in an unsorted state until a sort
+ * function is set.
**/
static void
gtk_tree_model_sort_set_model (GtkTreeModelSort *tree_model_sort,
*
* Converts @child_path to a path relative to @tree_model_sort. That is,
* @child_path points to a path in the child model. The returned path will
- * point to the same row in the sorted model. If @child_path isn't a valid path
- * on the child model, then %NULL is returned.
+ * point to the same row in the sorted model. If @child_path isn't a valid
+ * path on the child model, then %NULL is returned.
*
* Return value: A newly allocated #GtkTreePath, or %NULL
**/
* @tree_model_sort: A #GtkTreeModelSort
* @sorted_path: A #GtkTreePath to convert
*
- * Converts @sorted_path to a path on the child model of @tree_model_sort. That
- * is, @sorted_path points to a location in @tree_model_sort. The returned path
- * will point to the same location in the model not being sorted. If @sorted_path
- * does not point to a location in the child model, %NULL is returned.
+ * Converts @sorted_path to a path on the child model of @tree_model_sort.
+ * That is, @sorted_path points to a location in @tree_model_sort. The
+ * returned path will point to the same location in the model not being
+ * sorted. If @sorted_path does not point to a location in the child model,
+ * %NULL is returned.
*
* Return value: A newly allocated #GtkTreePath, or %NULL
**/
if (gtk_tree_model_iter_next (tree_model_sort->child_model, &iter) == FALSE &&
i < length - 1)
{
- g_warning ("There is a discrepency between the sort model and the child model.");
+ g_warning ("There is a discrepancy between the sort model and the child model.");
return;
}
}
* @tree_model_sort: A #GtkTreeModelSort.
* @iter: A #GtkTreeIter.
*
- * WARNING: This function is slow. Only use it for debugging and/or testing
- * purposes.
+ * <warning><para>
+ * This function is slow. Only use it for debugging and/or testing purposes.
+ * </para></warning>
*
* Checks if the given iter is a valid iter for this #GtkTreeModelSort.
*
return gtk_tree_model_sort_iter_is_valid_helper (iter,
tree_model_sort->root);
}
+
+#define __GTK_TREE_MODEL_SORT_C__
+#include "gtkaliasdef.c"