From a9b45a63203f49629bc1f8ca85f0dc4af1b750c5 Mon Sep 17 00:00:00 2001 From: Benedikt Meurer <benny@xfce.org> Date: Sat, 18 Feb 2006 18:18:46 +0000 Subject: [PATCH] 2006-02-18 Benedikt Meurer <benny@xfce.org> * thunar/thunar-location-button.{c,h}, thunar/Makefile.am, thunar/thunar-location-buttons.c: Put the location button stuff into a new class ThunarLocationButton. * po/POTFILES.in: Add thunar-location-button.c here. (Old svn revision: 19950) --- ChangeLog | 7 + po/POTFILES.in | 1 + thunar/Makefile.am | 2 + thunar/thunar-location-button.c | 824 +++++++++++++++++++++++++++++++ thunar/thunar-location-button.h | 53 ++ thunar/thunar-location-buttons.c | 512 +------------------ 6 files changed, 912 insertions(+), 487 deletions(-) create mode 100644 thunar/thunar-location-button.c create mode 100644 thunar/thunar-location-button.h diff --git a/ChangeLog b/ChangeLog index 7d508d181..8ebd76082 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +2006-02-18 Benedikt Meurer <benny@xfce.org> + + * thunar/thunar-location-button.{c,h}, thunar/Makefile.am, + thunar/thunar-location-buttons.c: Put the location button stuff into + a new class ThunarLocationButton. + * po/POTFILES.in: Add thunar-location-button.c here. + 2006-02-16 Benedikt Meurer <benny@xfce.org> * THANKS: Change Nick's email address as requested. diff --git a/po/POTFILES.in b/po/POTFILES.in index 31a4403db..93aa7633d 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -57,6 +57,7 @@ thunar/thunar-icon-view.c thunar/thunar-launcher.c thunar/thunar-list-model.c thunar/thunar-location-bar.c +thunar/thunar-location-button.c thunar/thunar-location-buttons.c thunar/thunar-location-dialog.c thunar/thunar-location-entry.c diff --git a/thunar/Makefile.am b/thunar/Makefile.am index 0c195c217..45c87bbaa 100644 --- a/thunar/Makefile.am +++ b/thunar/Makefile.am @@ -81,6 +81,8 @@ Thunar_SOURCES = \ thunar-list-model.h \ thunar-location-bar.c \ thunar-location-bar.h \ + thunar-location-button.c \ + thunar-location-button.h \ thunar-location-buttons.c \ thunar-location-buttons.h \ thunar-location-dialog.c \ diff --git a/thunar/thunar-location-button.c b/thunar/thunar-location-button.c new file mode 100644 index 000000000..5229794da --- /dev/null +++ b/thunar/thunar-location-button.c @@ -0,0 +1,824 @@ +/* $Id$ */ +/*- + * Copyright (c) 2005-2006 Benedikt Meurer <benny@xfce.org> + * + * 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. + * + * This program 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 General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#ifdef HAVE_MEMORY_H +#include <memory.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif + +#include <thunar/thunar-application.h> +#include <thunar/thunar-dnd.h> +#include <thunar/thunar-icon-factory.h> +#include <thunar/thunar-location-button.h> +#include <thunar/thunar-pango-extensions.h> + + + +/* Property identifiers */ +enum +{ + PROP_0, + PROP_ACTIVE, + PROP_FILE, +}; + +/* Signal identifiers */ +enum +{ + CLICKED, + LAST_SIGNAL, +}; + + + +static void thunar_location_button_class_init (ThunarLocationButtonClass *klass); +static void thunar_location_button_init (ThunarLocationButton *location_button); +static void thunar_location_button_finalize (GObject *object); +static void thunar_location_button_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); +static void thunar_location_button_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); +static void thunar_location_button_style_set (GtkWidget *widget, + GtkStyle *previous_style); +static GdkDragAction thunar_location_button_get_dest_actions (ThunarLocationButton *location_button, + GdkDragContext *context, + GtkWidget *button, + guint time); +static void thunar_location_button_file_changed (ThunarLocationButton *location_button, + ThunarFile *file); +static gboolean thunar_location_button_button_release_event (GtkWidget *button, + GdkEventButton *event, + ThunarLocationButton *location_button); +static gboolean thunar_location_button_drag_drop (GtkWidget *button, + GdkDragContext *context, + gint x, + gint y, + guint time, + ThunarLocationButton *location_button); +static void thunar_location_button_drag_data_get (GtkWidget *button, + GdkDragContext *context, + GtkSelectionData *selection_data, + guint info, + guint time, + ThunarLocationButton *location_button); +static void thunar_location_button_drag_data_received (GtkWidget *button, + GdkDragContext *context, + gint x, + gint y, + GtkSelectionData *selection_data, + guint info, + guint time, + ThunarLocationButton *location_button); +static void thunar_location_button_drag_leave (GtkWidget *button, + GdkDragContext *context, + guint time, + ThunarLocationButton *location_button); +static gboolean thunar_location_button_drag_motion (GtkWidget *button, + GdkDragContext *context, + gint x, + gint y, + guint time, + ThunarLocationButton *location_button); +static gboolean thunar_location_button_enter_timeout (gpointer user_data); +static void thunar_location_button_enter_timeout_destroy (gpointer user_data); + + + +struct _ThunarLocationButtonClass +{ + GtkAlignmentClass __parent__; +}; + +struct _ThunarLocationButton +{ + GtkAlignment __parent__; + + GtkWidget *image; + GtkWidget *label; + + /* enter folders using DnD */ + gint enter_timeout_id; + + /* drop support for the button */ + GList *drop_path_list; + guint drop_data_ready : 1; + guint drop_occurred : 1; + + /* public properties */ + guint active : 1; + ThunarFile *file; +}; + + + +static const GtkTargetEntry drag_targets[] = +{ + { "text/uri-list", 0, 0 }, +}; + +static GObjectClass *thunar_location_button_parent_class; +static guint location_button_signals[LAST_SIGNAL]; + + + +GType +thunar_location_button_get_type (void) +{ + static GType type = G_TYPE_INVALID; + + if (G_UNLIKELY (type == G_TYPE_INVALID)) + { + static const GTypeInfo info = + { + sizeof (ThunarLocationButtonClass), + NULL, + NULL, + (GClassInitFunc) thunar_location_button_class_init, + NULL, + NULL, + sizeof (ThunarLocationButton), + 0, + (GInstanceInitFunc) thunar_location_button_init, + NULL, + }; + + type = g_type_register_static (GTK_TYPE_ALIGNMENT, I_("ThunarLocationButton"), &info, 0); + } + + return type; +} + + + +static void +thunar_location_button_class_init (ThunarLocationButtonClass *klass) +{ + GtkWidgetClass *gtkwidget_class; + GObjectClass *gobject_class; + + /* determine the parent type class */ + thunar_location_button_parent_class = g_type_class_peek_parent (klass); + + gobject_class = G_OBJECT_CLASS (klass); + gobject_class->finalize = thunar_location_button_finalize; + gobject_class->get_property = thunar_location_button_get_property; + gobject_class->set_property = thunar_location_button_set_property; + + gtkwidget_class = GTK_WIDGET_CLASS (klass); + gtkwidget_class->style_set = thunar_location_button_style_set; + + /** + * ThunarLocationButton:active: + * + * Whether the location button is currently active. + **/ + g_object_class_install_property (gobject_class, + PROP_ACTIVE, + g_param_spec_boolean ("active", + "active", + "active", + FALSE, + EXO_PARAM_READWRITE)); + + /** + * ThunarLocationButton:file: + * + * The #ThunarFile represented by this location button. + **/ + g_object_class_install_property (gobject_class, + PROP_FILE, + g_param_spec_object ("file", + "file", + "file", + THUNAR_TYPE_FILE, + EXO_PARAM_READWRITE)); + + /** + * ThunarLocationButton::clicked: + * @location_button : a #ThunarLocationButton. + * + * Emitted by @location_button when the user clicks on the + * @location_button or thunar_location_button_clicked() is + * called. + **/ + location_button_signals[CLICKED] = + g_signal_new (I_("clicked"), + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); +} + + + +static void +thunar_location_button_init (ThunarLocationButton *location_button) +{ + GtkWidget *button; + GtkWidget *hbox; + + location_button->enter_timeout_id = -1; + + gtk_widget_push_composite_child (); + + /* create the toggle button */ + button = gtk_toggle_button_new (); + GTK_WIDGET_UNSET_FLAGS (button, GTK_CAN_FOCUS); + g_signal_connect_swapped (G_OBJECT (button), "clicked", G_CALLBACK (thunar_location_button_clicked), location_button); + exo_mutual_binding_new (G_OBJECT (location_button), "active", G_OBJECT (button), "active"); + gtk_container_add (GTK_CONTAINER (location_button), button); + gtk_widget_show (button); + + /* setup drag support for the button */ + gtk_drag_source_set (GTK_WIDGET (button), GDK_BUTTON1_MASK, drag_targets, G_N_ELEMENTS (drag_targets), GDK_ACTION_LINK); + gtk_drag_dest_set (GTK_WIDGET (button), GTK_DEST_DEFAULT_HIGHLIGHT | GTK_DEST_DEFAULT_MOTION, drag_targets, + G_N_ELEMENTS (drag_targets), GDK_ACTION_ASK | GDK_ACTION_COPY | GDK_ACTION_LINK | GDK_ACTION_MOVE); + g_signal_connect (G_OBJECT (button), "button-release-event", G_CALLBACK (thunar_location_button_button_release_event), location_button); + g_signal_connect (G_OBJECT (button), "drag-drop", G_CALLBACK (thunar_location_button_drag_drop), location_button); + g_signal_connect (G_OBJECT (button), "drag-data-get", G_CALLBACK (thunar_location_button_drag_data_get), location_button); + g_signal_connect (G_OBJECT (button), "drag-data-received", G_CALLBACK (thunar_location_button_drag_data_received), location_button); + g_signal_connect (G_OBJECT (button), "drag-leave", G_CALLBACK (thunar_location_button_drag_leave), location_button); + g_signal_connect (G_OBJECT (button), "drag-motion", G_CALLBACK (thunar_location_button_drag_motion), location_button); + + /* create the horizontal box */ + hbox = gtk_hbox_new (FALSE, 2); + gtk_container_add (GTK_CONTAINER (button), hbox); + gtk_widget_show (hbox); + + /* create the button image */ + location_button->image = gtk_image_new (); + gtk_box_pack_start (GTK_BOX (hbox), location_button->image, FALSE, FALSE, 0); + gtk_widget_show (location_button->image); + + /* create the button label */ + location_button->label = g_object_new (GTK_TYPE_LABEL, NULL); + gtk_box_pack_start (GTK_BOX (hbox), location_button->label, TRUE, TRUE, 0); + gtk_widget_show (location_button->label); + + gtk_widget_pop_composite_child (); +} + + + +static void +thunar_location_button_finalize (GObject *object) +{ + ThunarLocationButton *location_button = THUNAR_LOCATION_BUTTON (object); + + /* release the drop path list (just in case the drag-leave wasn't fired before) */ + thunar_vfs_path_list_free (location_button->drop_path_list); + + /* be sure to cancel any pending enter timeout */ + if (G_UNLIKELY (location_button->enter_timeout_id >= 0)) + g_source_remove (location_button->enter_timeout_id); + + /* disconnect from the file */ + thunar_location_button_set_file (location_button, NULL); + + (*G_OBJECT_CLASS (thunar_location_button_parent_class)->finalize) (object); +} + + + +static void +thunar_location_button_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + ThunarLocationButton *location_button = THUNAR_LOCATION_BUTTON (object); + + switch (prop_id) + { + case PROP_ACTIVE: + g_value_set_boolean (value, thunar_location_button_get_active (location_button)); + break; + + case PROP_FILE: + g_value_set_object (value, thunar_location_button_get_file (location_button)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + + + +static void +thunar_location_button_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + ThunarLocationButton *location_button = THUNAR_LOCATION_BUTTON (object); + + switch (prop_id) + { + case PROP_ACTIVE: + thunar_location_button_set_active (location_button, g_value_get_boolean (value)); + break; + + case PROP_FILE: + thunar_location_button_set_file (location_button, g_value_get_object (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + + + +static void +thunar_location_button_style_set (GtkWidget *widget, + GtkStyle *previous_style) +{ + ThunarLocationButton *location_button = THUNAR_LOCATION_BUTTON (widget); + + /* update the user interface if we have a file */ + if (G_LIKELY (location_button->file != NULL)) + thunar_location_button_file_changed (location_button, location_button->file); + + (*GTK_WIDGET_CLASS (thunar_location_button_parent_class)->style_set) (widget, previous_style); +} + + + +static GdkDragAction +thunar_location_button_get_dest_actions (ThunarLocationButton *location_button, + GdkDragContext *context, + GtkWidget *button, + guint time) +{ + GdkDragAction actions = 0; + GdkDragAction action = 0; + + /* check if we can drop here */ + if (G_LIKELY (location_button->file != NULL)) + { + /* determine the possible drop actions for the file (and the suggested action if any) */ + actions = thunar_file_accepts_drop (location_button->file, location_button->drop_path_list, context, &action); + } + + /* tell Gdk whether we can drop here */ + gdk_drag_status (context, action, time); + + return actions; +} + + + +static void +thunar_location_button_file_changed (ThunarLocationButton *location_button, + ThunarFile *file) +{ + ThunarIconFactory *icon_factory; + GtkIconTheme *icon_theme; + GdkPixbuf *icon; + gint size; + + g_return_if_fail (THUNAR_IS_LOCATION_BUTTON (location_button)); + g_return_if_fail (location_button->file == file); + g_return_if_fail (THUNAR_IS_FILE (file)); + + /* determine the icon size for buttons */ + gtk_icon_size_lookup (GTK_ICON_SIZE_BUTTON, &size, &size); + + /* update the icon for the image and DnD */ + icon_theme = gtk_icon_theme_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (location_button))); + icon_factory = thunar_icon_factory_get_for_icon_theme (icon_theme); + icon = thunar_icon_factory_load_file_icon (icon_factory, file, THUNAR_FILE_ICON_STATE_DEFAULT, size); + gtk_drag_source_set_icon_pixbuf (GTK_BIN (location_button)->child, icon); + gtk_image_set_from_pixbuf (GTK_IMAGE (location_button->image), icon); + g_object_unref (G_OBJECT (icon_factory)); + g_object_unref (G_OBJECT (icon)); + + /* the label is only visible for non-root folders */ + if (thunar_file_is_root (file)) + { + /* hide the label widget */ + gtk_widget_hide (location_button->label); + } + else + { + /* update and show the label widget */ + gtk_label_set_text (GTK_LABEL (location_button->label), thunar_file_get_display_name (file)); + gtk_widget_show (location_button->label); + } +} + + + +static gboolean +thunar_location_button_button_release_event (GtkWidget *button, + GdkEventButton *event, + ThunarLocationButton *location_button) +{ + ThunarApplication *application; + + g_return_val_if_fail (THUNAR_IS_LOCATION_BUTTON (location_button), FALSE); + + /* open folder in window on middle-click events */ + if (G_UNLIKELY (event->button == 2 && location_button->file != NULL)) + { + /* popup a new window */ + application = thunar_application_get (); + thunar_application_open_window (application, location_button->file, gtk_widget_get_screen (button)); + g_object_unref (G_OBJECT (application)); + } + + return FALSE; +} + + + +static gboolean +thunar_location_button_drag_drop (GtkWidget *button, + GdkDragContext *context, + gint x, + gint y, + guint time, + ThunarLocationButton *location_button) +{ + GdkAtom target; + + g_return_val_if_fail (GTK_IS_WIDGET (button), FALSE); + g_return_val_if_fail (THUNAR_IS_LOCATION_BUTTON (location_button), FALSE); + + /* determine the DnD target and see if we can handle it */ + target = gtk_drag_dest_find_target (button, context, NULL); + if (G_UNLIKELY (target != gdk_atom_intern ("text/uri-list", FALSE))) + return FALSE; + + /* set state so drag-data-received knows that + * this is really a drop this time. + */ + location_button->drop_occurred = TRUE; + + /* request the drag data from the source */ + gtk_drag_get_data (button, context, target, time); + + /* we'll call gtk_drag_finish() later */ + return TRUE; +} + + + +static void +thunar_location_button_drag_data_get (GtkWidget *button, + GdkDragContext *context, + GtkSelectionData *selection_data, + guint info, + guint time, + ThunarLocationButton *location_button) +{ + gchar *uri_string; + GList path_list; + + g_return_if_fail (GTK_IS_WIDGET (button)); + g_return_if_fail (THUNAR_IS_LOCATION_BUTTON (location_button)); + + /* verify that we have a valid file */ + if (G_LIKELY (location_button->file != NULL)) + { + /* transform the path into an uri list string */ + path_list.data = thunar_file_get_path (location_button->file); path_list.next = path_list.prev = NULL; + uri_string = thunar_vfs_path_list_to_string (&path_list); + + /* set the uri list for the drag selection */ + gtk_selection_data_set (selection_data, selection_data->target, 8, (guchar *) uri_string, strlen (uri_string)); + + /* cleanup */ + g_free (uri_string); + } +} + + + +static void +thunar_location_button_drag_data_received (GtkWidget *button, + GdkDragContext *context, + gint x, + gint y, + GtkSelectionData *selection_data, + guint info, + guint time, + ThunarLocationButton *location_button) +{ + GdkDragAction actions; + GdkDragAction action; + gboolean succeed = FALSE; + + /* check if we don't already know the drop data */ + if (G_LIKELY (!location_button->drop_data_ready)) + { + /* extract the URI list from the selection data (if valid) */ + if (selection_data->format == 8 && selection_data->length > 0) + location_button->drop_path_list = thunar_vfs_path_list_from_string ((const gchar *) selection_data->data, NULL); + + /* reset the state */ + location_button->drop_data_ready = TRUE; + } + + /* check if the data was dropped */ + if (G_UNLIKELY (location_button->drop_occurred)) + { + /* reset the state */ + location_button->drop_occurred = FALSE; + + /* determine the drop dest actions */ + actions = thunar_location_button_get_dest_actions (location_button, context, button, time); + if (G_LIKELY ((actions & (GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK)) != 0)) + { + /* as the user what to do with the drop data */ + action = (context->action == GDK_ACTION_ASK) ? thunar_dnd_ask (button, time, actions) : context->action; + + /* perform the requested action */ + if (G_LIKELY (action != 0)) + succeed = thunar_dnd_perform (button, location_button->file, location_button->drop_path_list, action, NULL); + } + + /* tell the peer that we handled the drop */ + gtk_drag_finish (context, succeed, FALSE, time); + + /* disable the highlighting and release the drag data */ + thunar_location_button_drag_leave (button, context, time, location_button); + } +} + + + +static void +thunar_location_button_drag_leave (GtkWidget *button, + GdkDragContext *context, + guint time, + ThunarLocationButton *location_button) +{ + g_return_if_fail (GTK_IS_BUTTON (button)); + g_return_if_fail (THUNAR_IS_LOCATION_BUTTON (location_button)); + + /* reset the "drop data ready" status and free the path list */ + if (G_LIKELY (location_button->drop_data_ready)) + { + thunar_vfs_path_list_free (location_button->drop_path_list); + location_button->drop_data_ready = FALSE; + location_button->drop_path_list = NULL; + } + + /* be sure to cancel any running enter timeout */ + if (G_LIKELY (location_button->enter_timeout_id >= 0)) + g_source_remove (location_button->enter_timeout_id); +} + + + +static gboolean +thunar_location_button_drag_motion (GtkWidget *button, + GdkDragContext *context, + gint x, + gint y, + guint time, + ThunarLocationButton *location_button) +{ + GtkSettings *settings; + GdkAtom target; + gint delay; + + g_return_val_if_fail (GTK_IS_BUTTON (button), FALSE); + g_return_val_if_fail (THUNAR_IS_LOCATION_BUTTON (location_button), FALSE); + + /* schedule the enter timeout if not already done */ + if (G_UNLIKELY (location_button->enter_timeout_id < 0)) + { + /* we use the gtk-menu-popdown-delay here, which seems to be sane for our purpose */ + settings = gtk_settings_get_for_screen (gtk_widget_get_screen (button)); + g_object_get (G_OBJECT (settings), "gtk-menu-popdown-delay", &delay, NULL); + + /* schedule the timeout */ + location_button->enter_timeout_id = g_timeout_add_full (G_PRIORITY_DEFAULT, delay, thunar_location_button_enter_timeout, + location_button, thunar_location_button_enter_timeout_destroy); + } + + /* request the drop on-demand (if we don't have it already) */ + if (G_UNLIKELY (!location_button->drop_data_ready)) + { + /* check if we can handle that drag data (can only drop text/uri-list) */ + target = gtk_drag_dest_find_target (button, context, NULL); + if (G_LIKELY (target == gdk_atom_intern ("text/uri-list", FALSE))) + { + /* request the drop data from the source */ + gtk_drag_get_data (button, context, target, time); + } + + /* tell GDK that it cannot drop here (yet!) */ + gdk_drag_status (context, 0, time); + } + else + { + /* check whether we can drop into the buttons' folder */ + thunar_location_button_get_dest_actions (location_button, context, button, time); + } + + return FALSE; +} + + + +static gboolean +thunar_location_button_enter_timeout (gpointer user_data) +{ + ThunarLocationButton *location_button = THUNAR_LOCATION_BUTTON (user_data); + + GDK_THREADS_ENTER (); + + /* We emulate a "clicked" event here, because else the buttons + * would be destroyed and replaced by new buttons, which causes + * the Gtk DND code to dump core once the mouse leaves the area + * of the new button. + * + * Besides that, handling this as "clicked" event allows the user + * to go back to the initial directory. + */ + thunar_location_button_clicked (location_button); + + GDK_THREADS_LEAVE (); + + return FALSE; +} + + + +static void +thunar_location_button_enter_timeout_destroy (gpointer user_data) +{ + THUNAR_LOCATION_BUTTON (user_data)->enter_timeout_id = -1; +} + + + +/** + * thunar_location_button_new: + * + * Allocates a new #ThunarLocationButton instance. + * + * Return value: the newly allocated #ThunarLocationButton. + **/ +GtkWidget* +thunar_location_button_new (void) +{ + return g_object_new (THUNAR_TYPE_LOCATION_BUTTON, NULL); +} + + + +/** + * thunar_location_button_get_active: + * @location_button : a #ThunarLocationButton. + * + * Returns %TRUE if @location_button is currently active. + * + * Return value: %TRUE if @location_button is currently active. + **/ +gboolean +thunar_location_button_get_active (ThunarLocationButton *location_button) +{ + g_return_val_if_fail (THUNAR_IS_LOCATION_BUTTON (location_button), FALSE); + return location_button->active; +} + + + +/** + * thunar_location_button_set_active: + * @location_button : a #ThunarLocationButton. + * @active : %TRUE if @location_button should be active. + * + * Sets the active state of @location_button to @active. + **/ +void +thunar_location_button_set_active (ThunarLocationButton *location_button, + gboolean active) +{ + g_return_if_fail (THUNAR_IS_LOCATION_BUTTON (location_button)); + + /* apply the new state */ + location_button->active = active; + + /* use a bold label for active location buttons */ + gtk_label_set_attributes (GTK_LABEL (location_button->label), active ? thunar_pango_attr_list_bold () : NULL); + + /* notify listeners */ + g_object_notify (G_OBJECT (location_button), "active"); +} + + + +/** + * thunar_location_button_get_file: + * @location_button : a #ThunarLocationButton. + * + * Returns the #ThunarFile for @location_button. + * + * Return value: the #ThunarFile for @location_button. + **/ +ThunarFile* +thunar_location_button_get_file (ThunarLocationButton *location_button) +{ + g_return_val_if_fail (THUNAR_IS_LOCATION_BUTTON (location_button), NULL); + return location_button->file; +} + + + +/** + * thunar_location_button_set_file: + * @location_button : a #ThunarLocationButton. + * @file : a #ThunarFile or %NULL. + * + * Sets the file for @location_button to @file. + **/ +void +thunar_location_button_set_file (ThunarLocationButton *location_button, + ThunarFile *file) +{ + g_return_if_fail (THUNAR_IS_LOCATION_BUTTON (location_button)); + g_return_if_fail (file == NULL || THUNAR_IS_FILE (file)); + + /* check if we already use that file */ + if (G_UNLIKELY (location_button->file == file)) + return; + + /* disconnect from the previous file */ + if (location_button->file != NULL) + { + g_signal_handlers_disconnect_by_func (G_OBJECT (location_button->file), thunar_location_button_file_changed, location_button); + g_object_unref (G_OBJECT (location_button->file)); + } + + /* activate the new file */ + location_button->file = file; + + /* connect to the new file */ + if (G_LIKELY (file != NULL)) + { + /* take a reference on the new file */ + g_object_ref (G_OBJECT (file)); + + /* stay informed about changes to the file */ + g_signal_connect_swapped (G_OBJECT (file), "changed", G_CALLBACK (thunar_location_button_file_changed), location_button); + + /* update our internal state for the new file (if realized) */ + if (GTK_WIDGET_REALIZED (location_button)) + thunar_location_button_file_changed (location_button, file); + } + + /* notify listeners */ + g_object_notify (G_OBJECT (location_button), "file"); +} + + + +/** + * thunar_location_button_clicked: + * @location_button : a #ThunarLocationButton. + * + * Emits the ::clicked signal on @location_button. + **/ +void +thunar_location_button_clicked (ThunarLocationButton *location_button) +{ + g_return_if_fail (THUNAR_IS_LOCATION_BUTTON (location_button)); + g_signal_emit (G_OBJECT (location_button), location_button_signals[CLICKED], 0); +} + + diff --git a/thunar/thunar-location-button.h b/thunar/thunar-location-button.h new file mode 100644 index 000000000..a6ea44de7 --- /dev/null +++ b/thunar/thunar-location-button.h @@ -0,0 +1,53 @@ +/* $Id$ */ +/*- + * Copyright (c) 2005-2006 Benedikt Meurer <benny@xfce.org> + * + * 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. + * + * This program 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 General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __THUNAR_LOCATION_BUTTON_H__ +#define __THUNAR_LOCATION_BUTTON_H__ + +#include <thunar/thunar-file.h> + +G_BEGIN_DECLS; + +typedef struct _ThunarLocationButtonClass ThunarLocationButtonClass; +typedef struct _ThunarLocationButton ThunarLocationButton; + +#define THUNAR_TYPE_LOCATION_BUTTON (thunar_location_button_get_type ()) +#define THUNAR_LOCATION_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), THUNAR_TYPE_LOCATION_BUTTON, ThunarLocationButton)) +#define THUNAR_LOCATION_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), THUNAR_TYPE_LOCATION_BUTTON, ThunarLocationButtonClass)) +#define THUNAR_IS_LOCATION_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), THUNAR_TYPE_LOCATION_BUTTON)) +#define THUNAR_IS_LOCATION_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), THUNAR_TYPE_LOCATION_BUTTON)) +#define THUNAR_LOCATION_BUTTON_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), THUNAR_TYPE_LOCATION_BUTTON, ThunarLocationButtonClass)) + +GType thunar_location_button_get_type (void) G_GNUC_CONST; + +GtkWidget *thunar_location_button_new (void) G_GNUC_MALLOC; + +gboolean thunar_location_button_get_active (ThunarLocationButton *location_button); +void thunar_location_button_set_active (ThunarLocationButton *location_button, + gboolean active); + +ThunarFile *thunar_location_button_get_file (ThunarLocationButton *location_button); +void thunar_location_button_set_file (ThunarLocationButton *location_button, + ThunarFile *file); + +void thunar_location_button_clicked (ThunarLocationButton *location_button); + +G_END_DECLS; + +#endif /* !__THUNAR_LOCATION_BUTTON_H__ */ diff --git a/thunar/thunar-location-buttons.c b/thunar/thunar-location-buttons.c index d220caba1..5c4ce15a0 100644 --- a/thunar/thunar-location-buttons.c +++ b/thunar/thunar-location-buttons.c @@ -24,19 +24,10 @@ #include <config.h> #endif -#ifdef HAVE_MEMORY_H -#include <memory.h> -#endif -#ifdef HAVE_STRING_H -#include <string.h> -#endif - -#include <thunar/thunar-dnd.h> #include <thunar/thunar-file-monitor.h> #include <thunar/thunar-gobject-extensions.h> -#include <thunar/thunar-icon-factory.h> +#include <thunar/thunar-location-button.h> #include <thunar/thunar-location-buttons.h> -#include <thunar/thunar-pango-extensions.h> @@ -76,8 +67,6 @@ static void thunar_location_buttons_size_allocate (GtkWidget GtkAllocation *allocation); static void thunar_location_buttons_state_changed (GtkWidget *widget, GtkStateType previous_state); -static void thunar_location_buttons_style_set (GtkWidget *widget, - GtkStyle *previous_style); static void thunar_location_buttons_grab_notify (GtkWidget *widget, gboolean was_grabbed); static void thunar_location_buttons_add (GtkContainer *container, @@ -109,45 +98,8 @@ static void thunar_location_buttons_scroll_left (GtkWidget ThunarLocationButtons *buttons); static void thunar_location_buttons_scroll_right (GtkWidget *button, ThunarLocationButtons *buttons); -static void thunar_location_buttons_clicked (GtkWidget *button, - ThunarLocationButtons *buttons); -static GdkDragAction thunar_location_buttons_get_dest_actions (ThunarLocationButtons *buttons, - GdkDragContext *context, - GtkWidget *button, - guint time, - ThunarFile **file_return); -static gboolean thunar_location_buttons_drag_drop (GtkWidget *button, - GdkDragContext *context, - gint x, - gint y, - guint time, - ThunarLocationButtons *buttons); -static void thunar_location_buttons_drag_data_get (GtkWidget *button, - GdkDragContext *context, - GtkSelectionData *selection_data, - guint info, - guint time, - ThunarLocationButtons *buttons); -static void thunar_location_buttons_drag_data_received (GtkWidget *button, - GdkDragContext *context, - gint x, - gint y, - GtkSelectionData *selection_data, - guint info, - guint time, +static void thunar_location_buttons_clicked (ThunarLocationButton *button, ThunarLocationButtons *buttons); -static void thunar_location_buttons_drag_leave (GtkWidget *button, - GdkDragContext *context, - guint time, - ThunarLocationButtons *buttons); -static gboolean thunar_location_buttons_drag_motion (GtkWidget *button, - GdkDragContext *context, - gint x, - gint y, - guint time, - ThunarLocationButtons *buttons); -static gboolean thunar_location_buttons_enter_timeout (gpointer user_data); -static void thunar_location_buttons_enter_timeout_destroy (gpointer user_data); @@ -174,24 +126,6 @@ struct _ThunarLocationButtons GList *first_scrolled_button; gint scroll_timeout_id; - - /* enter directories using DnD */ - GtkWidget *enter_button; - gint enter_timeout_id; - - /* Drop support for the buttons */ - GList *drop_path_list; - guint drop_data_ready : 1; - guint drop_occurred : 1; -}; - - -static GQuark gtk_label_quark = 0; -static GQuark thunar_file_quark = 0; - -static const GtkTargetEntry drag_targets[] = -{ - { "text/uri-list", 0, 0 }, }; @@ -255,9 +189,6 @@ thunar_location_buttons_class_init (ThunarLocationButtonsClass *klass) /* determine the parent type class */ thunar_location_buttons_parent_class = g_type_class_peek_parent (klass); - gtk_label_quark = g_quark_from_static_string ("gtk-label"); - thunar_file_quark = g_quark_from_static_string ("thunar-file"); - gobject_class = G_OBJECT_CLASS (klass); gobject_class->finalize = thunar_location_buttons_finalize; gobject_class->get_property = thunar_location_buttons_get_property; @@ -268,7 +199,6 @@ thunar_location_buttons_class_init (ThunarLocationButtonsClass *klass) gtkwidget_class->size_request = thunar_location_buttons_size_request; gtkwidget_class->size_allocate = thunar_location_buttons_size_allocate; gtkwidget_class->state_changed = thunar_location_buttons_state_changed; - gtkwidget_class->style_set = thunar_location_buttons_style_set; gtkwidget_class->grab_notify = thunar_location_buttons_grab_notify; gtkcontainer_class = GTK_CONTAINER_CLASS (klass); @@ -325,7 +255,6 @@ thunar_location_buttons_init (ThunarLocationButtons *buttons) GTK_WIDGET_SET_FLAGS (buttons, GTK_NO_WINDOW); gtk_widget_set_redraw_on_allocate (GTK_WIDGET (buttons), FALSE); - buttons->enter_timeout_id = -1; buttons->scroll_timeout_id = -1; gtk_widget_push_composite_child (); @@ -370,13 +299,6 @@ thunar_location_buttons_finalize (GObject *object) g_return_if_fail (THUNAR_IS_LOCATION_BUTTONS (buttons)); - /* release the drop path list (just in case the drag-leave wasn't fired before) */ - thunar_vfs_path_list_free (buttons->drop_path_list); - - /* be sure to cancel any enter timeout */ - if (G_UNLIKELY (buttons->enter_timeout_id >= 0)) - g_source_remove (buttons->enter_timeout_id); - /* be sure to cancel the scrolling */ thunar_location_buttons_stop_scrolling (buttons); @@ -464,14 +386,14 @@ thunar_location_buttons_set_current_directory (ThunarNavigator *navigator, /* check if we already have a button for that directory */ for (lp = buttons->list; lp != NULL; lp = lp->next) - if (g_object_get_qdata (G_OBJECT (lp->data), thunar_file_quark) == current_directory) + if (thunar_location_button_get_file (lp->data) == current_directory) break; /* if we already have a button for that directory, just activate it */ if (G_UNLIKELY (lp != NULL)) { /* fake a "clicked" event for that button */ - gtk_button_clicked (GTK_BUTTON (lp->data)); + thunar_location_button_clicked (THUNAR_LOCATION_BUTTON (lp->data)); } else { @@ -760,49 +682,6 @@ thunar_location_buttons_state_changed (GtkWidget *widget, -static void -thunar_location_buttons_style_set (GtkWidget *widget, - GtkStyle *previous_style) -{ - ThunarLocationButtons *buttons = THUNAR_LOCATION_BUTTONS (widget); - ThunarIconFactory *icon_factory; - GtkIconTheme *icon_theme; - ThunarFile *file; - GdkPixbuf *icon; - GList *children; - GList *lp; - gint size; - - if (G_LIKELY (GTK_WIDGET_REALIZED (widget))) - { - /* lookup the icon size for buttons */ - gtk_icon_size_lookup (GTK_ICON_SIZE_BUTTON, &size, &size); - - /* determine the icon factory to use */ - icon_theme = gtk_icon_theme_get_for_screen (gtk_widget_get_screen (widget)); - icon_factory = thunar_icon_factory_get_for_icon_theme (icon_theme); - - /* update the icons for every button */ - for (lp = buttons->list; lp != NULL; lp = lp->next) - { - file = g_object_get_qdata (G_OBJECT (lp->data), thunar_file_quark); - children = gtk_container_get_children (GTK_CONTAINER (GTK_BIN (lp->data)->child)); - icon = thunar_icon_factory_load_file_icon (icon_factory, file, THUNAR_FILE_ICON_STATE_DEFAULT, size); - gtk_drag_source_set_icon_pixbuf (GTK_WIDGET (lp->data), icon); - gtk_image_set_from_pixbuf (GTK_IMAGE (children->data), icon); - g_object_unref (G_OBJECT (icon)); - g_list_free (children); - } - - /* release the icon factory */ - g_object_unref (G_OBJECT (icon_factory)); - } - - (*GTK_WIDGET_CLASS (thunar_location_buttons_parent_class)->style_set) (widget, previous_style); -} - - - static void thunar_location_buttons_grab_notify (GtkWidget *widget, gboolean was_grabbed) @@ -890,72 +769,19 @@ static GtkWidget* thunar_location_buttons_make_button (ThunarLocationButtons *buttons, ThunarFile *file) { - ThunarIconFactory *icon_factory; - GtkWidget *button; - GtkWidget *image; - GtkWidget *label; - GtkWidget *hbox; - GdkPixbuf *icon; - gint size; - - gtk_icon_size_lookup (GTK_ICON_SIZE_BUTTON, &size, &size); + GtkWidget *button; /* allocate the button */ - button = gtk_toggle_button_new (); - - /* don't allow location buttons to grab the focus */ - GTK_WIDGET_UNSET_FLAGS (button, GTK_CAN_FOCUS); - - /* setup drag support for the button */ - gtk_drag_source_set (GTK_WIDGET (button), GDK_BUTTON1_MASK, drag_targets, - G_N_ELEMENTS (drag_targets), GDK_ACTION_LINK); - gtk_drag_dest_set (GTK_WIDGET (button), GTK_DEST_DEFAULT_HIGHLIGHT | GTK_DEST_DEFAULT_MOTION, drag_targets, - G_N_ELEMENTS (drag_targets), GDK_ACTION_ASK | GDK_ACTION_COPY | GDK_ACTION_LINK | GDK_ACTION_MOVE); - g_signal_connect (G_OBJECT (button), "drag-drop", G_CALLBACK (thunar_location_buttons_drag_drop), buttons); - g_signal_connect (G_OBJECT (button), "drag-data-get", G_CALLBACK (thunar_location_buttons_drag_data_get), buttons); - g_signal_connect (G_OBJECT (button), "drag-data-received", G_CALLBACK (thunar_location_buttons_drag_data_received), buttons); - g_signal_connect (G_OBJECT (button), "drag-leave", G_CALLBACK (thunar_location_buttons_drag_leave), buttons); - g_signal_connect (G_OBJECT (button), "drag-motion", G_CALLBACK (thunar_location_buttons_drag_motion), buttons); - - hbox = gtk_hbox_new (FALSE, 2); - gtk_container_add (GTK_CONTAINER (button), hbox); - gtk_widget_show (hbox); - - image = gtk_image_new (); - gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0); - gtk_widget_show (image); - - icon_factory = thunar_icon_factory_get_default (); - icon = thunar_icon_factory_load_file_icon (icon_factory, file, THUNAR_FILE_ICON_STATE_DEFAULT, size); - gtk_drag_source_set_icon_pixbuf (button, icon); - gtk_image_set_from_pixbuf (GTK_IMAGE (image), icon); - g_object_unref (G_OBJECT (icon_factory)); - g_object_unref (G_OBJECT (icon)); - - if (!thunar_file_is_root (file)) - { - /* only non-root nodes have a label */ - label = g_object_new (GTK_TYPE_LABEL, NULL); - exo_binding_new (G_OBJECT (file), "display-name", G_OBJECT (label), "label"); - g_object_set_qdata (G_OBJECT (button), gtk_label_quark, label); - gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0); - gtk_widget_show (label); - - /* current directory gets a bold label */ - if (file == buttons->current_directory) - gtk_label_set_attributes (GTK_LABEL (label), thunar_pango_attr_list_bold ()); - } + button = thunar_location_button_new (); - /* the current directory is toggled */ - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), (file == buttons->current_directory)); + /* connect to the file */ + thunar_location_button_set_file (THUNAR_LOCATION_BUTTON (button), file); - /* take a reference on the ThunarFile for the button */ - g_object_set_qdata_full (G_OBJECT (button), thunar_file_quark, file, g_object_unref); - g_object_ref (G_OBJECT (file)); + /* the current directory is active */ + thunar_location_button_set_active (THUNAR_LOCATION_BUTTON (button), (file == buttons->current_directory)); /* get notifications about user actions */ - g_signal_connect (G_OBJECT (button), "clicked", - G_CALLBACK (thunar_location_buttons_clicked), buttons); + g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (thunar_location_buttons_clicked), buttons); return button; } @@ -995,14 +821,14 @@ thunar_location_buttons_file_destroyed (ThunarFileMonitor *file_monitor, for (lp = children; lp != NULL; lp = lp->next) { /* stop as soon as we reach the current-directory button */ - if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (lp->data))) + if (thunar_location_button_get_active (THUNAR_LOCATION_BUTTON (lp->data))) { lp = NULL; break; } /* check if the button is for the destroyed file */ - if (g_object_get_qdata (G_OBJECT (lp->data), thunar_file_quark) == file) + if (thunar_location_button_get_file (THUNAR_LOCATION_BUTTON (lp->data)) == file) break; } @@ -1218,18 +1044,17 @@ thunar_location_buttons_scroll_right (GtkWidget *button, static void -thunar_location_buttons_clicked (GtkWidget *button, +thunar_location_buttons_clicked (ThunarLocationButton *button, ThunarLocationButtons *buttons) { ThunarFile *directory; - GtkWidget *label; GList *lp; - g_return_if_fail (GTK_IS_TOGGLE_BUTTON (button)); + g_return_if_fail (THUNAR_IS_LOCATION_BUTTON (button)); g_return_if_fail (THUNAR_IS_LOCATION_BUTTONS (buttons)); /* determine the directory associated with the clicked button */ - directory = g_object_get_qdata (G_OBJECT (button), thunar_file_quark); + directory = thunar_location_button_get_file (button); /* disconnect from previous current directory (if any) */ if (G_LIKELY (buttons->current_directory != NULL)) @@ -1237,29 +1062,21 @@ thunar_location_buttons_clicked (GtkWidget *button, /* setup the new current directory */ buttons->current_directory = directory; - g_object_ref (G_OBJECT (directory)); + + /* take a reference on the new directory (if any) */ + if (G_LIKELY (directory != NULL)) + g_object_ref (G_OBJECT (directory)); /* update all buttons */ for (lp = buttons->list; lp != NULL; lp = lp->next) { - /* query button data */ - button = GTK_WIDGET (lp->data); - label = g_object_get_qdata (G_OBJECT (button), gtk_label_quark); - directory = g_object_get_qdata (G_OBJECT (button), thunar_file_quark); + /* determine the directory for this button */ + button = THUNAR_LOCATION_BUTTON (lp->data); + directory = thunar_location_button_get_file (button); - /* update the label (if any) */ - if (G_LIKELY (label != NULL)) - { - /* current directory gets a bold label */ - if (directory == buttons->current_directory) - gtk_label_set_attributes (GTK_LABEL (label), thunar_pango_attr_list_bold ()); - else - gtk_label_set_attributes (GTK_LABEL (label), NULL); - } - - /* update the toggle button state (making sure to not recurse with the "clicked" handler) */ + /* update the location button state (making sure to not recurse with the "clicked" handler) */ g_signal_handlers_block_by_func (G_OBJECT (button), thunar_location_buttons_clicked, buttons); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), directory == buttons->current_directory); + thunar_location_button_set_active (button, directory == buttons->current_directory); g_signal_handlers_unblock_by_func (G_OBJECT (button), thunar_location_buttons_clicked, buttons); } @@ -1271,285 +1088,6 @@ thunar_location_buttons_clicked (GtkWidget *button, -static GdkDragAction -thunar_location_buttons_get_dest_actions (ThunarLocationButtons *buttons, - GdkDragContext *context, - GtkWidget *button, - guint time, - ThunarFile **file_return) -{ - GdkDragAction actions = 0; - GdkDragAction action = 0; - ThunarFile *file; - - /* determine the file for the given button */ - file = g_object_get_qdata (G_OBJECT (button), thunar_file_quark); - - /* check if we can drop here */ - if (G_LIKELY (file != NULL)) - { - /* determine the possible drop actions for the file (and the suggested action if any) */ - actions = thunar_file_accepts_drop (file, buttons->drop_path_list, context, &action); - if (G_LIKELY (actions != 0)) - { - /* tell the caller about the file (if it's interested) */ - if (G_UNLIKELY (file_return != NULL)) - *file_return = g_object_ref (G_OBJECT (file)); - } - } - - /* tell Gdk whether we can drop here */ - gdk_drag_status (context, action, time); - - return actions; -} - - - -static gboolean -thunar_location_buttons_drag_drop (GtkWidget *button, - GdkDragContext *context, - gint x, - gint y, - guint time, - ThunarLocationButtons *buttons) -{ - GdkAtom target; - - g_return_val_if_fail (GTK_IS_WIDGET (button), FALSE); - g_return_val_if_fail (THUNAR_IS_LOCATION_BUTTONS (buttons), FALSE); - - /* determine the DnD target and see if we can handle it */ - target = gtk_drag_dest_find_target (button, context, NULL); - if (G_UNLIKELY (target != gdk_atom_intern ("text/uri-list", FALSE))) - return FALSE; - - /* set state so drag-data-received knows that - * this is really a drop this time. - */ - buttons->drop_occurred = TRUE; - - /* request the drag data from the source */ - gtk_drag_get_data (button, context, target, time); - - /* we'll call gtk_drag_finish() later */ - return TRUE; -} - - - -static void -thunar_location_buttons_drag_data_get (GtkWidget *button, - GdkDragContext *context, - GtkSelectionData *selection_data, - guint info, - guint time, - ThunarLocationButtons *buttons) -{ - ThunarFile *file; - GList path_list; - gchar *uri_string; - - g_return_if_fail (GTK_IS_WIDGET (button)); - g_return_if_fail (THUNAR_IS_LOCATION_BUTTONS (buttons)); - - /* determine the uri of the file in question */ - file = g_object_get_qdata (G_OBJECT (button), thunar_file_quark); - - /* transform the path into an uri list string */ - path_list.data = thunar_file_get_path (file); path_list.next = path_list.prev = NULL; - uri_string = thunar_vfs_path_list_to_string (&path_list); - - /* set the uri list for the drag selection */ - gtk_selection_data_set (selection_data, selection_data->target, 8, (guchar *) uri_string, strlen (uri_string)); - - /* cleanup */ - g_free (uri_string); -} - - - -static void -thunar_location_buttons_drag_data_received (GtkWidget *button, - GdkDragContext *context, - gint x, - gint y, - GtkSelectionData *selection_data, - guint info, - guint time, - ThunarLocationButtons *buttons) -{ - GdkDragAction actions; - GdkDragAction action; - ThunarFile *file; - gboolean succeed = FALSE; - - /* check if we don't already know the drop data */ - if (G_LIKELY (!buttons->drop_data_ready)) - { - /* extract the URI list from the selection data (if valid) */ - if (selection_data->format == 8 && selection_data->length > 0) - buttons->drop_path_list = thunar_vfs_path_list_from_string ((const gchar *) selection_data->data, NULL); - - /* reset the state */ - buttons->drop_data_ready = TRUE; - } - - /* check if the data was dropped */ - if (G_UNLIKELY (buttons->drop_occurred)) - { - /* reset the state */ - buttons->drop_occurred = FALSE; - - /* determine the drop dest actions */ - actions = thunar_location_buttons_get_dest_actions (buttons, context, button, time, &file); - if (G_LIKELY ((actions & (GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK)) != 0)) - { - /* as the user what to do with the drop data */ - action = (context->action == GDK_ACTION_ASK) ? thunar_dnd_ask (button, time, actions) : context->action; - - /* perform the requested action */ - if (G_LIKELY (action != 0)) - succeed = thunar_dnd_perform (button, file, buttons->drop_path_list, action, NULL); - } - - /* release the file reference */ - if (G_LIKELY (file != NULL)) - g_object_unref (G_OBJECT (file)); - - /* tell the peer that we handled the drop */ - gtk_drag_finish (context, succeed, FALSE, time); - - /* disable the highlighting and release the drag data */ - thunar_location_buttons_drag_leave (button, context, time, buttons); - } -} - - - -static void -thunar_location_buttons_drag_leave (GtkWidget *button, - GdkDragContext *context, - guint time, - ThunarLocationButtons *buttons) -{ - g_return_if_fail (GTK_IS_BUTTON (button)); - g_return_if_fail (THUNAR_IS_LOCATION_BUTTONS (buttons)); - - /* reset the "drop data ready" status and free the path list */ - if (G_LIKELY (buttons->drop_data_ready)) - { - thunar_vfs_path_list_free (buttons->drop_path_list); - buttons->drop_data_ready = FALSE; - buttons->drop_path_list = NULL; - } - - /* be sure to cancel any running enter timeout */ - if (G_LIKELY (buttons->enter_timeout_id >= 0)) - g_source_remove (buttons->enter_timeout_id); -} - - - -static gboolean -thunar_location_buttons_drag_motion (GtkWidget *button, - GdkDragContext *context, - gint x, - gint y, - guint time, - ThunarLocationButtons *buttons) -{ - GtkSettings *settings; - GdkAtom target; - gint delay; - - g_return_val_if_fail (GTK_IS_BUTTON (button), FALSE); - g_return_val_if_fail (THUNAR_IS_LOCATION_BUTTONS (buttons), FALSE); - - /* schedule the enter timeout if not already done */ - if (G_UNLIKELY (buttons->enter_timeout_id < 0)) - { - /* remember the new button */ - buttons->enter_button = button; - g_object_add_weak_pointer (G_OBJECT (button), (gpointer) &buttons->enter_button); - - /* we use the gtk-menu-popdown-delay here, which seems to be sane for our purpose */ - settings = gtk_settings_get_for_screen (gtk_widget_get_screen (button)); - g_object_get (G_OBJECT (settings), "gtk-menu-popdown-delay", &delay, NULL); - - /* schedule the timeout */ - buttons->enter_timeout_id = g_timeout_add_full (G_PRIORITY_DEFAULT, delay, thunar_location_buttons_enter_timeout, - buttons, thunar_location_buttons_enter_timeout_destroy); - } - - /* request the drop on-demand (if we don't have it already) */ - if (G_UNLIKELY (!buttons->drop_data_ready)) - { - /* check if we can handle that drag data (can only drop text/uri-list) */ - target = gtk_drag_dest_find_target (button, context, NULL); - if (G_LIKELY (target == gdk_atom_intern ("text/uri-list", FALSE))) - { - /* request the drop data from the source */ - gtk_drag_get_data (button, context, target, time); - } - - /* tell GDK that it cannot drop here (yet!) */ - gdk_drag_status (context, 0, time); - } - else - { - /* check whether we can drop into the buttons' folder */ - thunar_location_buttons_get_dest_actions (buttons, context, button, time, NULL); - } - - return FALSE; -} - - - -static gboolean -thunar_location_buttons_enter_timeout (gpointer user_data) -{ - ThunarLocationButtons *buttons = THUNAR_LOCATION_BUTTONS (user_data); - - GDK_THREADS_ENTER (); - - /* We emulate a "clicked" event here, because else the buttons - * would be destroyed and replaced by new buttons, which causes - * the Gtk DND code to dump core once the mouse leaves the area - * of the new button. - * - * Besides that, handling this as "clicked" event allows the user - * to go back to the initial directory. - */ - if (G_LIKELY (buttons->enter_button != NULL)) - thunar_location_buttons_clicked (buttons->enter_button, buttons); - - GDK_THREADS_LEAVE (); - - return FALSE; -} - - - -static void -thunar_location_buttons_enter_timeout_destroy (gpointer user_data) -{ - ThunarLocationButtons *buttons = THUNAR_LOCATION_BUTTONS (user_data); - - /* disconnect from the remembered button */ - if (G_LIKELY (buttons->enter_button != NULL)) - { - g_object_remove_weak_pointer (G_OBJECT (buttons->enter_button), (gpointer) &buttons->enter_button); - buttons->enter_button = NULL; - } - - /* reset the timeout id */ - buttons->enter_timeout_id = -1; -} - - - /** * thunar_location_buttons_new: * -- GitLab