forked from AuroraMiddleware/gtk
5e256590db
This includes GtkCellArea GtkCellAreaBox GtkCellAreaContext GtkCellEditable GtkCellRenderer GtkCellRendererAccel GtkCellRendererCombo GtkCellRendererPixbuf GtkCellRendererProgress GtkCellRendererSpin GtkCellRendererSpinner GtkCellRendererText GtkCellRendererToggle GtkCellView GtkComboBox GtkComboBoxText GtkIconView GtkListStore GtkTreeModel GtkTreeModelFilter GtkTreeModelSort GtkTreeStore GtkTreeView GtkTreeViewColumn GtkTreeSelection
575 lines
15 KiB
C
575 lines
15 KiB
C
/* gtktreedatalist.c
|
|
* Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford <jrb@redhat.com>
|
|
*
|
|
* 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/>.
|
|
*
|
|
* This file contains code shared between GtkTreeStore and GtkListStore. Please
|
|
* do not use it.
|
|
*/
|
|
|
|
#include "config.h"
|
|
#include "gtktreedatalistprivate.h"
|
|
#include <string.h>
|
|
|
|
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
|
|
|
|
/* node allocation
|
|
*/
|
|
GtkTreeDataList *
|
|
_gtk_tree_data_list_alloc (void)
|
|
{
|
|
GtkTreeDataList *list;
|
|
|
|
list = g_slice_new0 (GtkTreeDataList);
|
|
|
|
return list;
|
|
}
|
|
|
|
void
|
|
_gtk_tree_data_list_free (GtkTreeDataList *list,
|
|
GType *column_headers)
|
|
{
|
|
GtkTreeDataList *tmp, *next;
|
|
int i = 0;
|
|
|
|
tmp = list;
|
|
|
|
while (tmp)
|
|
{
|
|
next = tmp->next;
|
|
if (g_type_is_a (column_headers [i], G_TYPE_STRING))
|
|
g_free ((char *) tmp->data.v_pointer);
|
|
else if (g_type_is_a (column_headers [i], G_TYPE_OBJECT) && tmp->data.v_pointer != NULL)
|
|
g_object_unref (tmp->data.v_pointer);
|
|
else if (g_type_is_a (column_headers [i], G_TYPE_BOXED) && tmp->data.v_pointer != NULL)
|
|
g_boxed_free (column_headers [i], (gpointer) tmp->data.v_pointer);
|
|
else if (g_type_is_a (column_headers [i], G_TYPE_VARIANT) && tmp->data.v_pointer != NULL)
|
|
g_variant_unref ((gpointer) tmp->data.v_pointer);
|
|
|
|
g_slice_free (GtkTreeDataList, tmp);
|
|
i++;
|
|
tmp = next;
|
|
}
|
|
}
|
|
|
|
gboolean
|
|
_gtk_tree_data_list_check_type (GType type)
|
|
{
|
|
int i = 0;
|
|
static const GType type_list[] =
|
|
{
|
|
G_TYPE_BOOLEAN,
|
|
G_TYPE_CHAR,
|
|
G_TYPE_UCHAR,
|
|
G_TYPE_INT,
|
|
G_TYPE_UINT,
|
|
G_TYPE_LONG,
|
|
G_TYPE_ULONG,
|
|
G_TYPE_INT64,
|
|
G_TYPE_UINT64,
|
|
G_TYPE_ENUM,
|
|
G_TYPE_FLAGS,
|
|
G_TYPE_FLOAT,
|
|
G_TYPE_DOUBLE,
|
|
G_TYPE_STRING,
|
|
G_TYPE_POINTER,
|
|
G_TYPE_BOXED,
|
|
G_TYPE_OBJECT,
|
|
G_TYPE_VARIANT,
|
|
G_TYPE_INVALID
|
|
};
|
|
|
|
if (! G_TYPE_IS_VALUE_TYPE (type))
|
|
return FALSE;
|
|
|
|
|
|
while (type_list[i] != G_TYPE_INVALID)
|
|
{
|
|
if (g_type_is_a (type, type_list[i]))
|
|
return TRUE;
|
|
i++;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
static inline GType
|
|
get_fundamental_type (GType type)
|
|
{
|
|
GType result;
|
|
|
|
result = G_TYPE_FUNDAMENTAL (type);
|
|
|
|
if (result == G_TYPE_INTERFACE)
|
|
{
|
|
if (g_type_is_a (type, G_TYPE_OBJECT))
|
|
result = G_TYPE_OBJECT;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
void
|
|
_gtk_tree_data_list_node_to_value (GtkTreeDataList *list,
|
|
GType type,
|
|
GValue *value)
|
|
{
|
|
g_value_init (value, type);
|
|
|
|
switch (get_fundamental_type (type))
|
|
{
|
|
case G_TYPE_BOOLEAN:
|
|
g_value_set_boolean (value, (gboolean) list->data.v_int);
|
|
break;
|
|
case G_TYPE_CHAR:
|
|
g_value_set_schar (value, (char) list->data.v_char);
|
|
break;
|
|
case G_TYPE_UCHAR:
|
|
g_value_set_uchar (value, (guchar) list->data.v_uchar);
|
|
break;
|
|
case G_TYPE_INT:
|
|
g_value_set_int (value, (int) list->data.v_int);
|
|
break;
|
|
case G_TYPE_UINT:
|
|
g_value_set_uint (value, (guint) list->data.v_uint);
|
|
break;
|
|
case G_TYPE_LONG:
|
|
g_value_set_long (value, list->data.v_long);
|
|
break;
|
|
case G_TYPE_ULONG:
|
|
g_value_set_ulong (value, list->data.v_ulong);
|
|
break;
|
|
case G_TYPE_INT64:
|
|
g_value_set_int64 (value, list->data.v_int64);
|
|
break;
|
|
case G_TYPE_UINT64:
|
|
g_value_set_uint64 (value, list->data.v_uint64);
|
|
break;
|
|
case G_TYPE_ENUM:
|
|
g_value_set_enum (value, list->data.v_int);
|
|
break;
|
|
case G_TYPE_FLAGS:
|
|
g_value_set_flags (value, list->data.v_uint);
|
|
break;
|
|
case G_TYPE_FLOAT:
|
|
g_value_set_float (value, (float) list->data.v_float);
|
|
break;
|
|
case G_TYPE_DOUBLE:
|
|
g_value_set_double (value, (double) list->data.v_double);
|
|
break;
|
|
case G_TYPE_STRING:
|
|
g_value_set_string (value, (char *) list->data.v_pointer);
|
|
break;
|
|
case G_TYPE_POINTER:
|
|
g_value_set_pointer (value, (gpointer) list->data.v_pointer);
|
|
break;
|
|
case G_TYPE_BOXED:
|
|
g_value_set_boxed (value, (gpointer) list->data.v_pointer);
|
|
break;
|
|
case G_TYPE_VARIANT:
|
|
g_value_set_variant (value, (gpointer) list->data.v_pointer);
|
|
break;
|
|
case G_TYPE_OBJECT:
|
|
g_value_set_object (value, (GObject *) list->data.v_pointer);
|
|
break;
|
|
default:
|
|
g_warning ("%s: Unsupported type (%s) retrieved.", G_STRLOC, g_type_name (value->g_type));
|
|
break;
|
|
}
|
|
}
|
|
|
|
void
|
|
_gtk_tree_data_list_value_to_node (GtkTreeDataList *list,
|
|
GValue *value)
|
|
{
|
|
switch (get_fundamental_type (G_VALUE_TYPE (value)))
|
|
{
|
|
case G_TYPE_BOOLEAN:
|
|
list->data.v_int = g_value_get_boolean (value);
|
|
break;
|
|
case G_TYPE_CHAR:
|
|
list->data.v_char = g_value_get_schar (value);
|
|
break;
|
|
case G_TYPE_UCHAR:
|
|
list->data.v_uchar = g_value_get_uchar (value);
|
|
break;
|
|
case G_TYPE_INT:
|
|
list->data.v_int = g_value_get_int (value);
|
|
break;
|
|
case G_TYPE_UINT:
|
|
list->data.v_uint = g_value_get_uint (value);
|
|
break;
|
|
case G_TYPE_LONG:
|
|
list->data.v_long = g_value_get_long (value);
|
|
break;
|
|
case G_TYPE_ULONG:
|
|
list->data.v_ulong = g_value_get_ulong (value);
|
|
break;
|
|
case G_TYPE_INT64:
|
|
list->data.v_int64 = g_value_get_int64 (value);
|
|
break;
|
|
case G_TYPE_UINT64:
|
|
list->data.v_uint64 = g_value_get_uint64 (value);
|
|
break;
|
|
case G_TYPE_ENUM:
|
|
list->data.v_int = g_value_get_enum (value);
|
|
break;
|
|
case G_TYPE_FLAGS:
|
|
list->data.v_uint = g_value_get_flags (value);
|
|
break;
|
|
case G_TYPE_POINTER:
|
|
list->data.v_pointer = g_value_get_pointer (value);
|
|
break;
|
|
case G_TYPE_FLOAT:
|
|
list->data.v_float = g_value_get_float (value);
|
|
break;
|
|
case G_TYPE_DOUBLE:
|
|
list->data.v_double = g_value_get_double (value);
|
|
break;
|
|
case G_TYPE_STRING:
|
|
g_free (list->data.v_pointer);
|
|
list->data.v_pointer = g_value_dup_string (value);
|
|
break;
|
|
case G_TYPE_OBJECT:
|
|
if (list->data.v_pointer)
|
|
g_object_unref (list->data.v_pointer);
|
|
list->data.v_pointer = g_value_dup_object (value);
|
|
break;
|
|
case G_TYPE_BOXED:
|
|
if (list->data.v_pointer)
|
|
g_boxed_free (G_VALUE_TYPE (value), list->data.v_pointer);
|
|
list->data.v_pointer = g_value_dup_boxed (value);
|
|
break;
|
|
case G_TYPE_VARIANT:
|
|
if (list->data.v_pointer)
|
|
g_variant_unref (list->data.v_pointer);
|
|
list->data.v_pointer = g_value_dup_variant (value);
|
|
break;
|
|
default:
|
|
g_warning ("%s: Unsupported type (%s) stored.", G_STRLOC, g_type_name (G_VALUE_TYPE (value)));
|
|
break;
|
|
}
|
|
}
|
|
|
|
GtkTreeDataList *
|
|
_gtk_tree_data_list_node_copy (GtkTreeDataList *list,
|
|
GType type)
|
|
{
|
|
GtkTreeDataList *new_list;
|
|
|
|
g_return_val_if_fail (list != NULL, NULL);
|
|
|
|
new_list = _gtk_tree_data_list_alloc ();
|
|
new_list->next = NULL;
|
|
|
|
switch (get_fundamental_type (type))
|
|
{
|
|
case G_TYPE_BOOLEAN:
|
|
case G_TYPE_CHAR:
|
|
case G_TYPE_UCHAR:
|
|
case G_TYPE_INT:
|
|
case G_TYPE_UINT:
|
|
case G_TYPE_LONG:
|
|
case G_TYPE_ULONG:
|
|
case G_TYPE_INT64:
|
|
case G_TYPE_UINT64:
|
|
case G_TYPE_ENUM:
|
|
case G_TYPE_FLAGS:
|
|
case G_TYPE_POINTER:
|
|
case G_TYPE_FLOAT:
|
|
case G_TYPE_DOUBLE:
|
|
new_list->data = list->data;
|
|
break;
|
|
case G_TYPE_STRING:
|
|
new_list->data.v_pointer = g_strdup (list->data.v_pointer);
|
|
break;
|
|
case G_TYPE_OBJECT:
|
|
case G_TYPE_INTERFACE:
|
|
new_list->data.v_pointer = list->data.v_pointer;
|
|
if (new_list->data.v_pointer)
|
|
g_object_ref (new_list->data.v_pointer);
|
|
break;
|
|
case G_TYPE_BOXED:
|
|
if (list->data.v_pointer)
|
|
new_list->data.v_pointer = g_boxed_copy (type, list->data.v_pointer);
|
|
else
|
|
new_list->data.v_pointer = NULL;
|
|
break;
|
|
case G_TYPE_VARIANT:
|
|
if (list->data.v_pointer)
|
|
new_list->data.v_pointer = g_variant_ref (list->data.v_pointer);
|
|
else
|
|
new_list->data.v_pointer = NULL;
|
|
break;
|
|
default:
|
|
g_warning ("Unsupported node type (%s) copied.", g_type_name (type));
|
|
break;
|
|
}
|
|
|
|
return new_list;
|
|
}
|
|
|
|
int
|
|
_gtk_tree_data_list_compare_func (GtkTreeModel *model,
|
|
GtkTreeIter *a,
|
|
GtkTreeIter *b,
|
|
gpointer user_data)
|
|
{
|
|
int column = GPOINTER_TO_INT (user_data);
|
|
GType type = gtk_tree_model_get_column_type (model, column);
|
|
GValue a_value = G_VALUE_INIT;
|
|
GValue b_value = G_VALUE_INIT;
|
|
int retval;
|
|
const char *stra, *strb;
|
|
|
|
gtk_tree_model_get_value (model, a, column, &a_value);
|
|
gtk_tree_model_get_value (model, b, column, &b_value);
|
|
|
|
switch (get_fundamental_type (type))
|
|
{
|
|
case G_TYPE_BOOLEAN:
|
|
if (g_value_get_boolean (&a_value) < g_value_get_boolean (&b_value))
|
|
retval = -1;
|
|
else if (g_value_get_boolean (&a_value) == g_value_get_boolean (&b_value))
|
|
retval = 0;
|
|
else
|
|
retval = 1;
|
|
break;
|
|
case G_TYPE_CHAR:
|
|
if (g_value_get_schar (&a_value) < g_value_get_schar (&b_value))
|
|
retval = -1;
|
|
else if (g_value_get_schar (&a_value) == g_value_get_schar (&b_value))
|
|
retval = 0;
|
|
else
|
|
retval = 1;
|
|
break;
|
|
case G_TYPE_UCHAR:
|
|
if (g_value_get_uchar (&a_value) < g_value_get_uchar (&b_value))
|
|
retval = -1;
|
|
else if (g_value_get_uchar (&a_value) == g_value_get_uchar (&b_value))
|
|
retval = 0;
|
|
else
|
|
retval = 1;
|
|
break;
|
|
case G_TYPE_INT:
|
|
if (g_value_get_int (&a_value) < g_value_get_int (&b_value))
|
|
retval = -1;
|
|
else if (g_value_get_int (&a_value) == g_value_get_int (&b_value))
|
|
retval = 0;
|
|
else
|
|
retval = 1;
|
|
break;
|
|
case G_TYPE_UINT:
|
|
if (g_value_get_uint (&a_value) < g_value_get_uint (&b_value))
|
|
retval = -1;
|
|
else if (g_value_get_uint (&a_value) == g_value_get_uint (&b_value))
|
|
retval = 0;
|
|
else
|
|
retval = 1;
|
|
break;
|
|
case G_TYPE_LONG:
|
|
if (g_value_get_long (&a_value) < g_value_get_long (&b_value))
|
|
retval = -1;
|
|
else if (g_value_get_long (&a_value) == g_value_get_long (&b_value))
|
|
retval = 0;
|
|
else
|
|
retval = 1;
|
|
break;
|
|
case G_TYPE_ULONG:
|
|
if (g_value_get_ulong (&a_value) < g_value_get_ulong (&b_value))
|
|
retval = -1;
|
|
else if (g_value_get_ulong (&a_value) == g_value_get_ulong (&b_value))
|
|
retval = 0;
|
|
else
|
|
retval = 1;
|
|
break;
|
|
case G_TYPE_INT64:
|
|
if (g_value_get_int64 (&a_value) < g_value_get_int64 (&b_value))
|
|
retval = -1;
|
|
else if (g_value_get_int64 (&a_value) == g_value_get_int64 (&b_value))
|
|
retval = 0;
|
|
else
|
|
retval = 1;
|
|
break;
|
|
case G_TYPE_UINT64:
|
|
if (g_value_get_uint64 (&a_value) < g_value_get_uint64 (&b_value))
|
|
retval = -1;
|
|
else if (g_value_get_uint64 (&a_value) == g_value_get_uint64 (&b_value))
|
|
retval = 0;
|
|
else
|
|
retval = 1;
|
|
break;
|
|
case G_TYPE_ENUM:
|
|
/* this is somewhat bogus. */
|
|
if (g_value_get_enum (&a_value) < g_value_get_enum (&b_value))
|
|
retval = -1;
|
|
else if (g_value_get_enum (&a_value) == g_value_get_enum (&b_value))
|
|
retval = 0;
|
|
else
|
|
retval = 1;
|
|
break;
|
|
case G_TYPE_FLAGS:
|
|
/* this is even more bogus. */
|
|
if (g_value_get_flags (&a_value) < g_value_get_flags (&b_value))
|
|
retval = -1;
|
|
else if (g_value_get_flags (&a_value) == g_value_get_flags (&b_value))
|
|
retval = 0;
|
|
else
|
|
retval = 1;
|
|
break;
|
|
case G_TYPE_FLOAT:
|
|
if (g_value_get_float (&a_value) < g_value_get_float (&b_value))
|
|
retval = -1;
|
|
else if (g_value_get_float (&a_value) == g_value_get_float (&b_value))
|
|
retval = 0;
|
|
else
|
|
retval = 1;
|
|
break;
|
|
case G_TYPE_DOUBLE:
|
|
if (g_value_get_double (&a_value) < g_value_get_double (&b_value))
|
|
retval = -1;
|
|
else if (g_value_get_double (&a_value) == g_value_get_double (&b_value))
|
|
retval = 0;
|
|
else
|
|
retval = 1;
|
|
break;
|
|
case G_TYPE_STRING:
|
|
stra = g_value_get_string (&a_value);
|
|
strb = g_value_get_string (&b_value);
|
|
if (stra == NULL) stra = "";
|
|
if (strb == NULL) strb = "";
|
|
retval = g_utf8_collate (stra, strb);
|
|
break;
|
|
case G_TYPE_VARIANT:
|
|
case G_TYPE_POINTER:
|
|
case G_TYPE_BOXED:
|
|
case G_TYPE_OBJECT:
|
|
default:
|
|
g_warning ("Attempting to sort on invalid type %s", g_type_name (type));
|
|
retval = FALSE;
|
|
break;
|
|
}
|
|
|
|
g_value_unset (&a_value);
|
|
g_value_unset (&b_value);
|
|
|
|
return retval;
|
|
}
|
|
|
|
|
|
GList *
|
|
_gtk_tree_data_list_header_new (int n_columns,
|
|
GType *types)
|
|
{
|
|
GList *retval = NULL;
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < n_columns; i ++)
|
|
{
|
|
GtkTreeDataSortHeader *header;
|
|
|
|
header = g_slice_new (GtkTreeDataSortHeader);
|
|
|
|
retval = g_list_prepend (retval, header);
|
|
header->sort_column_id = i;
|
|
header->func = _gtk_tree_data_list_compare_func;
|
|
header->destroy = NULL;
|
|
header->data = GINT_TO_POINTER (i);
|
|
}
|
|
return g_list_reverse (retval);
|
|
}
|
|
|
|
void
|
|
_gtk_tree_data_list_header_free (GList *list)
|
|
{
|
|
GList *tmp;
|
|
|
|
for (tmp = list; tmp; tmp = tmp->next)
|
|
{
|
|
GtkTreeDataSortHeader *header = (GtkTreeDataSortHeader *) tmp->data;
|
|
|
|
if (header->destroy)
|
|
{
|
|
GDestroyNotify d = header->destroy;
|
|
|
|
header->destroy = NULL;
|
|
d (header->data);
|
|
}
|
|
|
|
g_slice_free (GtkTreeDataSortHeader, header);
|
|
}
|
|
g_list_free (list);
|
|
}
|
|
|
|
GtkTreeDataSortHeader *
|
|
_gtk_tree_data_list_get_header (GList *header_list,
|
|
int sort_column_id)
|
|
{
|
|
GtkTreeDataSortHeader *header = NULL;
|
|
|
|
for (; header_list; header_list = header_list->next)
|
|
{
|
|
header = (GtkTreeDataSortHeader*) header_list->data;
|
|
if (header->sort_column_id == sort_column_id)
|
|
return header;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
GList *
|
|
_gtk_tree_data_list_set_header (GList *header_list,
|
|
int sort_column_id,
|
|
GtkTreeIterCompareFunc func,
|
|
gpointer data,
|
|
GDestroyNotify destroy)
|
|
{
|
|
GList *list = header_list;
|
|
GtkTreeDataSortHeader *header = NULL;
|
|
|
|
for (; list; list = list->next)
|
|
{
|
|
header = (GtkTreeDataSortHeader*) list->data;
|
|
if (header->sort_column_id == sort_column_id)
|
|
break;
|
|
header = NULL;
|
|
|
|
if (list->next == NULL)
|
|
break;
|
|
}
|
|
|
|
if (header == NULL)
|
|
{
|
|
header = g_slice_new0 (GtkTreeDataSortHeader);
|
|
header->sort_column_id = sort_column_id;
|
|
if (list)
|
|
list = g_list_append (list, header);
|
|
else
|
|
header_list = g_list_append (header_list, header);
|
|
}
|
|
|
|
if (header->destroy)
|
|
{
|
|
GDestroyNotify d = header->destroy;
|
|
|
|
header->destroy = NULL;
|
|
d (header->data);
|
|
}
|
|
|
|
header->func = func;
|
|
header->data = data;
|
|
header->destroy = destroy;
|
|
|
|
return header_list;
|
|
}
|