diff --git a/ChangeLog b/ChangeLog index f4c157801db64a9fa0fbf988f8e013d3ffcda948..a7412abfa2b3382562dfab37ea6040a16a54ac7f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,20 @@ +2005-07-21 Benedikt Meurer <benny@xfce.org> + + * thunar/thunar-favourites-view.c: Add support for the LINK DnD action. + * thunar/thunar-location-buttons.c(thunar_location_buttons_init): Use + GDK_ACTION_LINK instead of GDK_ACTION_COPY to avoid accident copying + of directories. + * thunar/thunar-statusbar.{c,h}: Implement the ThunarNavigator + interface. + * thunar/thunar-statusbar.{c,h}: Add a "loading" indicator to the + statusbar. Add support to dnd from the shortcut indicator to the + favourites list. + * thunar/thunar-window.c(thunar_window_init): Connect the statusbar as + a navigator. Forward the "loading" property from the view to the + statusbar. + * configure.in.in: Check for additional headers. Add optional + dependency on cairo. + 2005-07-20 Benedikt Meurer <benny@xfce.org> * configure.in.in: Rename to "thunar" again, as this is now the main diff --git a/configure.in.in b/configure.in.in index 80aadc30b0fe421c5738e9fcf37c732a8de4288c..92fc73a5a80c3a6542ce031569d1cc41ab58efd7 100644 --- a/configure.in.in +++ b/configure.in.in @@ -41,8 +41,8 @@ AC_PROG_INSTALL() AC_PROG_LIBTOOL() dnl Check for standard headers -AC_CHECK_HEADERS([dirent.h errno.h fcntl.h fstab.h grp.h locale.h memory.h \ - pwd.h \ +AC_CHECK_HEADERS([dirent.h errno.h fcntl.h fstab.h grp.h locale.h \ + math.h memory.h pwd.h \ stdlib.h string.h sys/cdio.h sys/event.h \ sys/mount.h sys/stat.h sys/time.h sys/param.h time.h]) @@ -53,6 +53,9 @@ dnl Check for required packages XDT_CHECK_PACKAGE([EXO], [exo-0.3], [0.3.1]) XDT_CHECK_PACKAGE([GTHREAD], [gthread-2.0], [2.6.0]) +dnl Check for optional packages +XDT_CHECK_OPTIONAL_PACKAGE([CAIRO], [cairo], [0.5], [cairo], [Cairo]) + dnl Check for the system flavour AC_MSG_CHECKING([for system flavour]) case "$target_os" in diff --git a/thunar/Makefile.am b/thunar/Makefile.am index 2b12f5f1e8ea7fc254e28352d7fb6d3f9ff08a77..582e85cac77c100f020b3ab31bb363dc4311c66d 100644 --- a/thunar/Makefile.am +++ b/thunar/Makefile.am @@ -79,11 +79,13 @@ Thunar_SOURCES = \ thunar-window-ui.h Thunar_CFLAGS = \ + $(CAIRO_CFLAGS) \ $(EXO_CFLAGS) \ $(GTHREAD_CFLAGS) Thunar_LDFLAGS = \ -no-undefined \ + $(CAIRO_LIBS) \ $(EXO_LIBS) \ $(GTHREAD_LIBS) diff --git a/thunar/thunar-favourites-view.c b/thunar/thunar-favourites-view.c index bdbf7c11637dee6be0ab08cc4c8eef9296dc07a3..2003102761e47b29c08d82953e238321f6704896 100644 --- a/thunar/thunar-favourites-view.c +++ b/thunar/thunar-favourites-view.c @@ -187,7 +187,7 @@ thunar_favourites_view_init (ThunarFavouritesView *view) */ gtk_drag_dest_set (GTK_WIDGET (view), GTK_DEST_DEFAULT_ALL, drop_targets, G_N_ELEMENTS (drop_targets), - GDK_ACTION_COPY | GDK_ACTION_MOVE); + GDK_ACTION_COPY | GDK_ACTION_LINK | GDK_ACTION_MOVE); #if GTK_CHECK_VERSION(2,6,0) gtk_tree_view_set_row_separator_func (GTK_TREE_VIEW (view), thunar_favourites_view_separator_func, NULL, NULL); @@ -348,7 +348,9 @@ thunar_favourites_view_drag_motion (GtkWidget *widget, GtkTreePath *path; /* check the action that should be performed */ - if (context->suggested_action == GDK_ACTION_COPY || (context->actions & GDK_ACTION_COPY) != 0) + if (context->suggested_action == GDK_ACTION_LINK || (context->actions & GDK_ACTION_LINK) != 0) + action = GDK_ACTION_LINK; + else if (context->suggested_action == GDK_ACTION_COPY || (context->actions & GDK_ACTION_COPY) != 0) action = GDK_ACTION_COPY; else if (context->suggested_action == GDK_ACTION_MOVE || (context->actions & GDK_ACTION_MOVE) != 0) action = GDK_ACTION_MOVE; diff --git a/thunar/thunar-location-buttons.c b/thunar/thunar-location-buttons.c index 4a0de6a3419c0dbc8053c54b2e9f793617b97df4..76327406e18e1df3886df6385abe83f23d7ab405 100644 --- a/thunar/thunar-location-buttons.c +++ b/thunar/thunar-location-buttons.c @@ -734,7 +734,7 @@ thunar_location_buttons_make_button (ThunarLocationButtons *buttons, /* 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_COPY); + G_N_ELEMENTS (drag_targets), GDK_ACTION_LINK); g_signal_connect (G_OBJECT (button), "drag-data-get", G_CALLBACK (thunar_location_buttons_drag_data_get), buttons); diff --git a/thunar/thunar-statusbar.c b/thunar/thunar-statusbar.c index bff400df1e70c0edfb8d8f6e48abafb9f82a3853..1d3d6841eac0177893e8ea705fda2300f92e735a 100644 --- a/thunar/thunar-statusbar.c +++ b/thunar/thunar-statusbar.c @@ -21,26 +21,653 @@ #include <config.h> #endif -#include <exo/exo.h> +#ifdef HAVE_MATH_H +#include <math.h> +#endif +#ifdef HAVE_MEMORY_H +#include <memory.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif #include <thunar/thunar-statusbar.h> +#ifdef HAVE_CAIRO +#include <cairo/cairo-xlib.h> +#endif + + + +enum +{ + ICON_PROP_0, + ICON_PROP_FILE, + ICON_PROP_LOADING, +}; + + + +typedef struct _ThunarStatusbarIconClass ThunarStatusbarIconClass; +typedef struct _ThunarStatusbarIcon ThunarStatusbarIcon; + + + +#define THUNAR_TYPE_STATUSBAR_ICON (thunar_statusbar_icon_get_type ()) +#define THUNAR_STATUSBAR_ICON(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), THUNAR_TYPE_STATUSBAR_ICON, ThunarStatusbarIcon)) +#define THUNAR_STATUSBAR_ICON_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), THUNAR_TYPE_STATUSBAR_ICON, ThunarStatusbarIconClass)) +#define THUNAR_IS_STATUSBAR_ICON(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), THUNAR_TYPE_STATUSBAR_ICON)) +#define THUNAR_IS_STATUSBAR_ICON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), THUNAR_TYPE_STATUSBAR_ICON)) +#define THUNAR_STATUSBAR_ICON_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), THUNAR_TYPE_STATUSBAR_ICON, ThunarStatusbarIconClass)) + + + +static GType thunar_statusbar_icon_get_type (void) G_GNUC_CONST; +static void thunar_statusbar_icon_class_init (ThunarStatusbarIconClass *klass); +static void thunar_statusbar_icon_init (ThunarStatusbarIcon *statusbar_icon); +static void thunar_statusbar_icon_finalize (GObject *object); +static void thunar_statusbar_icon_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); +static void thunar_statusbar_icon_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); +static void thunar_statusbar_icon_size_request (GtkWidget *widget, + GtkRequisition *requisition); +static void thunar_statusbar_icon_realize (GtkWidget *widget); +static gboolean thunar_statusbar_icon_expose_event (GtkWidget *widget, + GdkEventExpose *event); +static void thunar_statusbar_icon_drag_begin (GtkWidget *widget, + GdkDragContext *context); +static void thunar_statusbar_icon_drag_data_get (GtkWidget *widget, + GdkDragContext *context, + GtkSelectionData *selection_data, + guint info, + guint time); +static gboolean thunar_statusbar_icon_timer (gpointer user_data); +static void thunar_statusbar_icon_timer_destroy (gpointer user_data); +static ThunarFile *thunar_statusbar_icon_get_file (ThunarStatusbarIcon *statusbar_icon); +static void thunar_statusbar_icon_set_file (ThunarStatusbarIcon *statusbar_icon, + ThunarFile *file); +static gboolean thunar_statusbar_icon_get_loading (ThunarStatusbarIcon *statusbar_icon); +static void thunar_statusbar_icon_set_loading (ThunarStatusbarIcon *statusbar_icon, + gboolean loading); + + + +struct _ThunarStatusbarIconClass +{ + GtkWidgetClass __parent__; +}; + +struct _ThunarStatusbarIcon +{ + GtkWidget __parent__; + + ThunarFile *file; + GdkPixbuf *lucent; + gint angle; + gint timer_id; +}; + + + +static GObjectClass *thunar_statusbar_icon_parent_class; + +static const GtkTargetEntry drag_targets[] = +{ + { "text/uri-list", 0, 0 }, +}; + + + +static GType +thunar_statusbar_icon_get_type (void) +{ + static GType type = G_TYPE_INVALID; + + if (G_UNLIKELY (type == G_TYPE_INVALID)) + { + static const GTypeInfo info = + { + sizeof (ThunarStatusbarIconClass), + NULL, + NULL, + (GClassInitFunc) thunar_statusbar_icon_class_init, + NULL, + NULL, + sizeof (ThunarStatusbarIcon), + 0, + (GInstanceInitFunc) thunar_statusbar_icon_init, + NULL, + }; + + type = g_type_register_static (GTK_TYPE_WIDGET, "ThunarStatusbarIcon", &info, 0); + } + + return type; +} + + + + +static void +thunar_statusbar_icon_class_init (ThunarStatusbarIconClass *klass) +{ + GtkWidgetClass *gtkwidget_class; + GObjectClass *gobject_class; + + thunar_statusbar_icon_parent_class = g_type_class_peek_parent (klass); + + gobject_class = G_OBJECT_CLASS (klass); + gobject_class->finalize = thunar_statusbar_icon_finalize; + gobject_class->get_property = thunar_statusbar_icon_get_property; + gobject_class->set_property = thunar_statusbar_icon_set_property; + + gtkwidget_class = GTK_WIDGET_CLASS (klass); + gtkwidget_class->size_request = thunar_statusbar_icon_size_request; + gtkwidget_class->realize = thunar_statusbar_icon_realize; + gtkwidget_class->expose_event = thunar_statusbar_icon_expose_event; + gtkwidget_class->drag_begin = thunar_statusbar_icon_drag_begin; + gtkwidget_class->drag_data_get = thunar_statusbar_icon_drag_data_get; + + /** + * ThunarStatusbarIcon:file: + * + * The #ThunarFile whose icon should be displayed by this + * #ThunarStatusbarIcon widget. + **/ + g_object_class_install_property (gobject_class, + ICON_PROP_FILE, + g_param_spec_object ("file", + _("File"), + _("The file whose icon to display"), + THUNAR_TYPE_FILE, + EXO_PARAM_READWRITE)); + + /** + * ThunarStatusbarIcon:loading: + * + * Whether this #ThunarStatusbarIcon should display a + * loading animation. + **/ + g_object_class_install_property (gobject_class, + ICON_PROP_LOADING, + g_param_spec_boolean ("loading", + _("Loading"), + _("Whether to display a loading animation"), + FALSE, + EXO_PARAM_READWRITE)); +} + + + +static void +thunar_statusbar_icon_init (ThunarStatusbarIcon *statusbar_icon) +{ + statusbar_icon->timer_id = -1; + + /* setup drag support for the icon */ + gtk_drag_source_set (GTK_WIDGET (statusbar_icon), GDK_BUTTON1_MASK, + drag_targets, G_N_ELEMENTS (drag_targets), + GDK_ACTION_LINK); +} + + + +static void +thunar_statusbar_icon_finalize (GObject *object) +{ + ThunarStatusbarIcon *statusbar_icon = THUNAR_STATUSBAR_ICON (object); + + /* drop the file reference */ + thunar_statusbar_icon_set_file (statusbar_icon, NULL); + + /* cancel the redraw timer */ + thunar_statusbar_icon_set_loading (statusbar_icon, FALSE); + + G_OBJECT_CLASS (thunar_statusbar_icon_parent_class)->finalize (object); +} + + + +static void +thunar_statusbar_icon_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + ThunarStatusbarIcon *statusbar_icon = THUNAR_STATUSBAR_ICON (object); + + switch (prop_id) + { + case ICON_PROP_FILE: + g_value_set_object (value, thunar_statusbar_icon_get_file (statusbar_icon)); + break; + + case ICON_PROP_LOADING: + g_value_set_boolean (value, thunar_statusbar_icon_get_loading (statusbar_icon)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + + + +static void +thunar_statusbar_icon_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + ThunarStatusbarIcon *statusbar_icon = THUNAR_STATUSBAR_ICON (object); + + switch (prop_id) + { + case ICON_PROP_FILE: + thunar_statusbar_icon_set_file (statusbar_icon, g_value_get_object (value)); + break; + + case ICON_PROP_LOADING: + thunar_statusbar_icon_set_loading (statusbar_icon, g_value_get_boolean (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + + + +static void +thunar_statusbar_icon_size_request (GtkWidget *widget, + GtkRequisition *requisition) +{ + requisition->width = 16 + 2 * widget->style->xthickness; + requisition->height = 16 + 2 * widget->style->ythickness; +} + + + +static void +thunar_statusbar_icon_realize (GtkWidget *widget) +{ + GdkWindowAttr attr; + gint attr_mask; + + GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED); + + attr.x = widget->allocation.x; + attr.y = widget->allocation.y; + attr.width = widget->allocation.width; + attr.height = widget->allocation.height; + attr.wclass = GDK_INPUT_OUTPUT; + attr.window_type = GDK_WINDOW_CHILD; + attr.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK; + attr.visual = gtk_widget_get_visual (widget); + attr.colormap = gtk_widget_get_colormap (widget); + attr_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP; + + widget->window = gdk_window_new (widget->parent->window, &attr, attr_mask); + gtk_style_attach (widget->style, widget->window); + gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL); + gdk_window_set_user_data (widget->window, widget); +} + + + +static GdkPixbuf* +create_lucent_pixbuf (GdkPixbuf *src) +{ + GdkPixbuf *dest; + guchar *target_pixels; + guchar *original_pixels; + guchar *pixsrc; + guchar *pixdest; + gint width, height, has_alpha, src_row_stride, dst_row_stride; + gint i, j; + + if (G_UNLIKELY (!gdk_pixbuf_get_has_alpha (src))) + { + g_object_ref (G_OBJECT (src)); + return src; + } + + dest = gdk_pixbuf_new (gdk_pixbuf_get_colorspace (src), + gdk_pixbuf_get_has_alpha (src), + gdk_pixbuf_get_bits_per_sample (src), + gdk_pixbuf_get_width (src), + gdk_pixbuf_get_height (src)); + + has_alpha = gdk_pixbuf_get_has_alpha (src); + width = gdk_pixbuf_get_width (src); + height = gdk_pixbuf_get_height (src); + src_row_stride = gdk_pixbuf_get_rowstride (src); + dst_row_stride = gdk_pixbuf_get_rowstride (dest); + target_pixels = gdk_pixbuf_get_pixels (dest); + original_pixels = gdk_pixbuf_get_pixels (src); + + for (i = 0; i < height; i++) + { + pixdest = target_pixels + i * dst_row_stride; + pixsrc = original_pixels + i * src_row_stride; + for (j = 0; j < width; j++) + { + pixdest[0] = pixsrc[0]; + pixdest[1] = pixsrc[1]; + pixdest[2] = pixsrc[2]; + pixdest[3] = pixsrc[3] / 3; + pixdest += 4; + pixsrc += 4; + } + } + + return dest; +} + + + +#if GTK_CHECK_VERSION(2,7,2) +static cairo_t* +get_cairo_context (GdkWindow *window) +{ + cairo_t *cr; + gint w; + gint h; + + gdk_drawable_get_size (GDK_DRAWABLE (window), &w, &h); + + cr = gdk_cairo_create (window); + cairo_scale (cr, h, w); + cairo_translate (cr, 0.5, 0.5); + + return cr; +} +#elif defined(HAVE_CAIRO) +static cairo_t* +get_cairo_context (GdkWindow *window) +{ + cairo_surface_t *surface; + GdkDrawable *drawable; + cairo_t *cr; + gint w; + gint h; + gint x; + gint y; + + gdk_window_get_internal_paint_info (window, &drawable, &x, &y); + gdk_drawable_get_size (drawable, &w, &h); + + surface = cairo_xlib_surface_create (GDK_DRAWABLE_XDISPLAY (drawable), GDK_DRAWABLE_XID (drawable), + gdk_x11_visual_get_xvisual (gdk_drawable_get_visual (drawable)), w, h); + cr = cairo_create (surface); + cairo_surface_destroy (surface); + + cairo_translate (cr, -x, -y); + cairo_scale (cr, h, w); + cairo_translate (cr, 0.5, 0.5); + + return cr; +} +#endif + + + +static gboolean +thunar_statusbar_icon_expose_event (GtkWidget *widget, + GdkEventExpose *event) +{ + ThunarStatusbarIcon *statusbar_icon = THUNAR_STATUSBAR_ICON (widget); + GdkRectangle icon_area; + GdkPixbuf *icon; + + /* draw the icon if we have a file */ + if (G_LIKELY (statusbar_icon->file != NULL)) + { + icon = thunar_file_load_icon (statusbar_icon->file, 16); + + /* use the lucent variant if we're currently loading */ + if (thunar_statusbar_icon_get_loading (statusbar_icon)) + { + if (G_UNLIKELY (statusbar_icon->lucent == NULL)) + statusbar_icon->lucent = create_lucent_pixbuf (icon); + + g_object_unref (G_OBJECT (icon)); + icon = statusbar_icon->lucent; + g_object_ref (G_OBJECT (icon)); + } + + icon_area.width = gdk_pixbuf_get_width (icon); + icon_area.height = gdk_pixbuf_get_height (icon); + icon_area.x = (widget->allocation.width - icon_area.width) / 2; + icon_area.y = (widget->allocation.height - icon_area.height) / 2; + + gdk_draw_pixbuf (widget->window, widget->style->black_gc, icon, + 0, 0, icon_area.x, icon_area.y, + icon_area.width, icon_area.height, + GDK_RGB_DITHER_NORMAL, 0, 0); + + g_object_unref (G_OBJECT (icon)); + } + +#if defined(HAVE_CAIRO) || GTK_CHECK_VERSION(2,7,1) + /* render the animation */ + if (thunar_statusbar_icon_get_loading (statusbar_icon)) + { + cairo_t *cr; + gdouble n; + + cr = get_cairo_context (widget->window); + + cairo_rotate (cr, (statusbar_icon->angle * 2.0 * M_PI) / 360.0); + + for (n = 0.0; n < 2.0; n += 0.25) + { + cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, (n - 0.1) / 1.9); + cairo_arc (cr, cos (n * M_PI) / 3.0, sin (n * M_PI) / 3.0, 0.12, 0.0, 2 * M_PI); + cairo_fill (cr); + } + + cairo_destroy (cr); + } +#endif + + return TRUE; +} + + + +static void +thunar_statusbar_icon_drag_begin (GtkWidget *widget, + GdkDragContext *context) +{ + ThunarStatusbarIcon *statusbar_icon = THUNAR_STATUSBAR_ICON (widget); + GdkPixbuf *icon; + + /* setup the drag source icon */ + if (G_LIKELY (statusbar_icon->file != NULL)) + { + icon = thunar_file_load_icon (statusbar_icon->file, 24); + gtk_drag_source_set_icon_pixbuf (widget, icon); + g_object_unref (G_OBJECT (icon)); + } + + if (GTK_WIDGET_CLASS (thunar_statusbar_icon_parent_class)->drag_begin != NULL) + (*GTK_WIDGET_CLASS (thunar_statusbar_icon_parent_class)->drag_begin) (widget, context); +} + + + +static void +thunar_statusbar_icon_drag_data_get (GtkWidget *widget, + GdkDragContext *context, + GtkSelectionData *selection_data, + guint info, + guint time) +{ + ThunarStatusbarIcon *statusbar_icon = THUNAR_STATUSBAR_ICON (widget); + ThunarVfsURI *uri; + GList *uri_list; + gchar *uri_string; + + if (G_LIKELY (statusbar_icon->file != NULL)) + { + /* determine the uri of the file in question */ + uri = thunar_file_get_uri (statusbar_icon->file); + + /* transform the uri into an uri list string */ + uri_list = thunar_vfs_uri_list_prepend (NULL, uri); + uri_string = thunar_vfs_uri_list_to_string (uri_list, 0); + thunar_vfs_uri_list_free (uri_list); + + /* set the uri for the drag selection */ + gtk_selection_data_set (selection_data, selection_data->target, + 8, uri_string, strlen (uri_string)); + + /* cleanup */ + g_free (uri_string); + } + else + { + /* abort the drag */ + gdk_drag_abort (context, time); + } + + if (GTK_WIDGET_CLASS (thunar_statusbar_icon_parent_class)->drag_data_get != NULL) + (*GTK_WIDGET_CLASS (thunar_statusbar_icon_parent_class)->drag_data_get) (widget, context, selection_data, info, time); +} + + + +static gboolean +thunar_statusbar_icon_timer (gpointer user_data) +{ + ThunarStatusbarIcon *statusbar_icon = THUNAR_STATUSBAR_ICON (user_data); + + GDK_THREADS_ENTER (); + statusbar_icon->angle = (statusbar_icon->angle + 30) % 360; + gtk_widget_queue_draw (GTK_WIDGET (statusbar_icon)); + GDK_THREADS_LEAVE (); + + return TRUE; +} + + +static void +thunar_statusbar_icon_timer_destroy (gpointer user_data) +{ + THUNAR_STATUSBAR_ICON (user_data)->timer_id = -1; +} + + + +static ThunarFile* +thunar_statusbar_icon_get_file (ThunarStatusbarIcon *statusbar_icon) +{ + g_return_val_if_fail (THUNAR_IS_STATUSBAR_ICON (statusbar_icon), NULL); + return statusbar_icon->file; +} + + + +static void +thunar_statusbar_icon_set_file (ThunarStatusbarIcon *statusbar_icon, + ThunarFile *file) +{ + g_return_if_fail (THUNAR_IS_STATUSBAR_ICON (statusbar_icon)); + g_return_if_fail (file == NULL || THUNAR_IS_FILE (file)); + + /* drop any cached icon */ + if (G_LIKELY (statusbar_icon->lucent != NULL)) + { + g_object_unref (G_OBJECT (statusbar_icon->lucent)); + statusbar_icon->lucent = NULL; + } + + /* disconnect from the previous file */ + if (G_LIKELY (statusbar_icon->file != NULL)) + { + g_object_unref (G_OBJECT (statusbar_icon->file)); + } + + /* activate the new file */ + statusbar_icon->file = file; + + /* connect to the new file */ + if (G_LIKELY (file != NULL)) + { + /* take a reference on the file */ + g_object_ref (G_OBJECT (file)); + } + + /* schedule a redraw with the new file */ + gtk_widget_queue_draw (GTK_WIDGET (statusbar_icon)); +} + + + +static gboolean +thunar_statusbar_icon_get_loading (ThunarStatusbarIcon *statusbar_icon) +{ + g_return_val_if_fail (THUNAR_IS_STATUSBAR_ICON (statusbar_icon), FALSE); + return (statusbar_icon->timer_id >= 0); +} + + + +static void +thunar_statusbar_icon_set_loading (ThunarStatusbarIcon *statusbar_icon, + gboolean loading) +{ + g_return_if_fail (THUNAR_IS_STATUSBAR_ICON (statusbar_icon)); + + if (loading && statusbar_icon->timer_id < 0) + { + statusbar_icon->timer_id = g_timeout_add_full (G_PRIORITY_LOW, 65, thunar_statusbar_icon_timer, + statusbar_icon, thunar_statusbar_icon_timer_destroy); + } + else if (!loading && statusbar_icon->timer_id >= 0) + { + g_source_remove (statusbar_icon->timer_id); + } + + /* schedule a redraw with the new loading state */ + gtk_widget_queue_draw (GTK_WIDGET (statusbar_icon)); +} + + enum { - PROP_0, - PROP_TEXT, + BAR_PROP_0, + BAR_PROP_CURRENT_DIRECTORY, + BAR_PROP_LOADING, + BAR_PROP_TEXT, }; -static void thunar_statusbar_class_init (ThunarStatusbarClass *klass); -static void thunar_statusbar_init (ThunarStatusbar *statusbar); -static void thunar_statusbar_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec); +static void thunar_statusbar_class_init (ThunarStatusbarClass *klass); +static void thunar_statusbar_navigator_init (ThunarNavigatorIface *iface); +static void thunar_statusbar_init (ThunarStatusbar *statusbar); +static void thunar_statusbar_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); +static void thunar_statusbar_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); +static ThunarFile *thunar_statusbar_get_current_directory (ThunarNavigator *navigator); +static void thunar_statusbar_set_current_directory (ThunarNavigator *navigator, + ThunarFile *current_directory); @@ -52,12 +679,17 @@ struct _ThunarStatusbarClass struct _ThunarStatusbar { GtkStatusbar __parent__; + GtkWidget *icon; guint context_id; }; -G_DEFINE_TYPE (ThunarStatusbar, thunar_statusbar, GTK_TYPE_STATUSBAR); +G_DEFINE_TYPE_WITH_CODE (ThunarStatusbar, + thunar_statusbar, + GTK_TYPE_STATUSBAR, + G_IMPLEMENT_INTERFACE (THUNAR_TYPE_NAVIGATOR, + thunar_statusbar_navigator_init)); @@ -67,8 +699,32 @@ thunar_statusbar_class_init (ThunarStatusbarClass *klass) GObjectClass *gobject_class; gobject_class = G_OBJECT_CLASS (klass); + gobject_class->get_property = thunar_statusbar_get_property; gobject_class->set_property = thunar_statusbar_set_property; + /** + * ThunarStatusbar:current-directory: + * + * Inherited from #ThunarNavigator. + **/ + g_object_class_override_property (gobject_class, + BAR_PROP_CURRENT_DIRECTORY, + "current-directory"); + + /** + * ThunarStatusbar:loading: + * + * Write-only property, which tells the statusbar whether to display + * a "loading" indicator or not. + **/ + g_object_class_install_property (gobject_class, + BAR_PROP_LOADING, + g_param_spec_boolean ("loading", + _("Loading"), + _("Whether to display a loading animation"), + FALSE, + EXO_PARAM_WRITABLE)); + /** * ThunarStatusbar:text: * @@ -76,7 +732,7 @@ thunar_statusbar_class_init (ThunarStatusbarClass *klass) * can only be written. **/ g_object_class_install_property (gobject_class, - PROP_TEXT, + BAR_PROP_TEXT, g_param_spec_string ("text", _("Statusbar text"), _("The main text to be displayed in the statusbar"), @@ -86,9 +742,41 @@ thunar_statusbar_class_init (ThunarStatusbarClass *klass) +static void +thunar_statusbar_navigator_init (ThunarNavigatorIface *iface) +{ + iface->get_current_directory = thunar_statusbar_get_current_directory; + iface->set_current_directory = thunar_statusbar_set_current_directory; +} + + + static void thunar_statusbar_init (ThunarStatusbar *statusbar) { + GtkWidget *label; + GtkWidget *box; + + /* remove the label from the statusbars frame */ + label = GTK_STATUSBAR (statusbar)->label; + g_object_ref (G_OBJECT (label)); + gtk_container_remove (GTK_CONTAINER (GTK_STATUSBAR (statusbar)->frame), label); + + /* add a HBox instead */ + box = gtk_hbox_new (FALSE, 6); + gtk_container_add (GTK_CONTAINER (GTK_STATUSBAR (statusbar)->frame), box); + gtk_widget_show (box); + + /* add the icon to the HBox */ + statusbar->icon = g_object_new (THUNAR_TYPE_STATUSBAR_ICON, NULL); + gtk_box_pack_start (GTK_BOX (box), statusbar->icon, FALSE, FALSE, 0); + gtk_widget_show (statusbar->icon); + + /* readd the label to the HBox */ + gtk_box_pack_start (GTK_BOX (box), label, TRUE, TRUE, 0); + g_object_unref (G_OBJECT (label)); + gtk_widget_show (label); + statusbar->context_id = gtk_statusbar_get_context_id (GTK_STATUSBAR (statusbar), "Main text"); gtk_statusbar_set_has_resize_grip (GTK_STATUSBAR (statusbar), TRUE); @@ -96,6 +784,26 @@ thunar_statusbar_init (ThunarStatusbar *statusbar) +static void +thunar_statusbar_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + switch (prop_id) + { + case BAR_PROP_CURRENT_DIRECTORY: + g_value_set_object (value, thunar_navigator_get_current_directory (THUNAR_NAVIGATOR (object))); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + + + static void thunar_statusbar_set_property (GObject *object, guint prop_id, @@ -106,7 +814,15 @@ thunar_statusbar_set_property (GObject *object, switch (prop_id) { - case PROP_TEXT: + case BAR_PROP_CURRENT_DIRECTORY: + thunar_navigator_set_current_directory (THUNAR_NAVIGATOR (object), g_value_get_object (value)); + break; + + case BAR_PROP_LOADING: + thunar_statusbar_set_loading (statusbar, g_value_get_boolean (value)); + break; + + case BAR_PROP_TEXT: thunar_statusbar_set_text (statusbar, g_value_get_string (value)); break; @@ -118,6 +834,27 @@ thunar_statusbar_set_property (GObject *object, +static ThunarFile* +thunar_statusbar_get_current_directory (ThunarNavigator *navigator) +{ + return thunar_statusbar_icon_get_file (THUNAR_STATUSBAR_ICON (THUNAR_STATUSBAR (navigator)->icon)); +} + + + +static void +thunar_statusbar_set_current_directory (ThunarNavigator *navigator, + ThunarFile *current_directory) +{ + /* setup the new directory for the icon */ + thunar_statusbar_icon_set_file (THUNAR_STATUSBAR_ICON (THUNAR_STATUSBAR (navigator)->icon), current_directory); + + /* notify others */ + g_object_notify (G_OBJECT (navigator), "current-directory"); +} + + + /** * thunar_statusbar_new: * @@ -134,6 +871,22 @@ thunar_statusbar_new (void) +/** + * thunar_statusbar_set_loading: + * @statusbar : a #ThunarStatusbar instance. + * @loading : %TRUE if the @statusbar should display a load indicator. + **/ +void +thunar_statusbar_set_loading (ThunarStatusbar *statusbar, + gboolean loading) +{ + g_return_if_fail (THUNAR_IS_STATUSBAR (statusbar)); + + thunar_statusbar_icon_set_loading (THUNAR_STATUSBAR_ICON (statusbar->icon), loading); +} + + + /** * thunar_statusbar_set_text: * @statusbar : a #ThunarStatusbar instance. diff --git a/thunar/thunar-statusbar.h b/thunar/thunar-statusbar.h index b7c0366ddef9bdae0e53355752be07e75820528d..fe23bbb54f660f83df300904ca2d129ac562088d 100644 --- a/thunar/thunar-statusbar.h +++ b/thunar/thunar-statusbar.h @@ -20,7 +20,7 @@ #ifndef __THUNAR_STATUSBAR_H__ #define __THUNAR_STATUSBAR_H__ -#include <gtk/gtk.h> +#include <thunar/thunar-navigator.h> G_BEGIN_DECLS; @@ -34,12 +34,14 @@ typedef struct _ThunarStatusbar ThunarStatusbar; #define THUNAR_IS_STATUSBAR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), THUNAR_TYPE_STATUSBAR)) #define THUNAR_STATUSBAR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), THUNAR_TYPE_STATUSBAR, ThunarStatusbarClass)) -GType thunar_statusbar_get_type (void) G_GNUC_CONST; +GType thunar_statusbar_get_type (void) G_GNUC_CONST; -GtkWidget *thunar_statusbar_new (void); +GtkWidget *thunar_statusbar_new (void); -void thunar_statusbar_set_text (ThunarStatusbar *statusbar, - const gchar *text); +void thunar_statusbar_set_loading (ThunarStatusbar *statusbar, + gboolean loading); +void thunar_statusbar_set_text (ThunarStatusbar *statusbar, + const gchar *text); G_END_DECLS; diff --git a/thunar/thunar-window.c b/thunar/thunar-window.c index 1c0c5a8cd7fd931f4d5d7ab0f8e64341ced3f45c..f989705f25e0013cf07610400072dce31ad5c8b9 100644 --- a/thunar/thunar-window.c +++ b/thunar/thunar-window.c @@ -246,8 +246,11 @@ thunar_window_init (ThunarWindow *window) gtk_widget_show (window->view); window->statusbar = thunar_statusbar_new (); - exo_binding_new (G_OBJECT (window->view), "statusbar-text", - G_OBJECT (window->statusbar), "text"); + g_signal_connect_swapped (G_OBJECT (window->statusbar), "change-directory", + G_CALLBACK (thunar_window_set_current_directory), window); + exo_binding_new (G_OBJECT (window), "current-directory", G_OBJECT (window->statusbar), "current-directory"); + exo_binding_new (G_OBJECT (window->view), "loading", G_OBJECT (window->statusbar), "loading"); + exo_binding_new (G_OBJECT (window->view), "statusbar-text", G_OBJECT (window->statusbar), "text"); gtk_box_pack_start (GTK_BOX (vbox), window->statusbar, FALSE, FALSE, 0); gtk_widget_show (window->statusbar);