diff --git a/src/gnome-utils/Makefile.am b/src/gnome-utils/Makefile.am index b054f31f62..fd401182ad 100644 --- a/src/gnome-utils/Makefile.am +++ b/src/gnome-utils/Makefile.am @@ -82,6 +82,7 @@ libgncmod_gnome_utils_la_SOURCES = \ gnc-tree-model.c \ gnc-tree-model-account-types.c \ gnc-tree-model-account.c \ + gnc-tree-model-account-drag.c \ gnc-tree-model-budget.c \ gnc-tree-model-commodity.c \ gnc-tree-model-price.c \ @@ -150,6 +151,7 @@ gncinclude_HEADERS = \ gnc-tree-model.h \ gnc-tree-model-account-types.h \ gnc-tree-model-account.h \ + gnc-tree-model-account-drag.h \ gnc-tree-model-budget.h \ gnc-tree-model-commodity.h \ gnc-tree-model-price.h \ diff --git a/src/gnome-utils/gnc-tree-model-account-drag.c b/src/gnome-utils/gnc-tree-model-account-drag.c new file mode 100644 index 0000000000..31250cc9c3 --- /dev/null +++ b/src/gnome-utils/gnc-tree-model-account-drag.c @@ -0,0 +1,732 @@ +/* gnc-tree-model-account-drag.c + * Copyright (C) 2009 Matt Lavin + * + * 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, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/** @addtogroup GUI + @{ */ +/** @addtogroup GuiTreeModel GnuCash Tree Model + @{ */ +/** @file gnc-tree-model-account-drag.c + @brief GtkTreeModel wrapper that supports dragging Accounts for reparenting + @author Matt Lavin +*/ + +#include "gnc-tree-model-account-drag.h" +#include "gnc-component-manager.h" + +#include + +/** Static Globals *******************************************************/ +static QofLogModule log_module = GNC_MOD_GUI; + +/* general (object/interface init, etc) */ +static void gnc_tree_model_account_drag_tree_model_init (GtkTreeModelIface *iface); +static void gnc_tree_model_account_drag_tree_sortable_init (GtkTreeSortableIface *iface); +static void gnc_tree_model_account_drag_drag_source_init (GtkTreeDragSourceIface*iface); +static void gnc_tree_model_account_drag_drag_dest_init (GtkTreeDragDestIface *iface); +static void gnc_tree_model_account_drag_finalize (GObject *object); + +/* TreeModel signal handlers */ +static void gnc_tree_model_account_drag_row_changed(GtkTreeModel *c_model, + GtkTreePath *c_path, + GtkTreeIter *c_iter, + gpointer data); +static void gnc_tree_model_account_drag_row_inserted(GtkTreeModel *c_model, + GtkTreePath *c_path, + GtkTreeIter *c_iter, + gpointer data); +static void gnc_tree_model_account_drag_row_has_child_toggled(GtkTreeModel *c_model, + GtkTreePath *c_path, + GtkTreeIter *c_iter, + gpointer data); +static void gnc_tree_model_account_drag_row_deleted(GtkTreeModel *c_model, + GtkTreePath *c_path, + gpointer data); +static void gnc_tree_model_account_drag_rows_reordered(GtkTreeModel *c_model, + GtkTreePath *c_path, + GtkTreeIter *c_iter, + gint *new_order, + gpointer data); + +/* TreeModel interface */ +static GtkTreeModelFlags gnc_tree_model_account_drag_get_flags (GtkTreeModel *tree_model); +static gint gnc_tree_model_account_drag_get_n_columns (GtkTreeModel *tree_model); +static GType gnc_tree_model_account_drag_get_column_type (GtkTreeModel *tree_model, + gint index); +static gboolean gnc_tree_model_account_drag_get_iter (GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreePath *path); +static GtkTreePath *gnc_tree_model_account_drag_get_path (GtkTreeModel *tree_model, + GtkTreeIter *iter); +static void gnc_tree_model_account_drag_get_value (GtkTreeModel *tree_model, + GtkTreeIter *iter, + gint column, + GValue *value); +static gboolean gnc_tree_model_account_drag_iter_next (GtkTreeModel *tree_model, + GtkTreeIter *iter); +static gboolean gnc_tree_model_account_drag_iter_children (GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreeIter *parent); +static gboolean gnc_tree_model_account_drag_iter_has_child (GtkTreeModel *tree_model, + GtkTreeIter *iter); +static gint gnc_tree_model_account_drag_iter_n_children (GtkTreeModel *tree_model, + GtkTreeIter *iter); +static gboolean gnc_tree_model_account_drag_iter_nth_child (GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreeIter *parent, + gint n); +static gboolean gnc_tree_model_account_drag_iter_parent (GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreeIter *child); +static void gnc_tree_model_account_drag_ref_node (GtkTreeModel *tree_model, + GtkTreeIter *iter); +static void gnc_tree_model_account_drag_unref_node (GtkTreeModel *tree_model, + GtkTreeIter *iter); + +/* TreeDragSource interface */ +static gboolean gnc_tree_model_account_drag_row_draggable (GtkTreeDragSource *drag_source, + GtkTreePath *path); +static gboolean gnc_tree_model_account_drag_drag_data_get (GtkTreeDragSource *drag_source, + GtkTreePath *path, + GtkSelectionData *selection_data); +static gboolean gnc_tree_model_account_drag_drag_data_delete (GtkTreeDragSource *drag_source, + GtkTreePath *path); + +/* TreeDragDest interface */ +static gboolean gnc_tree_model_account_drag_drag_data_received(GtkTreeDragDest *drag_dest, + GtkTreePath *dest, + GtkSelectionData *selection_data); + +static gboolean gnc_tree_model_account_drag_row_drop_possible(GtkTreeDragDest *drag_dest, + GtkTreePath *dest_path, + GtkSelectionData *selection_data); + + +/* TreeSortable interface */ +static gboolean gnc_tree_model_account_drag_get_sort_column_id (GtkTreeSortable *sortable, + gint *sort_column_id, + GtkSortType *order); +static void gnc_tree_model_account_drag_set_sort_column_id (GtkTreeSortable *sortable, + gint sort_column_id, + GtkSortType order); +static void gnc_tree_model_account_drag_set_sort_func (GtkTreeSortable *sortable, + gint sort_column_id, + GtkTreeIterCompareFunc func, + gpointer data, + GDestroyNotify destroy); +static void gnc_tree_model_account_drag_set_default_sort_func (GtkTreeSortable *sortable, + GtkTreeIterCompareFunc func, + gpointer data, + GDestroyNotify destroy); +static gboolean gnc_tree_model_account_drag_has_default_sort_func (GtkTreeSortable *sortable); + +G_DEFINE_TYPE_WITH_CODE (GncTreeModelAccountDrag, gnc_tree_model_account_drag, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL, + gnc_tree_model_account_drag_tree_model_init) + G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_SORTABLE, + gnc_tree_model_account_drag_tree_sortable_init) + G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_SOURCE, + gnc_tree_model_account_drag_drag_source_init) + G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_DEST, + gnc_tree_model_account_drag_drag_dest_init)) + + +static void +gnc_tree_model_account_drag_init (GncTreeModelAccountDrag *tree_model_drag) +{ +} + +static void +gnc_tree_model_account_drag_class_init (GncTreeModelAccountDragClass *class) +{ + GObjectClass *object_class; + + object_class = (GObjectClass *) class; + + object_class->finalize = gnc_tree_model_account_drag_finalize; +} + + +static void +gnc_tree_model_account_drag_tree_model_init (GtkTreeModelIface *iface) +{ + iface->get_flags = gnc_tree_model_account_drag_get_flags; + iface->get_n_columns = gnc_tree_model_account_drag_get_n_columns; + iface->get_column_type = gnc_tree_model_account_drag_get_column_type; + iface->get_iter = gnc_tree_model_account_drag_get_iter; + iface->get_path = gnc_tree_model_account_drag_get_path; + iface->get_value = gnc_tree_model_account_drag_get_value; + iface->iter_next = gnc_tree_model_account_drag_iter_next; + iface->iter_children = gnc_tree_model_account_drag_iter_children; + iface->iter_has_child = gnc_tree_model_account_drag_iter_has_child; + iface->iter_n_children = gnc_tree_model_account_drag_iter_n_children; + iface->iter_nth_child = gnc_tree_model_account_drag_iter_nth_child; + iface->iter_parent = gnc_tree_model_account_drag_iter_parent; + iface->ref_node = gnc_tree_model_account_drag_ref_node; + iface->unref_node = gnc_tree_model_account_drag_unref_node; +} + +static void +gnc_tree_model_account_drag_tree_sortable_init (GtkTreeSortableIface *iface) +{ + iface->get_sort_column_id = gnc_tree_model_account_drag_get_sort_column_id; + iface->set_sort_column_id = gnc_tree_model_account_drag_set_sort_column_id; + iface->set_sort_func = gnc_tree_model_account_drag_set_sort_func; + iface->set_default_sort_func = gnc_tree_model_account_drag_set_default_sort_func; + iface->has_default_sort_func = gnc_tree_model_account_drag_has_default_sort_func; +} + +static void +gnc_tree_model_account_drag_drag_source_init (GtkTreeDragSourceIface *iface) +{ + iface->row_draggable = gnc_tree_model_account_drag_row_draggable; + iface->drag_data_delete = gnc_tree_model_account_drag_drag_data_delete; + iface->drag_data_get = gnc_tree_model_account_drag_drag_data_get; +} + +static void +gnc_tree_model_account_drag_drag_dest_init (GtkTreeDragDestIface *iface) +{ + iface->drag_data_received= gnc_tree_model_account_drag_drag_data_received; + iface->row_drop_possible = gnc_tree_model_account_drag_row_drop_possible; +} + +/** + * gnc_tree_model_account_drag_new_with_model: + * @child_model: A #GtkTreeModel + * + * Creates a new #GtkTreeModel, with @child_model as the child model. + * + * Return value: A new #GtkTreeModel. + */ +GtkTreeModel * +gnc_tree_model_account_drag_new_with_model (GtkTreeModel *child_model, + GtkWidget* widget, + GncTreeModelAccountDragLookupFunc lookup_func, + gpointer user_data) +{ + GtkTreeModel *retval; + GncTreeModelAccountDrag* model; + + g_return_val_if_fail (GTK_IS_TREE_MODEL (child_model), NULL); + g_return_val_if_fail (lookup_func != NULL, NULL); + + retval = g_object_new (gnc_tree_model_account_drag_get_type (), NULL); + + model = GNC_TREE_MODEL_ACCOUNT_DRAG(retval); + g_object_ref(child_model); + model->child_model = child_model; + g_object_ref(widget); + model->widget = widget; + model->lookup_func = lookup_func; + model->user_data = user_data; + + // Register signal handlers + model->changed_id = g_signal_connect (child_model, "row-changed", + G_CALLBACK (gnc_tree_model_account_drag_row_changed), + model); + model->inserted_id = g_signal_connect (child_model, "row-inserted", + G_CALLBACK (gnc_tree_model_account_drag_row_inserted), + model); + model->has_child_toggled_id + = g_signal_connect (child_model, "row-has-child-toggled", + G_CALLBACK (gnc_tree_model_account_drag_row_has_child_toggled), + model); + model->deleted_id = g_signal_connect (child_model, "row-deleted", + G_CALLBACK (gnc_tree_model_account_drag_row_deleted), + model); + model->reordered_id = g_signal_connect (child_model, "rows-reordered", + G_CALLBACK (gnc_tree_model_account_drag_rows_reordered), + model); + + + return retval; +} + +GtkTreeModel * +gnc_tree_model_account_drag_get_model (GncTreeModelAccountDrag *model) +{ + g_return_val_if_fail (model != NULL, NULL); + return model->child_model; +} + +/* GObject callbacks */ +static void +gnc_tree_model_account_drag_finalize (GObject *object) +{ + GncTreeModelAccountDrag *tree_model = (GncTreeModelAccountDrag *) object; + + // Disconnect signal handlers + g_signal_handler_disconnect (tree_model->child_model, + tree_model->changed_id); + g_signal_handler_disconnect (tree_model->child_model, + tree_model->inserted_id); + g_signal_handler_disconnect (tree_model->child_model, + tree_model->has_child_toggled_id); + g_signal_handler_disconnect (tree_model->child_model, + tree_model->deleted_id); + g_signal_handler_disconnect (tree_model->child_model, + tree_model->reordered_id); + + + g_object_unref(tree_model->child_model); + tree_model->child_model = NULL; + tree_model->lookup_func = NULL; + tree_model->user_data = NULL; + + g_object_unref(tree_model->widget); + tree_model->widget = NULL; + + /* must chain up */ + G_OBJECT_CLASS (gnc_tree_model_account_drag_parent_class)->finalize (object); +} + +/* TreeModel signal handlers */ +static void +gnc_tree_model_account_drag_row_changed(GtkTreeModel *c_model, + GtkTreePath *c_path, + GtkTreeIter *c_iter, + gpointer data) +{ + g_return_if_fail(GNC_IS_TREE_MODEL_ACCOUNT_DRAG(data)); + gtk_tree_model_row_changed(GTK_TREE_MODEL(data), c_path, c_iter); +} + +static void +gnc_tree_model_account_drag_row_inserted(GtkTreeModel *c_model, + GtkTreePath *c_path, + GtkTreeIter *c_iter, + gpointer data) +{ + g_return_if_fail(GNC_IS_TREE_MODEL_ACCOUNT_DRAG(data)); + gtk_tree_model_row_inserted(GTK_TREE_MODEL(data), c_path, c_iter); +} + +static void +gnc_tree_model_account_drag_row_has_child_toggled(GtkTreeModel *c_model, + GtkTreePath *c_path, + GtkTreeIter *c_iter, + gpointer data) +{ + g_return_if_fail(GNC_IS_TREE_MODEL_ACCOUNT_DRAG(data)); + gtk_tree_model_row_has_child_toggled(GTK_TREE_MODEL(data), c_path, c_iter); +} + +static void +gnc_tree_model_account_drag_row_deleted(GtkTreeModel *c_model, + GtkTreePath *c_path, + gpointer data) +{ + g_return_if_fail(GNC_IS_TREE_MODEL_ACCOUNT_DRAG(data)); + gtk_tree_model_row_deleted(GTK_TREE_MODEL(data), c_path); +} + +static void +gnc_tree_model_account_drag_rows_reordered(GtkTreeModel *c_model, + GtkTreePath *c_path, + GtkTreeIter *c_iter, + gint *new_order, + gpointer data) +{ + g_return_if_fail(GNC_IS_TREE_MODEL_ACCOUNT_DRAG(data)); + gtk_tree_model_rows_reordered(GTK_TREE_MODEL(data), c_path, c_iter, new_order); +} + +/* Fulfill our model requirements */ +static GtkTreeModelFlags +gnc_tree_model_account_drag_get_flags (GtkTreeModel *tree_model) +{ + GncTreeModelAccountDrag *tree_model_drag = (GncTreeModelAccountDrag *) tree_model; + + g_return_val_if_fail (tree_model_drag->child_model != NULL, 0); + + return gtk_tree_model_get_flags (tree_model_drag->child_model); +} + +static gint +gnc_tree_model_account_drag_get_n_columns (GtkTreeModel *tree_model) +{ + GncTreeModelAccountDrag *tree_model_drag = (GncTreeModelAccountDrag *) tree_model; + + g_return_val_if_fail (tree_model_drag->child_model != NULL, 0); + + return gtk_tree_model_get_n_columns (tree_model_drag->child_model); +} + +static GType +gnc_tree_model_account_drag_get_column_type (GtkTreeModel *tree_model, + gint index) +{ + GncTreeModelAccountDrag *tree_model_drag = (GncTreeModelAccountDrag *) tree_model; + + g_return_val_if_fail (tree_model_drag->child_model != NULL, G_TYPE_INVALID); + + return gtk_tree_model_get_column_type (tree_model_drag->child_model, index); +} + +static gboolean +gnc_tree_model_account_drag_get_iter (GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreePath *path) +{ + GncTreeModelAccountDrag *tree_model_drag = (GncTreeModelAccountDrag *) tree_model; + + g_return_val_if_fail (tree_model_drag->child_model != NULL, FALSE); + + return gtk_tree_model_get_iter (tree_model_drag->child_model, iter, path); +} + +static GtkTreePath * +gnc_tree_model_account_drag_get_path (GtkTreeModel *tree_model, + GtkTreeIter *iter) +{ + GncTreeModelAccountDrag *tree_model_drag = (GncTreeModelAccountDrag *) tree_model; + + g_return_val_if_fail (tree_model_drag->child_model != NULL, NULL); + + return gtk_tree_model_get_path (tree_model_drag->child_model, iter); +} + +static void +gnc_tree_model_account_drag_get_value (GtkTreeModel *tree_model, + GtkTreeIter *iter, + gint column, + GValue *value) +{ + GncTreeModelAccountDrag *tree_model_drag = (GncTreeModelAccountDrag *) tree_model; + + g_return_if_fail (tree_model_drag->child_model != NULL); + + gtk_tree_model_get_value (tree_model_drag->child_model, + iter, column, value); +} + +static gboolean +gnc_tree_model_account_drag_iter_next (GtkTreeModel *tree_model, + GtkTreeIter *iter) +{ + GncTreeModelAccountDrag *tree_model_drag = (GncTreeModelAccountDrag *) tree_model; + + g_return_val_if_fail (tree_model_drag->child_model != NULL, FALSE); + + return gtk_tree_model_iter_next (tree_model_drag->child_model, iter); +} + +static gboolean +gnc_tree_model_account_drag_iter_children (GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreeIter *parent) +{ + GncTreeModelAccountDrag *tree_model_drag = (GncTreeModelAccountDrag *) tree_model; + + g_return_val_if_fail (tree_model_drag->child_model != NULL, FALSE); + + return gtk_tree_model_iter_children (tree_model_drag->child_model, iter, parent); +} + +static gboolean +gnc_tree_model_account_drag_iter_has_child (GtkTreeModel *tree_model, + GtkTreeIter *iter) +{ + GncTreeModelAccountDrag *tree_model_drag = (GncTreeModelAccountDrag *) tree_model; + + g_return_val_if_fail (tree_model_drag->child_model != NULL, FALSE); + + return gtk_tree_model_iter_has_child (tree_model_drag->child_model, iter); +} + +static gint +gnc_tree_model_account_drag_iter_n_children (GtkTreeModel *tree_model, + GtkTreeIter *iter) +{ + GncTreeModelAccountDrag *tree_model_drag = (GncTreeModelAccountDrag *) tree_model; + + g_return_val_if_fail (tree_model_drag->child_model != NULL, 0); + + return gtk_tree_model_iter_n_children (tree_model_drag->child_model, iter); +} + +static gboolean +gnc_tree_model_account_drag_iter_nth_child (GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreeIter *parent, + gint n) +{ + GncTreeModelAccountDrag *tree_model_drag = (GncTreeModelAccountDrag *) tree_model; + + g_return_val_if_fail (tree_model_drag->child_model != NULL, FALSE); + + return gtk_tree_model_iter_nth_child(tree_model_drag->child_model, iter, parent, n); +} + +static gboolean +gnc_tree_model_account_drag_iter_parent (GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreeIter *child) +{ + GncTreeModelAccountDrag *tree_model_drag = (GncTreeModelAccountDrag *) tree_model; + + g_return_val_if_fail (tree_model_drag->child_model != NULL, FALSE); + + return gtk_tree_model_iter_parent (tree_model_drag->child_model, iter, child); +} + +static void +gnc_tree_model_account_drag_ref_node (GtkTreeModel *tree_model, + GtkTreeIter *iter) +{ + GncTreeModelAccountDrag *tree_model_drag = (GncTreeModelAccountDrag *) tree_model; + + g_return_if_fail (tree_model_drag->child_model != NULL); + + gtk_tree_model_ref_node (tree_model_drag->child_model, iter); +} + +static void +gnc_tree_model_account_drag_unref_node (GtkTreeModel *tree_model, + GtkTreeIter *iter) +{ + GncTreeModelAccountDrag *tree_model_drag = (GncTreeModelAccountDrag *) tree_model; + + g_return_if_fail (tree_model_drag->child_model != NULL); + + gtk_tree_model_unref_node (tree_model_drag->child_model, iter); +} + +/* Sortable interface */ +static gboolean +gnc_tree_model_account_drag_get_sort_column_id (GtkTreeSortable *sortable, + gint *sort_column_id, + GtkSortType *order) +{ + GncTreeModelAccountDrag *tree_model_drag = (GncTreeModelAccountDrag *) sortable; + + g_return_val_if_fail (tree_model_drag->child_model != NULL, FALSE); + + return gtk_tree_sortable_get_sort_column_id(GTK_TREE_SORTABLE(tree_model_drag->child_model), sort_column_id, order); +} + +static void +gnc_tree_model_account_drag_set_sort_column_id (GtkTreeSortable *sortable, + gint sort_column_id, + GtkSortType order) +{ + GncTreeModelAccountDrag *tree_model_drag = (GncTreeModelAccountDrag *) sortable; + + g_return_if_fail (tree_model_drag->child_model != NULL); + + gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(tree_model_drag->child_model), sort_column_id, order); +} + +static void +gnc_tree_model_account_drag_set_sort_func (GtkTreeSortable *sortable, + gint sort_column_id, + GtkTreeIterCompareFunc func, + gpointer data, + GDestroyNotify destroy) +{ + GncTreeModelAccountDrag *tree_model_drag = (GncTreeModelAccountDrag *) sortable; + + g_return_if_fail (tree_model_drag->child_model != NULL); + + gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(tree_model_drag->child_model), sort_column_id, func, data, destroy); +} + +static void +gnc_tree_model_account_drag_set_default_sort_func (GtkTreeSortable *sortable, + GtkTreeIterCompareFunc func, + gpointer data, + GDestroyNotify destroy) +{ + GncTreeModelAccountDrag *tree_model_drag = (GncTreeModelAccountDrag *) sortable; + + g_return_if_fail (tree_model_drag->child_model != NULL); + + gtk_tree_sortable_set_default_sort_func(GTK_TREE_SORTABLE(tree_model_drag->child_model), func, data, destroy); +} + +static gboolean +gnc_tree_model_account_drag_has_default_sort_func (GtkTreeSortable *sortable) +{ + GncTreeModelAccountDrag *tree_model_drag = (GncTreeModelAccountDrag *) sortable; + + g_return_val_if_fail (tree_model_drag->child_model != NULL, FALSE); + + return gtk_tree_sortable_has_default_sort_func(GTK_TREE_SORTABLE(tree_model_drag->child_model)); +} + +/** GtkTreeDragSource implementation methods ****************************/ + +static gboolean +gnc_tree_model_account_drag_row_draggable(GtkTreeDragSource *drag_source, + GtkTreePath *path) +{ + DEBUG("Inside gnc_tree_model_account_drag_source_row_draggable\n"); + return TRUE; +} + +static gboolean +gnc_tree_model_account_drag_drag_data_get(GtkTreeDragSource *drag_source, + GtkTreePath *path, + GtkSelectionData *selection_data) +{ + Account *account; + gchar *full_account_name; + GncTreeModelAccountDrag* account_drag; + + g_return_val_if_fail(GNC_IS_TREE_MODEL_ACCOUNT_DRAG(drag_source), FALSE); + account_drag = GNC_TREE_MODEL_ACCOUNT_DRAG(drag_source); + + account = account_drag->lookup_func(path, account_drag->user_data); + if (account == NULL) + return FALSE; + + full_account_name = gnc_account_get_full_name(account); + + gtk_selection_data_set(selection_data, selection_data->target, 8, (const guchar *)full_account_name, strlen(full_account_name)); + DEBUG("Inside gnc_tree_model_account_drag_source_drag_data_get. Target type \"%s\" desired. Account name \"%s\" was selected.\n", gdk_atom_name(selection_data->target), full_account_name); + + g_free(full_account_name); + + return TRUE; +} + +static gboolean +gnc_tree_model_account_drag_drag_data_delete(GtkTreeDragSource *drag_source, + GtkTreePath *path) +{ + DEBUG("Inside gnc_tree_model_account_drag_source_drag_data_delete\n"); + return TRUE; +} + +/** GtkTreeDragDest implementation methods ****************************/ + +static gboolean +gnc_tree_model_account_drag_account_is_child_of(Account *child, Account* possible_parent) +{ + Account* parent = child; + + while (parent != NULL) + { + if (parent == possible_parent) + { + return TRUE; + } + + parent = gnc_account_get_parent(parent); + } + + return FALSE; +} + +static gboolean +gnc_tree_model_account_drag_drag_data_received(GtkTreeDragDest *drag_dest, + GtkTreePath *dest, + GtkSelectionData *selection_data) +{ + GtkTreePath *parent_path; + Account *new_parent, *dragged_account; + gchar *parent_full_account_name, *dragged_full_account_name; + GncTreeModelAccountDrag* account_drag; + + DEBUG("Inside gnc_tree_model_account_drag_drag_data_received.\n"); + + g_return_val_if_fail(GNC_IS_TREE_MODEL_ACCOUNT_DRAG(drag_dest), FALSE); + account_drag = GNC_TREE_MODEL_ACCOUNT_DRAG(drag_dest); + + // The dest path is spec's as the element to insert before, + // but the parent is what's really desired, so move up + // the path to find the parent. + parent_path = gtk_tree_path_copy(dest); + g_return_val_if_fail(parent_path != NULL, FALSE); + + gtk_tree_path_up(parent_path); + new_parent = account_drag->lookup_func(parent_path, account_drag->user_data); + gtk_tree_path_free(parent_path); + g_return_val_if_fail(new_parent != NULL, FALSE); + + parent_full_account_name = gnc_account_get_full_name(new_parent); + DEBUG("Account name \"%s\" was drop target.\n", parent_full_account_name); + g_free(parent_full_account_name); + + g_return_val_if_fail(g_utf8_validate((gchar*)selection_data->data, selection_data->length, NULL), FALSE); + dragged_full_account_name = g_strndup((gchar*)selection_data->data, selection_data->length); + DEBUG("Account name \"%s\" was dragged account.\n", dragged_full_account_name); + + dragged_account = gnc_account_lookup_by_full_name(new_parent, dragged_full_account_name); + g_free(dragged_full_account_name); + g_return_val_if_fail(dragged_account != NULL, FALSE); + + // Make sure that the new hierarchy would still be valid. Accounts cannot be children + // of themselves + if (gnc_tree_model_account_drag_account_is_child_of(new_parent, dragged_account)) { + GtkWindow *window; + GtkWidget *dialog; + + window = account_drag->widget == NULL ? NULL : GTK_WINDOW (gtk_widget_get_toplevel (account_drag->widget)); + + dialog = gtk_message_dialog_new (window, + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_CLOSE, + _("You cannot move an account into itself.")); + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG(dialog), + _("The destination account is a child of the dragged account.")); + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); + + return FALSE; + } + + // Make sure the parent account has a valid type for the new child + if (xaccAccountTypesCompatible(xaccAccountGetType(new_parent), xaccAccountGetType(dragged_account)) == FALSE) { + GtkWindow *window; + GtkWidget *dialog; + + window = account_drag->widget == NULL ? NULL : GTK_WINDOW (gtk_widget_get_toplevel (account_drag->widget)); + + dialog = gtk_message_dialog_new (window, + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_CLOSE, + _("The account cannot be moved because the account types are incompatible.")); + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG(dialog), + _("The destination account type, %s, is not compatible with the new parent's type, %s."), + xaccAccountGetTypeStr(xaccAccountGetType(dragged_account)), + xaccAccountGetTypeStr(xaccAccountGetType(new_parent))); + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); + + return FALSE; + } + + gnc_suspend_gui_refresh (); + gnc_account_append_child (new_parent, dragged_account); + gnc_resume_gui_refresh (); + + return TRUE; +} + +static gboolean +gnc_tree_model_account_drag_row_drop_possible(GtkTreeDragDest *drag_dest, + GtkTreePath *dest, + GtkSelectionData *selection_data) +{ + DEBUG("Inside gnc_tree_model_account_drag_dest_row_drop_possible."); + return TRUE; +} diff --git a/src/gnome-utils/gnc-tree-model-account-drag.h b/src/gnome-utils/gnc-tree-model-account-drag.h new file mode 100644 index 0000000000..38b5f02fdd --- /dev/null +++ b/src/gnome-utils/gnc-tree-model-account-drag.h @@ -0,0 +1,78 @@ +/* gnc-tree-model-account-drag.h + * Copyright (C) 2009 Matt Lavin + * + * 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, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/** @addtogroup GUI + @{ */ +/** @addtogroup GuiTreeModel GnuCash Tree Model + @{ */ +/** @file gnc-tree-model-account-drag.h + @brief GtkTreeModel wrapper that supports dragging Accounts for reparenting + @author Matt Lavin +*/ + +#ifndef __GNC_TREE_MODEL_ACCOUNT_DRAG_H +#define __GNC_TREE_MODEL_ACCOUNT_DRAG_H + +#include +#include + +#include "config.h" +#include "Account.h" +#include "gnc-tree-view-account.h" + +G_BEGIN_DECLS + +#define GNC_TYPE_TREE_MODEL_ACCOUNT_DRAG (gnc_tree_model_account_drag_get_type ()) +#define GNC_TREE_MODEL_ACCOUNT_DRAG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GNC_TYPE_TREE_MODEL_ACCOUNT_DRAG, GncTreeModelAccountDrag)) +#define GNC_TREE_MODEL_ACCOUNT_DRAG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GNC_TYPE_TREE_MODEL_ACCOUNT_DRAG, GncTreeModelAccountDragClass)) +#define GNC_IS_TREE_MODEL_ACCOUNT_DRAG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GNC_TYPE_TREE_MODEL_ACCOUNT_DRAG)) +#define GNC_IS_TREE_MODEL_ACCOUNT_DRAG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GNC_TYPE_TREE_MODEL_ACCOUNT_DRAG)) +#define GNC_TREE_MODEL_ACCOUNT_DRAG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GNC_TYPE_TREE_MODEL_ACCOUNT_DRAG, GncTreeModelAccountDragClass)) + +typedef Account* (*GncTreeModelAccountDragLookupFunc) (GtkTreePath *path, + gpointer user_data); + +typedef struct { + GObject parent; + + /* < private > */ + GtkTreeModel *child_model; + GtkWidget* widget; + GncTreeModelAccountDragLookupFunc lookup_func; + gpointer user_data; + + gulong changed_id; + gulong inserted_id; + gulong has_child_toggled_id; + gulong deleted_id; + gulong reordered_id; +} GncTreeModelAccountDrag; + +typedef struct { + GObjectClass parent_class; +} GncTreeModelAccountDragClass; + + +GType gnc_tree_model_account_drag_get_type (void) G_GNUC_CONST; +GtkTreeModel *gnc_tree_model_account_drag_new_with_model (GtkTreeModel *child_model, GtkWidget* widget, GncTreeModelAccountDragLookupFunc lookup_func, gpointer user_data); +GtkTreeModel *gnc_tree_model_account_drag_get_model (GncTreeModelAccountDrag *model); + +G_END_DECLS + +#endif /* __GNC_TREE_MODEL_ACCOUNT_DRAG_H */ diff --git a/src/gnome-utils/gnc-tree-view-account.c b/src/gnome-utils/gnc-tree-view-account.c index 7f9136daf5..b595fec254 100644 --- a/src/gnome-utils/gnc-tree-view-account.c +++ b/src/gnome-utils/gnc-tree-view-account.c @@ -30,6 +30,7 @@ #include "gnc-tree-view.h" #include "gnc-tree-model-account.h" +#include "gnc-tree-model-account-drag.h" #include "gnc-tree-model-account-types.h" #include "gnc-tree-view-account.h" @@ -62,10 +63,16 @@ static void gnc_tree_view_account_finalize (GObject *object); static void gtva_update_column_names (GncTreeView *view); static void gtva_currency_changed_cb (void); +static Account* gnc_tree_view_account_loookup_account (GtkTreePath *path, + gpointer data); + static gboolean gnc_tree_view_account_filter_helper (GtkTreeModel *model, GtkTreeIter *iter, gpointer data); +static GtkTreeModel* +gnc_tree_view_account_get_sort_model (GncTreeViewAccount *view); + static void gtva_setup_column_renderer_edited_cb(GncTreeViewAccount *account_view, GtkTreeViewColumn *column, GtkCellRenderer *renderer, @@ -469,7 +476,7 @@ GtkTreeView * gnc_tree_view_account_new_with_root (Account *root, gboolean show_root) { GncTreeView *view; - GtkTreeModel *model, *f_model, *s_model; + GtkTreeModel *model, *f_model, *s_model, *d_model; GtkTreePath *virtual_root_path = NULL; const gchar *sample_type, *sample_commodity; GncTreeViewAccountPrivate *priv; @@ -497,9 +504,13 @@ gnc_tree_view_account_new_with_root (Account *root, gboolean show_root) /* Set up the view private sort layer on the common model. */ s_model = gtk_tree_model_sort_new_with_model(f_model); g_object_unref(G_OBJECT(f_model)); - gnc_tree_view_set_model (view, s_model); + + d_model = gnc_tree_model_account_drag_new_with_model(s_model, GTK_WIDGET(view), gnc_tree_view_account_loookup_account, view); g_object_unref(G_OBJECT(s_model)); + gnc_tree_view_set_model (view, d_model); + g_object_unref(G_OBJECT(d_model)); + /* Set default visibilities */ gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(view), FALSE); @@ -700,6 +711,28 @@ gnc_tree_view_account_new (gboolean show_root) g_free(path_string); \ } + +static Account* +gnc_tree_view_account_loookup_account (GtkTreePath *path, + gpointer data) +{ + g_return_val_if_fail(GNC_IS_TREE_VIEW_ACCOUNT(data), NULL); + + return gnc_tree_view_account_get_account_from_path(GNC_TREE_VIEW_ACCOUNT(data), path); +} + + +static GtkTreeModel* +gnc_tree_view_account_get_sort_model (GncTreeViewAccount *view) +{ + GtkTreeModel *d_model, *s_model; + + d_model = gtk_tree_view_get_model(GTK_TREE_VIEW(view)); + s_model = gnc_tree_model_account_drag_get_model(GNC_TREE_MODEL_ACCOUNT_DRAG(d_model)); + + return s_model; +} + static GtkTreePath * gnc_tree_view_account_get_path_from_account (GncTreeViewAccount *view, Account *account) @@ -715,7 +748,7 @@ gnc_tree_view_account_get_path_from_account (GncTreeViewAccount *view, } /* Reach down to the real model and get a path for this account */ - s_model = gtk_tree_view_get_model(GTK_TREE_VIEW(view)); + s_model = gnc_tree_view_account_get_sort_model(view); f_model = gtk_tree_model_sort_get_model(GTK_TREE_MODEL_SORT(s_model)); model = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(f_model)); path = gnc_tree_model_account_get_path_from_account (GNC_TREE_MODEL_ACCOUNT(model), account); @@ -754,7 +787,7 @@ gnc_tree_view_account_get_iter_from_account (GncTreeViewAccount *view, ENTER("view %p, account %p (%s)", view, account, xaccAccountGetName(account)); /* Reach down to the real model and get an iter for this account */ - s_model = gtk_tree_view_get_model(GTK_TREE_VIEW(view)); + s_model = gnc_tree_view_account_get_sort_model(view); f_model = gtk_tree_model_sort_get_model(GTK_TREE_MODEL_SORT(s_model)); model = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(f_model)); if (!gnc_tree_model_account_get_iter_from_account ( @@ -793,7 +826,7 @@ gnc_tree_view_account_count_children (GncTreeViewAccount *view, } /* Any children? */ - s_model = gtk_tree_view_get_model(GTK_TREE_VIEW(view)); + s_model = gnc_tree_view_account_get_sort_model(view); num_children = gtk_tree_model_iter_n_children(s_model, &s_iter); LEAVE("%d children", num_children); return num_children; @@ -925,7 +958,7 @@ gnc_tree_view_account_refilter (GncTreeViewAccount *view) g_return_if_fail(GNC_IS_TREE_VIEW_ACCOUNT(view)); - s_model = gtk_tree_view_get_model(GTK_TREE_VIEW(view)); + s_model = gnc_tree_view_account_get_sort_model(view); f_model = gtk_tree_model_sort_get_model(GTK_TREE_MODEL_SORT(s_model)); gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (f_model)); } @@ -979,7 +1012,7 @@ gnc_tree_view_account_get_account_from_path (GncTreeViewAccount *view, g_return_val_if_fail (GNC_IS_TREE_VIEW_ACCOUNT (view), NULL); g_return_val_if_fail (s_path != NULL, NULL); - s_model = gtk_tree_view_get_model(GTK_TREE_VIEW(view)); + s_model = gnc_tree_view_account_get_sort_model(view); f_path = gtk_tree_model_sort_convert_path_to_child_path ( GTK_TREE_MODEL_SORT (s_model), s_path); if (!f_path) { @@ -1010,17 +1043,19 @@ gnc_tree_view_account_get_account_from_path (GncTreeViewAccount *view, Account * -gnc_tree_view_account_get_account_from_iter (GtkTreeModel *s_model, +gnc_tree_view_account_get_account_from_iter (GtkTreeModel *d_model, GtkTreeIter *s_iter) { - GtkTreeModel *model, *f_model; + GtkTreeModel *model, *f_model, *s_model; GtkTreeIter iter, f_iter; Account *account; - g_return_val_if_fail (GTK_IS_TREE_MODEL_SORT(s_model), NULL); + g_return_val_if_fail (GNC_IS_TREE_MODEL_ACCOUNT_DRAG(d_model), NULL); g_return_val_if_fail (s_iter != NULL, NULL); - ENTER("model %p, iter %p", s_model, s_iter); + ENTER("model %p, iter %p", d_model, s_iter); + + s_model = gnc_tree_model_account_drag_get_model(GNC_TREE_MODEL_ACCOUNT_DRAG (d_model)); gtk_tree_model_sort_convert_iter_to_child_iter (GTK_TREE_MODEL_SORT(s_model), &f_iter, @@ -1044,7 +1079,7 @@ Account * gnc_tree_view_account_get_selected_account (GncTreeViewAccount *view) { GtkTreeSelection *selection; - GtkTreeModel *f_model, *s_model; + GtkTreeModel *f_model, *s_model, *d_model; GtkTreeIter iter, f_iter, s_iter; Account *account; GtkSelectionMode mode; @@ -1057,11 +1092,13 @@ gnc_tree_view_account_get_selected_account (GncTreeViewAccount *view) if ((mode != GTK_SELECTION_SINGLE) && (mode != GTK_SELECTION_BROWSE)) { return NULL; } - if (!gtk_tree_selection_get_selected (selection, &s_model, &s_iter)) { + if (!gtk_tree_selection_get_selected (selection, &d_model, &s_iter)) { LEAVE("no account, get_selected failed"); return FALSE; } + + s_model = gnc_tree_model_account_drag_get_model(GNC_TREE_MODEL_ACCOUNT_DRAG (d_model)); gtk_tree_model_sort_convert_iter_to_child_iter (GTK_TREE_MODEL_SORT (s_model), &f_iter, &s_iter); @@ -1097,7 +1134,7 @@ gnc_tree_view_account_set_selected_account (GncTreeViewAccount *view, if (account == NULL) return; - s_model = gtk_tree_view_get_model(GTK_TREE_VIEW(view)); + s_model = gnc_tree_view_account_get_sort_model(view); f_model = gtk_tree_model_sort_get_model(GTK_TREE_MODEL_SORT(s_model)); model = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(f_model)); @@ -1223,7 +1260,7 @@ gnc_tree_view_account_set_selected_accounts (GncTreeViewAccount *view, g_return_if_fail (GNC_IS_TREE_VIEW_ACCOUNT (view)); - s_model = gtk_tree_view_get_model(GTK_TREE_VIEW(view)); + s_model = gnc_tree_view_account_get_sort_model(view); f_model = gtk_tree_model_sort_get_model(GTK_TREE_MODEL_SORT(s_model)); model = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(f_model)); @@ -1304,7 +1341,7 @@ gnc_tree_view_account_select_subaccounts (GncTreeViewAccount *view, } /* Any children? */ - s_model = gtk_tree_view_get_model(GTK_TREE_VIEW(view)); + s_model = gnc_tree_view_account_get_sort_model(view); num_children = gtk_tree_model_iter_n_children(s_model, &si_account); if (num_children == 0) { LEAVE("no children"); @@ -1377,7 +1414,7 @@ gnc_tree_view_account_get_cursor_account (GncTreeViewAccount *view) ENTER("view %p", view); g_return_val_if_fail (GNC_IS_TREE_VIEW_ACCOUNT (view), NULL); - s_model = gtk_tree_view_get_model (GTK_TREE_VIEW(view)); + s_model = gnc_tree_view_account_get_sort_model(view); gtk_tree_view_get_cursor (GTK_TREE_VIEW(view), &s_path, NULL); if (!s_path) { LEAVE("no account"); @@ -1457,15 +1494,14 @@ gtva_currency_changed_cb (void) static void account_cell_kvp_data_func (GtkTreeViewColumn *tree_column, GtkCellRenderer *cell, - GtkTreeModel *s_model, + GtkTreeModel *model, GtkTreeIter *s_iter, gpointer key) { Account *account; kvp_frame * frame; - g_return_if_fail (GTK_IS_TREE_MODEL_SORT (s_model)); - account = gnc_tree_view_account_get_account_from_iter(s_model, s_iter); + account = gnc_tree_view_account_get_account_from_iter(model, s_iter); frame = xaccAccountGetSlots(account); g_object_set (G_OBJECT (cell), @@ -1503,10 +1539,10 @@ gnc_tree_view_account_add_kvp_column (GncTreeViewAccount *view, } static void col_edited_helper(GtkCellRendererText *cell, gchar *path_string, - gchar *new_text, gpointer _s_model) + gchar *new_text, gpointer _model) { Account *account; - GtkTreeModel *s_model; + GtkTreeModel *model; GtkTreeIter s_iter; GncTreeViewAccountColumnTextEdited col_edited_cb; GtkTreeViewColumn *col; @@ -1515,26 +1551,25 @@ static void col_edited_helper(GtkCellRendererText *cell, gchar *path_string, "column_edited_callback"); col = GTK_TREE_VIEW_COLUMN(g_object_get_data(G_OBJECT(cell), "column_view")); - s_model = GTK_TREE_MODEL(_s_model); + model = GTK_TREE_MODEL(_model); - if (!gtk_tree_model_get_iter_from_string(s_model, &s_iter, path_string)) + if (!gtk_tree_model_get_iter_from_string(model, &s_iter, path_string)) return; - account = gnc_tree_view_account_get_account_from_iter(s_model, &s_iter); + account = gnc_tree_view_account_get_account_from_iter(model, &s_iter); col_edited_cb(account, col, new_text); } static void col_source_helper(GtkTreeViewColumn *col, GtkCellRenderer *cell, - GtkTreeModel *s_model, GtkTreeIter *s_iter, + GtkTreeModel *model, GtkTreeIter *iter, gpointer _col_source_cb) { Account *account; gchar *text; GncTreeViewAccountColumnSource col_source_cb; - g_return_if_fail (GTK_IS_TREE_MODEL_SORT (s_model)); col_source_cb = (GncTreeViewAccountColumnSource) _col_source_cb; - account = gnc_tree_view_account_get_account_from_iter(s_model, s_iter); + account = gnc_tree_view_account_get_account_from_iter(model, iter); text = col_source_cb(account, col, cell); g_object_set (G_OBJECT (cell), "text", text, "xalign", 1.0, NULL); g_free(text); @@ -1556,7 +1591,7 @@ gtva_setup_column_renderer_edited_cb(GncTreeViewAccount *account_view, { g_object_set(G_OBJECT(renderer), "editable", FALSE, NULL); g_object_set_data(G_OBJECT(renderer), "column_edited_callback", col_edited_cb); - s_model = gtk_tree_view_get_model(GTK_TREE_VIEW(account_view)); + s_model = gnc_tree_view_account_get_sort_model(account_view); g_signal_handlers_disconnect_by_func(G_OBJECT(renderer), col_edited_cb, s_model); g_object_set_data(G_OBJECT(renderer), "column_view", column); } @@ -1565,7 +1600,7 @@ gtva_setup_column_renderer_edited_cb(GncTreeViewAccount *account_view, g_object_set(G_OBJECT(renderer), "editable", TRUE, NULL); g_object_set_data(G_OBJECT(renderer), "column_edited_callback", col_edited_cb); - s_model = gtk_tree_view_get_model(GTK_TREE_VIEW(account_view)); + s_model = gnc_tree_view_account_get_sort_model(account_view); g_signal_connect(G_OBJECT(renderer), "edited", (GCallback) col_edited_helper, s_model); g_object_set_data(G_OBJECT(renderer), "column_view", column); diff --git a/src/gnome/gnc-plugin-page-account-tree.c b/src/gnome/gnc-plugin-page-account-tree.c index d3f133c5ee..8480c4f09f 100644 --- a/src/gnome/gnc-plugin-page-account-tree.c +++ b/src/gnome/gnc-plugin-page-account-tree.c @@ -407,6 +407,25 @@ gnc_plugin_page_account_tree_close_cb (gpointer user_data) gnc_main_window_close_page(plugin_page); } +static const GtkTargetEntry dnd_targets[] = { + { "gnc_account", GTK_TARGET_SAME_WIDGET, 0 } +}; + + +static void +gnc_enable_account_dragging(GtkTreeView *tree_view) +{ + GtkTreeModel *model; + + model = gtk_tree_view_get_model (tree_view); + + g_return_if_fail (G_TYPE_CHECK_INSTANCE_TYPE (model, GTK_TYPE_TREE_DRAG_DEST)); + g_return_if_fail (G_TYPE_CHECK_INSTANCE_TYPE (model, GTK_TYPE_TREE_DRAG_SOURCE)); + + gtk_tree_view_enable_model_drag_source(tree_view, GDK_BUTTON1_MASK, dnd_targets, 1, GDK_ACTION_MOVE); + gtk_tree_view_enable_model_drag_dest(tree_view, dnd_targets, 1, GDK_ACTION_MOVE); +} + static GtkWidget * gnc_plugin_page_account_tree_create_widget (GncPluginPage *plugin_page) { @@ -466,6 +485,9 @@ gnc_plugin_page_account_tree_create_widget (GncPluginPage *plugin_page) g_signal_connect (G_OBJECT (tree_view), "row-activated", G_CALLBACK (gnc_plugin_page_account_tree_double_click_cb), page); + /* Support dragging accounts for re-ordering/re-parenting */ + gnc_enable_account_dragging(tree_view); + gtk_tree_view_set_headers_visible(tree_view, TRUE); gnc_plugin_page_account_tree_selection_changed_cb (NULL, page); gtk_widget_show (GTK_WIDGET (tree_view));