medit/moo/mooutils/mooutils-treeview.c

1293 lines
38 KiB
C

/*
* mooutils-treeview.c
*
* Copyright (C) 2004-2006 by Yevgen Muntyan <muntyan@math.tamu.edu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* See COPYING file that comes with this distribution.
*/
#include "mooutils/mooutils-treeview.h"
#include "mooutils/moomarshals.h"
#define MOO_TYPE_TREE_HELPER (_moo_tree_helper_get_type ())
#define MOO_TREE_HELPER(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), MOO_TYPE_TREE_HELPER, MooTreeHelper))
#define MOO_TREE_HELPER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MOO_TYPE_TREE_HELPER, MooTreeHelperClass))
#define MOO_IS_TREE_HELPER(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), MOO_TYPE_TREE_HELPER))
#define MOO_IS_TREE_HELPER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MOO_TYPE_TREE_HELPER))
#define MOO_TREE_HELPER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MOO_TYPE_TREE_HELPER, MooTreeHelperClass))
typedef enum {
TREE_VIEW,
COMBO_BOX
} WidgetType;
struct _MooTreeHelper {
GtkObject parent;
WidgetType type;
gpointer widget;
GtkWidget *new_btn;
GtkWidget *delete_btn;
GtkWidget *up_btn;
GtkWidget *down_btn;
};
GType _moo_tree_helper_get_type (void) G_GNUC_CONST;
static void moo_tree_helper_new_row (MooTreeHelper *helper);
static void moo_tree_helper_delete_row (MooTreeHelper *helper);
static void moo_tree_helper_row_up (MooTreeHelper *helper);
static void moo_tree_helper_row_down (MooTreeHelper *helper);
static void moo_tree_helper_real_update_widgets (MooTreeHelper *helper,
GtkTreeModel *model,
GtkTreePath *path);
static GtkTreeModel *moo_tree_helper_get_model (MooTreeHelper *helper);
static gboolean moo_tree_helper_get_selected (MooTreeHelper *helper,
GtkTreeModel **model,
GtkTreeIter *iter);
G_DEFINE_TYPE (MooTreeHelper, _moo_tree_helper, GTK_TYPE_OBJECT)
enum {
NEW_ROW,
DELETE_ROW,
MOVE_ROW,
UPDATE_WIDGETS,
UPDATE_MODEL,
TREE_NUM_SIGNALS
};
static guint tree_signals[TREE_NUM_SIGNALS];
static void
tree_selection_changed (GtkTreeSelection *selection,
MooTreeHelper *helper)
{
GtkTreeRowReference *old_row;
GtkTreeModel *model;
GtkTreeIter iter;
GtkTreePath *path, *old_path;
old_row = g_object_get_data (G_OBJECT (selection), "moo-tree-helper-current-row");
old_path = old_row ? gtk_tree_row_reference_get_path (old_row) : NULL;
if (old_row && !old_path)
{
g_object_set_data (G_OBJECT (selection), "moo-tree-helper-current-row", NULL);
old_row = NULL;
}
if (gtk_tree_selection_get_selected (selection, &model, &iter))
{
path = gtk_tree_model_get_path (model, &iter);
if (old_path && !gtk_tree_path_compare (old_path, path))
{
gtk_tree_path_free (old_path);
gtk_tree_path_free (path);
return;
}
}
else
{
if (!old_path)
return;
path = NULL;
}
if (old_path)
moo_tree_helper_update_model (helper, model, old_path);
moo_tree_helper_real_update_widgets (helper, model, path);
if (path)
{
GtkTreeRowReference *row;
row = gtk_tree_row_reference_new (model, path);
g_object_set_data_full (G_OBJECT (selection), "moo-tree-helper-current-row", row,
(GDestroyNotify) gtk_tree_row_reference_free);
}
else
{
g_object_set_data (G_OBJECT (selection), "moo-tree-helper-current-row", NULL);
}
gtk_tree_path_free (path);
gtk_tree_path_free (old_path);
}
static void
combo_changed (GtkComboBox *combo,
MooTreeHelper *helper)
{
GtkTreeRowReference *old_row;
GtkTreeModel *model;
GtkTreeIter iter;
GtkTreePath *path, *old_path;
g_return_if_fail (MOO_IS_TREE_HELPER (helper));
g_return_if_fail (combo == helper->widget);
old_row = g_object_get_data (G_OBJECT (combo), "moo-tree-helper-current-row");
old_path = old_row ? gtk_tree_row_reference_get_path (old_row) : NULL;
if (old_row && !old_path)
{
g_object_set_data (G_OBJECT (combo), "moo-tree-helper-current-row", NULL);
old_row = NULL;
}
if (moo_tree_helper_get_selected (helper, &model, &iter))
{
path = gtk_tree_model_get_path (model, &iter);
if (old_path && !gtk_tree_path_compare (old_path, path))
{
gtk_tree_path_free (old_path);
gtk_tree_path_free (path);
return;
}
}
else
{
if (!old_path)
return;
path = NULL;
}
if (old_path)
moo_tree_helper_update_model (helper, model, old_path);
moo_tree_helper_real_update_widgets (helper, model, path);
if (path)
{
GtkTreeRowReference *row;
row = gtk_tree_row_reference_new (model, path);
g_object_set_data_full (G_OBJECT (combo), "moo-tree-helper-current-row", row,
(GDestroyNotify) gtk_tree_row_reference_free);
}
else
{
g_object_set_data (G_OBJECT (combo), "moo-tree-helper-current-row", NULL);
}
gtk_tree_path_free (path);
gtk_tree_path_free (old_path);
}
static void
moo_tree_helper_destroy (GtkObject *object)
{
MooTreeHelper *helper = MOO_TREE_HELPER (object);
if (helper->widget)
{
GtkTreeSelection *selection;
g_signal_handlers_disconnect_by_func (helper->widget,
(gpointer) gtk_object_destroy,
helper);
switch (helper->type)
{
case TREE_VIEW:
selection = gtk_tree_view_get_selection (helper->widget);
g_signal_handlers_disconnect_by_func (selection,
(gpointer) tree_selection_changed,
helper);
break;
case COMBO_BOX:
g_signal_handlers_disconnect_by_func (helper->widget,
(gpointer) combo_changed,
helper);
break;
}
if (helper->new_btn)
g_signal_handlers_disconnect_by_func (helper->new_btn,
(gpointer) moo_tree_helper_new_row,
helper);
if (helper->delete_btn)
g_signal_handlers_disconnect_by_func (helper->delete_btn,
(gpointer) moo_tree_helper_delete_row,
helper);
if (helper->up_btn)
g_signal_handlers_disconnect_by_func (helper->up_btn,
(gpointer) moo_tree_helper_row_up,
helper);
if (helper->down_btn)
g_signal_handlers_disconnect_by_func (helper->down_btn,
(gpointer) moo_tree_helper_row_down,
helper);
helper->widget = NULL;
}
GTK_OBJECT_CLASS (_moo_tree_helper_parent_class)->destroy (object);
}
static void
_moo_tree_helper_class_init (MooTreeHelperClass *klass)
{
GTK_OBJECT_CLASS(klass)->destroy = moo_tree_helper_destroy;
tree_signals[NEW_ROW] =
g_signal_new ("new-row",
G_OBJECT_CLASS_TYPE (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (MooTreeHelperClass, new_row),
g_signal_accumulator_true_handled, NULL,
_moo_marshal_BOOL__OBJECT_BOXED,
G_TYPE_BOOLEAN, 2,
GTK_TYPE_TREE_MODEL,
GTK_TYPE_TREE_PATH | G_SIGNAL_TYPE_STATIC_SCOPE);
tree_signals[DELETE_ROW] =
g_signal_new ("delete-row",
G_OBJECT_CLASS_TYPE (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (MooTreeHelperClass, delete_row),
g_signal_accumulator_true_handled, NULL,
_moo_marshal_BOOL__OBJECT_BOXED,
G_TYPE_BOOLEAN, 2,
GTK_TYPE_TREE_MODEL,
GTK_TYPE_TREE_PATH | G_SIGNAL_TYPE_STATIC_SCOPE);
tree_signals[MOVE_ROW] =
g_signal_new ("move-row",
G_OBJECT_CLASS_TYPE (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (MooTreeHelperClass, move_row),
g_signal_accumulator_true_handled, NULL,
_moo_marshal_BOOL__OBJECT_BOXED_BOXED,
G_TYPE_BOOLEAN, 3,
GTK_TYPE_TREE_MODEL,
GTK_TYPE_TREE_PATH | G_SIGNAL_TYPE_STATIC_SCOPE,
GTK_TYPE_TREE_PATH | G_SIGNAL_TYPE_STATIC_SCOPE);
tree_signals[UPDATE_WIDGETS] =
g_signal_new ("update-widgets",
G_OBJECT_CLASS_TYPE (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (MooTreeHelperClass, update_widgets),
NULL, NULL,
_moo_marshal_VOID__OBJECT_BOXED_BOXED,
G_TYPE_NONE, 3,
GTK_TYPE_TREE_MODEL,
GTK_TYPE_TREE_PATH | G_SIGNAL_TYPE_STATIC_SCOPE,
GTK_TYPE_TREE_ITER | G_SIGNAL_TYPE_STATIC_SCOPE);
tree_signals[UPDATE_MODEL] =
g_signal_new ("update-model",
G_OBJECT_CLASS_TYPE (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (MooTreeHelperClass, update_model),
NULL, NULL,
_moo_marshal_VOID__OBJECT_BOXED_BOXED,
G_TYPE_NONE, 3,
GTK_TYPE_TREE_MODEL,
GTK_TYPE_TREE_PATH | G_SIGNAL_TYPE_STATIC_SCOPE,
GTK_TYPE_TREE_ITER | G_SIGNAL_TYPE_STATIC_SCOPE);
}
void
moo_tree_view_select_first (GtkTreeView *tree_view)
{
GtkTreeSelection *selection;
GtkTreeModel *model;
GtkTreeIter iter;
g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
selection = gtk_tree_view_get_selection (tree_view);
model = gtk_tree_view_get_model (tree_view);
if (gtk_tree_model_get_iter_first (model, &iter))
gtk_tree_selection_select_iter (selection, &iter);
}
void
moo_combo_box_select_first (GtkComboBox *combo)
{
GtkTreeModel *model;
GtkTreeIter iter;
g_return_if_fail (GTK_IS_COMBO_BOX (combo));
model = gtk_combo_box_get_model (combo);
if (gtk_tree_model_get_iter_first (model, &iter))
gtk_combo_box_set_active_iter (combo, &iter);
}
static void
moo_tree_helper_real_update_widgets (MooTreeHelper *helper,
GtkTreeModel *model,
GtkTreePath *path)
{
GtkTreeIter iter;
if (!path)
{
if (helper->delete_btn)
gtk_widget_set_sensitive (helper->delete_btn, FALSE);
if (helper->up_btn)
gtk_widget_set_sensitive (helper->up_btn, FALSE);
if (helper->down_btn)
gtk_widget_set_sensitive (helper->down_btn, FALSE);
}
else
{
int *indices;
int n_rows;
if (helper->delete_btn)
gtk_widget_set_sensitive (helper->delete_btn, TRUE);
indices = gtk_tree_path_get_indices (path);
if (helper->up_btn)
gtk_widget_set_sensitive (helper->up_btn, indices[0] != 0);
n_rows = gtk_tree_model_iter_n_children (model, NULL);
if (helper->down_btn)
gtk_widget_set_sensitive (helper->down_btn, indices[0] != n_rows - 1);
}
if (path)
gtk_tree_model_get_iter (model, &iter, path);
g_signal_emit (helper, tree_signals[UPDATE_WIDGETS], 0,
model, path, path ? &iter : NULL);
}
void
moo_tree_helper_update_model (MooTreeHelper *helper,
GtkTreeModel *model,
GtkTreePath *path)
{
GtkTreePath *freeme = NULL;
GtkTreeIter iter;
g_return_if_fail (MOO_IS_TREE_HELPER (helper));
g_return_if_fail (!model || GTK_IS_TREE_MODEL (model));
if (!model)
model = moo_tree_helper_get_model (helper);
if (!path && moo_tree_helper_get_selected (helper, NULL, &iter))
path = freeme = gtk_tree_model_get_path (model, &iter);
if (path)
{
gtk_tree_model_get_iter (model, &iter, path);
g_signal_emit (helper, tree_signals[UPDATE_MODEL], 0,
model, path, &iter);
}
gtk_tree_path_free (freeme);
}
static GtkTreeModel *
moo_tree_helper_get_model (MooTreeHelper *helper)
{
switch (helper->type)
{
case TREE_VIEW:
return gtk_tree_view_get_model (helper->widget);
case COMBO_BOX:
return gtk_combo_box_get_model (helper->widget);
}
g_return_val_if_reached (NULL);
}
static gboolean
moo_tree_helper_get_selected (MooTreeHelper *helper,
GtkTreeModel **model,
GtkTreeIter *iter)
{
GtkTreeSelection *selection;
switch (helper->type)
{
case TREE_VIEW:
selection = gtk_tree_view_get_selection (helper->widget);
return gtk_tree_selection_get_selected (selection, model, iter);
case COMBO_BOX:
if (model)
*model = gtk_combo_box_get_model (helper->widget);
return gtk_combo_box_get_active_iter (helper->widget, iter);
}
g_return_val_if_reached (FALSE);
}
static int
iter_get_index (GtkTreeModel *model,
GtkTreeIter *iter)
{
int index;
GtkTreePath *path;
path = gtk_tree_model_get_path (model, iter);
if (!path)
return -1;
index = gtk_tree_path_get_indices(path)[0];
gtk_tree_path_free (path);
return index;
}
static void
moo_tree_helper_new_row (MooTreeHelper *helper)
{
GtkTreeModel *model;
GtkTreeIter iter;
GtkTreeSelection *selection;
GtkTreePath *path;
int index;
gboolean result;
g_return_if_fail (helper->type == TREE_VIEW);
selection = gtk_tree_view_get_selection (helper->widget);
if (!gtk_tree_selection_get_selected (selection, &model, &iter))
index = gtk_tree_model_iter_n_children (model, NULL);
else
index = iter_get_index (model, &iter) + 1;
path = gtk_tree_path_new_from_indices (index, -1);
g_signal_emit (helper, tree_signals[NEW_ROW], 0, model, path, &result);
if (result && gtk_tree_model_get_iter (model, &iter, path))
gtk_tree_selection_select_iter (selection, &iter);
gtk_tree_path_free (path);
}
static void
moo_tree_helper_delete_row (MooTreeHelper *helper)
{
GtkTreeModel *model;
GtkTreeIter iter;
GtkTreeSelection *selection;
GtkTreePath *path;
gboolean result;
g_return_if_fail (helper->type == TREE_VIEW);
selection = gtk_tree_view_get_selection (helper->widget);
if (!gtk_tree_selection_get_selected (selection, &model, &iter))
g_return_if_reached ();
path = gtk_tree_model_get_path (model, &iter);
g_signal_emit (helper, tree_signals[DELETE_ROW], 0, model, path, &result);
if (result && (gtk_tree_model_get_iter (model, &iter, path) ||
(gtk_tree_path_prev (path) && gtk_tree_model_get_iter (model, &iter, path))))
gtk_tree_selection_select_iter (selection, &iter);
else
moo_tree_helper_update_widgets (helper);
gtk_tree_path_free (path);
}
static void
moo_tree_helper_row_move (MooTreeHelper *helper,
gboolean up)
{
GtkTreeModel *model;
GtkTreeIter iter;
GtkTreePath *path, *new_path;
GtkTreeSelection *selection;
gboolean result;
g_return_if_fail (helper->type == TREE_VIEW);
selection = gtk_tree_view_get_selection (helper->widget);
if (!gtk_tree_selection_get_selected (selection, &model, &iter))
g_return_if_reached ();
path = gtk_tree_model_get_path (model, &iter);
new_path = gtk_tree_path_copy (path);
if (up && !gtk_tree_path_prev (new_path))
g_return_if_reached ();
else if (!up)
gtk_tree_path_next (new_path);
g_signal_emit (helper, tree_signals[MOVE_ROW], 0, model, path, new_path, &result);
if (result)
moo_tree_helper_real_update_widgets (helper, model, new_path);
gtk_tree_path_free (new_path);
gtk_tree_path_free (path);
}
static void
moo_tree_helper_row_up (MooTreeHelper *helper)
{
moo_tree_helper_row_move (helper, TRUE);
}
static void
moo_tree_helper_row_down (MooTreeHelper *helper)
{
moo_tree_helper_row_move (helper, FALSE);
}
static void
_moo_tree_helper_init (G_GNUC_UNUSED MooTreeHelper *helper)
{
}
static void
moo_tree_helper_connect (MooTreeHelper *helper,
GtkWidget *widget,
GtkWidget *new_btn,
GtkWidget *delete_btn,
GtkWidget *up_btn,
GtkWidget *down_btn)
{
GtkTreeSelection *selection;
g_return_if_fail (MOO_IS_TREE_HELPER (helper));
g_return_if_fail (GTK_IS_TREE_VIEW (widget) || GTK_IS_COMBO_BOX (widget));
g_return_if_fail (helper->widget == NULL);
helper->widget = widget;
if (GTK_IS_TREE_VIEW (widget))
helper->type = TREE_VIEW;
else
helper->type = COMBO_BOX;
helper->new_btn = new_btn;
helper->delete_btn = delete_btn;
helper->up_btn = up_btn;
helper->down_btn = down_btn;
g_signal_connect_swapped (widget, "destroy",
G_CALLBACK (gtk_object_destroy),
helper);
switch (helper->type)
{
case TREE_VIEW:
selection = gtk_tree_view_get_selection (helper->widget);
g_signal_connect (selection, "changed",
G_CALLBACK (tree_selection_changed),
helper);
break;
case COMBO_BOX:
g_signal_connect (widget, "changed",
G_CALLBACK (combo_changed),
helper);
break;
}
if (new_btn)
g_signal_connect_swapped (new_btn, "clicked",
G_CALLBACK (moo_tree_helper_new_row),
helper);
if (delete_btn)
g_signal_connect_swapped (delete_btn, "clicked",
G_CALLBACK (moo_tree_helper_delete_row),
helper);
if (up_btn)
g_signal_connect_swapped (up_btn, "clicked",
G_CALLBACK (moo_tree_helper_row_up),
helper);
if (down_btn)
g_signal_connect_swapped (down_btn, "clicked",
G_CALLBACK (moo_tree_helper_row_down),
helper);
}
MooTreeHelper *
moo_tree_helper_new (GtkWidget *widget,
GtkWidget *new_btn,
GtkWidget *delete_btn,
GtkWidget *up_btn,
GtkWidget *down_btn)
{
MooTreeHelper *helper;
g_return_val_if_fail (GTK_IS_TREE_VIEW (widget) || GTK_IS_COMBO_BOX (widget), NULL);
helper = g_object_new (MOO_TYPE_TREE_HELPER, NULL);
gtk_object_sink (g_object_ref (helper));
moo_tree_helper_connect (helper, widget, new_btn, delete_btn, up_btn, down_btn);
return helper;
}
void
moo_tree_helper_update_widgets (MooTreeHelper *helper)
{
GtkTreeModel *model;
GtkTreeIter iter;
g_return_if_fail (MOO_IS_TREE_HELPER (helper));
if (moo_tree_helper_get_selected (helper, &model, &iter))
{
GtkTreePath *path = gtk_tree_model_get_path (model, &iter);
moo_tree_helper_real_update_widgets (MOO_TREE_HELPER (helper), model, path);
gtk_tree_path_free (path);
}
else
{
moo_tree_helper_real_update_widgets (MOO_TREE_HELPER (helper), NULL, NULL);
}
}
/****************************************************************************/
/* MooConfigHelper
*/
#define MOO_TYPE_CONFIG_HELPER (_moo_config_helper_get_type ())
#define MOO_CONFIG_HELPER(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), MOO_TYPE_CONFIG_HELPER, MooConfigHelper))
#define MOO_CONFIG_HELPER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MOO_TYPE_CONFIG_HELPER, MooConfigHelperClass))
#define MOO_IS_CONFIG_HELPER(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), MOO_TYPE_CONFIG_HELPER))
#define MOO_IS_CONFIG_HELPER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MOO_TYPE_CONFIG_HELPER))
#define MOO_CONFIG_HELPER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MOO_TYPE_CONFIG_HELPER, MooConfigHelperClass))
enum {
NEW_ITEM,
SET_FROM_ITEM,
SET_FROM_WIDGETS,
CONFIG_NUM_SIGNALS
};
static guint config_signals[CONFIG_NUM_SIGNALS];
typedef void (*MooConfigItemToWidget) (MooConfig *config,
MooConfigItem *item,
GtkWidget *widget,
gpointer data);
typedef void (*MooConfigWidgetToItem) (GtkWidget *widget,
MooConfig *config,
MooConfigItem *item,
gpointer data);
typedef struct {
char *key;
gboolean update_live;
gulong handler;
MooConfigItemToWidget item_to_widget;
MooConfigWidgetToItem widget_to_item;
gpointer data;
} WidgetInfo;
struct _MooConfigHelper {
MooTreeHelper parent;
GSList *widgets;
};
GType _moo_config_helper_get_type (void) G_GNUC_CONST;
G_DEFINE_TYPE (MooConfigHelper, _moo_config_helper, MOO_TYPE_TREE_HELPER)
static WidgetInfo *
get_info (gpointer widget)
{
return g_object_get_data (G_OBJECT (widget), "moo-config-helper-widget-info");
}
static void
moo_config_helper_destroy (GtkObject *object)
{
MooConfigHelper *helper = MOO_CONFIG_HELPER (object);
if (helper->widgets)
{
GSList *l;
for (l = helper->widgets; l != NULL; l = l->next)
{
GtkWidget *widget = l->data;
WidgetInfo *info = get_info (widget);
if (info->handler)
g_signal_handler_disconnect (widget, info->handler);
g_object_set_data (G_OBJECT (widget), "moo-config-helper-widget-info", NULL);
}
g_slist_free (helper->widgets);
helper->widgets = NULL;
}
GTK_OBJECT_CLASS(_moo_config_helper_parent_class)->destroy (object);
}
static gboolean
moo_config_helper_new_row (MooTreeHelper *tree_helper,
GtkTreeModel *model,
GtkTreePath *path)
{
int index;
MooConfigItem *item;
MooConfigHelper *helper = MOO_CONFIG_HELPER (tree_helper);
g_return_val_if_fail (path != NULL, FALSE);
g_return_val_if_fail (gtk_tree_path_get_depth (path) == 1, FALSE);
index = gtk_tree_path_get_indices(path)[0];
item = moo_config_new_item (MOO_CONFIG (model), index, TRUE);
g_return_val_if_fail (item != NULL, FALSE);
g_signal_emit (helper, config_signals[NEW_ITEM], 0, model, item);
return TRUE;
}
static gboolean
moo_config_helper_delete_row (G_GNUC_UNUSED MooTreeHelper *tree_helper,
GtkTreeModel *model,
GtkTreePath *path)
{
int index;
g_return_val_if_fail (path != NULL, FALSE);
g_return_val_if_fail (gtk_tree_path_get_depth (path) == 1, FALSE);
index = gtk_tree_path_get_indices(path)[0];
moo_config_delete_item (MOO_CONFIG (model), index, TRUE);
return TRUE;
}
static gboolean
moo_config_helper_move_row (G_GNUC_UNUSED MooTreeHelper *helper,
GtkTreeModel *model,
GtkTreePath *old_path,
GtkTreePath *new_path)
{
g_return_val_if_fail (old_path && new_path, FALSE);
g_return_val_if_fail (gtk_tree_path_get_depth (old_path) == 1, FALSE);
g_return_val_if_fail (gtk_tree_path_get_depth (new_path) == 1, FALSE);
moo_config_move_item (MOO_CONFIG (model),
gtk_tree_path_get_indices(old_path)[0],
gtk_tree_path_get_indices(new_path)[0],
TRUE);
return TRUE;
}
static void
moo_config_helper_update_widgets_real (MooTreeHelper *tree_helper,
GtkTreeModel *model,
GtkTreePath *path,
GtkTreeIter *iter)
{
GSList *l;
MooConfigItem *item = NULL;
MooConfigHelper *helper = MOO_CONFIG_HELPER (tree_helper);
g_return_if_fail (MOO_IS_CONFIG (model));
if (path)
{
gtk_tree_model_get (model, iter, 0, &item, -1);
g_return_if_fail (item != NULL);
}
for (l = helper->widgets; l != NULL; l = l->next)
{
GtkWidget *widget;
WidgetInfo *info;
widget = l->data;
info = get_info (widget);
if (info->handler)
g_signal_handler_block (widget, info->handler);
gtk_widget_set_sensitive (widget, item != NULL);
if (item)
{
info->item_to_widget (MOO_CONFIG (model), item, widget, info->data);
}
else
{
if (GTK_IS_TOGGLE_BUTTON (widget))
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), FALSE);
else if (GTK_IS_ENTRY (widget))
gtk_entry_set_text (GTK_ENTRY (widget), "");
else
gtk_text_buffer_set_text (gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget)),
"", -1);
}
if (info->handler)
g_signal_handler_unblock (widget, info->handler);
}
}
static void
moo_config_helper_update_model_real (MooTreeHelper *tree_helper,
GtkTreeModel *model,
GtkTreePath *path,
GtkTreeIter *iter)
{
GSList *l;
MooConfigItem *item = NULL;
MooConfigHelper *helper = MOO_CONFIG_HELPER (tree_helper);
g_return_if_fail (MOO_IS_CONFIG (model));
g_return_if_fail (path != NULL);
gtk_tree_model_get (model, iter, 0, &item, -1);
if (!item)
return;
for (l = helper->widgets; l != NULL; l = l->next)
{
GtkWidget *widget;
WidgetInfo *info;
widget = l->data;
info = get_info (widget);
info->widget_to_item (widget, MOO_CONFIG (model), item, info->data);
}
}
static void
_moo_config_helper_class_init (MooConfigHelperClass *klass)
{
MooTreeHelperClass *tree_class = MOO_TREE_HELPER_CLASS (klass);
GTK_OBJECT_CLASS(klass)->destroy = moo_config_helper_destroy;
tree_class->new_row = moo_config_helper_new_row;
tree_class->delete_row = moo_config_helper_delete_row;
tree_class->move_row = moo_config_helper_move_row;
tree_class->update_widgets = moo_config_helper_update_widgets_real;
tree_class->update_model = moo_config_helper_update_model_real;
config_signals[NEW_ITEM] =
g_signal_new ("new-item",
G_OBJECT_CLASS_TYPE (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (MooConfigHelperClass, new_item),
NULL, NULL,
_moo_marshal_VOID__OBJECT_POINTER,
G_TYPE_NONE, 2,
MOO_TYPE_CONFIG,
MOO_TYPE_CONFIG_ITEM);
config_signals[SET_FROM_ITEM] =
g_signal_new ("set-from-item",
G_OBJECT_CLASS_TYPE (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (MooConfigHelperClass, set_from_item),
NULL, NULL,
_moo_marshal_VOID__OBJECT_POINTER,
G_TYPE_NONE, 2,
MOO_TYPE_CONFIG,
MOO_TYPE_CONFIG_ITEM);
config_signals[SET_FROM_WIDGETS] =
g_signal_new ("set-from-widgets",
G_OBJECT_CLASS_TYPE (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (MooConfigHelperClass, set_from_widgets),
NULL, NULL,
_moo_marshal_VOID__OBJECT_POINTER,
G_TYPE_NONE, 2,
MOO_TYPE_CONFIG,
MOO_TYPE_CONFIG_ITEM);
}
static void
_moo_config_helper_init (MooConfigHelper *helper)
{
helper->widgets = NULL;
}
MooConfigHelper *
moo_config_helper_new (GtkWidget *tree_view,
GtkWidget *new_btn,
GtkWidget *delete_btn,
GtkWidget *up_btn,
GtkWidget *down_btn)
{
MooConfigHelper *helper;
g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL);
helper = g_object_new (MOO_TYPE_CONFIG_HELPER, NULL);
gtk_object_sink (g_object_ref (helper));
moo_tree_helper_connect (MOO_TREE_HELPER (helper), tree_view,
new_btn, delete_btn, up_btn, down_btn);
return helper;
}
static void
button_toggled (MooTreeHelper *helper,
GtkToggleButton *button)
{
GtkTreeModel *model;
GtkTreeIter iter;
MooConfigItem *item = NULL;
WidgetInfo *info;
GtkTreeSelection *selection;
selection = gtk_tree_view_get_selection (helper->widget);
if (!gtk_tree_selection_get_selected (selection, &model, &iter))
return;
info = get_info (button);
g_return_if_fail (info != NULL);
gtk_tree_model_get (model, &iter, 0, &item, -1);
g_return_if_fail (item != NULL);
moo_config_set_bool (MOO_CONFIG (model), item, info->key,
gtk_toggle_button_get_active (button), TRUE);
}
static void
entry_changed (MooTreeHelper *helper,
GtkEntry *entry)
{
GtkTreeModel *model;
GtkTreeIter iter;
const char *text;
MooConfigItem *item = NULL;
WidgetInfo *info;
GtkTreeSelection *selection;
selection = gtk_tree_view_get_selection (helper->widget);
if (!gtk_tree_selection_get_selected (selection, &model, &iter))
return;
info = get_info (entry);
g_return_if_fail (info != NULL);
text = gtk_entry_get_text (entry);
gtk_tree_model_get (model, &iter, 0, &item, -1);
g_return_if_fail (item != NULL);
if (info->key)
moo_config_set (MOO_CONFIG (model), item,
info->key, text, TRUE);
else
moo_config_set_item_content (MOO_CONFIG (model), item,
text, TRUE);
}
static void
toggle_button_to_item (GtkToggleButton *button,
MooConfig *config,
MooConfigItem *item)
{
gboolean value;
WidgetInfo *info = get_info (button);
g_return_if_fail (info != NULL);
value = gtk_toggle_button_get_active (button);
moo_config_set_bool (config, item, info->key, value, TRUE);
}
static void
entry_to_item (GtkEntry *entry,
MooConfig *config,
MooConfigItem *item)
{
const char *text;
WidgetInfo *info = get_info (entry);
g_return_if_fail (info != NULL);
text = gtk_entry_get_text (entry);
if (info->key)
moo_config_set (config, item, info->key, text, TRUE);
else
moo_config_set_item_content (config, item, text, TRUE);
}
static void
text_view_to_item (GtkTextView *view,
MooConfig *config,
MooConfigItem *item)
{
char *text;
GtkTextBuffer *buffer;
GtkTextIter start, end;
WidgetInfo *info = get_info (view);
g_return_if_fail (info != NULL);
buffer = gtk_text_view_get_buffer (view);
gtk_text_buffer_get_bounds (buffer, &start, &end);
text = gtk_text_buffer_get_slice (buffer, &start, &end, TRUE);
if (!text[0])
{
g_free (text);
text = NULL;
}
if (info->key)
moo_config_set (config, item, info->key, text, TRUE);
else
moo_config_set_item_content (config, item, text, TRUE);
g_free (text);
}
static MooConfigWidgetToItem
get_widget_to_item_func (GtkWidget *widget)
{
if (GTK_IS_TOGGLE_BUTTON (widget))
return (MooConfigWidgetToItem) toggle_button_to_item;
else if (GTK_IS_ENTRY (widget))
return (MooConfigWidgetToItem) entry_to_item;
else if (GTK_IS_TEXT_VIEW (widget))
return (MooConfigWidgetToItem) text_view_to_item;
else
return NULL;
}
static void
item_to_toggle_button (MooConfig *config,
MooConfigItem *item,
GtkToggleButton *button)
{
gboolean value;
WidgetInfo *info = get_info (button);
g_return_if_fail (info != NULL);
value = moo_config_get_bool (config, item, info->key);
gtk_toggle_button_set_active (button, value);
}
static void
item_to_entry (G_GNUC_UNUSED MooConfig *config,
MooConfigItem *item,
GtkEntry *entry)
{
const char *text;
WidgetInfo *info = get_info (entry);
g_return_if_fail (info != NULL);
if (info->key)
text = moo_config_item_get (item, info->key);
else
text = moo_config_item_get_content (item);
gtk_entry_set_text (entry, text ? text : "");
}
static void
item_to_text_view (G_GNUC_UNUSED MooConfig *config,
MooConfigItem *item,
GtkTextView *view)
{
const char *text;
GtkTextBuffer *buffer;
WidgetInfo *info = get_info (view);
g_return_if_fail (info != NULL);
if (info->key)
text = moo_config_item_get (item, info->key);
else
text = moo_config_item_get_content (item);
buffer = gtk_text_view_get_buffer (view);
gtk_text_buffer_set_text (buffer, text ? text : "", -1);
}
static MooConfigItemToWidget
get_item_to_widget_func (GtkWidget *widget)
{
if (GTK_IS_TOGGLE_BUTTON (widget))
return (MooConfigItemToWidget) item_to_toggle_button;
else if (GTK_IS_ENTRY (widget))
return (MooConfigItemToWidget) item_to_entry;
else if (GTK_IS_TEXT_VIEW (widget))
return (MooConfigItemToWidget) item_to_text_view;
else
return NULL;
}
static WidgetInfo *
widget_info_new (const char *key,
gboolean update_live)
{
WidgetInfo *info = g_new0 (WidgetInfo, 1);
info->key = g_strdup (key);
info->update_live = update_live;
return info;
}
static void
widget_info_free (WidgetInfo *info)
{
if (info)
{
g_free (info->key);
g_free (info);
}
}
static void
moo_config_helper_add_widget_full (MooConfigHelper *helper,
GtkWidget *widget,
const char *key,
MooConfigWidgetToItem widget_to_item_func,
MooConfigItemToWidget item_to_widget_func,
gpointer data,
gboolean update_live)
{
WidgetInfo *info;
g_return_if_fail (!update_live || key != NULL);
g_return_if_fail (GTK_IS_TOGGLE_BUTTON (widget) ||
GTK_IS_ENTRY (widget) || GTK_IS_TEXT_VIEW (widget));
if (!widget_to_item_func)
{
widget_to_item_func = get_widget_to_item_func (widget);
g_return_if_fail (widget_to_item_func != NULL);
}
if (!item_to_widget_func)
{
item_to_widget_func = get_item_to_widget_func (widget);
g_return_if_fail (item_to_widget_func != NULL);
}
info = widget_info_new (key, update_live);
info->widget_to_item = widget_to_item_func;
info->item_to_widget = item_to_widget_func;
info->data = data;
helper->widgets = g_slist_prepend (helper->widgets, widget);
g_object_set_data_full (G_OBJECT (widget), "moo-config-helper-widget-info", info,
(GDestroyNotify) widget_info_free);
if (update_live)
{
if (GTK_IS_TOGGLE_BUTTON (widget))
info->handler =
g_signal_connect_swapped (widget, "toggled",
G_CALLBACK (button_toggled),
helper);
else if (GTK_IS_ENTRY (widget))
info->handler =
g_signal_connect_swapped (widget, "changed",
G_CALLBACK (entry_changed),
helper);
}
}
void
moo_config_helper_add_widget (MooConfigHelper *helper,
GtkWidget *widget,
const char *key,
gboolean update_live)
{
g_return_if_fail (MOO_IS_CONFIG_HELPER (helper));
g_return_if_fail (GTK_IS_WIDGET (widget));
moo_config_helper_add_widget_full (helper, widget, key,
NULL, NULL, NULL,
update_live);
}
void
moo_config_helper_update_model (MooConfigHelper *helper,
GtkTreeModel *model,
GtkTreePath *path)
{
g_return_if_fail (MOO_IS_CONFIG_HELPER (helper));
moo_tree_helper_update_model (MOO_TREE_HELPER (helper), model, path);
}