From 02ccf6526b553abfae1832ec8e5773c9f1f3f6b4 Mon Sep 17 00:00:00 2001 From: Ettore Perazzoli Date: Mon, 15 May 2000 03:26:18 +0000 Subject: Initial drag and drop support for the tree view: now you can drag folders into the shortcut bar. svn path=/trunk/; revision=3032 --- shell/ChangeLog | 40 +++++++ shell/e-storage-set-view.c | 258 ++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 284 insertions(+), 14 deletions(-) diff --git a/shell/ChangeLog b/shell/ChangeLog index b485d511d3..bb2c7c8f4d 100644 --- a/shell/ChangeLog +++ b/shell/ChangeLog @@ -1,3 +1,43 @@ +2000-05-15 Ettore Perazzoli + + * e-storage-set-view.c: New members `in_drag' and `drag_button' in + `EStorageSetViewPrivate'. New static variables `drag_types', + `num_drag_types', `target_list'. + (class_init): Create the `target_list'. + (init): Initialize the private `in_drag' member to false. + Initialize the private `drag_button' member to zero. + (button_release_event): Set it to false. + (motion_notify_event): New function, implementation of + `GtkWidget::motion_notify_event'. If `in_drag' is false, set it + to true and set ourselves up as a drag source. + (button_press_event): New function, implementation of + `GtkWidget::button_press_event'. Set `drag_button' to the event's + button number and then chain to the implementation in the parent + class. + (drag_end): New function, implementation of `GtkWidget::drag_end'. + (drag_data_get): New function, implementation of + `GtkWidget::drag_data_get'. + (set_e_shortcut_selection): New function, helper for `drag_data_get'. + (set_uri_list_selection): New function, helper for `drag_data_get'. + (class_init): Install these method implementations. + + * e-storage-set-view.c: New member `selected_row_path' in + `EStorageSetViewPrivate'. + (init): Initialize it to NULL. + (tree_select_row): Set it to the path of the selected row. Don't + emit "folder_selected" yet. Also, keep the grab. + (button_release_event): New function, implementation of + `GtkWidget::button_release_event'. If `selected_row_path' is not + NULL, emit the "folder_selected" signal with `selected_row_path' + as the parameter and then set `selected_row_path' to NULL again. + (class_init): Install `button_release_event'. + + * e-storage-set-view.c: Made `ICON_WIDTH' and `ICON_HEIGHT' global + #defines. + (e_storage_set_view_construct): Set the row height to + `ICON_HEIGHT'. Alos, set the selection mode to + `GTK_SELECTION_BROWSE'. + 2000-05-15 Iain Holmes * e-local-folder.c (get_string_value): Use the correct function to diff --git a/shell/e-storage-set-view.c b/shell/e-storage-set-view.c index a858f3b626..8bcb59abda 100644 --- a/shell/e-storage-set-view.c +++ b/shell/e-storage-set-view.c @@ -42,8 +42,19 @@ struct _EStorageSetViewPrivate { without the other, as they share the dynamically allocated path. */ GHashTable *ctree_node_to_path; GHashTable *path_to_ctree_node; + + /* Path of the row selected by the latest "tree_select_row" signal. */ + const char *selected_row_path; + + /* Whether we are currently performing a drag from this view. */ + int in_drag : 1; + + /* Button used for the drag. This is initialized in the `button_press_event' + handler. */ + int drag_button; }; + enum { FOLDER_SELECTED, LAST_SIGNAL @@ -51,6 +62,30 @@ enum { static guint signals[LAST_SIGNAL] = { 0 }; + +#define ICON_WIDTH 24 +#define ICON_HEIGHT 24 + + +/* DND stuff. */ + +enum _DndTargetType { + DND_TARGET_TYPE_URI_LIST, + DND_TARGET_TYPE_E_SHORTCUT +}; +typedef enum _DndTargetType DndTargetType; + +#define URI_LIST_TYPE "text/uri-list" +#define E_SHORTCUT_TYPE "E-SHORTCUT" + +static GtkTargetEntry drag_types [] = { + { URI_LIST_TYPE, 0, DND_TARGET_TYPE_URI_LIST }, + { E_SHORTCUT_TYPE, 0, DND_TARGET_TYPE_E_SHORTCUT } +}; +static const int num_drag_types = sizeof (drag_types) / sizeof (drag_types[0]); + +static GtkTargetList *target_list; + /* GtkObject methods. */ @@ -82,6 +117,188 @@ destroy (GtkObject *object) (* GTK_OBJECT_CLASS (parent_class)->destroy) (object); } + +/* GtkWidget methods. */ + +static int +button_press_event (GtkWidget *widget, + GdkEventButton *event) +{ + EStorageSetView *storage_set_view; + EStorageSetViewPrivate *priv; + + (* GTK_WIDGET_CLASS (parent_class)->button_press_event) (widget, event); + + storage_set_view = E_STORAGE_SET_VIEW (widget); + priv = storage_set_view->priv; + + if (priv->in_drag) + return FALSE; + + priv->drag_button = event->button; + + /* KLUDGE ALERT. So look at this. We need to grab the pointer now, to check for + motion events and maybe start a drag operation. And GtkCTree seems to do it + already in the `button_press_event'. *But* for some reason something is very + broken somewhere and the grab misbehaves when done by GtkCTree's + `button_press_event'. So we have to ungrab the pointer and re-grab it our way. + Weee! */ + + gdk_pointer_ungrab (GDK_CURRENT_TIME); + gdk_flush (); + gdk_pointer_grab (GTK_CLIST (widget)->clist_window, FALSE, + GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, + NULL, NULL, event->time); + + return TRUE; +} + +static int +motion_notify_event (GtkWidget *widget, + GdkEventMotion *event) +{ + EStorageSetView *storage_set_view; + EStorageSetViewPrivate *priv; + + if (event->window != GTK_CLIST (widget)->clist_window) + return (* GTK_WIDGET_CLASS (parent_class)->motion_notify_event) (widget, event); + + storage_set_view = E_STORAGE_SET_VIEW (widget); + priv = storage_set_view->priv; + + if (priv->in_drag || priv->drag_button == 0) + return FALSE; + + priv->in_drag = TRUE; + + gtk_drag_begin (widget, target_list, GDK_ACTION_MOVE, + priv->drag_button, (GdkEvent *) event); + + return TRUE; +} + +static int +button_release_event (GtkWidget *widget, + GdkEventButton *event) +{ + EStorageSetView *storage_set_view; + EStorageSetViewPrivate *priv; + + if (event->window != GTK_CLIST (widget)->clist_window) + return (* GTK_WIDGET_CLASS (parent_class)->button_release_event) (widget, event); + + storage_set_view = E_STORAGE_SET_VIEW (widget); + priv = storage_set_view->priv; + + if (! priv->in_drag && priv->selected_row_path != NULL) { + gdk_pointer_ungrab (GDK_CURRENT_TIME); + gdk_flush (); + + gtk_signal_emit (GTK_OBJECT (widget), signals[FOLDER_SELECTED], + priv->selected_row_path); + priv->selected_row_path = NULL; + } + + return TRUE; +} + +static void +drag_end (GtkWidget *widget, + GdkDragContext *context) +{ + EStorageSetView *storage_set_view; + EStorageSetViewPrivate *priv; + + storage_set_view = E_STORAGE_SET_VIEW (widget); + priv = storage_set_view->priv; + + priv->in_drag = FALSE; + priv->drag_button = 0; +} + +static void +set_uri_list_selection (EStorageSetView *storage_set_view, + GtkSelectionData *selection_data) +{ + EStorageSetViewPrivate *priv; + char *uri_list; + + priv = storage_set_view->priv; + + /* FIXME: Get `evolution:' from somewhere instead of hardcoding it here. */ + uri_list = g_strconcat ("evolution:", priv->selected_row_path, "\n", NULL); + gtk_selection_data_set (selection_data, selection_data->target, + 8, (guchar *) uri_list, strlen (uri_list)); + g_free (uri_list); +} + +static void +set_e_shortcut_selection (EStorageSetView *storage_set_view, + GtkSelectionData *selection_data) +{ + EStorageSetViewPrivate *priv; + int shortcut_len; + char *shortcut; + const char *trailing_slash; + const char *name; + + priv = storage_set_view->priv; + + trailing_slash = strrchr (priv->selected_row_path, '/'); + if (trailing_slash == NULL) + name = NULL; + else + name = trailing_slash + 1; + + /* FIXME: Get `evolution:' from somewhere instead of hardcoding it here. */ + + if (name != NULL) + shortcut_len = strlen (name); + else + shortcut_len = 0; + + shortcut_len ++; /* Separating zero. */ + + shortcut_len += strlen ("evolution:"); + shortcut_len += strlen (priv->selected_row_path); + shortcut_len ++; /* Trailing zero. */ + + shortcut = g_malloc (shortcut_len); + + if (name == NULL) + sprintf (shortcut, "%cevolution:%s", '\0', priv->selected_row_path); + else + sprintf (shortcut, "%s%cevolution:%s", name, '\0', priv->selected_row_path); + + gtk_selection_data_set (selection_data, selection_data->target, + 8, (guchar *) shortcut, shortcut_len); + + g_free (shortcut); +} + +static void +drag_data_get (GtkWidget *widget, + GdkDragContext *context, + GtkSelectionData *selection_data, + guint info, + guint32 time) +{ + EStorageSetView *storage_set_view; + + storage_set_view = E_STORAGE_SET_VIEW (widget); + + switch (info) { + case DND_TARGET_TYPE_URI_LIST: + set_uri_list_selection (storage_set_view, selection_data); + break; + case DND_TARGET_TYPE_E_SHORTCUT: + set_e_shortcut_selection (storage_set_view, selection_data); + break; + default: + g_assert_not_reached (); + } +} + /* GtkCTree methods. */ @@ -91,20 +308,19 @@ tree_select_row (GtkCTree *ctree, gint column) { EStorageSetView *storage_set_view; + EStorageSetViewPrivate *priv; const char *path; (* GTK_CTREE_CLASS (parent_class)->tree_select_row) (ctree, row, column); - /* CTree is utterly stupid and grabs the pointer here. */ - gdk_pointer_ungrab (GDK_CURRENT_TIME); - gdk_flush (); - storage_set_view = E_STORAGE_SET_VIEW (ctree); + priv = storage_set_view->priv; path = g_hash_table_lookup (storage_set_view->priv->ctree_node_to_path, row); - g_return_if_fail (path != NULL); + if (path == NULL) + return; - gtk_signal_emit (GTK_OBJECT (storage_set_view), signals[FOLDER_SELECTED], path); + priv->selected_row_path = path; } @@ -112,6 +328,7 @@ static void class_init (EStorageSetViewClass *klass) { GtkObjectClass *object_class; + GtkWidgetClass *widget_class; GtkCTreeClass *ctree_class; parent_class = gtk_type_class (gtk_ctree_get_type ()); @@ -119,6 +336,13 @@ class_init (EStorageSetViewClass *klass) object_class = GTK_OBJECT_CLASS (klass); object_class->destroy = destroy; + widget_class = GTK_WIDGET_CLASS (klass); + widget_class->button_press_event = button_press_event; + widget_class->motion_notify_event = motion_notify_event; + widget_class->button_release_event = button_release_event; + widget_class->drag_end = drag_end; + widget_class->drag_data_get = drag_data_get; + ctree_class = GTK_CTREE_CLASS (klass); ctree_class->tree_select_row = tree_select_row; @@ -132,6 +356,11 @@ class_init (EStorageSetViewClass *klass) GTK_TYPE_STRING); gtk_object_class_add_signals (object_class, signals, LAST_SIGNAL); + + /* Set up DND. */ + + target_list = gtk_target_list_new (drag_types, num_drag_types); + g_assert (target_list != NULL); } static void @@ -143,9 +372,12 @@ init (EStorageSetView *storage_set_view) GTK_WIDGET_UNSET_FLAGS (storage_set_view, GTK_CAN_FOCUS); priv = g_new (EStorageSetViewPrivate, 1); + priv->storage_set = NULL; priv->ctree_node_to_path = g_hash_table_new (g_direct_hash, g_direct_equal); priv->path_to_ctree_node = g_hash_table_new (g_str_hash, g_str_equal); + priv->selected_row_path = NULL; + priv->in_drag = FALSE; storage_set_view->priv = priv; } @@ -165,9 +397,6 @@ get_pixmap_and_mask_for_folder (EStorageSetView *storage_set_view, GdkVisual *visual; GdkGC *gc; -#define ICON_WIDTH 16 -#define ICON_HEIGHT 16 - storage_set = storage_set_view->priv->storage_set; folder_type_repository = e_storage_set_get_folder_type_repository (storage_set); @@ -201,9 +430,6 @@ get_pixmap_and_mask_for_folder (EStorageSetView *storage_set_view, ICON_WIDTH, ICON_HEIGHT, 0x7f); gdk_pixbuf_unref (scaled_pixbuf); - -#undef ICON_WIDTH -#undef ICON_HEIGHT } static int @@ -303,8 +529,12 @@ e_storage_set_view_construct (EStorageSetView *storage_set_view, g_return_if_fail (E_IS_STORAGE_SET (storage_set)); ctree = GTK_CTREE (storage_set_view); + + /* Set up GtkCTree/GtkCList parameters. */ gtk_ctree_construct (ctree, 1, 0, NULL); gtk_ctree_set_line_style (ctree, GTK_CTREE_LINES_DOTTED); + gtk_clist_set_selection_mode (GTK_CLIST (ctree), GTK_SELECTION_BROWSE); + gtk_clist_set_row_height (GTK_CLIST (ctree), ICON_HEIGHT); priv = storage_set_view->priv; @@ -318,9 +548,9 @@ e_storage_set_view_construct (EStorageSetView *storage_set_view, for (p = storage_list; p != NULL; p = p->next) { storage = E_STORAGE (p->data); - name = e_storage_get_name (storage); /* Yuck. */ + name = e_storage_get_name (storage); + text[0] = (char *) name; /* Yuck. */ - text[0] = (char *) name; parent = gtk_ctree_insert_node (ctree, NULL, NULL, text, 3, NULL, NULL, NULL, NULL, -- cgit v1.2.3