forked from AuroraMiddleware/gtk
ef4356b567
2001-01-26 Havoc Pennington <hp@redhat.com> * gtk/gtktextlayout.c (convert_color): adapt to handle PangoColor * gtk/gtktreeview.c (gtk_tree_view_widget_to_tree_coords): fix to not offset by TREE_VIEW_HEADER_HEIGHT (gtk_tree_view_tree_to_widget_coords): fix to not offset by TREE_VIEW_HEADER_HEIGHT * configure.in (included_loaders): for me, --with-included-loaders generates the error "the specified loader yes does not exist", i.e. the arg defaults to "yes", so change test for value "" to test for value "yes", and include all loaders in that case. * gtk/gtkrbtree.c (_gtk_rbtree_get_depth): new function * gtk/gtktreeview.c (gtk_tree_view_get_cell_rect): fix to properly handle TREE_VIEW_VERTICAL_SEPARATOR (gtk_tree_view_bin_expose): fix to consider the row offset as pointing halfway into vertical separator. (gtk_tree_view_draw_node_focus_rect): ditto * gtk/gtkdebug.h, gtk/gtkmain.c (gtk_init_check): Add --gtk-debug=updates, which causes gdk_window_set_debug_updates (TRUE) to be called. * gdk/gdkwindow.c (gdk_window_set_debug_updates): Allow enabling a debug mode where the invalid region is colored in on invalidate, so you can see the flicker and know whether your redraw code is doing a good job. * gtk/gtktreeview.c (gtk_tree_view_queue_draw_node): Work in tree window coordinates (clip rect is in tree window coords) * gtk/Makefile.am: add gtktreednd.[hc] * gtk/gtkliststore.c: implement gtktreednd interfaces. * gtk/gtktreednd.c, gtk/gtktreednd.h: New interface to support drag-and-drop data operations on a model (so we can set up tree drag-and-drop automatically) * gtk/testgtk.c: Add a window to change sensitivity in the GtkLabel test; add a way to change the entry frame in GtkEntry test * gtk/gtkentry.c (gtk_entry_set_has_frame): (gtk_entry_get_has_frame): new functions to remove the frame around an entry (gtk_entry_size_request): shrink requisition if no frame (gtk_entry_draw_focus): don't draw frame if no frame * gtk/gtkstyle.c (gtk_default_draw_check): draw custom look for checks inside a cell renderer (gtk_default_draw_option): ditto for options * gtk/gtktreeviewcolumn.c (update_button_contents): add/remove children from the alignment, not the button (gtk_tree_view_column_init): ref/sink the column, to emulate GObject refcounting. * gtk/gtkcellrenderer.c (gtk_cell_renderer_init): ref/sink * gtk/gtkcellrenderertoggle.c (gtk_cell_renderer_toggle_render): Use theme functions to draw the toggles * gdk/gdkpango.c (gdk_pango_get_gc): use GdkRGB to alloc colors * gdk/gdkpango.h, gdk/gdkpango.c: Add GdkPangoAttrStipple and GdkPangoAttrEmbossed to use in rendering insensitive text * gdk/gdkpango.c (gdk_draw_layout_line): render new properties * gtk/gtkstyle.c (gtk_default_draw_layout): handle sensitivity using new GDK features
1867 lines
49 KiB
C
1867 lines
49 KiB
C
|
|
#include <gtk/gtk.h>
|
|
#include <string.h>
|
|
|
|
|
|
/* Don't copy this bad example; inline RGB data is always a better
|
|
* idea than inline XPMs.
|
|
*/
|
|
static char *book_closed_xpm[] = {
|
|
"16 16 6 1",
|
|
" c None s None",
|
|
". c black",
|
|
"X c red",
|
|
"o c yellow",
|
|
"O c #808080",
|
|
"# c white",
|
|
" ",
|
|
" .. ",
|
|
" ..XX. ",
|
|
" ..XXXXX. ",
|
|
" ..XXXXXXXX. ",
|
|
".ooXXXXXXXXX. ",
|
|
"..ooXXXXXXXXX. ",
|
|
".X.ooXXXXXXXXX. ",
|
|
".XX.ooXXXXXX.. ",
|
|
" .XX.ooXXX..#O ",
|
|
" .XX.oo..##OO. ",
|
|
" .XX..##OO.. ",
|
|
" .X.#OO.. ",
|
|
" ..O.. ",
|
|
" .. ",
|
|
" "
|
|
};
|
|
|
|
static GtkWidget* create_prop_editor (GObject *object);
|
|
static void run_automated_tests (void);
|
|
|
|
/* This custom model is to test custom model use. */
|
|
|
|
#define GTK_TYPE_MODEL_TYPES (gtk_tree_model_types_get_type ())
|
|
#define GTK_TREE_MODEL_TYPES(obj) (GTK_CHECK_CAST ((obj), GTK_TYPE_MODEL_TYPES, GtkTreeModelTypes))
|
|
#define GTK_TREE_MODEL_TYPES_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), GTK_TYPE_MODEL_TYPES, GtkTreeModelTypesClass))
|
|
#define GTK_IS_TREE_MODEL_TYPES(obj) (GTK_CHECK_TYPE ((obj), GTK_TYPE_MODEL_TYPES))
|
|
#define GTK_IS_TREE_MODEL_TYPES_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((obj), GTK_TYPE_MODEL_TYPES))
|
|
|
|
typedef struct _GtkTreeModelTypes GtkTreeModelTypes;
|
|
typedef struct _GtkTreeModelTypesClass GtkTreeModelTypesClass;
|
|
|
|
struct _GtkTreeModelTypes
|
|
{
|
|
GtkObject parent;
|
|
|
|
gint stamp;
|
|
};
|
|
|
|
struct _GtkTreeModelTypesClass
|
|
{
|
|
GtkObjectClass parent_class;
|
|
|
|
guint (* get_flags) (GtkTreeModel *tree_model);
|
|
gint (* get_n_columns) (GtkTreeModel *tree_model);
|
|
GType (* get_column_type) (GtkTreeModel *tree_model,
|
|
gint index);
|
|
gboolean (* get_iter) (GtkTreeModel *tree_model,
|
|
GtkTreeIter *iter,
|
|
GtkTreePath *path);
|
|
GtkTreePath *(* get_path) (GtkTreeModel *tree_model,
|
|
GtkTreeIter *iter);
|
|
void (* get_value) (GtkTreeModel *tree_model,
|
|
GtkTreeIter *iter,
|
|
gint column,
|
|
GValue *value);
|
|
gboolean (* iter_next) (GtkTreeModel *tree_model,
|
|
GtkTreeIter *iter);
|
|
gboolean (* iter_children) (GtkTreeModel *tree_model,
|
|
GtkTreeIter *iter,
|
|
GtkTreeIter *parent);
|
|
gboolean (* iter_has_child) (GtkTreeModel *tree_model,
|
|
GtkTreeIter *iter);
|
|
gint (* iter_n_children) (GtkTreeModel *tree_model,
|
|
GtkTreeIter *iter);
|
|
gboolean (* iter_nth_child) (GtkTreeModel *tree_model,
|
|
GtkTreeIter *iter,
|
|
GtkTreeIter *parent,
|
|
gint n);
|
|
gboolean (* iter_parent) (GtkTreeModel *tree_model,
|
|
GtkTreeIter *iter,
|
|
GtkTreeIter *child);
|
|
void (* ref_iter) (GtkTreeModel *tree_model,
|
|
GtkTreeIter *iter);
|
|
void (* unref_iter) (GtkTreeModel *tree_model,
|
|
GtkTreeIter *iter);
|
|
|
|
/* These will be moved into the GtkTreeModelIface eventually */
|
|
void (* changed) (GtkTreeModel *tree_model,
|
|
GtkTreePath *path,
|
|
GtkTreeIter *iter);
|
|
void (* inserted) (GtkTreeModel *tree_model,
|
|
GtkTreePath *path,
|
|
GtkTreeIter *iter);
|
|
void (* child_toggled) (GtkTreeModel *tree_model,
|
|
GtkTreePath *path,
|
|
GtkTreeIter *iter);
|
|
void (* deleted) (GtkTreeModel *tree_model,
|
|
GtkTreePath *path);
|
|
};
|
|
|
|
GtkType gtk_tree_model_types_get_type (void);
|
|
GtkTreeModelTypes *gtk_tree_model_types_new (void);
|
|
|
|
typedef enum
|
|
{
|
|
COLUMNS_NONE,
|
|
COLUMNS_ONE,
|
|
COLUMNS_LOTS,
|
|
COLUMNS_LAST
|
|
} ColumnsType;
|
|
|
|
static gchar *column_type_names[] = {
|
|
"No columns",
|
|
"One column",
|
|
"Many columns"
|
|
};
|
|
|
|
#define N_COLUMNS 9
|
|
|
|
static GType*
|
|
get_model_types (void)
|
|
{
|
|
static GType column_types[N_COLUMNS] = { 0 };
|
|
|
|
if (column_types[0] == 0)
|
|
{
|
|
column_types[0] = G_TYPE_STRING;
|
|
column_types[1] = G_TYPE_STRING;
|
|
column_types[2] = GDK_TYPE_PIXBUF;
|
|
column_types[3] = G_TYPE_FLOAT;
|
|
column_types[4] = G_TYPE_UINT;
|
|
column_types[5] = G_TYPE_UCHAR;
|
|
column_types[6] = G_TYPE_CHAR;
|
|
#define BOOL_COLUMN 7
|
|
column_types[BOOL_COLUMN] = G_TYPE_BOOLEAN;
|
|
column_types[8] = G_TYPE_INT;
|
|
}
|
|
|
|
return column_types;
|
|
}
|
|
|
|
static void
|
|
col_clicked_cb (GtkTreeViewColumn *col, gpointer data)
|
|
{
|
|
GtkWindow *win;
|
|
|
|
win = GTK_WINDOW (create_prop_editor (G_OBJECT (col)));
|
|
|
|
gtk_window_set_title (win, gtk_tree_view_column_get_title (col));
|
|
}
|
|
|
|
static void
|
|
setup_column (GtkTreeViewColumn *col)
|
|
{
|
|
g_signal_connect_data (G_OBJECT (col),
|
|
"clicked",
|
|
(GCallback) col_clicked_cb,
|
|
NULL,
|
|
NULL,
|
|
FALSE,
|
|
FALSE);
|
|
}
|
|
|
|
static void
|
|
toggled_callback (GtkCellRendererToggle *celltoggle,
|
|
gchar *path_string,
|
|
GtkTreeView *tree_view)
|
|
{
|
|
GtkTreeModel *model = NULL;
|
|
GtkTreeModelSort *sort_model = NULL;
|
|
GtkTreePath *path;
|
|
GtkTreeIter iter;
|
|
gboolean active = FALSE;
|
|
|
|
g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
|
|
|
|
model = gtk_tree_view_get_model (tree_view);
|
|
|
|
if (GTK_IS_TREE_MODEL_SORT (model))
|
|
{
|
|
sort_model = GTK_TREE_MODEL_SORT (model);
|
|
model = gtk_tree_model_sort_get_model (sort_model);
|
|
}
|
|
|
|
if (model == NULL)
|
|
return;
|
|
|
|
if (sort_model)
|
|
{
|
|
g_warning ("FIXME implement conversion from TreeModelSort iter to child model iter");
|
|
return;
|
|
}
|
|
|
|
path = gtk_tree_path_new_from_string (path_string);
|
|
if (!gtk_tree_model_get_iter (model,
|
|
&iter, path))
|
|
{
|
|
g_warning ("%s: bad path?", G_STRLOC);
|
|
return;
|
|
}
|
|
gtk_tree_path_free (path);
|
|
|
|
if (GTK_IS_LIST_STORE (model))
|
|
{
|
|
gtk_list_store_get (GTK_LIST_STORE (model),
|
|
&iter,
|
|
BOOL_COLUMN,
|
|
&active,
|
|
-1);
|
|
|
|
gtk_list_store_set (GTK_LIST_STORE (model),
|
|
&iter,
|
|
BOOL_COLUMN,
|
|
!active,
|
|
-1);
|
|
}
|
|
else if (GTK_IS_TREE_STORE (model))
|
|
{
|
|
gtk_tree_store_get (GTK_TREE_STORE (model),
|
|
&iter,
|
|
BOOL_COLUMN,
|
|
&active,
|
|
-1);
|
|
|
|
gtk_tree_store_set (GTK_TREE_STORE (model),
|
|
&iter,
|
|
BOOL_COLUMN,
|
|
!active,
|
|
-1);
|
|
}
|
|
else
|
|
g_warning ("don't know how to actually toggle value for model type %s",
|
|
g_type_name (G_TYPE_FROM_INSTANCE (model)));
|
|
}
|
|
|
|
|
|
static ColumnsType current_column_type = COLUMNS_LOTS;
|
|
|
|
static void
|
|
set_columns_type (GtkTreeView *tree_view, ColumnsType type)
|
|
{
|
|
GtkTreeViewColumn *col;
|
|
GtkCellRenderer *rend;
|
|
GdkPixbuf *pixbuf;
|
|
GtkWidget *image;
|
|
|
|
current_column_type = type;
|
|
|
|
col = gtk_tree_view_get_column (tree_view, 0);
|
|
while (col)
|
|
{
|
|
gtk_tree_view_remove_column (tree_view, col);
|
|
|
|
col = gtk_tree_view_get_column (tree_view, 0);
|
|
}
|
|
|
|
switch (type)
|
|
{
|
|
case COLUMNS_NONE:
|
|
break;
|
|
|
|
case COLUMNS_LOTS:
|
|
rend = gtk_cell_renderer_text_new ();
|
|
|
|
col = gtk_tree_view_column_new_with_attributes ("Column 1",
|
|
rend,
|
|
"text", 1,
|
|
NULL);
|
|
setup_column (col);
|
|
|
|
gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), col);
|
|
|
|
g_object_unref (G_OBJECT (rend));
|
|
g_object_unref (G_OBJECT (col));
|
|
|
|
rend = gtk_cell_renderer_text_pixbuf_new ();
|
|
|
|
col = gtk_tree_view_column_new_with_attributes ("Column 2",
|
|
rend,
|
|
"text", 0,
|
|
"pixbuf", 2,
|
|
NULL);
|
|
|
|
setup_column (col);
|
|
|
|
gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), col);
|
|
|
|
g_object_unref (G_OBJECT (rend));
|
|
g_object_unref (G_OBJECT (col));
|
|
|
|
rend = gtk_cell_renderer_toggle_new ();
|
|
|
|
g_signal_connect_data (G_OBJECT (rend), "toggled",
|
|
toggled_callback, tree_view,
|
|
NULL, FALSE, FALSE);
|
|
|
|
col = gtk_tree_view_column_new_with_attributes ("Column 3",
|
|
rend,
|
|
"active", BOOL_COLUMN,
|
|
NULL);
|
|
|
|
setup_column (col);
|
|
|
|
gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), col);
|
|
|
|
pixbuf = gdk_pixbuf_new_from_xpm_data (book_closed_xpm);
|
|
|
|
image = gtk_image_new_from_pixbuf (pixbuf);
|
|
|
|
g_object_unref (G_OBJECT (pixbuf));
|
|
|
|
gtk_widget_show (image);
|
|
|
|
gtk_tree_view_column_set_widget (col, image);
|
|
|
|
g_object_unref (G_OBJECT (rend));
|
|
g_object_unref (G_OBJECT (col));
|
|
|
|
rend = gtk_cell_renderer_toggle_new ();
|
|
|
|
/* you could also set this per-row by tying it to a column
|
|
* in the model of course.
|
|
*/
|
|
g_object_set (G_OBJECT (rend), "radio", TRUE, NULL);
|
|
|
|
g_signal_connect_data (G_OBJECT (rend), "toggled",
|
|
toggled_callback, tree_view,
|
|
NULL, FALSE, FALSE);
|
|
|
|
col = gtk_tree_view_column_new_with_attributes ("Column 4",
|
|
rend,
|
|
"active", BOOL_COLUMN,
|
|
NULL);
|
|
|
|
setup_column (col);
|
|
|
|
gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), col);
|
|
|
|
g_object_unref (G_OBJECT (rend));
|
|
g_object_unref (G_OBJECT (col));
|
|
|
|
#if 0
|
|
|
|
rend = gtk_cell_renderer_text_new ();
|
|
|
|
col = gtk_tree_view_column_new_with_attributes ("Column 5",
|
|
rend,
|
|
"text", 3,
|
|
NULL);
|
|
|
|
setup_column (col);
|
|
|
|
gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), col);
|
|
|
|
g_object_unref (G_OBJECT (rend));
|
|
g_object_unref (G_OBJECT (col));
|
|
|
|
|
|
rend = gtk_cell_renderer_text_new ();
|
|
|
|
col = gtk_tree_view_column_new_with_attributes ("Column 6",
|
|
rend,
|
|
"text", 4,
|
|
NULL);
|
|
|
|
setup_column (col);
|
|
|
|
gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), col);
|
|
|
|
g_object_unref (G_OBJECT (rend));
|
|
g_object_unref (G_OBJECT (col));
|
|
|
|
|
|
rend = gtk_cell_renderer_text_new ();
|
|
|
|
col = gtk_tree_view_column_new_with_attributes ("Column 7",
|
|
rend,
|
|
"text", 5,
|
|
NULL);
|
|
|
|
setup_column (col);
|
|
|
|
gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), col);
|
|
|
|
g_object_unref (G_OBJECT (rend));
|
|
g_object_unref (G_OBJECT (col));
|
|
|
|
rend = gtk_cell_renderer_text_new ();
|
|
|
|
col = gtk_tree_view_column_new_with_attributes ("Column 8",
|
|
rend,
|
|
"text", 6,
|
|
NULL);
|
|
|
|
setup_column (col);
|
|
|
|
gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), col);
|
|
|
|
g_object_unref (G_OBJECT (rend));
|
|
g_object_unref (G_OBJECT (col));
|
|
|
|
|
|
rend = gtk_cell_renderer_text_new ();
|
|
|
|
col = gtk_tree_view_column_new_with_attributes ("Column 9",
|
|
rend,
|
|
"text", 7,
|
|
NULL);
|
|
|
|
setup_column (col);
|
|
|
|
gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), col);
|
|
|
|
g_object_unref (G_OBJECT (rend));
|
|
g_object_unref (G_OBJECT (col));
|
|
|
|
|
|
rend = gtk_cell_renderer_text_new ();
|
|
|
|
col = gtk_tree_view_column_new_with_attributes ("Column 10",
|
|
rend,
|
|
"text", 8,
|
|
NULL);
|
|
|
|
setup_column (col);
|
|
|
|
gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), col);
|
|
|
|
g_object_unref (G_OBJECT (rend));
|
|
g_object_unref (G_OBJECT (col));
|
|
|
|
#endif
|
|
|
|
gtk_tree_view_set_expander_column (tree_view, 1);
|
|
|
|
/* FALL THRU */
|
|
|
|
case COLUMNS_ONE:
|
|
rend = gtk_cell_renderer_text_new ();
|
|
|
|
col = gtk_tree_view_column_new_with_attributes ("Column 0",
|
|
rend,
|
|
"text", 0,
|
|
NULL);
|
|
|
|
setup_column (col);
|
|
|
|
gtk_tree_view_insert_column (GTK_TREE_VIEW (tree_view), col, 0);
|
|
|
|
g_object_unref (G_OBJECT (rend));
|
|
g_object_unref (G_OBJECT (col));
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static ColumnsType
|
|
get_columns_type (void)
|
|
{
|
|
return current_column_type;
|
|
}
|
|
|
|
static GdkPixbuf *our_pixbuf;
|
|
|
|
typedef enum
|
|
{
|
|
/* MODEL_TYPES, */
|
|
MODEL_TREE,
|
|
MODEL_LIST,
|
|
MODEL_SORTED_TREE,
|
|
MODEL_SORTED_LIST,
|
|
MODEL_EMPTY_LIST,
|
|
MODEL_EMPTY_TREE,
|
|
MODEL_NULL,
|
|
MODEL_LAST
|
|
} ModelType;
|
|
|
|
/* FIXME add a custom model to test */
|
|
static GtkTreeModel *models[MODEL_LAST];
|
|
static const char *model_names[MODEL_LAST] = {
|
|
"GtkTreeStore",
|
|
"GtkListStore",
|
|
"GtkTreeModelSort wrapping GtkTreeStore",
|
|
"GtkTreeModelSort wrapping GtkListStore",
|
|
"Empty GtkListStore",
|
|
"Empty GtkTreeStore",
|
|
"NULL (no model)"
|
|
};
|
|
|
|
static GtkTreeModel*
|
|
create_list_model (void)
|
|
{
|
|
GtkListStore *store;
|
|
GtkTreeIter iter;
|
|
gint i;
|
|
GType *t;
|
|
|
|
t = get_model_types ();
|
|
|
|
store = gtk_list_store_new_with_types (N_COLUMNS,
|
|
t[0], t[1], t[2],
|
|
t[3], t[4], t[5],
|
|
t[6], t[7], t[8]);
|
|
|
|
i = 0;
|
|
while (i < 200)
|
|
{
|
|
char *msg;
|
|
|
|
gtk_list_store_append (store, &iter);
|
|
|
|
msg = g_strdup_printf ("%d", i);
|
|
|
|
gtk_list_store_set (store, &iter, 0, msg, 1, "Foo! Foo! Foo!",
|
|
2, our_pixbuf,
|
|
3, 7.0, 4, (guint) 9000,
|
|
5, 'f', 6, 'g',
|
|
7, TRUE, 8, 23245454,
|
|
-1);
|
|
|
|
g_free (msg);
|
|
|
|
++i;
|
|
}
|
|
|
|
return GTK_TREE_MODEL (store);
|
|
}
|
|
|
|
static void
|
|
typesystem_recurse (GType type,
|
|
GtkTreeIter *parent_iter,
|
|
GtkTreeStore *store)
|
|
{
|
|
GType* children;
|
|
guint n_children = 0;
|
|
gint i;
|
|
GtkTreeIter iter;
|
|
gchar *str;
|
|
|
|
gtk_tree_store_append (store, &iter, parent_iter);
|
|
|
|
str = g_strdup_printf ("%d", type);
|
|
gtk_tree_store_set (store, &iter, 0, str, 1, g_type_name (type),
|
|
2, our_pixbuf,
|
|
3, 7.0, 4, (guint) 9000,
|
|
5, 'f', 6, 'g',
|
|
7, TRUE, 8, 23245454,
|
|
-1);
|
|
g_free (str);
|
|
|
|
children = g_type_children (type, &n_children);
|
|
|
|
i = 0;
|
|
while (i < n_children)
|
|
{
|
|
typesystem_recurse (children[i], &iter, store);
|
|
|
|
++i;
|
|
}
|
|
|
|
g_free (children);
|
|
}
|
|
|
|
static GtkTreeModel*
|
|
create_tree_model (void)
|
|
{
|
|
GtkTreeStore *store;
|
|
gint i;
|
|
GType *t;
|
|
volatile GType dummy; /* G_GNUC_CONST makes the optimizer remove
|
|
* get_type calls if you don't do something
|
|
* like this
|
|
*/
|
|
|
|
/* Make the tree more interesting */
|
|
dummy = gtk_scrolled_window_get_type ();
|
|
dummy = gtk_label_get_type ();
|
|
dummy = gtk_hscrollbar_get_type ();
|
|
dummy = gtk_vscrollbar_get_type ();
|
|
dummy = pango_layout_get_type ();
|
|
|
|
t = get_model_types ();
|
|
|
|
store = gtk_tree_store_new_with_types (N_COLUMNS,
|
|
t[0], t[1], t[2],
|
|
t[3], t[4], t[5],
|
|
t[6], t[7], t[8]);
|
|
|
|
i = 0;
|
|
while (i < G_TYPE_LAST_RESERVED_FUNDAMENTAL)
|
|
{
|
|
typesystem_recurse (i, NULL, store);
|
|
|
|
++i;
|
|
}
|
|
|
|
return GTK_TREE_MODEL (store);
|
|
}
|
|
|
|
static void
|
|
model_selected (GtkOptionMenu *om, gpointer data)
|
|
{
|
|
GtkTreeView *tree_view = GTK_TREE_VIEW (data);
|
|
gint hist;
|
|
|
|
hist = gtk_option_menu_get_history (om);
|
|
|
|
if (models[hist] != gtk_tree_view_get_model (tree_view))
|
|
{
|
|
gtk_tree_view_set_model (tree_view, models[hist]);
|
|
}
|
|
}
|
|
|
|
static void
|
|
columns_selected (GtkOptionMenu *om, gpointer data)
|
|
{
|
|
GtkTreeView *tree_view = GTK_TREE_VIEW (data);
|
|
gint hist;
|
|
|
|
hist = gtk_option_menu_get_history (om);
|
|
|
|
if (hist != get_columns_type ())
|
|
{
|
|
set_columns_type (tree_view, hist);
|
|
}
|
|
}
|
|
|
|
|
|
enum
|
|
{
|
|
TARGET_GTK_TREE_MODEL_ROW
|
|
};
|
|
|
|
static GtkTargetEntry row_targets[] = {
|
|
{ "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_APP,
|
|
TARGET_GTK_TREE_MODEL_ROW }
|
|
};
|
|
|
|
int
|
|
main (int argc,
|
|
char **argv)
|
|
{
|
|
GtkWidget *window;
|
|
GtkWidget *sw;
|
|
GtkWidget *tv;
|
|
GtkWidget *table;
|
|
GtkWidget *om;
|
|
GtkWidget *menu;
|
|
GtkTreeModel *model;
|
|
gint i;
|
|
|
|
gtk_init (&argc, &argv);
|
|
|
|
our_pixbuf = gdk_pixbuf_new_from_xpm_data ((const char **) book_closed_xpm);
|
|
|
|
#if 0
|
|
models[MODEL_TYPES] = GTK_TREE_MODEL (gtk_tree_model_types_new ());
|
|
#endif
|
|
models[MODEL_LIST] = create_list_model ();
|
|
models[MODEL_TREE] = create_tree_model ();
|
|
|
|
model = create_list_model ();
|
|
models[MODEL_SORTED_LIST] = gtk_tree_model_sort_new_with_model (model, NULL, 0);
|
|
g_object_unref (G_OBJECT (model));
|
|
|
|
model = create_tree_model ();
|
|
models[MODEL_SORTED_TREE] = gtk_tree_model_sort_new_with_model (model, NULL, 0);
|
|
g_object_unref (G_OBJECT (model));
|
|
|
|
models[MODEL_EMPTY_LIST] = GTK_TREE_MODEL (gtk_list_store_new ());
|
|
models[MODEL_EMPTY_TREE] = GTK_TREE_MODEL (gtk_tree_store_new ());
|
|
|
|
models[MODEL_NULL] = NULL;
|
|
|
|
run_automated_tests ();
|
|
|
|
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
|
|
|
gtk_window_set_default_size (GTK_WINDOW (window), 400, 400);
|
|
|
|
table = gtk_table_new (3, 1, FALSE);
|
|
|
|
gtk_container_add (GTK_CONTAINER (window), table);
|
|
|
|
tv = gtk_tree_view_new_with_model (models[0]);
|
|
|
|
gtk_tree_view_set_rows_drag_source (GTK_TREE_VIEW (tv),
|
|
GDK_BUTTON1_MASK,
|
|
row_targets,
|
|
G_N_ELEMENTS (row_targets),
|
|
GDK_ACTION_MOVE | GDK_ACTION_COPY,
|
|
NULL, NULL);
|
|
|
|
gtk_tree_view_set_rows_drag_dest (GTK_TREE_VIEW (tv),
|
|
row_targets,
|
|
G_N_ELEMENTS (row_targets),
|
|
GDK_ACTION_MOVE | GDK_ACTION_COPY,
|
|
NULL, NULL);
|
|
|
|
/* Model menu */
|
|
|
|
menu = gtk_menu_new ();
|
|
|
|
i = 0;
|
|
while (i < MODEL_LAST)
|
|
{
|
|
GtkWidget *mi;
|
|
const char *name;
|
|
|
|
name = model_names[i];
|
|
|
|
mi = gtk_menu_item_new_with_label (name);
|
|
|
|
gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi);
|
|
|
|
#if 0
|
|
window = create_prop_editor (G_OBJECT (models[i]));
|
|
|
|
gtk_window_set_title (GTK_WINDOW (window),
|
|
name);
|
|
#endif
|
|
|
|
++i;
|
|
}
|
|
gtk_widget_show_all (menu);
|
|
|
|
om = gtk_option_menu_new ();
|
|
gtk_option_menu_set_menu (GTK_OPTION_MENU (om), menu);
|
|
|
|
gtk_table_attach (GTK_TABLE (table), om,
|
|
0, 1, 0, 1,
|
|
0, 0,
|
|
0, 0);
|
|
|
|
gtk_signal_connect (GTK_OBJECT (om),
|
|
"changed",
|
|
GTK_SIGNAL_FUNC (model_selected),
|
|
tv);
|
|
|
|
/* Columns menu */
|
|
|
|
menu = gtk_menu_new ();
|
|
|
|
i = 0;
|
|
while (i < COLUMNS_LAST)
|
|
{
|
|
GtkWidget *mi;
|
|
const char *name;
|
|
|
|
name = column_type_names[i];
|
|
|
|
mi = gtk_menu_item_new_with_label (name);
|
|
|
|
gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi);
|
|
|
|
++i;
|
|
}
|
|
gtk_widget_show_all (menu);
|
|
|
|
om = gtk_option_menu_new ();
|
|
gtk_option_menu_set_menu (GTK_OPTION_MENU (om), menu);
|
|
|
|
gtk_table_attach (GTK_TABLE (table), om,
|
|
0, 1, 1, 2,
|
|
0, 0,
|
|
0, 0);
|
|
|
|
set_columns_type (GTK_TREE_VIEW (tv), COLUMNS_LOTS);
|
|
gtk_option_menu_set_history (GTK_OPTION_MENU (om), COLUMNS_LOTS);
|
|
|
|
gtk_signal_connect (GTK_OBJECT (om),
|
|
"changed",
|
|
GTK_SIGNAL_FUNC (columns_selected),
|
|
tv);
|
|
|
|
sw = gtk_scrolled_window_new (NULL, NULL);
|
|
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
|
|
GTK_POLICY_AUTOMATIC,
|
|
GTK_POLICY_AUTOMATIC);
|
|
|
|
gtk_table_attach (GTK_TABLE (table), sw,
|
|
0, 1, 2, 3,
|
|
GTK_EXPAND | GTK_FILL,
|
|
GTK_EXPAND | GTK_FILL,
|
|
0, 0);
|
|
|
|
gtk_container_add (GTK_CONTAINER (sw), tv);
|
|
|
|
gtk_widget_show_all (window);
|
|
|
|
gtk_main ();
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* GtkTreeModelTypes
|
|
*/
|
|
|
|
enum {
|
|
CHANGED,
|
|
INSERTED,
|
|
CHILD_TOGGLED,
|
|
DELETED,
|
|
|
|
LAST_SIGNAL
|
|
};
|
|
|
|
static void gtk_tree_model_types_init (GtkTreeModelTypes *model_types);
|
|
static void gtk_tree_model_types_class_init (GtkTreeModelTypesClass *class);
|
|
static void gtk_tree_model_types_tree_model_init (GtkTreeModelIface *iface);
|
|
static gint gtk_real_model_types_get_n_columns (GtkTreeModel *tree_model);
|
|
static GType gtk_real_model_types_get_column_type (GtkTreeModel *tree_model,
|
|
gint index);
|
|
static GtkTreePath *gtk_real_model_types_get_path (GtkTreeModel *tree_model,
|
|
GtkTreeIter *iter);
|
|
static void gtk_real_model_types_get_value (GtkTreeModel *tree_model,
|
|
GtkTreeIter *iter,
|
|
gint column,
|
|
GValue *value);
|
|
static gboolean gtk_real_model_types_iter_next (GtkTreeModel *tree_model,
|
|
GtkTreeIter *iter);
|
|
static gboolean gtk_real_model_types_iter_children (GtkTreeModel *tree_model,
|
|
GtkTreeIter *iter,
|
|
GtkTreeIter *parent);
|
|
static gboolean gtk_real_model_types_iter_has_child (GtkTreeModel *tree_model,
|
|
GtkTreeIter *iter);
|
|
static gint gtk_real_model_types_iter_n_children (GtkTreeModel *tree_model,
|
|
GtkTreeIter *iter);
|
|
static gboolean gtk_real_model_types_iter_nth_child (GtkTreeModel *tree_model,
|
|
GtkTreeIter *iter,
|
|
GtkTreeIter *parent,
|
|
gint n);
|
|
static gboolean gtk_real_model_types_iter_parent (GtkTreeModel *tree_model,
|
|
GtkTreeIter *iter,
|
|
GtkTreeIter *child);
|
|
|
|
|
|
static guint model_types_signals[LAST_SIGNAL] = { 0 };
|
|
|
|
|
|
GtkType
|
|
gtk_tree_model_types_get_type (void)
|
|
{
|
|
static GtkType model_types_type = 0;
|
|
|
|
if (!model_types_type)
|
|
{
|
|
static const GTypeInfo model_types_info =
|
|
{
|
|
sizeof (GtkTreeModelTypesClass),
|
|
NULL, /* base_init */
|
|
NULL, /* base_finalize */
|
|
(GClassInitFunc) gtk_tree_model_types_class_init,
|
|
NULL, /* class_finalize */
|
|
NULL, /* class_data */
|
|
sizeof (GtkTreeModelTypes),
|
|
0,
|
|
(GInstanceInitFunc) gtk_tree_model_types_init
|
|
};
|
|
|
|
static const GInterfaceInfo tree_model_info =
|
|
{
|
|
(GInterfaceInitFunc) gtk_tree_model_types_tree_model_init,
|
|
NULL,
|
|
NULL
|
|
};
|
|
|
|
model_types_type = g_type_register_static (GTK_TYPE_OBJECT, "GtkTreeModelTypes", &model_types_info, 0);
|
|
g_type_add_interface_static (model_types_type,
|
|
GTK_TYPE_TREE_MODEL,
|
|
&tree_model_info);
|
|
}
|
|
|
|
return model_types_type;
|
|
}
|
|
|
|
GtkTreeModelTypes *
|
|
gtk_tree_model_types_new (void)
|
|
{
|
|
GtkTreeModelTypes *retval;
|
|
|
|
retval = GTK_TREE_MODEL_TYPES (g_object_new (GTK_TYPE_MODEL_TYPES, NULL));
|
|
|
|
return retval;
|
|
}
|
|
|
|
static void
|
|
gtk_tree_model_types_class_init (GtkTreeModelTypesClass *class)
|
|
{
|
|
GObjectClass *object_class;
|
|
|
|
object_class = (GObjectClass*) class;
|
|
|
|
model_types_signals[CHANGED] =
|
|
g_signal_newc ("changed",
|
|
GTK_CLASS_TYPE (object_class),
|
|
G_SIGNAL_RUN_FIRST,
|
|
GTK_SIGNAL_OFFSET (GtkTreeModelTypesClass, changed),
|
|
NULL,
|
|
gtk_marshal_VOID__BOXED_BOXED,
|
|
G_TYPE_NONE, 2,
|
|
G_TYPE_POINTER,
|
|
G_TYPE_POINTER);
|
|
model_types_signals[INSERTED] =
|
|
g_signal_newc ("inserted",
|
|
GTK_CLASS_TYPE (object_class),
|
|
G_SIGNAL_RUN_FIRST,
|
|
GTK_SIGNAL_OFFSET (GtkTreeModelTypesClass, inserted),
|
|
NULL,
|
|
gtk_marshal_VOID__BOXED_BOXED,
|
|
G_TYPE_NONE, 2,
|
|
G_TYPE_POINTER,
|
|
G_TYPE_POINTER);
|
|
model_types_signals[CHILD_TOGGLED] =
|
|
g_signal_newc ("child_toggled",
|
|
GTK_CLASS_TYPE (object_class),
|
|
G_SIGNAL_RUN_FIRST,
|
|
GTK_SIGNAL_OFFSET (GtkTreeModelTypesClass, child_toggled),
|
|
NULL,
|
|
gtk_marshal_VOID__BOXED_BOXED,
|
|
G_TYPE_NONE, 2,
|
|
G_TYPE_POINTER,
|
|
G_TYPE_POINTER);
|
|
model_types_signals[DELETED] =
|
|
g_signal_newc ("deleted",
|
|
GTK_CLASS_TYPE (object_class),
|
|
G_SIGNAL_RUN_FIRST,
|
|
GTK_SIGNAL_OFFSET (GtkTreeModelTypesClass, deleted),
|
|
NULL,
|
|
gtk_marshal_VOID__BOXED,
|
|
G_TYPE_NONE, 1,
|
|
G_TYPE_POINTER);
|
|
}
|
|
|
|
static void
|
|
gtk_tree_model_types_tree_model_init (GtkTreeModelIface *iface)
|
|
{
|
|
iface->get_n_columns = gtk_real_model_types_get_n_columns;
|
|
iface->get_column_type = gtk_real_model_types_get_column_type;
|
|
iface->get_path = gtk_real_model_types_get_path;
|
|
iface->get_value = gtk_real_model_types_get_value;
|
|
iface->iter_next = gtk_real_model_types_iter_next;
|
|
iface->iter_children = gtk_real_model_types_iter_children;
|
|
iface->iter_has_child = gtk_real_model_types_iter_has_child;
|
|
iface->iter_n_children = gtk_real_model_types_iter_n_children;
|
|
iface->iter_nth_child = gtk_real_model_types_iter_nth_child;
|
|
iface->iter_parent = gtk_real_model_types_iter_parent;
|
|
}
|
|
|
|
static void
|
|
gtk_tree_model_types_init (GtkTreeModelTypes *model_types)
|
|
{
|
|
model_types->stamp = g_random_int ();
|
|
}
|
|
|
|
static GType column_types[] = {
|
|
G_TYPE_STRING, /* GType */
|
|
G_TYPE_STRING /* type name */
|
|
};
|
|
|
|
static gint
|
|
gtk_real_model_types_get_n_columns (GtkTreeModel *tree_model)
|
|
{
|
|
return G_N_ELEMENTS (column_types);
|
|
}
|
|
|
|
static GType
|
|
gtk_real_model_types_get_column_type (GtkTreeModel *tree_model,
|
|
gint index)
|
|
{
|
|
g_return_val_if_fail (index < G_N_ELEMENTS (column_types), G_TYPE_INVALID);
|
|
|
|
return column_types[index];
|
|
}
|
|
|
|
#if 0
|
|
/* Use default implementation of this */
|
|
static gboolean
|
|
gtk_real_model_types_get_iter (GtkTreeModel *tree_model,
|
|
GtkTreeIter *iter,
|
|
GtkTreePath *path)
|
|
{
|
|
|
|
}
|
|
#endif
|
|
|
|
/* The toplevel nodes of the tree are the reserved types, G_TYPE_NONE through
|
|
* G_TYPE_RESERVED_FUNDAMENTAL.
|
|
*/
|
|
|
|
static GtkTreePath *
|
|
gtk_real_model_types_get_path (GtkTreeModel *tree_model,
|
|
GtkTreeIter *iter)
|
|
{
|
|
GtkTreePath *retval;
|
|
GType type;
|
|
GType parent;
|
|
|
|
g_return_val_if_fail (GTK_IS_TREE_MODEL_TYPES (tree_model), NULL);
|
|
g_return_val_if_fail (iter != NULL, NULL);
|
|
|
|
type = GPOINTER_TO_INT (iter->user_data);
|
|
|
|
retval = gtk_tree_path_new ();
|
|
|
|
parent = g_type_parent (type);
|
|
while (parent != G_TYPE_INVALID)
|
|
{
|
|
GType* children = g_type_children (parent, NULL);
|
|
gint i = 0;
|
|
|
|
if (!children || children[0] == G_TYPE_INVALID)
|
|
{
|
|
g_warning ("bad iterator?");
|
|
return NULL;
|
|
}
|
|
|
|
while (children[i] != type)
|
|
++i;
|
|
|
|
gtk_tree_path_prepend_index (retval, i);
|
|
|
|
g_free (children);
|
|
|
|
type = parent;
|
|
parent = g_type_parent (parent);
|
|
}
|
|
|
|
/* The fundamental type itself is the index on the toplevel */
|
|
gtk_tree_path_prepend_index (retval, type);
|
|
|
|
return retval;
|
|
}
|
|
|
|
static void
|
|
gtk_real_model_types_get_value (GtkTreeModel *tree_model,
|
|
GtkTreeIter *iter,
|
|
gint column,
|
|
GValue *value)
|
|
{
|
|
GType type;
|
|
|
|
type = GPOINTER_TO_INT (iter->user_data);
|
|
|
|
switch (column)
|
|
{
|
|
case 0:
|
|
{
|
|
gchar *str;
|
|
|
|
g_value_init (value, G_TYPE_STRING);
|
|
|
|
str = g_strdup_printf ("%d", type);
|
|
g_value_set_string (value, str);
|
|
g_free (str);
|
|
}
|
|
break;
|
|
|
|
case 1:
|
|
g_value_init (value, G_TYPE_STRING);
|
|
g_value_set_string (value, g_type_name (type));
|
|
break;
|
|
|
|
default:
|
|
g_warning ("Bad column %d requested", column);
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
gtk_real_model_types_iter_next (GtkTreeModel *tree_model,
|
|
GtkTreeIter *iter)
|
|
{
|
|
|
|
GType parent;
|
|
GType type;
|
|
|
|
type = GPOINTER_TO_INT (iter->user_data);
|
|
|
|
parent = g_type_parent (type);
|
|
|
|
if (parent == G_TYPE_INVALID)
|
|
{
|
|
/* fundamental type, add 1 */
|
|
if ((type + 1) < G_TYPE_LAST_RESERVED_FUNDAMENTAL)
|
|
{
|
|
iter->user_data = GINT_TO_POINTER (type + 1);
|
|
return TRUE;
|
|
}
|
|
else
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
GType* children = g_type_children (parent, NULL);
|
|
gint i = 0;
|
|
|
|
g_assert (children != NULL);
|
|
|
|
while (children[i] != type)
|
|
++i;
|
|
|
|
++i;
|
|
|
|
if (children[i] != G_TYPE_INVALID)
|
|
{
|
|
g_free (children);
|
|
iter->user_data = GINT_TO_POINTER (children[i]);
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
g_free (children);
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
gtk_real_model_types_iter_children (GtkTreeModel *tree_model,
|
|
GtkTreeIter *iter,
|
|
GtkTreeIter *parent)
|
|
{
|
|
GType type;
|
|
GType* children;
|
|
|
|
type = GPOINTER_TO_INT (parent->user_data);
|
|
|
|
children = g_type_children (type, NULL);
|
|
|
|
if (!children || children[0] == G_TYPE_INVALID)
|
|
{
|
|
g_free (children);
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
iter->user_data = GINT_TO_POINTER (children[0]);
|
|
g_free (children);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
gtk_real_model_types_iter_has_child (GtkTreeModel *tree_model,
|
|
GtkTreeIter *iter)
|
|
{
|
|
GType type;
|
|
GType* children;
|
|
|
|
type = GPOINTER_TO_INT (iter->user_data);
|
|
|
|
children = g_type_children (type, NULL);
|
|
|
|
if (!children || children[0] == G_TYPE_INVALID)
|
|
{
|
|
g_free (children);
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
g_free (children);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
static gint
|
|
gtk_real_model_types_iter_n_children (GtkTreeModel *tree_model,
|
|
GtkTreeIter *iter)
|
|
{
|
|
if (iter == NULL)
|
|
{
|
|
return G_TYPE_LAST_RESERVED_FUNDAMENTAL - 1;
|
|
}
|
|
else
|
|
{
|
|
GType type;
|
|
GType* children;
|
|
guint n_children = 0;
|
|
|
|
type = GPOINTER_TO_INT (iter->user_data);
|
|
|
|
children = g_type_children (type, &n_children);
|
|
|
|
g_free (children);
|
|
|
|
return n_children;
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
gtk_real_model_types_iter_nth_child (GtkTreeModel *tree_model,
|
|
GtkTreeIter *iter,
|
|
GtkTreeIter *parent,
|
|
gint n)
|
|
{
|
|
if (parent == NULL)
|
|
{
|
|
/* fundamental type */
|
|
if (n < G_TYPE_LAST_RESERVED_FUNDAMENTAL)
|
|
{
|
|
iter->user_data = GINT_TO_POINTER (n);
|
|
return TRUE;
|
|
}
|
|
else
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
GType type = GPOINTER_TO_INT (parent->user_data);
|
|
guint n_children = 0;
|
|
GType* children = g_type_children (type, &n_children);
|
|
|
|
if (n_children == 0)
|
|
{
|
|
g_free (children);
|
|
return FALSE;
|
|
}
|
|
else if (n >= n_children)
|
|
{
|
|
g_free (children);
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
iter->user_data = GINT_TO_POINTER (children[n]);
|
|
g_free (children);
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
gtk_real_model_types_iter_parent (GtkTreeModel *tree_model,
|
|
GtkTreeIter *iter,
|
|
GtkTreeIter *child)
|
|
{
|
|
GType type;
|
|
GType parent;
|
|
|
|
type = GPOINTER_TO_INT (child->user_data);
|
|
|
|
parent = g_type_parent (type);
|
|
|
|
if (parent == G_TYPE_INVALID)
|
|
{
|
|
if (type >= G_TYPE_LAST_RESERVED_FUNDAMENTAL)
|
|
g_warning ("no parent for %d %s\n", type, g_type_name (type));
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
iter->user_data = GINT_TO_POINTER (parent);
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Property editor thingy
|
|
*/
|
|
|
|
static void
|
|
get_param_specs (GObject *object,
|
|
GParamSpec ***specs,
|
|
gint *n_specs)
|
|
{
|
|
/* Use private interface for now, fix later */
|
|
*specs = G_OBJECT_GET_CLASS (object)->property_specs;
|
|
*n_specs = G_OBJECT_GET_CLASS (object)->n_property_specs;
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
gpointer instance;
|
|
guint id;
|
|
} DisconnectData;
|
|
|
|
static void
|
|
disconnect_func (gpointer data)
|
|
{
|
|
DisconnectData *dd = data;
|
|
|
|
g_signal_handler_disconnect (dd->instance, dd->id);
|
|
g_free (dd);
|
|
}
|
|
|
|
static void
|
|
g_object_connect_property (GObject *object,
|
|
const gchar *prop_name,
|
|
GtkSignalFunc func,
|
|
gpointer data,
|
|
GObject *alive_object)
|
|
{
|
|
gchar *with_detail = g_strconcat ("notify::", prop_name, NULL);
|
|
DisconnectData *dd;
|
|
|
|
dd = g_new (DisconnectData, 1);
|
|
|
|
dd->id = g_signal_connect_data (object, with_detail,
|
|
func, data,
|
|
NULL, FALSE, FALSE);
|
|
|
|
dd->instance = object;
|
|
|
|
g_object_set_data_full (G_OBJECT (alive_object),
|
|
"alive-object",
|
|
dd,
|
|
disconnect_func);
|
|
|
|
g_free (with_detail);
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
GObject *obj;
|
|
gchar *prop;
|
|
} ObjectProperty;
|
|
|
|
static void
|
|
free_object_property (ObjectProperty *p)
|
|
{
|
|
g_free (p->prop);
|
|
g_free (p);
|
|
}
|
|
|
|
static void
|
|
connect_controller (GObject *controller,
|
|
const gchar *signal,
|
|
GObject *model,
|
|
const gchar *prop_name,
|
|
GtkSignalFunc func)
|
|
{
|
|
ObjectProperty *p;
|
|
|
|
p = g_new (ObjectProperty, 1);
|
|
p->obj = model;
|
|
p->prop = g_strdup (prop_name);
|
|
|
|
g_signal_connect_data (controller, signal, func, p,
|
|
(GClosureNotify)free_object_property,
|
|
FALSE, FALSE);
|
|
}
|
|
|
|
static void
|
|
int_modified (GtkAdjustment *adj, gpointer data)
|
|
{
|
|
ObjectProperty *p = data;
|
|
|
|
g_object_set (p->obj, p->prop, (int) adj->value, NULL);
|
|
}
|
|
|
|
static void
|
|
int_changed (GObject *object, GParamSpec *pspec, gpointer data)
|
|
{
|
|
GtkAdjustment *adj = GTK_ADJUSTMENT (data);
|
|
GValue val = { 0, };
|
|
|
|
g_value_init (&val, G_TYPE_INT);
|
|
g_object_get_property (object, pspec->name, &val);
|
|
|
|
if (g_value_get_int (&val) != (int)adj->value)
|
|
gtk_adjustment_set_value (adj, g_value_get_int (&val));
|
|
|
|
g_value_unset (&val);
|
|
}
|
|
|
|
|
|
static void
|
|
string_modified (GtkEntry *entry, gpointer data)
|
|
{
|
|
ObjectProperty *p = data;
|
|
gchar *text;
|
|
|
|
text = gtk_entry_get_text (entry);
|
|
|
|
g_object_set (p->obj, p->prop, text, NULL);
|
|
}
|
|
|
|
static void
|
|
string_changed (GObject *object, GParamSpec *pspec, gpointer data)
|
|
{
|
|
GtkEntry *entry = GTK_ENTRY (data);
|
|
GValue val = { 0, };
|
|
gchar *str;
|
|
gchar *text;
|
|
|
|
g_value_init (&val, G_TYPE_STRING);
|
|
g_object_get_property (object, pspec->name, &val);
|
|
|
|
str = g_value_get_string (&val);
|
|
if (str == NULL)
|
|
str = "";
|
|
text = gtk_entry_get_text (entry);
|
|
|
|
if (strcmp (str, text) != 0)
|
|
gtk_entry_set_text (entry, str);
|
|
|
|
g_value_unset (&val);
|
|
}
|
|
|
|
static void
|
|
bool_modified (GtkToggleButton *tb, gpointer data)
|
|
{
|
|
ObjectProperty *p = data;
|
|
|
|
g_object_set (p->obj, p->prop, (int) tb->active, NULL);
|
|
}
|
|
|
|
static void
|
|
bool_changed (GObject *object, GParamSpec *pspec, gpointer data)
|
|
{
|
|
GtkToggleButton *tb = GTK_TOGGLE_BUTTON (data);
|
|
GValue val = { 0, };
|
|
|
|
g_value_init (&val, G_TYPE_BOOLEAN);
|
|
g_object_get_property (object, pspec->name, &val);
|
|
|
|
if (g_value_get_boolean (&val) != tb->active)
|
|
gtk_toggle_button_set_active (tb, g_value_get_boolean (&val));
|
|
|
|
gtk_label_set_text (GTK_LABEL (GTK_BIN (tb)->child), g_value_get_boolean (&val) ?
|
|
"TRUE" : "FALSE");
|
|
|
|
g_value_unset (&val);
|
|
}
|
|
|
|
|
|
static void
|
|
enum_modified (GtkOptionMenu *om, gpointer data)
|
|
{
|
|
ObjectProperty *p = data;
|
|
gint i;
|
|
GParamSpec *spec;
|
|
GEnumClass *eclass;
|
|
|
|
spec = g_object_class_find_property (G_OBJECT_GET_CLASS (p->obj),
|
|
p->prop);
|
|
|
|
eclass = G_ENUM_CLASS (g_type_class_peek (spec->value_type));
|
|
|
|
i = gtk_option_menu_get_history (om);
|
|
|
|
g_object_set (p->obj, p->prop, eclass->values[i].value, NULL);
|
|
}
|
|
|
|
static void
|
|
enum_changed (GObject *object, GParamSpec *pspec, gpointer data)
|
|
{
|
|
GtkOptionMenu *om = GTK_OPTION_MENU (data);
|
|
GValue val = { 0, };
|
|
GEnumClass *eclass;
|
|
gint i;
|
|
|
|
eclass = G_ENUM_CLASS (g_type_class_peek (pspec->value_type));
|
|
|
|
g_value_init (&val, pspec->value_type);
|
|
g_object_get_property (object, pspec->name, &val);
|
|
|
|
i = 0;
|
|
while (i < eclass->n_values)
|
|
{
|
|
if (eclass->values[i].value == g_value_get_enum (&val))
|
|
break;
|
|
++i;
|
|
}
|
|
|
|
if (gtk_option_menu_get_history (om) != i)
|
|
gtk_option_menu_set_history (om, i);
|
|
|
|
g_value_unset (&val);
|
|
}
|
|
|
|
static GtkWidget*
|
|
create_prop_editor (GObject *object)
|
|
{
|
|
GtkWidget *win;
|
|
GtkWidget *vbox;
|
|
GtkWidget *hbox;
|
|
GtkWidget *label;
|
|
GtkWidget *prop_edit;
|
|
GtkWidget *sw;
|
|
gint n_specs = 0;
|
|
GParamSpec **specs = NULL;
|
|
gint i;
|
|
GtkAdjustment *adj;
|
|
|
|
win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
|
|
|
/* hold a strong ref to the object we're editing */
|
|
g_object_ref (G_OBJECT (object));
|
|
g_object_set_data_full (G_OBJECT (win), "model-object",
|
|
object, (GDestroyNotify)g_object_unref);
|
|
|
|
vbox = gtk_vbox_new (TRUE, 2);
|
|
|
|
sw = gtk_scrolled_window_new (NULL, NULL);
|
|
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
|
|
GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
|
|
|
|
gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (sw), vbox);
|
|
gtk_container_add (GTK_CONTAINER (win), sw);
|
|
|
|
get_param_specs (object, &specs, &n_specs);
|
|
|
|
i = 0;
|
|
while (i < n_specs)
|
|
{
|
|
GParamSpec *spec = specs[i];
|
|
gboolean can_modify;
|
|
|
|
prop_edit = NULL;
|
|
|
|
can_modify = ((spec->flags & G_PARAM_WRITABLE) != 0 &&
|
|
(spec->flags & G_PARAM_CONSTRUCT_ONLY) == 0);
|
|
|
|
if ((spec->flags & G_PARAM_READABLE) == 0)
|
|
{
|
|
/* can't display unreadable properties */
|
|
++i;
|
|
continue;
|
|
}
|
|
|
|
switch (spec->value_type)
|
|
{
|
|
case G_TYPE_INT:
|
|
hbox = gtk_hbox_new (FALSE, 10);
|
|
label = gtk_label_new (spec->nick);
|
|
gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
|
|
gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
|
|
adj = GTK_ADJUSTMENT (gtk_adjustment_new (G_PARAM_SPEC_INT (spec)->default_value,
|
|
G_PARAM_SPEC_INT (spec)->minimum,
|
|
G_PARAM_SPEC_INT (spec)->maximum,
|
|
1,
|
|
MAX ((G_PARAM_SPEC_INT (spec)->maximum -
|
|
G_PARAM_SPEC_INT (spec)->minimum) / 10, 1),
|
|
0.0));
|
|
|
|
prop_edit = gtk_spin_button_new (adj, 1.0, 0);
|
|
gtk_box_pack_end (GTK_BOX (hbox), prop_edit, FALSE, FALSE, 0);
|
|
|
|
gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
|
|
|
|
g_object_connect_property (object, spec->name,
|
|
GTK_SIGNAL_FUNC (int_changed),
|
|
adj, G_OBJECT (adj));
|
|
|
|
if (can_modify)
|
|
connect_controller (G_OBJECT (adj), "value_changed",
|
|
object, spec->name, (GtkSignalFunc) int_modified);
|
|
break;
|
|
|
|
case G_TYPE_STRING:
|
|
hbox = gtk_hbox_new (FALSE, 10);
|
|
label = gtk_label_new (spec->nick);
|
|
gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
|
|
gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
|
|
|
|
prop_edit = gtk_entry_new ();
|
|
gtk_box_pack_end (GTK_BOX (hbox), prop_edit, FALSE, FALSE, 0);
|
|
|
|
gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
|
|
|
|
g_object_connect_property (object, spec->name,
|
|
GTK_SIGNAL_FUNC (string_changed),
|
|
prop_edit, G_OBJECT (prop_edit));
|
|
|
|
if (can_modify)
|
|
connect_controller (G_OBJECT (prop_edit), "changed",
|
|
object, spec->name, (GtkSignalFunc) string_modified);
|
|
break;
|
|
|
|
case G_TYPE_BOOLEAN:
|
|
hbox = gtk_hbox_new (FALSE, 10);
|
|
label = gtk_label_new (spec->nick);
|
|
gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
|
|
gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
|
|
|
|
prop_edit = gtk_toggle_button_new_with_label ("");
|
|
gtk_box_pack_end (GTK_BOX (hbox), prop_edit, FALSE, FALSE, 0);
|
|
|
|
gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
|
|
|
|
g_object_connect_property (object, spec->name,
|
|
GTK_SIGNAL_FUNC (bool_changed),
|
|
prop_edit, G_OBJECT (prop_edit));
|
|
|
|
if (can_modify)
|
|
connect_controller (G_OBJECT (prop_edit), "toggled",
|
|
object, spec->name, (GtkSignalFunc) bool_modified);
|
|
break;
|
|
|
|
default:
|
|
if (g_type_is_a (spec->value_type, G_TYPE_ENUM))
|
|
{
|
|
GtkWidget *menu;
|
|
GEnumClass *eclass;
|
|
gint i;
|
|
|
|
hbox = gtk_hbox_new (FALSE, 10);
|
|
label = gtk_label_new (spec->nick);
|
|
gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
|
|
gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
|
|
|
|
prop_edit = gtk_option_menu_new ();
|
|
|
|
menu = gtk_menu_new ();
|
|
|
|
eclass = G_ENUM_CLASS (g_type_class_peek (spec->value_type));
|
|
|
|
i = 0;
|
|
while (i < eclass->n_values)
|
|
{
|
|
GtkWidget *mi;
|
|
|
|
mi = gtk_menu_item_new_with_label (eclass->values[i].value_name);
|
|
|
|
gtk_widget_show (mi);
|
|
|
|
gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi);
|
|
|
|
++i;
|
|
}
|
|
|
|
gtk_option_menu_set_menu (GTK_OPTION_MENU (prop_edit), menu);
|
|
|
|
gtk_box_pack_end (GTK_BOX (hbox), prop_edit, FALSE, FALSE, 0);
|
|
|
|
gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
|
|
|
|
g_object_connect_property (object, spec->name,
|
|
GTK_SIGNAL_FUNC (enum_changed),
|
|
prop_edit, G_OBJECT (prop_edit));
|
|
|
|
if (can_modify)
|
|
connect_controller (G_OBJECT (prop_edit), "changed",
|
|
object, spec->name, (GtkSignalFunc) enum_modified);
|
|
}
|
|
else
|
|
{
|
|
gchar *msg = g_strdup_printf ("%s: don't know how to edit type %s",
|
|
spec->nick, g_type_name (spec->value_type));
|
|
hbox = gtk_hbox_new (FALSE, 10);
|
|
label = gtk_label_new (msg);
|
|
g_free (msg);
|
|
gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
|
|
gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
|
|
gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (prop_edit)
|
|
{
|
|
if (!can_modify)
|
|
gtk_widget_set_sensitive (prop_edit, FALSE);
|
|
|
|
/* set initial value */
|
|
g_object_notify (object, spec->name);
|
|
}
|
|
|
|
++i;
|
|
}
|
|
|
|
gtk_window_set_default_size (GTK_WINDOW (win), 300, 500);
|
|
|
|
gtk_widget_show_all (win);
|
|
|
|
return win;
|
|
}
|
|
|
|
/*
|
|
* Automated testing
|
|
*/
|
|
|
|
static void
|
|
treestore_torture_recurse (GtkTreeStore *store,
|
|
GtkTreeIter *root,
|
|
gint depth)
|
|
{
|
|
GtkTreeModel *model;
|
|
gint i;
|
|
GtkTreeIter iter;
|
|
|
|
model = GTK_TREE_MODEL (store);
|
|
|
|
if (depth > 2)
|
|
return;
|
|
|
|
++depth;
|
|
|
|
gtk_tree_store_append (store, &iter, root);
|
|
|
|
gtk_tree_model_iter_children (model, &iter, root);
|
|
|
|
i = 0;
|
|
while (i < 100)
|
|
{
|
|
gtk_tree_store_append (store, &iter, root);
|
|
++i;
|
|
}
|
|
|
|
while (gtk_tree_model_iter_children (model, &iter, root))
|
|
gtk_tree_store_remove (store, &iter);
|
|
|
|
gtk_tree_store_append (store, &iter, root);
|
|
|
|
/* inserts before last node in tree */
|
|
i = 0;
|
|
while (i < 100)
|
|
{
|
|
gtk_tree_store_insert_before (store, &iter, root, &iter);
|
|
++i;
|
|
}
|
|
|
|
/* inserts after the node before the last node */
|
|
i = 0;
|
|
while (i < 100)
|
|
{
|
|
gtk_tree_store_insert_after (store, &iter, root, &iter);
|
|
++i;
|
|
}
|
|
|
|
/* inserts after the last node */
|
|
gtk_tree_store_append (store, &iter, root);
|
|
|
|
i = 0;
|
|
while (i < 100)
|
|
{
|
|
gtk_tree_store_insert_after (store, &iter, root, &iter);
|
|
++i;
|
|
}
|
|
|
|
/* remove everything again */
|
|
while (gtk_tree_model_iter_children (model, &iter, root))
|
|
gtk_tree_store_remove (store, &iter);
|
|
|
|
|
|
/* Prepends */
|
|
gtk_tree_store_prepend (store, &iter, root);
|
|
|
|
i = 0;
|
|
while (i < 100)
|
|
{
|
|
gtk_tree_store_prepend (store, &iter, root);
|
|
++i;
|
|
}
|
|
|
|
/* remove everything again */
|
|
while (gtk_tree_model_iter_children (model, &iter, root))
|
|
gtk_tree_store_remove (store, &iter);
|
|
|
|
gtk_tree_store_append (store, &iter, root);
|
|
gtk_tree_store_append (store, &iter, root);
|
|
gtk_tree_store_append (store, &iter, root);
|
|
gtk_tree_store_append (store, &iter, root);
|
|
|
|
while (gtk_tree_model_iter_children (model, &iter, root))
|
|
{
|
|
treestore_torture_recurse (store, &iter, depth);
|
|
gtk_tree_store_remove (store, &iter);
|
|
}
|
|
}
|
|
|
|
static void
|
|
run_automated_tests (void)
|
|
{
|
|
g_print ("Running automated tests...\n");
|
|
|
|
/* FIXME TreePath basic verification */
|
|
|
|
/* FIXME generic consistency checks on the models */
|
|
|
|
{
|
|
/* Make sure list store mutations don't crash anything */
|
|
GtkListStore *store;
|
|
GtkTreeModel *model;
|
|
gint i;
|
|
GtkTreeIter iter;
|
|
|
|
store = gtk_list_store_new_with_types (1, G_TYPE_INT);
|
|
|
|
model = GTK_TREE_MODEL (store);
|
|
|
|
i = 0;
|
|
while (i < 100)
|
|
{
|
|
gtk_list_store_append (store, &iter);
|
|
++i;
|
|
}
|
|
|
|
while (gtk_tree_model_get_first (model, &iter))
|
|
gtk_list_store_remove (store, &iter);
|
|
|
|
gtk_list_store_append (store, &iter);
|
|
|
|
/* inserts before last node in list */
|
|
i = 0;
|
|
while (i < 100)
|
|
{
|
|
gtk_list_store_insert_before (store, &iter, &iter);
|
|
++i;
|
|
}
|
|
|
|
/* inserts after the node before the last node */
|
|
i = 0;
|
|
while (i < 100)
|
|
{
|
|
gtk_list_store_insert_after (store, &iter, &iter);
|
|
++i;
|
|
}
|
|
|
|
/* inserts after the last node */
|
|
gtk_list_store_append (store, &iter);
|
|
|
|
i = 0;
|
|
while (i < 100)
|
|
{
|
|
gtk_list_store_insert_after (store, &iter, &iter);
|
|
++i;
|
|
}
|
|
|
|
/* remove everything again */
|
|
while (gtk_tree_model_get_first (model, &iter))
|
|
gtk_list_store_remove (store, &iter);
|
|
|
|
|
|
/* Prepends */
|
|
gtk_list_store_prepend (store, &iter);
|
|
|
|
i = 0;
|
|
while (i < 100)
|
|
{
|
|
gtk_list_store_prepend (store, &iter);
|
|
++i;
|
|
}
|
|
|
|
/* remove everything again */
|
|
while (gtk_tree_model_get_first (model, &iter))
|
|
gtk_list_store_remove (store, &iter);
|
|
|
|
g_object_unref (G_OBJECT (store));
|
|
}
|
|
|
|
{
|
|
/* Make sure tree store mutations don't crash anything */
|
|
GtkTreeStore *store;
|
|
|
|
store = gtk_tree_store_new_with_types (1, G_TYPE_INT);
|
|
|
|
treestore_torture_recurse (store, NULL, 0);
|
|
|
|
g_object_unref (G_OBJECT (store));
|
|
}
|
|
|
|
g_print ("Passed.\n");
|
|
}
|