gtk2/gtk/gtktreemodelsort.c
Alberto Ruiz 496f0892fc introspection: This patch fixes nullable return values fixes for the following symbols in gtk
gtk_accel_group_query
  gtk_accel_group_from_accel_closure
  gtk_accel_label_get_accel_widget
  gtk_accessible_get_widget
  gtk_actionable_get_action_name
  gtk_app_chooser_get_app_info
  gtk_app_chooser_button_get_heading
  gtk_app_chooser_dialog_get_heading
  gtk_application_get_window_by_id
  gtk_assistant_get_nth_page
  gtk_binding_set_find
  gtk_builder_get_object
  gtk_builder_lookup_callback_symbol
  gtk_builder_get_application
  gtk_button_get_image
  gtk_cell_area_get_focus_from_sibling
  gtk_cell_renderer_start_editing
  gtk_cell_view_get_model
  gtk_cell_view_get_displayed_row
  gtk_clipboard_get_owner
  gtk_container_get_focus_child
  gtk_container_get_focus_vadjustment
  gtk_container_get_focus_hadjustment
  gtk_dialog_get_widget_for_response
  gtk_drag_get_source_widget
  gtk_drag_dest_get_target_list
  gtk_drag_source_get_target_list
  gtk_entry_completion_get_model
  gtk_entry_completion_compute_prefix
  gtk_expander_get_label_widget
  gtk_file_chooser_get_filename
  gtk_file_chooser_get_current_folder
  gtk_file_chooser_get_uri
  gtk_file_chooser_get_current_folder_uri
  gtk_file_chooser_get_preview_widget
  gtk_file_chooser_get_preview_file
  gtk_file_chooser_get_preview_filename
  gtk_file_chooser_get_preview_uri
  gtk_file_chooser_get_extra_widget
  gtk_file_chooser_get_filter
  gtk_file_chooser_native_get_accept_label
  gtk_file_chooser_native_get_cancel_label
  gtk_file_filter_get_name
  gtk_font_chooser_get_font_family
  gtk_font_chooser_get_font_face
  gtk_font_chooser_get_font
  gtk_font_chooser_get_font_desc
  gtk_font_chooser_get_font_map
  gtk_frame_get_label
  gtk_gesture_get_device
  gtk_gesture_get_window
  gtk_gl_area_get_error
  gtk_header_bar_get_title
  gtk_header_bar_get_subtitle
  gtk_header_bar_get_custom_title
  gtk_icon_info_get_filename
  gtk_icon_view_get_path_at_pos
  gtk_icon_view_get_model
  gtk_image_get_pixbuf
  gtk_image_get_animation
  gtk_label_get_mnemonic_widget
  gtk_label_get_attributes
  gtk_check_version
  gtk_menu_button_get_popup
  gtk_menu_button_get_menu_model
  gtk_menu_button_get_align_widget
  gtk_menu_button_get_popover
  gtk_menu_item_get_submenu
  gtk_menu_item_get_accel_path
  gtk_native_dialog_get_title
  gtk_native_dialog_get_transient_for
  gtk_notebook_get_nth_page
  gtk_notebook_get_tab_label_text
  gtk_notebook_get_menu_label
  gtk_notebook_get_menu_label_text
  gtk_notebook_get_group_name
  gtk_notebook_get_action_widget
  gtk_offscreen_window_get_surface
  gtk_offscreen_window_get_pixbuf
  gtk_paned_get_child1
  gtk_paned_get_child2
  gtk_places_sidebar_get_location
  gtk_places_sidebar_get_nth_bookmark
  gtk_plug_get_socket_window
  gtk_popover_get_default_widget
  gtk_progress_bar_get_text
  gtk_recent_filter_get_name
  gtk_recent_manager_lookup_item
  gtk_settings_get_default
  gtk_socket_get_plug_window
  gtk_stack_sidebar_get_stack
  gtk_stack_switcher_get_stack
  gtk_style_context_get_section
  gtk_style_context_get_parent
  gtk_style_context_get_frame_clock
  gtk_test_find_widget
  gtk_text_buffer_get_mark
  gtk_text_tag_table_lookup
  gtk_text_view_get_tabs
  gtk_text_view_toggle_cursor_visible
  gtk_text_view_get_window
  gtk_toolbar_get_nth_item
  gtk_tool_button_get_label
  gtk_tool_button_get_icon_name
  gtk_tool_button_get_label_widget
  gtk_tool_button_get_icon_widget
  gtk_tool_palette_get_drop_item
  gtk_tool_palette_get_drop_group
  gtk_tree_model_filter_convert_child_path_to_path
  gtk_tree_model_filter_convert_path_to_child_path
  gtk_tree_model_sort_convert_child_path_to_path
  gtk_tree_model_sort_convert_path_to_child_path
  gtk_tree_view_get_column
  gtk_tree_view_get_bin_window
  gtk_tree_view_column_get_widget
  gtk_tree_view_column_get_tree_view
  gtk_widget_get_frame_clock
  gtk_window_group_get_current_device_grab
  GtkTextBufferSerializeFunc
2016-01-08 12:18:23 +00:00

