/* -*- Mode: C; c-file-style: "gnu"; tab-width: 8 -*- */ /* gtkpathbar.c * Copyright (C) 2004 Red Hat, Inc., Jonathan Blandford * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library. If not, see . */ #include "config.h" #include "gtkpathbar.h" #include #include "gtkbox.h" #include "gtkcssnodeprivate.h" #include "gtkdnd.h" #include "gtkdragsource.h" #include "gtkicontheme.h" #include "gtkimage.h" #include "gtkintl.h" #include "gtklabel.h" #include "gtkmain.h" #include "gtkmarshalers.h" #include "gtksettings.h" #include "gtktogglebutton.h" #include "gtkwidgetpath.h" #include "gtkwidgetprivate.h" #include "gtkeventcontrollerscroll.h" struct _GtkPathBarPrivate { GtkFileSystem *file_system; GFile *root_file; GFile *home_file; GFile *desktop_file; /* List of running GCancellable. When we cancel one, we remove it from this list. * The pathbar cancels all outstanding cancellables when it is disposed. * * In code that queues async I/O operations: * * - Obtain a cancellable from the async I/O APIs, and call add_cancellable(). * * To cancel a cancellable: * * - Call cancel_cancellable(). * * In async I/O callbacks: * * - Check right away if g_cancellable_is_cancelled(): if true, just * g_object_unref() the cancellable and return early (also free your * closure data if you have one). * * - If it was not cancelled, call cancellable_async_done(). This will * unref the cancellable and unqueue it from the pathbar's outstanding * cancellables. Do your normal work to process the async result and free * your closure data if you have one. */ GList *cancellables; GCancellable *get_info_cancellable; GIcon *root_icon; GIcon *home_icon; GIcon *desktop_icon; GtkEventController *scroll_controller; GList *button_list; GList *first_scrolled_button; GList *fake_root; GtkWidget *up_slider_button; GtkWidget *down_slider_button; guint settings_signal_id; gint16 slider_width; }; enum { PATH_CLICKED, LAST_SIGNAL }; typedef enum { NORMAL_BUTTON, ROOT_BUTTON, HOME_BUTTON, DESKTOP_BUTTON } ButtonType; #define BUTTON_DATA(x) ((ButtonData *)(x)) static guint path_bar_signals [LAST_SIGNAL] = { 0 }; /* Icon size for if we can't get it from the theme */ #define FALLBACK_ICON_SIZE 16 typedef struct _ButtonData ButtonData; struct _ButtonData { GtkWidget *button; ButtonType type; char *dir_name; GFile *file; GtkWidget *image; GtkWidget *label; GCancellable *cancellable; guint ignore_changes : 1; guint file_is_hidden : 1; }; /* This macro is used to check if a button can be used as a fake root. * All buttons in front of a fake root are automatically hidden when in a * directory below a fake root and replaced with the "<" arrow button. */ #define BUTTON_IS_FAKE_ROOT(button) ((button)->type == HOME_BUTTON) G_DEFINE_TYPE_WITH_PRIVATE (GtkPathBar, gtk_path_bar, GTK_TYPE_CONTAINER) static void gtk_path_bar_finalize (GObject *object); static void gtk_path_bar_dispose (GObject *object); static void gtk_path_bar_measure (GtkWidget *widget, GtkOrientation orientation, int for_size, int *minimum, int *natural, int *minimum_baseline, int *natural_baseline); static void gtk_path_bar_size_allocate (GtkWidget *widget, const GtkAllocation *allocation, int baseline, GtkAllocation *out_clip); static void gtk_path_bar_add (GtkContainer *container, GtkWidget *widget); static void gtk_path_bar_remove (GtkContainer *container, GtkWidget *widget); static void gtk_path_bar_forall (GtkContainer *container, GtkCallback callback, gpointer callback_data); static void gtk_path_bar_scroll_up (GtkPathBar *path_bar); static void gtk_path_bar_scroll_down (GtkPathBar *path_bar); static gboolean gtk_path_bar_slider_up_defocus (GtkWidget *widget, GdkEventButton *event, GtkPathBar *path_bar); static gboolean gtk_path_bar_slider_down_defocus (GtkWidget *widget, GdkEventButton *event, GtkPathBar *path_bar); static void gtk_path_bar_style_updated (GtkWidget *widget); static void gtk_path_bar_display_changed (GtkWidget *widget, GdkDisplay *previous_display); static void gtk_path_bar_check_icon_theme (GtkPathBar *path_bar); static void gtk_path_bar_update_button_appearance (GtkPathBar *path_bar, ButtonData *button_data, gboolean current_dir); static void gtk_path_bar_scroll_controller_scroll (GtkEventControllerScroll *scroll, gdouble dx, gdouble dy, GtkPathBar *path_bar); static void add_cancellable (GtkPathBar *path_bar, GCancellable *cancellable) { g_assert (g_list_find (path_bar->priv->cancellables, cancellable) == NULL); path_bar->priv->cancellables = g_list_prepend (path_bar->priv->cancellables, cancellable); } static void drop_node_for_cancellable (GtkPathBar *path_bar, GCancellable *cancellable) { GList *node; node = g_list_find (path_bar->priv->cancellables, cancellable); g_assert (node != NULL); node->data = NULL; path_bar->priv->cancellables = g_list_delete_link (path_bar->priv->cancellables, node); } static void cancel_cancellable (GtkPathBar *path_bar, GCancellable *cancellable) { drop_node_for_cancellable (path_bar, cancellable); g_cancellable_cancel (cancellable); } static void cancellable_async_done (GtkPathBar *path_bar, GCancellable *cancellable) { drop_node_for_cancellable (path_bar, cancellable); g_object_unref (cancellable); } static void cancel_all_cancellables (GtkPathBar *path_bar) { while (path_bar->priv->cancellables) { GCancellable *cancellable = path_bar->priv->cancellables->data; cancel_cancellable (path_bar, cancellable); } } static void gtk_path_bar_init (GtkPathBar *path_bar) { GtkStyleContext *context; path_bar->priv = gtk_path_bar_get_instance_private (path_bar); gtk_widget_init_template (GTK_WIDGET (path_bar)); /* Add the children manually because GtkPathBar derives from an abstract class, * Glade cannot edit a