X-Git-Url: https://git.street.me.uk/andy/viking.git/blobdiff_plain/50a14534a51f892500ee82f867e8ab2f85b936ae..0dff88eac247e849f58f8e77f3bfa9ab02f572fb:/src/viktreeview.c diff --git a/src/viktreeview.c b/src/viktreeview.c index b8935a6f..3611e8f1 100644 --- a/src/viktreeview.c +++ b/src/viktreeview.c @@ -23,7 +23,6 @@ #include #include "viking.h" - #include "config.h" #define TREEVIEW_GET(model,iter,what,dest) gtk_tree_model_get(GTK_TREE_MODEL(model),(iter),(what),(dest),-1) @@ -68,6 +67,9 @@ static void treeview_init ( VikTreeview *vt ); static void treeview_finalize ( GObject *gob ); static void treeview_add_columns ( VikTreeview *vt ); +static gboolean treeview_drag_data_received ( GtkTreeDragDest *drag_dest, GtkTreePath *dest, GtkSelectionData *selection_data ); +static gboolean treeview_drag_data_delete ( GtkTreeDragSource *drag_source, GtkTreePath *path ); + GType vik_treeview_get_type (void) { static GType vt_type = 0; @@ -104,11 +106,11 @@ static void treeview_class_init ( VikTreeviewClass *klass ) parent_class = g_type_class_peek_parent (klass); treeview_signals[VT_ITEM_EDITED_SIGNAL] = g_signal_new ( "item_edited", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (VikTreeviewClass, item_edited), NULL, NULL, - g_cclosure_marshal_VOID__UINT_POINTER, G_TYPE_NONE, 2, GTK_TYPE_TREE_ITER, G_TYPE_STRING); + gtk_marshal_VOID__POINTER_POINTER, G_TYPE_NONE, 2, GTK_TYPE_POINTER, G_TYPE_POINTER); /* VOID__UINT_POINTER: kinda hack-ish, but it works. */ treeview_signals[VT_ITEM_TOGGLED_SIGNAL] = g_signal_new ( "item_toggled", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (VikTreeviewClass, item_toggled), NULL, NULL, - g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, GTK_TYPE_TREE_ITER ); + g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, GTK_TYPE_POINTER ); } static void treeview_edited_cb (GtkCellRendererText *cell, gchar *path_str, const gchar *new_name, VikTreeview *vt) @@ -118,7 +120,7 @@ static void treeview_edited_cb (GtkCellRendererText *cell, gchar *path_str, cons /* get type and data */ vik_treeview_get_iter_from_path_str ( vt, &iter, path_str ); - g_signal_emit ( G_OBJECT(vt), treeview_signals[VT_ITEM_EDITED_SIGNAL], 0, &iter, new_name, 0 ); + g_signal_emit ( G_OBJECT(vt), treeview_signals[VT_ITEM_EDITED_SIGNAL], 0, &iter, new_name ); } static void treeview_toggled_cb (GtkCellRendererToggle *cell, gchar *path_str, VikTreeview *vt) @@ -170,9 +172,9 @@ gpointer vik_treeview_item_get_parent ( VikTreeview *vt, GtkTreeIter *iter ) return rv; } -void vik_treeview_get_iter_from_path_str ( VikTreeview *vt, GtkTreeIter *iter, const gchar *path_str ) +gboolean vik_treeview_get_iter_from_path_str ( VikTreeview *vt, GtkTreeIter *iter, const gchar *path_str ) { - gtk_tree_model_get_iter_from_string ( GTK_TREE_MODEL(vt->model), iter, path_str ); + return gtk_tree_model_get_iter_from_string ( GTK_TREE_MODEL(vt->model), iter, path_str ); } static void treeview_add_columns ( VikTreeview *vt ) @@ -258,9 +260,26 @@ void treeview_init ( VikTreeview *vt ) gtk_tree_selection_set_mode (gtk_tree_view_get_selection (GTK_TREE_VIEW (vt)), GTK_SELECTION_SINGLE); + /* Override treestore's dnd methods only; this is easier than deriving from GtkTreeStore. + * The downside is that all treestores will have this behavior, so this needs to be + * changed if we add more treeviews in the future. //Alex + */ + if (1) { + GtkTreeDragSourceIface *isrc; + GtkTreeDragDestIface *idest; + + isrc = g_type_interface_peek (g_type_class_peek(G_OBJECT_TYPE(vt->model)), GTK_TYPE_TREE_DRAG_SOURCE); + isrc->drag_data_delete = treeview_drag_data_delete; + + idest = g_type_interface_peek (g_type_class_peek(G_OBJECT_TYPE(vt->model)), GTK_TYPE_TREE_DRAG_DEST); + idest->drag_data_received = treeview_drag_data_received; + } + for ( i = 0; i < VIK_LAYER_NUM_TYPES; i++ ) vt->layer_type_icons[i] = vik_layer_load_icon ( i ); /* if icon can't be loaded, it will be null and simply not be shown. */ + gtk_tree_view_set_reorderable (GTK_TREE_VIEW(vt), TRUE); + } gboolean vik_treeview_item_get_parent_iter ( VikTreeview *vt, GtkTreeIter *iter, GtkTreeIter *parent ) @@ -369,7 +388,12 @@ void vik_treeview_insert_layer ( VikTreeview *vt, GtkTreeIter *parent_iter, GtkT { g_assert ( iter != NULL ); g_assert ( icon_type < VIK_LAYER_NUM_TYPES ); - gtk_tree_store_insert_before ( GTK_TREE_STORE(vt->model), iter, parent_iter, sibling ); + if (sibling) { + gtk_tree_store_insert_before ( GTK_TREE_STORE(vt->model), iter, parent_iter, sibling ); + } else { + gtk_tree_store_append ( GTK_TREE_STORE(vt->model), iter, parent_iter ); + } + gtk_tree_store_set ( GTK_TREE_STORE(vt->model), iter, NAME_COLUMN, name, VISIBLE_COLUMN, TRUE, TYPE_COLUMN, VIK_TREEVIEW_TYPE_LAYER, ITEM_PARENT_COLUMN, parent, ITEM_POINTER_COLUMN, item, ITEM_DATA_COLUMN, data, HAS_VISIBLE_COLUMN, TRUE, EDITABLE_COLUMN, TRUE, @@ -453,3 +477,94 @@ static void treeview_finalize ( GObject *gob ) G_OBJECT_CLASS(parent_class)->finalize(gob); } + +static gboolean treeview_drag_data_received (GtkTreeDragDest *drag_dest, GtkTreePath *dest, GtkSelectionData *selection_data) +{ + GtkTreeModel *tree_model; + GtkTreeStore *tree_store; + GtkTreeModel *src_model = NULL; + GtkTreePath *src_path = NULL, *dest_cp = NULL; + gboolean retval = FALSE; + GtkTreeIter src_iter, root_iter, dest_iter, dest_parent; + guint *i_src = NULL; + VikTreeview *vt; + VikLayer *vl; + + g_return_val_if_fail (GTK_IS_TREE_STORE (drag_dest), FALSE); + + tree_model = GTK_TREE_MODEL (drag_dest); + tree_store = GTK_TREE_STORE (drag_dest); + + if (gtk_tree_get_row_drag_data (selection_data, &src_model, &src_path) && src_model == tree_model) { + /* + * Copy src_path to dest. There are two subcases here, depending on what + * is being dragged. + * + * 1. src_path is a layer. In this case, interpret the drop + * as a request to move the layer to a different aggregate layer. + * If the destination is not an aggregate layer, use the first + * ancestor that is. + * + * 2. src_path is a sublayer. In this case, find ancestors of + * both source and destination nodes who are full layers, + * and call the move method of that layer type. + * + */ + i_src = gtk_tree_path_get_indices (src_path); + dest_cp = gtk_tree_path_copy (dest); + + gtk_tree_model_get_iter_first(tree_model, &root_iter); + TREEVIEW_GET(tree_model, &root_iter, ITEM_POINTER_COLUMN, &vl); + vt = vl->vt; + + if (!gtk_tree_model_get_iter (src_model, &src_iter, src_path)) { + goto out; + } + + if (gtk_tree_path_get_depth(dest_cp)>1) { /* can't be sibling of top layer */ + VikLayer *vl_src, *vl_dest; + + /* Find the first ancestor that is a full layer, and store in dest_parent. + * In addition, put in dest_iter where Gtk wants us to insert the dragged object. + * (Note that this may end up being an invalid iter). + */ + do { + gtk_tree_path_up(dest_cp); + dest_iter = dest_parent; + gtk_tree_model_get_iter (src_model, &dest_parent, dest_cp); + } while (gtk_tree_path_get_depth(dest_cp)>1 && + vik_treeview_item_get_type(vt, &dest_parent) != VIK_TREEVIEW_TYPE_LAYER); + + + g_assert ( vik_treeview_item_get_parent(vt, &src_iter) ); + vl_src = vik_treeview_item_get_parent(vt, &src_iter); + vl_dest = vik_treeview_item_get_pointer(vt, &dest_parent); + + /* TODO: might want to allow different types, and let the clients handle how they want */ + if (vl_src->type == vl_dest->type && vik_layer_get_interface(vl_dest->type)->drag_drop_request) { + g_print("moving an item from layer '%s' into layer '%s'\n", vl_src->name, vl_dest->name); + vik_layer_get_interface(vl_dest->type)->drag_drop_request(vl_src, vl_dest, &src_iter, dest); + } + } + } + + out: + if (dest_cp) + gtk_tree_path_free(dest_cp); + if (src_path) + gtk_tree_path_free (src_path); + + return retval; +} + +/* + * This may not be necessary. + */ +static gboolean treeview_drag_data_delete ( GtkTreeDragSource *drag_source, GtkTreePath *path ) +{ + gchar *s_dest = gtk_tree_path_to_string(path); + g_print("delete data from %s\n", s_dest); + g_free(s_dest); + return FALSE; +} +