2793 lines
84 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* gtktreemodelsort.c
* Copyright (C) 2000,2001 Red Hat, Inc., Jonathan Blandford <jrb@redhat.com>
* Copyright (C) 2001,2002 Kristian Rietveld <kris@gtk.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <string.h>
#include "gtktreemodelsort.h"
#include "gtktreesortable.h"
#include "gtktreestore.h"
#include "gtktreedatalist.h"
#include "gtkintl.h"
#include "gtkprivate.h"
#include "gtktreednd.h"
/**
* SECTION:gtktreemodelsort
* @Short_description: A GtkTreeModel which makes an underlying tree model sortable
* @Title: GtkTreeModelSort
* @See_also: #GtkTreeModel, #GtkListStore, #GtkTreeStore, #GtkTreeSortable, #GtkTreeModelFilter
*
* The #GtkTreeModelSort is a model which implements the #GtkTreeSortable
* interface. It does not hold any data itself, but rather is created with
* a child model and proxies its data. It has identical column types to
* this child model, and the changes in the child are propagated. The
* primary purpose of this model is to provide a way to sort a different
* model without modifying it. Note that the sort function used by
* #GtkTreeModelSort is not guaranteed to be stable.
*
* The use of this is best demonstrated through an example. In the
* following sample code we create two #GtkTreeView widgets each with a
* view of the same data. As the model is wrapped here by a
* #GtkTreeModelSort, the two #GtkTreeViews can each sort their
* view of the data without affecting the other. By contrast, if we
* simply put the same model in each widget, then sorting the first would
* sort the second.
*
* ## Using a #GtkTreeModelSort
*
* |[<!-- language="C" -->
* {
* GtkTreeView *tree_view1;
* GtkTreeView *tree_view2;
* GtkTreeModel *sort_model1;
* GtkTreeModel *sort_model2;
* GtkTreeModel *child_model;
*
* // get the child model
* child_model = get_my_model ();
*
* // Create the first tree
* sort_model1 = gtk_tree_model_sort_new_with_model (child_model);
* tree_view1 = gtk_tree_view_new_with_model (sort_model1);
*
* // Create the second tree
* sort_model2 = gtk_tree_model_sort_new_with_model (child_model);
* tree_view2 = gtk_tree_view_new_with_model (sort_model2);
*
* // Now we can sort the two models independently
* gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sort_model1),
* COLUMN_1, GTK_SORT_ASCENDING);
* gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sort_model2),
* COLUMN_1, GTK_SORT_DESCENDING);
* }
* ]|
*
* To demonstrate how to access the underlying child model from the sort
* model, the next example will be a callback for the #GtkTreeSelection
* #GtkTreeSelection::changed signal. In this callback, we get a string
* from COLUMN_1 of the model. We then modify the string, find the same
* selected row on the child model, and change the row there.
*
* ## Accessing the child model of in a selection changed callback
*
* |[<!-- language="C" -->
* void
* selection_changed (GtkTreeSelection *selection, gpointer data)
* {
* GtkTreeModel *sort_model = NULL;
* GtkTreeModel *child_model;
* GtkTreeIter sort_iter;
* GtkTreeIter child_iter;
* char *some_data = NULL;
* char *modified_data;
*
* // Get the current selected row and the model.
* if (! gtk_tree_selection_get_selected (selection,
* &sort_model,
* &sort_iter))
* return;
*
* // Look up the current value on the selected row and get
* // a new value to change it to.
* gtk_tree_model_get (GTK_TREE_MODEL (sort_model), &sort_iter,
* COLUMN_1, &some_data,
* -1);
*
* modified_data = change_the_data (some_data);
* g_free (some_data);
*
* // Get an iterator on the child model, instead of the sort model.
* gtk_tree_model_sort_convert_iter_to_child_iter (GTK_TREE_MODEL_SORT (sort_model),
* &child_iter,
* &sort_iter);
*
* // Get the child model and change the value of the row. In this
* // example, the child model is a GtkListStore. It could be any other
* // type of model, though.
* child_model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model));
* gtk_list_store_set (GTK_LIST_STORE (child_model), &child_iter,
* COLUMN_1, &modified_data,
* -1);
* g_free (modified_data);
* }
* ]|
*/
/* Notes on this implementation of GtkTreeModelSort
* ================================================
*
* Warnings
* --------
*
* In this code there is a potential for confusion as to whether an iter,
* path or value refers to the GtkTreeModelSort model, or to the child model
* that has been set. As a convention, variables referencing the child model
* will have an s_ prefix before them (ie. s_iter, s_value, s_path);
* Conversion of iterators and paths between GtkTreeModelSort and the child
* model is done through the various gtk_tree_model_sort_convert_* functions.
*
* Iterator format
* ---------------
*
* The iterator format of iterators handed out by GtkTreeModelSort is as
* follows:
*
* iter->stamp = tree_model_sort->stamp
* iter->user_data = SortLevel
* iter->user_data2 = SortElt
*
* Internal data structure
* -----------------------
*
* Using SortLevel and SortElt, GtkTreeModelSort maintains a “cache” of
* the mapping from GtkTreeModelSort nodes to nodes in the child model.
* This is to avoid sorting a level each time an operation is requested
* on GtkTreeModelSort, such as get iter, get path, get value.
*
* A SortElt corresponds to a single node. A node and its siblings are
* stored in a SortLevel. The SortLevel keeps a reference to the parent
* SortElt and its SortLevel (if any). The SortElt can have a "children"
* pointer set, which points at a child level (a sub level).
*
* In a SortLevel, nodes are stored in a GSequence. The GSequence
* allows for fast additions and removals, and supports sorting
* the level of SortElt nodes.
*
* It is important to recognize the two different mappings that play
* a part in this code:
* I. The mapping from the client to this model. The order in which
* nodes are stored in the GSequence is the order in which the
* nodes are exposed to clients of the GtkTreeModelSort.
* II. The mapping from this model to its child model. Each SortElt
* contains an “offset” field which is the offset of the
* corresponding node in the child model.
*
* Reference counting
* ------------------
*
* GtkTreeModelSort forwards all reference and unreference operations
* to the corresponding node in the child model. The reference count
* of each node is also maintained internally, in the “ref_count”
* fields in SortElt and SortLevel. For each ref and unref operation on
* a SortElt, the “ref_count” of the SortLevel is updated accordingly.
* In addition, if a SortLevel has a parent, a reference is taken on
* this parent. This happens in gtk_tree_model_sort_build_level() and
* the reference is released again in gtk_tree_model_sort_free_level().
* This ensures that when GtkTreeModelSort takes a reference on a node
* (for example during sorting), all parent nodes are referenced
* according to reference counting rule 1, see the GtkTreeModel
* documentation.
*
* When a level has a reference count of zero, which means that
* none of the nodes in the level is referenced, the level has
* a “zero ref count” on all its parents. As soon as the level
* reaches a reference count of zero, the zero ref count value is
* incremented by one on all parents of this level. Similarly, as
* soon as the reference count of a level changes from zero, the
* zero ref count value is decremented by one on all parents.
*
* The zero ref count value is used to clear unused portions of
* the cache. If a SortElt has a zero ref count of one, then
* its child level is unused and can be removed from the cache.
* If the zero ref count value is higher than one, then the
* child level contains sublevels which are unused as well.
* gtk_tree_model_sort_clear_cache() uses this to not recurse
* into levels which have a zero ref count of zero.
*/
typedef struct _SortElt SortElt;
typedef struct _SortLevel SortLevel;
typedef struct _SortData SortData;
struct _SortElt
{
GtkTreeIter iter;
SortLevel *children;
gint offset;
gint ref_count;
gint zero_ref_count;
gint old_index; /* used while sorting */
GSequenceIter *siter; /* iter into seq */
};
struct _SortLevel
{
GSequence *seq;
gint ref_count;
SortElt *parent_elt;
SortLevel *parent_level;
};
struct _SortData
{
GtkTreeModelSort *tree_model_sort;
GtkTreeIterCompareFunc sort_func;
gpointer sort_data;
GtkTreePath *parent_path;
gint *parent_path_indices;
gint parent_path_depth;
};
/* Properties */
enum {
PROP_0,
/* Construct args */
PROP_MODEL
};
struct _GtkTreeModelSortPrivate
{
gpointer root;
gint stamp;
guint child_flags;
GtkTreeModel *child_model;
gint zero_ref_count;
/* sort information */
GList *sort_list;
gint sort_column_id;
GtkSortType order;
/* default sort */
GtkTreeIterCompareFunc default_sort_func;
gpointer default_sort_data;
GDestroyNotify default_sort_destroy;
/* signal ids */
gulong changed_id;
gulong inserted_id;
gulong has_child_toggled_id;
gulong deleted_id;
gulong reordered_id;
};
/* Set this to 0 to disable caching of child iterators. This
* allows for more stringent testing. It is recommended to set this
* to one when refactoring this code and running the unit tests to
* catch more errors.
*/
#if 1
# define GTK_TREE_MODEL_SORT_CACHE_CHILD_ITERS(tree_model_sort) \
(((GtkTreeModelSort *)tree_model_sort)->priv->child_flags&GTK_TREE_MODEL_ITERS_PERSIST)
#else
# define GTK_TREE_MODEL_SORT_CACHE_CHILD_ITERS(tree_model_sort) (FALSE)
#endif
#define SORT_ELT(sort_elt) ((SortElt *)sort_elt)
#define SORT_LEVEL(sort_level) ((SortLevel *)sort_level)
#define GET_ELT(siter) ((SortElt *) (siter ? g_sequence_get (siter) : NULL))
#define GET_CHILD_ITER(tree_model_sort,ch_iter,so_iter) gtk_tree_model_sort_convert_iter_to_child_iter((GtkTreeModelSort*)(tree_model_sort), (ch_iter), (so_iter));
#define NO_SORT_FUNC ((GtkTreeIterCompareFunc) 0x1)
#define VALID_ITER(iter, tree_model_sort) ((iter) != NULL && (iter)->user_data != NULL && (iter)->user_data2 != NULL && (tree_model_sort)->priv->stamp == (iter)->stamp)
/* general (object/interface init, etc) */
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,
const GValue *value,
GParamSpec *pspec);
static void gtk_tree_model_sort_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec);
/* our signal handlers */
static void gtk_tree_model_sort_row_changed (GtkTreeModel *model,
GtkTreePath *start_path,
GtkTreeIter *start_iter,
gpointer data);
static void gtk_tree_model_sort_row_inserted (GtkTreeModel *model,
GtkTreePath *path,
GtkTreeIter *iter,
gpointer data);
static void gtk_tree_model_sort_row_has_child_toggled (GtkTreeModel *model,
GtkTreePath *path,
GtkTreeIter *iter,
gpointer data);
static void gtk_tree_model_sort_row_deleted (GtkTreeModel *model,
GtkTreePath *path,
gpointer data);
static void gtk_tree_model_sort_rows_reordered (GtkTreeModel *s_model,
GtkTreePath *s_path,
GtkTreeIter *s_iter,
gint *new_order,
gpointer data);
/* TreeModel interface */
static GtkTreeModelFlags gtk_tree_model_sort_get_flags (GtkTreeModel *tree_model);
static gint gtk_tree_model_sort_get_n_columns (GtkTreeModel *tree_model);
static GType gtk_tree_model_sort_get_column_type (GtkTreeModel *tree_model,
gint index);
static gboolean gtk_tree_model_sort_get_iter (GtkTreeModel *tree_model,
GtkTreeIter *iter,
GtkTreePath *path);
static GtkTreePath *gtk_tree_model_sort_get_path (GtkTreeModel *tree_model,
GtkTreeIter *iter);
static void gtk_tree_model_sort_get_value (GtkTreeModel *tree_model,
GtkTreeIter *iter,
gint column,
GValue *value);
static gboolean gtk_tree_model_sort_iter_next (GtkTreeModel *tree_model,
GtkTreeIter *iter);
static gboolean gtk_tree_model_sort_iter_previous (GtkTreeModel *tree_model,
GtkTreeIter *iter);
static gboolean gtk_tree_model_sort_iter_children (GtkTreeModel *tree_model,
GtkTreeIter *iter,
GtkTreeIter *parent);
static gboolean gtk_tree_model_sort_iter_has_child (GtkTreeModel *tree_model,
GtkTreeIter *iter);
static gint gtk_tree_model_sort_iter_n_children (GtkTreeModel *tree_model,
GtkTreeIter *iter);
static gboolean gtk_tree_model_sort_iter_nth_child (GtkTreeModel *tree_model,
GtkTreeIter *iter,
GtkTreeIter *parent,
gint n);
static gboolean gtk_tree_model_sort_iter_parent (GtkTreeModel *tree_model,
GtkTreeIter *iter,
GtkTreeIter *child);
static void gtk_tree_model_sort_ref_node (GtkTreeModel *tree_model,
GtkTreeIter *iter);
static void gtk_tree_model_sort_real_unref_node (GtkTreeModel *tree_model,
GtkTreeIter *iter,
gboolean propagate_unref);
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,
GtkSortType *order);
static void gtk_tree_model_sort_set_sort_column_id (GtkTreeSortable *sortable,
gint sort_column_id,
GtkSortType order);
static void gtk_tree_model_sort_set_sort_func (GtkTreeSortable *sortable,
gint sort_column_id,
GtkTreeIterCompareFunc func,
gpointer data,
GDestroyNotify destroy);
static void gtk_tree_model_sort_set_default_sort_func (GtkTreeSortable *sortable,
GtkTreeIterCompareFunc func,
gpointer data,
GDestroyNotify destroy);
static gboolean gtk_tree_model_sort_has_default_sort_func (GtkTreeSortable *sortable);
/* Private functions (sort funcs, level handling and other utils) */
static void gtk_tree_model_sort_build_level (GtkTreeModelSort *tree_model_sort,
SortLevel *parent_level,
SortElt *parent_elt);
static void gtk_tree_model_sort_free_level (GtkTreeModelSort *tree_model_sort,
SortLevel *sort_level,
gboolean unref);
static void gtk_tree_model_sort_increment_stamp (GtkTreeModelSort *tree_model_sort);
static void gtk_tree_model_sort_sort_level (GtkTreeModelSort *tree_model_sort,
SortLevel *level,
gboolean recurse,
gboolean emit_reordered);
static void gtk_tree_model_sort_sort (GtkTreeModelSort *tree_model_sort);
static gboolean gtk_tree_model_sort_insert_value (GtkTreeModelSort *tree_model_sort,
SortLevel *level,
GtkTreePath *s_path,
GtkTreeIter *s_iter);
static GtkTreePath *gtk_tree_model_sort_elt_get_path (SortLevel *level,
SortElt *elt);
static void gtk_tree_model_sort_set_model (GtkTreeModelSort *tree_model_sort,
GtkTreeModel *child_model);
static GtkTreePath *gtk_real_tree_model_sort_convert_child_path_to_path (GtkTreeModelSort *tree_model_sort,
GtkTreePath *child_path,
gboolean build_levels);
static gint gtk_tree_model_sort_compare_func (gconstpointer a,
gconstpointer b,
gpointer user_data);
static gint gtk_tree_model_sort_offset_compare_func (gconstpointer a,
gconstpointer b,
gpointer user_data);
static void gtk_tree_model_sort_clear_cache_helper (GtkTreeModelSort *tree_model_sort,
SortLevel *level);
G_DEFINE_TYPE_WITH_CODE (GtkTreeModelSort, gtk_tree_model_sort, G_TYPE_OBJECT,
G_ADD_PRIVATE (GtkTreeModelSort)
G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL,
gtk_tree_model_sort_tree_model_init)
G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_SORTABLE,
gtk_tree_model_sort_tree_sortable_init)
G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_SOURCE,
gtk_tree_model_sort_drag_source_init))
static void
gtk_tree_model_sort_init (GtkTreeModelSort *tree_model_sort)
{
GtkTreeModelSortPrivate *priv;
tree_model_sort->priv = priv =
gtk_tree_model_sort_get_instance_private (tree_model_sort);
priv->sort_column_id = GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID;
priv->stamp = 0;
priv->zero_ref_count = 0;
priv->root = NULL;
priv->sort_list = NULL;
}
static void
gtk_tree_model_sort_class_init (GtkTreeModelSortClass *class)
{
GObjectClass *object_class;
object_class = (GObjectClass *) class;
object_class->set_property = gtk_tree_model_sort_set_property;
object_class->get_property = gtk_tree_model_sort_get_property;
object_class->finalize = gtk_tree_model_sort_finalize;
/* Properties */
g_object_class_install_property (object_class,
PROP_MODEL,
g_param_spec_object ("model",
P_("TreeModelSort Model"),
P_("The model for the TreeModelSort to sort"),
GTK_TYPE_TREE_MODEL,
GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
}
static void
gtk_tree_model_sort_tree_model_init (GtkTreeModelIface *iface)
{
iface->get_flags = gtk_tree_model_sort_get_flags;
iface->get_n_columns = gtk_tree_model_sort_get_n_columns;
iface->get_column_type = gtk_tree_model_sort_get_column_type;
iface->get_iter = gtk_tree_model_sort_get_iter;
iface->get_path = gtk_tree_model_sort_get_path;
iface->get_value = gtk_tree_model_sort_get_value;
iface->iter_next = gtk_tree_model_sort_iter_next;
iface->iter_previous = gtk_tree_model_sort_iter_previous;
iface->iter_children = gtk_tree_model_sort_iter_children;
iface->iter_has_child = gtk_tree_model_sort_iter_has_child;
iface->iter_n_children = gtk_tree_model_sort_iter_n_children;
iface->iter_nth_child = gtk_tree_model_sort_iter_nth_child;
iface->iter_parent = gtk_tree_model_sort_iter_parent;
iface->ref_node = gtk_tree_model_sort_ref_node;
iface->unref_node = gtk_tree_model_sort_unref_node;
}
static void
gtk_tree_model_sort_tree_sortable_init (GtkTreeSortableIface *iface)
{
iface->get_sort_column_id = gtk_tree_model_sort_get_sort_column_id;
iface->set_sort_column_id = gtk_tree_model_sort_set_sort_column_id;
iface->set_sort_func = gtk_tree_model_sort_set_sort_func;
iface->set_default_sort_func = gtk_tree_model_sort_set_default_sort_func;
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.
*
* Returns: (transfer full): A new #GtkTreeModel.
*/
GtkTreeModel *
gtk_tree_model_sort_new_with_model (GtkTreeModel *child_model)
{
GtkTreeModel *retval;
g_return_val_if_fail (GTK_IS_TREE_MODEL (child_model), NULL);
retval = g_object_new (gtk_tree_model_sort_get_type (), NULL);
gtk_tree_model_sort_set_model (GTK_TREE_MODEL_SORT (retval), child_model);
return retval;
}
/* GObject callbacks */
static void
gtk_tree_model_sort_finalize (GObject *object)
{
GtkTreeModelSort *tree_model_sort = (GtkTreeModelSort *) object;
GtkTreeModelSortPrivate *priv = tree_model_sort->priv;
gtk_tree_model_sort_set_model (tree_model_sort, NULL);
if (priv->root)
gtk_tree_model_sort_free_level (tree_model_sort, priv->root, TRUE);
if (priv->sort_list)
{
_gtk_tree_data_list_header_free (priv->sort_list);
priv->sort_list = NULL;
}
if (priv->default_sort_destroy)
{
priv->default_sort_destroy (priv->default_sort_data);
priv->default_sort_destroy = NULL;
priv->default_sort_data = NULL;
}
/* must chain up */
G_OBJECT_CLASS (gtk_tree_model_sort_parent_class)->finalize (object);
}
static void
gtk_tree_model_sort_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GtkTreeModelSort *tree_model_sort = GTK_TREE_MODEL_SORT (object);
switch (prop_id)
{
case PROP_MODEL:
gtk_tree_model_sort_set_model (tree_model_sort, g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gtk_tree_model_sort_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GtkTreeModelSort *tree_model_sort = GTK_TREE_MODEL_SORT (object);
switch (prop_id)
{
case PROP_MODEL:
g_value_set_object (value, gtk_tree_model_sort_get_model (tree_model_sort));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
/* helpers */
static SortElt *
sort_elt_new (void)
{
return g_slice_new (SortElt);
}
static void
sort_elt_free (gpointer elt)
{
g_slice_free (SortElt, elt);
}
static void
increase_offset_iter (gpointer data,
gpointer user_data)
{
SortElt *elt = data;
gint offset = GPOINTER_TO_INT (user_data);
if (elt->offset >= offset)
elt->offset++;
}
static void
decrease_offset_iter (gpointer data,
gpointer user_data)
{
SortElt *elt = data;
gint offset = GPOINTER_TO_INT (user_data);
if (elt->offset > offset)
elt->offset--;
}
static void
fill_sort_data (SortData *data,
GtkTreeModelSort *tree_model_sort,
SortLevel *level)
{
GtkTreeModelSortPrivate *priv = tree_model_sort->priv;
data->tree_model_sort = tree_model_sort;
if (priv->sort_column_id != GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID)
{
GtkTreeDataSortHeader *header;
header = _gtk_tree_data_list_get_header (priv->sort_list,
priv->sort_column_id);
g_return_if_fail (header != NULL);
g_return_if_fail (header->func != NULL);
data->sort_func = header->func;
data->sort_data = header->data;
}
else
{
/* absolutely SHOULD NOT happen: */
g_return_if_fail (priv->default_sort_func != NULL);
data->sort_func = priv->default_sort_func;
data->sort_data = priv->default_sort_data;
}
if (level->parent_elt)
{
data->parent_path = gtk_tree_model_sort_elt_get_path (level->parent_level,
level->parent_elt);
gtk_tree_path_append_index (data->parent_path, 0);
}
else
{
data->parent_path = gtk_tree_path_new_first ();
}
data->parent_path_depth = gtk_tree_path_get_depth (data->parent_path);
data->parent_path_indices = gtk_tree_path_get_indices (data->parent_path);
}
static void
free_sort_data (SortData *data)
{
gtk_tree_path_free (data->parent_path);
}
static SortElt *
lookup_elt_with_offset (GtkTreeModelSort *tree_model_sort,
SortLevel *level,
gint offset,
GSequenceIter **ret_siter)
{
GSequenceIter *siter, *end_siter;
/* FIXME: We have to do a search like this, because the sequence is not
* (always) sorted on offset order. Perhaps we should introduce a
* second sequence which is sorted on offset order.
*/
end_siter = g_sequence_get_end_iter (level->seq);
for (siter = g_sequence_get_begin_iter (level->seq);
siter != end_siter;
siter = g_sequence_iter_next (siter))
{
SortElt *elt = g_sequence_get (siter);
if (elt->offset == offset)
break;
}
if (ret_siter)
*ret_siter = siter;
return GET_ELT (siter);
}
static void
gtk_tree_model_sort_row_changed (GtkTreeModel *s_model,
GtkTreePath *start_s_path,
GtkTreeIter *start_s_iter,
gpointer data)
{
GtkTreeModelSort *tree_model_sort = GTK_TREE_MODEL_SORT (data);
GtkTreeModelSortPrivate *priv = tree_model_sort->priv;
GtkTreePath *path = NULL;
GtkTreeIter iter;
GtkTreeIter tmpiter;
SortElt *elt;
SortLevel *level;
SortData sort_data;
gboolean free_s_path = FALSE;
gint index = 0, old_index;
g_return_if_fail (start_s_path != NULL || start_s_iter != NULL);
if (!start_s_path)
{
free_s_path = TRUE;
start_s_path = gtk_tree_model_get_path (s_model, start_s_iter);
}
path = gtk_real_tree_model_sort_convert_child_path_to_path (tree_model_sort,
start_s_path,
FALSE);
if (!path)
{
if (free_s_path)
gtk_tree_path_free (start_s_path);
return;
}
gtk_tree_model_get_iter (GTK_TREE_MODEL (data), &iter, path);
gtk_tree_model_sort_ref_node (GTK_TREE_MODEL (data), &iter);
level = iter.user_data;
elt = iter.user_data2;
if (g_sequence_get_length (level->seq) < 2 ||
(priv->sort_column_id == GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID &&
priv->default_sort_func == NO_SORT_FUNC))
{
if (free_s_path)
gtk_tree_path_free (start_s_path);
gtk_tree_model_row_changed (GTK_TREE_MODEL (data), path, &iter);
gtk_tree_model_sort_unref_node (GTK_TREE_MODEL (data), &iter);
gtk_tree_path_free (path);
return;
}
if (GTK_TREE_MODEL_SORT_CACHE_CHILD_ITERS (tree_model_sort))
tmpiter = elt->iter;
else
gtk_tree_model_get_iter (priv->child_model,
&tmpiter, start_s_path);
old_index = g_sequence_iter_get_position (elt->siter);
fill_sort_data (&sort_data, tree_model_sort, level);
g_sequence_sort_changed (elt->siter,
gtk_tree_model_sort_compare_func,
&sort_data);
free_sort_data (&sort_data);
index = g_sequence_iter_get_position (elt->siter);
/* Prepare the path for signal emission */
gtk_tree_path_up (path);
gtk_tree_path_append_index (path, index);
gtk_tree_model_sort_increment_stamp (tree_model_sort);
/* if the item moved, then emit rows_reordered */
if (old_index != index)
{
gint *new_order;
gint j;
GtkTreePath *tmppath;
new_order = g_new (gint, g_sequence_get_length (level->seq));
for (j = 0; j < g_sequence_get_length (level->seq); j++)
{
if (index > old_index)
{
if (j == index)
new_order[j] = old_index;
else if (j >= old_index && j < index)
new_order[j] = j + 1;
else
new_order[j] = j;
}
else if (index < old_index)
{
if (j == index)
new_order[j] = old_index;
else if (j > index && j <= old_index)
new_order[j] = j - 1;
else
new_order[j] = j;
}
/* else? shouldn't really happen */
}
if (level->parent_elt)
{
iter.stamp = priv->stamp;
iter.user_data = level->parent_level;
iter.user_data2 = level->parent_elt;
tmppath = gtk_tree_model_get_path (GTK_TREE_MODEL (tree_model_sort), &iter);
gtk_tree_model_rows_reordered (GTK_TREE_MODEL (tree_model_sort),
tmppath, &iter, new_order);
}
else
{
/* toplevel */
tmppath = gtk_tree_path_new ();
gtk_tree_model_rows_reordered (GTK_TREE_MODEL (tree_model_sort), tmppath,
NULL, new_order);
}
gtk_tree_path_free (tmppath);
g_free (new_order);
}
/* 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);
gtk_tree_model_sort_unref_node (GTK_TREE_MODEL (data), &iter);
gtk_tree_path_free (path);
if (free_s_path)
gtk_tree_path_free (start_s_path);
}
static void
gtk_tree_model_sort_row_inserted (GtkTreeModel *s_model,
GtkTreePath *s_path,
GtkTreeIter *s_iter,
gpointer data)
{
GtkTreeModelSort *tree_model_sort = GTK_TREE_MODEL_SORT (data);
GtkTreeModelSortPrivate *priv = tree_model_sort->priv;
GtkTreePath *path;
GtkTreeIter iter;
GtkTreeIter real_s_iter;
gint i = 0;
gboolean free_s_path = FALSE;
SortElt *elt;
SortLevel *level;
SortLevel *parent_level = NULL;
parent_level = level = SORT_LEVEL (priv->root);
g_return_if_fail (s_path != NULL || s_iter != NULL);
if (!s_path)
{
s_path = gtk_tree_model_get_path (s_model, s_iter);
free_s_path = TRUE;
}
if (!s_iter)
gtk_tree_model_get_iter (s_model, &real_s_iter, s_path);
else
real_s_iter = *s_iter;
if (!priv->root)
{
gtk_tree_model_sort_build_level (tree_model_sort, NULL, NULL);
/* the build level already put the inserted iter in the level,
so no need to handle this signal anymore */
goto done_and_submit;
}
/* find the parent level */
while (i < gtk_tree_path_get_depth (s_path) - 1)
{
if (!level)
{
/* level not yet build, we won't cover this signal */
goto done;
}
if (g_sequence_get_length (level->seq) < gtk_tree_path_get_indices (s_path)[i])
{
g_warning ("%s: A node was inserted with a parent that's not in the tree.\n"
"This possibly means that a GtkTreeModel inserted a child node\n"
"before the parent was inserted.",
G_STRLOC);
goto done;
}
elt = lookup_elt_with_offset (tree_model_sort, level,
gtk_tree_path_get_indices (s_path)[i],
NULL);
g_return_if_fail (elt != NULL);
if (!elt->children)
{
/* not covering this signal */
goto done;
}
level = elt->children;
parent_level = level;
i++;
}
if (!parent_level)
goto done;
if (level->ref_count == 0 && level != priv->root)
{
gtk_tree_model_sort_free_level (tree_model_sort, level, TRUE);
goto done;
}
if (!gtk_tree_model_sort_insert_value (tree_model_sort,
parent_level,
s_path,
&real_s_iter))
goto done;
done_and_submit:
path = gtk_real_tree_model_sort_convert_child_path_to_path (tree_model_sort,
s_path,
FALSE);
if (!path)
return;
gtk_tree_model_sort_increment_stamp (tree_model_sort);
gtk_tree_model_get_iter (GTK_TREE_MODEL (data), &iter, path);
gtk_tree_model_row_inserted (GTK_TREE_MODEL (data), path, &iter);
gtk_tree_path_free (path);
done:
if (free_s_path)
gtk_tree_path_free (s_path);
return;
}
static void
gtk_tree_model_sort_row_has_child_toggled (GtkTreeModel *s_model,
GtkTreePath *s_path,
GtkTreeIter *s_iter,
gpointer data)
{
GtkTreeModelSort *tree_model_sort = GTK_TREE_MODEL_SORT (data);
GtkTreePath *path;
GtkTreeIter iter;
g_return_if_fail (s_path != NULL && s_iter != NULL);
path = gtk_real_tree_model_sort_convert_child_path_to_path (tree_model_sort, s_path, FALSE);
if (path == NULL)
return;
gtk_tree_model_get_iter (GTK_TREE_MODEL (data), &iter, path);
gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (data), path, &iter);
gtk_tree_path_free (path);
}
static void
gtk_tree_model_sort_row_deleted (GtkTreeModel *s_model,
GtkTreePath *s_path,
gpointer data)
{
GtkTreeModelSort *tree_model_sort = GTK_TREE_MODEL_SORT (data);
GtkTreePath *path = NULL;
SortElt *elt;
SortLevel *level;
GtkTreeIter iter;
gint offset;
g_return_if_fail (s_path != NULL);
path = gtk_real_tree_model_sort_convert_child_path_to_path (tree_model_sort, s_path, FALSE);
if (path == NULL)
return;
gtk_tree_model_get_iter (GTK_TREE_MODEL (data), &iter, path);
level = SORT_LEVEL (iter.user_data);
elt = SORT_ELT (iter.user_data2);
offset = elt->offset;
gtk_tree_model_get_iter (GTK_TREE_MODEL (data), &iter, path);
while (elt->ref_count > 0)
gtk_tree_model_sort_real_unref_node (GTK_TREE_MODEL (data), &iter, FALSE);
/* If this node has children, we free the level (recursively) here
* and specify that unref may not be used, because parent and its
* children have been removed by now.
*/
if (elt->children)
gtk_tree_model_sort_free_level (tree_model_sort,
elt->children, FALSE);
if (level->ref_count == 0 && g_sequence_get_length (level->seq) == 1)
{
gtk_tree_model_sort_increment_stamp (tree_model_sort);
gtk_tree_model_row_deleted (GTK_TREE_MODEL (data), path);
gtk_tree_path_free (path);
if (level == tree_model_sort->priv->root)
{
gtk_tree_model_sort_free_level (tree_model_sort,
tree_model_sort->priv->root,
TRUE);
tree_model_sort->priv->root = NULL;
}
return;
}
g_sequence_remove (elt->siter);
elt = NULL;
/* The sequence is not ordered on offset, so we traverse the entire
* sequence.
*/
g_sequence_foreach (level->seq, decrease_offset_iter,
GINT_TO_POINTER (offset));
gtk_tree_model_sort_increment_stamp (tree_model_sort);
gtk_tree_model_row_deleted (GTK_TREE_MODEL (data), path);
gtk_tree_path_free (path);
}
static void
gtk_tree_model_sort_rows_reordered (GtkTreeModel *s_model,
GtkTreePath *s_path,
GtkTreeIter *s_iter,
gint *new_order,
gpointer data)
{
SortLevel *level;
GtkTreeIter iter;
GtkTreePath *path;
gint *tmp_array;
int i, length;
GSequenceIter *siter, *end_siter;
GtkTreeModelSort *tree_model_sort = GTK_TREE_MODEL_SORT (data);
GtkTreeModelSortPrivate *priv = tree_model_sort->priv;
g_return_if_fail (new_order != NULL);
if (s_path == NULL || gtk_tree_path_get_depth (s_path) == 0)
{
if (priv->root == NULL)
return;
path = gtk_tree_path_new ();
level = SORT_LEVEL (priv->root);
}
else
{
SortElt *elt;
path = gtk_real_tree_model_sort_convert_child_path_to_path (tree_model_sort, s_path, FALSE);
if (path == NULL)
return;
gtk_tree_model_get_iter (GTK_TREE_MODEL (data), &iter, path);
elt = SORT_ELT (iter.user_data2);
if (!elt->children)
{
gtk_tree_path_free (path);
return;
}
level = elt->children;
}
length = g_sequence_get_length (level->seq);
if (length < 2)
{
gtk_tree_path_free (path);
return;
}
tmp_array = g_new (int, length);
/* FIXME: I need to think about whether this can be done in a more
* efficient way?
*/
i = 0;
end_siter = g_sequence_get_end_iter (level->seq);
for (siter = g_sequence_get_begin_iter (level->seq);
siter != end_siter;
siter = g_sequence_iter_next (siter))
{
gint j;
SortElt *elt = g_sequence_get (siter);
for (j = 0; j < length; j++)
{
if (elt->offset == new_order[j])
tmp_array[i] = j;
}
i++;
}
/* This loop cannot be merged with the above loop nest, because that
* would introduce duplicate offsets.
*/
i = 0;
end_siter = g_sequence_get_end_iter (level->seq);
for (siter = g_sequence_get_begin_iter (level->seq);
siter != end_siter;
siter = g_sequence_iter_next (siter))
{
SortElt *elt = g_sequence_get (siter);
elt->offset = tmp_array[i];
i++;
}
g_free (tmp_array);
if (priv->sort_column_id == GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID &&
priv->default_sort_func == NO_SORT_FUNC)
{
gtk_tree_model_sort_sort_level (tree_model_sort, level,
FALSE, FALSE);
gtk_tree_model_sort_increment_stamp (tree_model_sort);
if (gtk_tree_path_get_depth (path))
{
gtk_tree_model_get_iter (GTK_TREE_MODEL (tree_model_sort),
&iter,
path);
gtk_tree_model_rows_reordered (GTK_TREE_MODEL (tree_model_sort),
path, &iter, new_order);
}
else
{
gtk_tree_model_rows_reordered (GTK_TREE_MODEL (tree_model_sort),
path, NULL, new_order);
}
}
gtk_tree_path_free (path);
}
/* Fulfill our model requirements */
static GtkTreeModelFlags
gtk_tree_model_sort_get_flags (GtkTreeModel *tree_model)
{
GtkTreeModelSort *tree_model_sort = (GtkTreeModelSort *) tree_model;
GtkTreeModelFlags flags;
g_return_val_if_fail (tree_model_sort->priv->child_model != NULL, 0);
flags = gtk_tree_model_get_flags (tree_model_sort->priv->child_model);
if ((flags & GTK_TREE_MODEL_LIST_ONLY) == GTK_TREE_MODEL_LIST_ONLY)
return GTK_TREE_MODEL_LIST_ONLY;
return 0;
}
static gint
gtk_tree_model_sort_get_n_columns (GtkTreeModel *tree_model)
{
GtkTreeModelSort *tree_model_sort = (GtkTreeModelSort *) tree_model;
if (tree_model_sort->priv->child_model == 0)
return 0;
return gtk_tree_model_get_n_columns (tree_model_sort->priv->child_model);
}
static GType
gtk_tree_model_sort_get_column_type (GtkTreeModel *tree_model,
gint index)
{
GtkTreeModelSort *tree_model_sort = (GtkTreeModelSort *) tree_model;
g_return_val_if_fail (tree_model_sort->priv->child_model != NULL, G_TYPE_INVALID);
return gtk_tree_model_get_column_type (tree_model_sort->priv->child_model, index);
}
static gboolean
gtk_tree_model_sort_get_iter (GtkTreeModel *tree_model,
GtkTreeIter *iter,
GtkTreePath *path)
{
GtkTreeModelSort *tree_model_sort = (GtkTreeModelSort *) tree_model;
GtkTreeModelSortPrivate *priv = tree_model_sort->priv;
gint *indices;
SortElt *elt;
SortLevel *level;
gint depth, i;
GSequenceIter *siter;
g_return_val_if_fail (priv->child_model != NULL, FALSE);
indices = gtk_tree_path_get_indices (path);
if (priv->root == NULL)
gtk_tree_model_sort_build_level (tree_model_sort, NULL, NULL);
level = SORT_LEVEL (priv->root);
depth = gtk_tree_path_get_depth (path);
if (depth == 0)
{
iter->stamp = 0;
return FALSE;
}
for (i = 0; i < depth - 1; i++)
{
if ((level == NULL) ||
(indices[i] >= g_sequence_get_length (level->seq)))
{
iter->stamp = 0;
return FALSE;
}
siter = g_sequence_get_iter_at_pos (level->seq, indices[i]);
if (g_sequence_iter_is_end (siter))
{
iter->stamp = 0;
return FALSE;
}
elt = GET_ELT (siter);
if (elt->children == NULL)
gtk_tree_model_sort_build_level (tree_model_sort, level, elt);
level = elt->children;
}
if (!level || indices[i] >= g_sequence_get_length (level->seq))
{
iter->stamp = 0;
return FALSE;
}
iter->stamp = priv->stamp;
iter->user_data = level;
siter = g_sequence_get_iter_at_pos (level->seq, indices[depth - 1]);
if (g_sequence_iter_is_end (siter))
{
iter->stamp = 0;
return FALSE;
}
iter->user_data2 = GET_ELT (siter);
return TRUE;
}
static GtkTreePath *
gtk_tree_model_sort_get_path (GtkTreeModel *tree_model,
GtkTreeIter *iter)
{
GtkTreeModelSort *tree_model_sort = (GtkTreeModelSort *) tree_model;
GtkTreeModelSortPrivate *priv = tree_model_sort->priv;
GtkTreePath *retval;
SortLevel *level;
SortElt *elt;
g_return_val_if_fail (priv->child_model != NULL, NULL);
g_return_val_if_fail (priv->stamp == iter->stamp, NULL);
retval = gtk_tree_path_new ();
level = SORT_LEVEL (iter->user_data);
elt = SORT_ELT (iter->user_data2);
while (level)
{
gint index;
index = g_sequence_iter_get_position (elt->siter);
gtk_tree_path_prepend_index (retval, index);
elt = level->parent_elt;
level = level->parent_level;
}
return retval;
}
static void
gtk_tree_model_sort_get_value (GtkTreeModel *tree_model,
GtkTreeIter *iter,
gint column,
GValue *value)
{
GtkTreeModelSort *tree_model_sort = (GtkTreeModelSort *) tree_model;
GtkTreeModelSortPrivate *priv = tree_model_sort->priv;
GtkTreeIter child_iter;
g_return_if_fail (priv->child_model != NULL);
g_return_if_fail (VALID_ITER (iter, tree_model_sort));
GET_CHILD_ITER (tree_model_sort, &child_iter, iter);
gtk_tree_model_get_value (priv->child_model,
&child_iter, column, value);
}
static gboolean
gtk_tree_model_sort_iter_next (GtkTreeModel *tree_model,
GtkTreeIter *iter)
{
GtkTreeModelSort *tree_model_sort = (GtkTreeModelSort *) tree_model;
GtkTreeModelSortPrivate *priv = tree_model_sort->priv;
SortElt *elt;
GSequenceIter *siter;
g_return_val_if_fail (priv->child_model != NULL, FALSE);
g_return_val_if_fail (priv->stamp == iter->stamp, FALSE);
elt = iter->user_data2;
siter = g_sequence_iter_next (elt->siter);
if (g_sequence_iter_is_end (siter))
{
iter->stamp = 0;
return FALSE;
}
iter->user_data2 = GET_ELT (siter);
return TRUE;
}
static gboolean
gtk_tree_model_sort_iter_previous (GtkTreeModel *tree_model,
GtkTreeIter *iter)
{
GtkTreeModelSort *tree_model_sort = (GtkTreeModelSort *) tree_model;
GtkTreeModelSortPrivate *priv = tree_model_sort->priv;
SortElt *elt;
GSequenceIter *siter;
g_return_val_if_fail (priv->child_model != NULL, FALSE);
g_return_val_if_fail (priv->stamp == iter->stamp, FALSE);
elt = iter->user_data2;
if (g_sequence_iter_is_begin (elt->siter))
{
iter->stamp = 0;
return FALSE;
}
siter = g_sequence_iter_prev (elt->siter);
iter->user_data2 = GET_ELT (siter);
return TRUE;
}
static gboolean
gtk_tree_model_sort_iter_children (GtkTreeModel *tree_model,
GtkTreeIter *iter,
GtkTreeIter *parent)
{
GtkTreeModelSort *tree_model_sort = (GtkTreeModelSort *) tree_model;
GtkTreeModelSortPrivate *priv = tree_model_sort->priv;
SortLevel *level;
iter->stamp = 0;
g_return_val_if_fail (priv->child_model != NULL, FALSE);
if (parent)
g_return_val_if_fail (VALID_ITER (parent, tree_model_sort), FALSE);
if (parent == NULL)
{
if (priv->root == NULL)
gtk_tree_model_sort_build_level (tree_model_sort, NULL, NULL);
if (priv->root == NULL)
return FALSE;
level = priv->root;
iter->stamp = priv->stamp;
iter->user_data = level;
iter->user_data2 = g_sequence_get (g_sequence_get_begin_iter (level->seq));
}
else
{
SortElt *elt;
level = SORT_LEVEL (parent->user_data);
elt = SORT_ELT (parent->user_data2);
if (elt->children == NULL)
gtk_tree_model_sort_build_level (tree_model_sort, level, elt);
if (elt->children == NULL)
return FALSE;
iter->stamp = priv->stamp;
iter->user_data = elt->children;
iter->user_data2 = g_sequence_get (g_sequence_get_begin_iter (elt->children->seq));
}
return TRUE;
}
static gboolean
gtk_tree_model_sort_iter_has_child (GtkTreeModel *tree_model,
GtkTreeIter *iter)
{
GtkTreeModelSort *tree_model_sort = (GtkTreeModelSort *) tree_model;
GtkTreeModelSortPrivate *priv = tree_model_sort->priv;
GtkTreeIter child_iter;
g_return_val_if_fail (priv->child_model != NULL, FALSE);
g_return_val_if_fail (VALID_ITER (iter, tree_model_sort), FALSE);
GET_CHILD_ITER (tree_model_sort, &child_iter, iter);
return gtk_tree_model_iter_has_child (priv->child_model, &child_iter);
}
static gint
gtk_tree_model_sort_iter_n_children (GtkTreeModel *tree_model,
GtkTreeIter *iter)
{
GtkTreeModelSort *tree_model_sort = (GtkTreeModelSort *) tree_model;
GtkTreeModelSortPrivate *priv = tree_model_sort->priv;
GtkTreeIter child_iter;
g_return_val_if_fail (priv->child_model != NULL, 0);
if (iter)
g_return_val_if_fail (VALID_ITER (iter, tree_model_sort), 0);
if (iter == NULL)
return gtk_tree_model_iter_n_children (priv->child_model, NULL);
GET_CHILD_ITER (tree_model_sort, &child_iter, iter);
return gtk_tree_model_iter_n_children (priv->child_model, &child_iter);
}
static gboolean
gtk_tree_model_sort_iter_nth_child (GtkTreeModel *tree_model,
GtkTreeIter *iter,
GtkTreeIter *parent,
gint n)
{
GtkTreeModelSort *tree_model_sort = (GtkTreeModelSort *) tree_model;
SortLevel *level;
/* We have this for the iter == parent case */
GtkTreeIter children;
if (parent)
g_return_val_if_fail (VALID_ITER (parent, tree_model_sort), FALSE);
/* Use this instead of has_child to force us to build the level, if needed */
if (gtk_tree_model_sort_iter_children (tree_model, &children, parent) == FALSE)
{
iter->stamp = 0;
return FALSE;
}
level = children.user_data;
if (n >= g_sequence_get_length (level->seq))
{
iter->stamp = 0;
return FALSE;
}
iter->stamp = tree_model_sort->priv->stamp;
iter->user_data = level;
iter->user_data2 = g_sequence_get (g_sequence_get_iter_at_pos (level->seq, n));
return TRUE;
}
static gboolean
gtk_tree_model_sort_iter_parent (GtkTreeModel *tree_model,
GtkTreeIter *iter,
GtkTreeIter *child)
{
GtkTreeModelSort *tree_model_sort = (GtkTreeModelSort *) tree_model;
GtkTreeModelSortPrivate *priv = tree_model_sort->priv;
SortLevel *level;
iter->stamp = 0;
g_return_val_if_fail (priv->child_model != NULL, FALSE);
g_return_val_if_fail (VALID_ITER (child, tree_model_sort), FALSE);
level = child->user_data;
if (level->parent_level)
{
iter->stamp = priv->stamp;
iter->user_data = level->parent_level;
iter->user_data2 = level->parent_elt;
return TRUE;
}
return FALSE;
}
static void
gtk_tree_model_sort_ref_node (GtkTreeModel *tree_model,
GtkTreeIter *iter)
{
GtkTreeModelSort *tree_model_sort = (GtkTreeModelSort *) tree_model;
GtkTreeModelSortPrivate *priv = tree_model_sort->priv;
GtkTreeIter child_iter;
SortLevel *level;
SortElt *elt;
g_return_if_fail (priv->child_model != NULL);
g_return_if_fail (VALID_ITER (iter, tree_model_sort));
GET_CHILD_ITER (tree_model_sort, &child_iter, iter);
/* Reference the node in the child model */
gtk_tree_model_ref_node (priv->child_model, &child_iter);
/* Increase the reference count of this element and its level */
level = iter->user_data;
elt = iter->user_data2;
elt->ref_count++;
level->ref_count++;
if (level->ref_count == 1)
{
SortLevel *parent_level = level->parent_level;
SortElt *parent_elt = level->parent_elt;
/* We were at zero -- time to decrement the zero_ref_count val */
while (parent_level)
{
parent_elt->zero_ref_count--;
parent_elt = parent_level->parent_elt;
parent_level = parent_level->parent_level;
}
if (priv->root != level)
priv->zero_ref_count--;
}
}
static void
gtk_tree_model_sort_real_unref_node (GtkTreeModel *tree_model,
GtkTreeIter *iter,
gboolean propagate_unref)
{
GtkTreeModelSort *tree_model_sort = (GtkTreeModelSort *) tree_model;
GtkTreeModelSortPrivate *priv = tree_model_sort->priv;
SortLevel *level;
SortElt *elt;
g_return_if_fail (priv->child_model != NULL);
g_return_if_fail (VALID_ITER (iter, tree_model_sort));
if (propagate_unref)
{
GtkTreeIter child_iter;
GET_CHILD_ITER (tree_model_sort, &child_iter, iter);
gtk_tree_model_unref_node (priv->child_model, &child_iter);
}
level = iter->user_data;
elt = iter->user_data2;
g_return_if_fail (elt->ref_count > 0);
elt->ref_count--;
level->ref_count--;
if (level->ref_count == 0)
{
SortLevel *parent_level = level->parent_level;
SortElt *parent_elt = level->parent_elt;
/* We are at zero -- time to increment the zero_ref_count val */
while (parent_level)
{
parent_elt->zero_ref_count++;
parent_elt = parent_level->parent_elt;
parent_level = parent_level->parent_level;
}
if (priv->root != level)
priv->zero_ref_count++;
}
}
static void
gtk_tree_model_sort_unref_node (GtkTreeModel *tree_model,
GtkTreeIter *iter)
{
gtk_tree_model_sort_real_unref_node (tree_model, iter, TRUE);
}
/* Sortable interface */
static gboolean
gtk_tree_model_sort_get_sort_column_id (GtkTreeSortable *sortable,
gint *sort_column_id,
GtkSortType *order)
{
GtkTreeModelSort *tree_model_sort = (GtkTreeModelSort *)sortable;
GtkTreeModelSortPrivate *priv = tree_model_sort->priv;
if (sort_column_id)
*sort_column_id = priv->sort_column_id;
if (order)
*order = priv->order;
if (priv->sort_column_id == GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID ||
priv->sort_column_id == GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID)
return FALSE;
return TRUE;
}
static void
gtk_tree_model_sort_set_sort_column_id (GtkTreeSortable *sortable,
gint sort_column_id,
GtkSortType order)
{
GtkTreeModelSort *tree_model_sort = (GtkTreeModelSort *)sortable;
GtkTreeModelSortPrivate *priv = tree_model_sort->priv;
if (sort_column_id != GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID)
{
if (sort_column_id != GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID)
{
GtkTreeDataSortHeader *header = NULL;
header = _gtk_tree_data_list_get_header (priv->sort_list,
sort_column_id);
/* we want to make sure that we have a function */
g_return_if_fail (header != NULL);
g_return_if_fail (header->func != NULL);
}
else
g_return_if_fail (priv->default_sort_func != NULL);
if (priv->sort_column_id == sort_column_id)
{
if (sort_column_id != GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID)
{
if (priv->order == order)
return;
}
else
return;
}
}
priv->sort_column_id = sort_column_id;
priv->order = order;
gtk_tree_sortable_sort_column_changed (sortable);
gtk_tree_model_sort_sort (tree_model_sort);
}
static void
gtk_tree_model_sort_set_sort_func (GtkTreeSortable *sortable,
gint sort_column_id,
GtkTreeIterCompareFunc func,
gpointer data,
GDestroyNotify destroy)
{
GtkTreeModelSort *tree_model_sort = (GtkTreeModelSort *) sortable;
GtkTreeModelSortPrivate *priv = tree_model_sort->priv;
priv->sort_list = _gtk_tree_data_list_set_header (priv->sort_list,
sort_column_id,
func, data, destroy);
if (priv->sort_column_id == sort_column_id)
gtk_tree_model_sort_sort (tree_model_sort);
}
static void
gtk_tree_model_sort_set_default_sort_func (GtkTreeSortable *sortable,
GtkTreeIterCompareFunc func,
gpointer data,
GDestroyNotify destroy)
{
GtkTreeModelSort *tree_model_sort = (GtkTreeModelSort *)sortable;
GtkTreeModelSortPrivate *priv = tree_model_sort->priv;
if (priv->default_sort_destroy)
{
GDestroyNotify d = priv->default_sort_destroy;
priv->default_sort_destroy = NULL;
d (priv->default_sort_data);
}
priv->default_sort_func = func;
priv->default_sort_data = data;
priv->default_sort_destroy = destroy;
if (priv->sort_column_id == GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID)
gtk_tree_model_sort_sort (tree_model_sort);
}
static gboolean
gtk_tree_model_sort_has_default_sort_func (GtkTreeSortable *sortable)
{
GtkTreeModelSort *tree_model_sort = (GtkTreeModelSort *)sortable;
return (tree_model_sort->priv->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;
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->priv->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;
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->priv->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;
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->priv->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,
gconstpointer b,
gpointer user_data)
{
SortData *data = (SortData *)user_data;
GtkTreeModelSort *tree_model_sort = data->tree_model_sort;
GtkTreeModelSortPrivate *priv = tree_model_sort->priv;
const SortElt *sa = a;
const SortElt *sb = b;
GtkTreeIter iter_a, iter_b;
gint retval;
if (GTK_TREE_MODEL_SORT_CACHE_CHILD_ITERS (tree_model_sort))
{
iter_a = sa->iter;
iter_b = sb->iter;
}
else
{
data->parent_path_indices [data->parent_path_depth-1] = sa->offset;
gtk_tree_model_get_iter (GTK_TREE_MODEL (priv->child_model), &iter_a, data->parent_path);
data->parent_path_indices [data->parent_path_depth-1] = sb->offset;
gtk_tree_model_get_iter (GTK_TREE_MODEL (priv->child_model), &iter_b, data->parent_path);
}
retval = (* data->sort_func) (GTK_TREE_MODEL (priv->child_model),
&iter_a, &iter_b,
data->sort_data);
if (priv->order == GTK_SORT_DESCENDING)
{
if (retval > 0)
retval = -1;
else if (retval < 0)
retval = 1;
}
return retval;
}
static gint
gtk_tree_model_sort_offset_compare_func (gconstpointer a,
gconstpointer b,
gpointer user_data)
{
gint retval;
const SortElt *sa = (SortElt *)a;
const SortElt *sb = (SortElt *)b;
SortData *data = (SortData *)user_data;
if (sa->offset < sb->offset)
retval = -1;
else if (sa->offset > sb->offset)
retval = 1;
else
retval = 0;
if (data->tree_model_sort->priv->order == GTK_SORT_DESCENDING)
{
if (retval > 0)
retval = -1;
else if (retval < 0)
retval = 1;
}
return retval;
}
static void
gtk_tree_model_sort_sort_level (GtkTreeModelSort *tree_model_sort,
SortLevel *level,
gboolean recurse,
gboolean emit_reordered)
{
GtkTreeModelSortPrivate *priv = tree_model_sort->priv;
gint i;
GSequenceIter *begin_siter, *end_siter, *siter;
SortElt *begin_elt;
gint *new_order;
GtkTreeIter iter;
GtkTreePath *path;
SortData data;
g_return_if_fail (level != NULL);
begin_siter = g_sequence_get_begin_iter (level->seq);
begin_elt = g_sequence_get (begin_siter);
if (g_sequence_get_length (level->seq) < 1 && !begin_elt->children)
return;
iter.stamp = priv->stamp;
iter.user_data = level;
iter.user_data2 = begin_elt;
gtk_tree_model_sort_ref_node (GTK_TREE_MODEL (tree_model_sort), &iter);
i = 0;
end_siter = g_sequence_get_end_iter (level->seq);
for (siter = g_sequence_get_begin_iter (level->seq);
siter != end_siter;
siter = g_sequence_iter_next (siter))
{
SortElt *elt = g_sequence_get (siter);
elt->old_index = i;
i++;
}
fill_sort_data (&data, tree_model_sort, level);
if (data.sort_func == NO_SORT_FUNC)
g_sequence_sort (level->seq, gtk_tree_model_sort_offset_compare_func,
&data);
else
g_sequence_sort (level->seq, gtk_tree_model_sort_compare_func, &data);
free_sort_data (&data);
new_order = g_new (gint, g_sequence_get_length (level->seq));
i = 0;
end_siter = g_sequence_get_end_iter (level->seq);
for (siter = g_sequence_get_begin_iter (level->seq);
siter != end_siter;
siter = g_sequence_iter_next (siter))
{
SortElt *elt = g_sequence_get (siter);
new_order[i++] = elt->old_index;
}
if (emit_reordered)
{
gtk_tree_model_sort_increment_stamp (tree_model_sort);
if (level->parent_elt)
{
iter.stamp = priv->stamp;
iter.user_data = level->parent_level;
iter.user_data2 = level->parent_elt;
path = gtk_tree_model_get_path (GTK_TREE_MODEL (tree_model_sort),
&iter);
gtk_tree_model_rows_reordered (GTK_TREE_MODEL (tree_model_sort), path,
&iter, new_order);
}
else
{
/* toplevel list */
path = gtk_tree_path_new ();
gtk_tree_model_rows_reordered (GTK_TREE_MODEL (tree_model_sort), path,
NULL, new_order);
}
gtk_tree_path_free (path);
}
/* recurse, if possible */
if (recurse)
{
end_siter = g_sequence_get_end_iter (level->seq);
for (siter = g_sequence_get_begin_iter (level->seq);
siter != end_siter;
siter = g_sequence_iter_next (siter))
{
SortElt *elt = g_sequence_get (siter);
if (elt->children)
gtk_tree_model_sort_sort_level (tree_model_sort,
elt->children,
TRUE, emit_reordered);
}
}
g_free (new_order);
/* get the iter we referenced at the beginning of this function and
* unref it again
*/
iter.stamp = priv->stamp;
iter.user_data = level;
iter.user_data2 = begin_elt;
gtk_tree_model_sort_unref_node (GTK_TREE_MODEL (tree_model_sort), &iter);
}
static void
gtk_tree_model_sort_sort (GtkTreeModelSort *tree_model_sort)
{
GtkTreeModelSortPrivate *priv = tree_model_sort->priv;
if (priv->sort_column_id == GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID)
return;
if (!priv->root)
return;
if (priv->sort_column_id != GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID)
{
GtkTreeDataSortHeader *header = NULL;
header = _gtk_tree_data_list_get_header (priv->sort_list,
priv->sort_column_id);
/* we want to make sure that we have a function */
g_return_if_fail (header != NULL);
g_return_if_fail (header->func != NULL);
}
else
g_return_if_fail (priv->default_sort_func != NULL);
gtk_tree_model_sort_sort_level (tree_model_sort, priv->root,
TRUE, TRUE);
}
/* signal helpers */
static gboolean
gtk_tree_model_sort_insert_value (GtkTreeModelSort *tree_model_sort,
SortLevel *level,
GtkTreePath *s_path,
GtkTreeIter *s_iter)
{
GtkTreeModelSortPrivate *priv = tree_model_sort->priv;
SortElt *elt;
SortData data;
gint offset;
elt = sort_elt_new ();
offset = gtk_tree_path_get_indices (s_path)[gtk_tree_path_get_depth (s_path) - 1];
if (GTK_TREE_MODEL_SORT_CACHE_CHILD_ITERS (tree_model_sort))
elt->iter = *s_iter;
elt->offset = offset;
elt->zero_ref_count = 0;
elt->ref_count = 0;
elt->children = NULL;
/* update all larger offsets */
g_sequence_foreach (level->seq, increase_offset_iter, GINT_TO_POINTER (offset));
fill_sort_data (&data, tree_model_sort, level);
if (priv->sort_column_id == GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID &&
priv->default_sort_func == NO_SORT_FUNC)
{
elt->siter = g_sequence_insert_sorted (level->seq, elt,
gtk_tree_model_sort_offset_compare_func,
&data);
}
else
{
elt->siter = g_sequence_insert_sorted (level->seq, elt,
gtk_tree_model_sort_compare_func,
&data);
}
free_sort_data (&data);
return TRUE;
}
/* sort elt stuff */
static GtkTreePath *
gtk_tree_model_sort_elt_get_path (SortLevel *level,
SortElt *elt)
{
SortLevel *walker = level;
SortElt *walker2 = elt;
GtkTreePath *path;
g_return_val_if_fail (level != NULL, NULL);
g_return_val_if_fail (elt != NULL, NULL);
path = gtk_tree_path_new ();
while (walker)
{
gtk_tree_path_prepend_index (path, walker2->offset);
if (!walker->parent_level)
break;
walker2 = walker->parent_elt;
walker = walker->parent_level;
}
return path;
}
/**
* gtk_tree_model_sort_set_model:
* @tree_model_sort: The #GtkTreeModelSort.
* @child_model: (allow-none): 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.
**/
static void
gtk_tree_model_sort_set_model (GtkTreeModelSort *tree_model_sort,
GtkTreeModel *child_model)
{
GtkTreeModelSortPrivate *priv = tree_model_sort->priv;
if (child_model)
g_object_ref (child_model);
if (priv->child_model)
{
g_signal_handler_disconnect (priv->child_model,
priv->changed_id);
g_signal_handler_disconnect (priv->child_model,
priv->inserted_id);
g_signal_handler_disconnect (priv->child_model,
priv->has_child_toggled_id);
g_signal_handler_disconnect (priv->child_model,
priv->deleted_id);
g_signal_handler_disconnect (priv->child_model,
priv->reordered_id);
/* reset our state */
if (priv->root)
gtk_tree_model_sort_free_level (tree_model_sort, priv->root, TRUE);
priv->root = NULL;
_gtk_tree_data_list_header_free (priv->sort_list);
priv->sort_list = NULL;
g_object_unref (priv->child_model);
}
priv->child_model = child_model;
if (child_model)
{
GType *types;
gint i, n_columns;
priv->changed_id =
g_signal_connect (child_model, "row-changed",
G_CALLBACK (gtk_tree_model_sort_row_changed),
tree_model_sort);
priv->inserted_id =
g_signal_connect (child_model, "row-inserted",
G_CALLBACK (gtk_tree_model_sort_row_inserted),
tree_model_sort);
priv->has_child_toggled_id =
g_signal_connect (child_model, "row-has-child-toggled",
G_CALLBACK (gtk_tree_model_sort_row_has_child_toggled),
tree_model_sort);
priv->deleted_id =
g_signal_connect (child_model, "row-deleted",
G_CALLBACK (gtk_tree_model_sort_row_deleted),
tree_model_sort);
priv->reordered_id =
g_signal_connect (child_model, "rows-reordered",
G_CALLBACK (gtk_tree_model_sort_rows_reordered),
tree_model_sort);
priv->child_flags = gtk_tree_model_get_flags (child_model);
n_columns = gtk_tree_model_get_n_columns (child_model);
types = g_new (GType, n_columns);
for (i = 0; i < n_columns; i++)
types[i] = gtk_tree_model_get_column_type (child_model, i);
priv->sort_list = _gtk_tree_data_list_header_new (n_columns, types);
g_free (types);
priv->default_sort_func = NO_SORT_FUNC;
priv->stamp = g_random_int ();
}
}
/**
* gtk_tree_model_sort_get_model:
* @tree_model: a #GtkTreeModelSort
*
* Returns the model the #GtkTreeModelSort is sorting.
*
* Returns: (transfer none): the "child model" being sorted
**/
GtkTreeModel *
gtk_tree_model_sort_get_model (GtkTreeModelSort *tree_model)
{
g_return_val_if_fail (GTK_IS_TREE_MODEL_SORT (tree_model), NULL);
return tree_model->priv->child_model;
}
static GtkTreePath *
gtk_real_tree_model_sort_convert_child_path_to_path (GtkTreeModelSort *tree_model_sort,
GtkTreePath *child_path,
gboolean build_levels)
{
GtkTreeModelSortPrivate *priv = tree_model_sort->priv;
gint *child_indices;
GtkTreePath *retval;
SortLevel *level;
gint i;
g_return_val_if_fail (priv->child_model != NULL, NULL);
g_return_val_if_fail (child_path != NULL, NULL);
retval = gtk_tree_path_new ();
child_indices = gtk_tree_path_get_indices (child_path);
if (priv->root == NULL && build_levels)
gtk_tree_model_sort_build_level (tree_model_sort, NULL, NULL);
level = SORT_LEVEL (priv->root);
for (i = 0; i < gtk_tree_path_get_depth (child_path); i++)
{
SortElt *tmp;
GSequenceIter *siter;
gboolean found_child = FALSE;
if (!level)
{
gtk_tree_path_free (retval);
return NULL;
}
if (child_indices[i] >= g_sequence_get_length (level->seq))
{
gtk_tree_path_free (retval);
return NULL;
}
tmp = lookup_elt_with_offset (tree_model_sort, level,
child_indices[i], &siter);
if (tmp)
{
gtk_tree_path_append_index (retval, g_sequence_iter_get_position (siter));
if (tmp->children == NULL && build_levels)
gtk_tree_model_sort_build_level (tree_model_sort, level, tmp);
level = tmp->children;
found_child = TRUE;
}
if (! found_child)
{
gtk_tree_path_free (retval);
return NULL;
}
}
return retval;
}
/**
* gtk_tree_model_sort_convert_child_path_to_path:
* @tree_model_sort: A #GtkTreeModelSort
* @child_path: A #GtkTreePath to convert
*
* 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 isnt a valid
* path on the child model, then %NULL is returned.
*
* Returns: (nullable) (transfer full): A newly allocated #GtkTreePath, or %NULL
**/
GtkTreePath *
gtk_tree_model_sort_convert_child_path_to_path (GtkTreeModelSort *tree_model_sort,
GtkTreePath *child_path)
{
g_return_val_if_fail (GTK_IS_TREE_MODEL_SORT (tree_model_sort), NULL);
g_return_val_if_fail (tree_model_sort->priv->child_model != NULL, NULL);
g_return_val_if_fail (child_path != NULL, NULL);
return gtk_real_tree_model_sort_convert_child_path_to_path (tree_model_sort, child_path, TRUE);
}
/**
* gtk_tree_model_sort_convert_child_iter_to_iter:
* @tree_model_sort: A #GtkTreeModelSort
* @sort_iter: (out): An uninitialized #GtkTreeIter.
* @child_iter: A valid #GtkTreeIter pointing to a row on the child model
*
* Sets @sort_iter to point to the row in @tree_model_sort that corresponds to
* the row pointed at by @child_iter. If @sort_iter was not set, %FALSE
* is returned. Note: a boolean is only returned since 2.14.
*
* Returns: %TRUE, if @sort_iter was set, i.e. if @sort_iter is a
* valid iterator pointer to a visible row in the child model.
**/
gboolean
gtk_tree_model_sort_convert_child_iter_to_iter (GtkTreeModelSort *tree_model_sort,
GtkTreeIter *sort_iter,
GtkTreeIter *child_iter)
{
gboolean ret;
GtkTreePath *child_path, *path;
GtkTreeModelSortPrivate *priv = tree_model_sort->priv;
g_return_val_if_fail (GTK_IS_TREE_MODEL_SORT (tree_model_sort), FALSE);
g_return_val_if_fail (priv->child_model != NULL, FALSE);
g_return_val_if_fail (sort_iter != NULL, FALSE);
g_return_val_if_fail (child_iter != NULL, FALSE);
g_return_val_if_fail (sort_iter != child_iter, FALSE);
sort_iter->stamp = 0;
child_path = gtk_tree_model_get_path (priv->child_model, child_iter);
g_return_val_if_fail (child_path != NULL, FALSE);
path = gtk_tree_model_sort_convert_child_path_to_path (tree_model_sort, child_path);
gtk_tree_path_free (child_path);
if (!path)
{
g_warning ("%s: The conversion of the child path to a GtkTreeModel sort path failed", G_STRLOC);
return FALSE;
}
ret = gtk_tree_model_get_iter (GTK_TREE_MODEL (tree_model_sort),
sort_iter, path);
gtk_tree_path_free (path);
return ret;
}
/**
* gtk_tree_model_sort_convert_path_to_child_path:
* @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.
*
* Returns: (nullable) (transfer full): A newly allocated #GtkTreePath, or %NULL
**/
GtkTreePath *
gtk_tree_model_sort_convert_path_to_child_path (GtkTreeModelSort *tree_model_sort,
GtkTreePath *sorted_path)
{
GtkTreeModelSortPrivate *priv = tree_model_sort->priv;
gint *sorted_indices;
GtkTreePath *retval;
SortLevel *level;
gint i;
g_return_val_if_fail (GTK_IS_TREE_MODEL_SORT (tree_model_sort), NULL);
g_return_val_if_fail (priv->child_model != NULL, NULL);
g_return_val_if_fail (sorted_path != NULL, NULL);
retval = gtk_tree_path_new ();
sorted_indices = gtk_tree_path_get_indices (sorted_path);
if (priv->root == NULL)
gtk_tree_model_sort_build_level (tree_model_sort, NULL, NULL);
level = SORT_LEVEL (priv->root);
for (i = 0; i < gtk_tree_path_get_depth (sorted_path); i++)
{
SortElt *elt = NULL;
GSequenceIter *siter;
if ((level == NULL) ||
(g_sequence_get_length (level->seq) <= sorted_indices[i]))
{
gtk_tree_path_free (retval);
return NULL;
}
siter = g_sequence_get_iter_at_pos (level->seq, sorted_indices[i]);
if (g_sequence_iter_is_end (siter))
{
gtk_tree_path_free (retval);
return NULL;
}
elt = GET_ELT (siter);
if (elt->children == NULL)
gtk_tree_model_sort_build_level (tree_model_sort, level, elt);
if (level == NULL)
{
gtk_tree_path_free (retval);
break;
}
gtk_tree_path_append_index (retval, elt->offset);
level = elt->children;
}
return retval;
}
/**
* gtk_tree_model_sort_convert_iter_to_child_iter:
* @tree_model_sort: A #GtkTreeModelSort
* @child_iter: (out): An uninitialized #GtkTreeIter
* @sorted_iter: A valid #GtkTreeIter pointing to a row on @tree_model_sort.
*
* Sets @child_iter to point to the row pointed to by @sorted_iter.
**/
void
gtk_tree_model_sort_convert_iter_to_child_iter (GtkTreeModelSort *tree_model_sort,
GtkTreeIter *child_iter,
GtkTreeIter *sorted_iter)
{
GtkTreeModelSortPrivate *priv = tree_model_sort->priv;
g_return_if_fail (GTK_IS_TREE_MODEL_SORT (tree_model_sort));
g_return_if_fail (priv->child_model != NULL);
g_return_if_fail (child_iter != NULL);
g_return_if_fail (VALID_ITER (sorted_iter, tree_model_sort));
g_return_if_fail (sorted_iter != child_iter);
if (GTK_TREE_MODEL_SORT_CACHE_CHILD_ITERS (tree_model_sort))
{
*child_iter = SORT_ELT (sorted_iter->user_data2)->iter;
}
else
{
GtkTreePath *path;
gboolean valid = FALSE;
path = gtk_tree_model_sort_elt_get_path (sorted_iter->user_data,
sorted_iter->user_data2);
valid = gtk_tree_model_get_iter (priv->child_model, child_iter, path);
gtk_tree_path_free (path);
g_return_if_fail (valid == TRUE);
}
}
static void
gtk_tree_model_sort_build_level (GtkTreeModelSort *tree_model_sort,
SortLevel *parent_level,
SortElt *parent_elt)
{
GtkTreeModelSortPrivate *priv = tree_model_sort->priv;
GtkTreeIter iter;
SortLevel *new_level;
gint length = 0;
gint i;
g_assert (priv->child_model != NULL);
if (parent_level == NULL)
{
if (gtk_tree_model_get_iter_first (priv->child_model, &iter) == FALSE)
return;
length = gtk_tree_model_iter_n_children (priv->child_model, NULL);
}
else
{
GtkTreeIter parent_iter;
GtkTreeIter child_parent_iter;
parent_iter.stamp = priv->stamp;
parent_iter.user_data = parent_level;
parent_iter.user_data2 = parent_elt;
gtk_tree_model_sort_convert_iter_to_child_iter (tree_model_sort,
&child_parent_iter,
&parent_iter);
if (gtk_tree_model_iter_children (priv->child_model,
&iter,
&child_parent_iter) == FALSE)
return;
/* stamp may have changed */
gtk_tree_model_sort_convert_iter_to_child_iter (tree_model_sort,
&child_parent_iter,
&parent_iter);
length = gtk_tree_model_iter_n_children (priv->child_model, &child_parent_iter);
gtk_tree_model_sort_ref_node (GTK_TREE_MODEL (tree_model_sort),
&parent_iter);
}
g_return_if_fail (length > 0);
new_level = g_new (SortLevel, 1);
new_level->seq = g_sequence_new (sort_elt_free);
new_level->ref_count = 0;
new_level->parent_level = parent_level;
new_level->parent_elt = parent_elt;
if (parent_elt)
parent_elt->children = new_level;
else
priv->root = new_level;
/* increase the count of zero ref_counts.*/
while (parent_level)
{
parent_elt->zero_ref_count++;
parent_elt = parent_level->parent_elt;
parent_level = parent_level->parent_level;
}
if (new_level != priv->root)
priv->zero_ref_count++;
for (i = 0; i < length; i++)
{
SortElt *sort_elt;
sort_elt = sort_elt_new ();
sort_elt->offset = i;
sort_elt->zero_ref_count = 0;
sort_elt->ref_count = 0;
sort_elt->children = NULL;
if (GTK_TREE_MODEL_SORT_CACHE_CHILD_ITERS (tree_model_sort))
{
sort_elt->iter = iter;
if (gtk_tree_model_iter_next (priv->child_model, &iter) == FALSE &&
i < length - 1)
{
if (parent_level)
{
GtkTreePath *level;
gchar *str;
level = gtk_tree_model_sort_elt_get_path (parent_level,
parent_elt);
str = gtk_tree_path_to_string (level);
gtk_tree_path_free (level);
g_warning ("%s: There is a discrepancy between the sort model "
"and the child model. The child model is "
"advertising a wrong length for level %s:.",
G_STRLOC, str);
g_free (str);
}
else
{
g_warning ("%s: There is a discrepancy between the sort model "
"and the child model. The child model is "
"advertising a wrong length for the root level.",
G_STRLOC);
}
return;
}
}
sort_elt->siter = g_sequence_append (new_level->seq, sort_elt);
}
/* sort level */
gtk_tree_model_sort_sort_level (tree_model_sort, new_level,
FALSE, FALSE);
}
static void
gtk_tree_model_sort_free_level (GtkTreeModelSort *tree_model_sort,
SortLevel *sort_level,
gboolean unref)
{
GtkTreeModelSortPrivate *priv = tree_model_sort->priv;
GSequenceIter *siter;
GSequenceIter *end_siter;
g_assert (sort_level);
end_siter = g_sequence_get_end_iter (sort_level->seq);
for (siter = g_sequence_get_begin_iter (sort_level->seq);
siter != end_siter;
siter = g_sequence_iter_next (siter))
{
SortElt *elt = g_sequence_get (siter);
if (elt->children)
gtk_tree_model_sort_free_level (tree_model_sort,
elt->children, unref);
}
if (sort_level->ref_count == 0)
{
SortLevel *parent_level = sort_level->parent_level;
SortElt *parent_elt = sort_level->parent_elt;
while (parent_level)
{
parent_elt->zero_ref_count--;
parent_elt = parent_level->parent_elt;
parent_level = parent_level->parent_level;
}
if (sort_level != priv->root)
priv->zero_ref_count--;
}
if (sort_level->parent_elt)
{
if (unref)
{
GtkTreeIter parent_iter;
parent_iter.stamp = tree_model_sort->priv->stamp;
parent_iter.user_data = sort_level->parent_level;
parent_iter.user_data2 = sort_level->parent_elt;
gtk_tree_model_sort_unref_node (GTK_TREE_MODEL (tree_model_sort),
&parent_iter);
}
sort_level->parent_elt->children = NULL;
}
else
priv->root = NULL;
g_sequence_free (sort_level->seq);
sort_level->seq = NULL;
g_free (sort_level);
sort_level = NULL;
}
static void
gtk_tree_model_sort_increment_stamp (GtkTreeModelSort *tree_model_sort)
{
GtkTreeModelSortPrivate *priv = tree_model_sort->priv;
do
{
priv->stamp++;
}
while (priv->stamp == 0);
gtk_tree_model_sort_clear_cache (tree_model_sort);
}
static void
gtk_tree_model_sort_clear_cache_helper_iter (gpointer data,
gpointer user_data)
{
GtkTreeModelSort *tree_model_sort = user_data;
SortElt *elt = data;
if (elt->zero_ref_count > 0)
gtk_tree_model_sort_clear_cache_helper (tree_model_sort, elt->children);
}
static void
gtk_tree_model_sort_clear_cache_helper (GtkTreeModelSort *tree_model_sort,
SortLevel *level)
{
g_assert (level != NULL);
g_sequence_foreach (level->seq, gtk_tree_model_sort_clear_cache_helper_iter,
tree_model_sort);
if (level->ref_count == 0 && level != tree_model_sort->priv->root)
gtk_tree_model_sort_free_level (tree_model_sort, level, TRUE);
}
/**
* gtk_tree_model_sort_reset_default_sort_func:
* @tree_model_sort: A #GtkTreeModelSort
*
* This resets the default sort function to be in the “unsorted” state. That
* is, it is in the same order as the child model. It will re-sort the model
* to be in the same order as the child model only if the #GtkTreeModelSort
* is in “unsorted” state.
**/
void
gtk_tree_model_sort_reset_default_sort_func (GtkTreeModelSort *tree_model_sort)
{
GtkTreeModelSortPrivate *priv = tree_model_sort->priv;
g_return_if_fail (GTK_IS_TREE_MODEL_SORT (tree_model_sort));
if (priv->default_sort_destroy)
{
GDestroyNotify d = priv->default_sort_destroy;
priv->default_sort_destroy = NULL;
d (priv->default_sort_data);
}
priv->default_sort_func = NO_SORT_FUNC;
priv->default_sort_data = NULL;
priv->default_sort_destroy = NULL;
if (priv->sort_column_id == GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID)
gtk_tree_model_sort_sort (tree_model_sort);
priv->sort_column_id = GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID;
}
/**
* gtk_tree_model_sort_clear_cache:
* @tree_model_sort: A #GtkTreeModelSort
*
* This function should almost never be called. It clears the @tree_model_sort
* of any cached iterators that havent been reffed with
* gtk_tree_model_ref_node(). This might be useful if the child model being
* sorted is static (and doesnt change often) and there has been a lot of
* unreffed access to nodes. As a side effect of this function, all unreffed
* iters will be invalid.
**/
void
gtk_tree_model_sort_clear_cache (GtkTreeModelSort *tree_model_sort)
{
g_return_if_fail (GTK_IS_TREE_MODEL_SORT (tree_model_sort));
if (tree_model_sort->priv->zero_ref_count > 0)
gtk_tree_model_sort_clear_cache_helper (tree_model_sort, (SortLevel *)tree_model_sort->priv->root);
}
static gboolean
gtk_tree_model_sort_iter_is_valid_helper (GtkTreeIter *iter,
SortLevel *level)
{
GSequenceIter *siter;
GSequenceIter *end_siter;
end_siter = g_sequence_get_end_iter (level->seq);
for (siter = g_sequence_get_begin_iter (level->seq);
siter != end_siter; siter = g_sequence_iter_next (siter))
{
SortElt *elt = g_sequence_get (siter);
if (iter->user_data == level && iter->user_data2 == elt)
return TRUE;
if (elt->children)
if (gtk_tree_model_sort_iter_is_valid_helper (iter, elt->children))
return TRUE;
}
return FALSE;
}
/**
* gtk_tree_model_sort_iter_is_valid:
* @tree_model_sort: A #GtkTreeModelSort.
* @iter: A #GtkTreeIter.
*
* > This function is slow. Only use it for debugging and/or testing
* > purposes.
*
* Checks if the given iter is a valid iter for this #GtkTreeModelSort.
*
* Returns: %TRUE if the iter is valid, %FALSE if the iter is invalid.
*
* Since: 2.2
**/
gboolean
gtk_tree_model_sort_iter_is_valid (GtkTreeModelSort *tree_model_sort,
GtkTreeIter *iter)
{
g_return_val_if_fail (GTK_IS_TREE_MODEL_SORT (tree_model_sort), FALSE);
g_return_val_if_fail (iter != NULL, FALSE);
if (!VALID_ITER (iter, tree_model_sort))
return FALSE;
return gtk_tree_model_sort_iter_is_valid_helper (iter,
tree_model_sort->priv->root);
}