/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ /* * Caja * * Copyright (C) 1999, 2000, 2001 Eazel, Inc. * * Caja 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. * * Caja 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * * Author: Andy Hertzfeld <andy@eazel.com> * */ #include <config.h> #include "caja-information-panel.h" #include "caja-sidebar-title.h" #include <eel/eel-background.h> #include <eel/eel-glib-extensions.h> #include <eel/eel-gtk-extensions.h> #include <eel/eel-stock-dialogs.h> #include <eel/eel-string.h> #include <eel/eel-vfs-extensions.h> #include <gdk-pixbuf/gdk-pixbuf.h> #include <gtk/gtk.h> #include <glib/gi18n.h> #include <libcaja-private/caja-dnd.h> #include <libcaja-private/caja-directory.h> #include <libcaja-private/caja-file-dnd.h> #include <libcaja-private/caja-file.h> #include <libcaja-private/caja-global-preferences.h> #include <libcaja-private/caja-keep-last-vertical-box.h> #include <libcaja-private/caja-metadata.h> #include <libcaja-private/caja-mime-actions.h> #include <libcaja-private/caja-program-choosing.h> #include <libcaja-private/caja-sidebar-provider.h> #include <libcaja-private/caja-module.h> struct CajaInformationPanelDetails { GtkWidget *container; CajaWindowInfo *window; CajaSidebarTitle *title; GtkWidget *button_box_centerer; GtkWidget *button_box; gboolean has_buttons; CajaFile *file; guint file_changed_connection; gboolean background_connected; char *default_background_color; char *default_background_image; char *current_background_color; char *current_background_image; }; /* button assignments */ #define CONTEXTUAL_MENU_BUTTON 3 static gboolean caja_information_panel_press_event (GtkWidget *widget, GdkEventButton *event); static void caja_information_panel_finalize (GObject *object); static void caja_information_panel_drag_data_received (GtkWidget *widget, GdkDragContext *context, int x, int y, GtkSelectionData *selection_data, guint info, guint time); static void caja_information_panel_read_defaults (CajaInformationPanel *information_panel); #if GTK_CHECK_VERSION (3, 0, 0) static void caja_information_panel_style_updated (GtkWidget *widget); #else static void caja_information_panel_style_set (GtkWidget *widget, GtkStyle *previous_style); #endif static void caja_information_panel_theme_changed (GSettings *settings, const gchar *key, gpointer user_data); static void caja_information_panel_update_appearance (CajaInformationPanel *information_panel); static void caja_information_panel_update_buttons (CajaInformationPanel *information_panel); static void background_metadata_changed_callback (CajaInformationPanel *information_panel); static void caja_information_panel_iface_init (CajaSidebarIface *iface); static void caja_information_panel_iface_init (CajaSidebarIface *iface); static void sidebar_provider_iface_init (CajaSidebarProviderIface *iface); static GType caja_information_panel_provider_get_type (void); enum { LOCATION_CHANGED, LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = { 0 }; /* drag and drop definitions */ enum { TARGET_URI_LIST, TARGET_COLOR, TARGET_BGIMAGE, TARGET_KEYWORD, TARGET_BACKGROUND_RESET, TARGET_MATE_URI_LIST }; static const GtkTargetEntry target_table[] = { { "text/uri-list", 0, TARGET_URI_LIST }, { "application/x-color", 0, TARGET_COLOR }, { "property/bgimage", 0, TARGET_BGIMAGE }, { "property/keyword", 0, TARGET_KEYWORD }, { "x-special/mate-reset-background", 0, TARGET_BACKGROUND_RESET }, { "x-special/mate-icon-list", 0, TARGET_MATE_URI_LIST } }; typedef enum { NO_PART, BACKGROUND_PART, ICON_PART } InformationPanelPart; typedef struct { GObject parent; } CajaInformationPanelProvider; typedef struct { GObjectClass parent; } CajaInformationPanelProviderClass; G_DEFINE_TYPE_WITH_CODE (CajaInformationPanel, caja_information_panel, EEL_TYPE_BACKGROUND_BOX, G_IMPLEMENT_INTERFACE (CAJA_TYPE_SIDEBAR, caja_information_panel_iface_init)); G_DEFINE_TYPE_WITH_CODE (CajaInformationPanelProvider, caja_information_panel_provider, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (CAJA_TYPE_SIDEBAR_PROVIDER, sidebar_provider_iface_init)); static const char * caja_information_panel_get_sidebar_id (CajaSidebar *sidebar) { return CAJA_INFORMATION_PANEL_ID; } static char * caja_information_panel_get_tab_label (CajaSidebar *sidebar) { return g_strdup (_("Information")); } static char * caja_information_panel_get_tab_tooltip (CajaSidebar *sidebar) { return g_strdup (_("Show Information")); } static GdkPixbuf * caja_information_panel_get_tab_icon (CajaSidebar *sidebar) { return NULL; } static void caja_information_panel_is_visible_changed (CajaSidebar *sidebar, gboolean is_visible) { /* Do nothing */ } static void caja_information_panel_iface_init (CajaSidebarIface *iface) { iface->get_sidebar_id = caja_information_panel_get_sidebar_id; iface->get_tab_label = caja_information_panel_get_tab_label; iface->get_tab_tooltip = caja_information_panel_get_tab_tooltip; iface->get_tab_icon = caja_information_panel_get_tab_icon; iface->is_visible_changed = caja_information_panel_is_visible_changed; } /* initializing the class object by installing the operations we override */ static void caja_information_panel_class_init (CajaInformationPanelClass *klass) { GtkWidgetClass *widget_class; GObjectClass *gobject_class; gobject_class = G_OBJECT_CLASS (klass); widget_class = GTK_WIDGET_CLASS (klass); gobject_class->finalize = caja_information_panel_finalize; widget_class->drag_data_received = caja_information_panel_drag_data_received; widget_class->button_press_event = caja_information_panel_press_event; #if GTK_CHECK_VERSION (3, 0, 0) widget_class->style_updated = caja_information_panel_style_updated; #else widget_class->style_set = caja_information_panel_style_set; #endif /* add the "location changed" signal */ signals[LOCATION_CHANGED] = g_signal_new ("location_changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (CajaInformationPanelClass, location_changed), NULL, NULL, g_cclosure_marshal_VOID__STRING, G_TYPE_NONE, 1, G_TYPE_STRING); g_type_class_add_private (klass, sizeof (CajaInformationPanelDetails)); } /* utility routine to allocate the box the holds the command buttons */ static void make_button_box (CajaInformationPanel *information_panel) { #if GTK_CHECK_VERSION (3, 0, 0) information_panel->details->button_box_centerer = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); #else information_panel->details->button_box_centerer = gtk_hbox_new (FALSE, 0); #endif gtk_box_pack_start (GTK_BOX (information_panel->details->container), information_panel->details->button_box_centerer, TRUE, TRUE, 0); information_panel->details->button_box = caja_keep_last_vertical_box_new (4); gtk_container_set_border_width (GTK_CONTAINER (information_panel->details->button_box), 8); gtk_widget_show (information_panel->details->button_box); gtk_box_pack_start (GTK_BOX (information_panel->details->button_box_centerer), information_panel->details->button_box, TRUE, TRUE, 0); information_panel->details->has_buttons = FALSE; } /* initialize the instance's fields, create the necessary subviews, etc. */ static void caja_information_panel_init (CajaInformationPanel *information_panel) { information_panel->details = G_TYPE_INSTANCE_GET_PRIVATE (information_panel, CAJA_TYPE_INFORMATION_PANEL, CajaInformationPanelDetails); /* load the default background */ caja_information_panel_read_defaults (information_panel); /* enable mouse tracking */ gtk_widget_add_events (GTK_WIDGET (information_panel), GDK_POINTER_MOTION_MASK); /* create the container box */ #if GTK_CHECK_VERSION (3, 0, 0) information_panel->details->container = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); #else information_panel->details->container = gtk_vbox_new (FALSE, 0); #endif gtk_container_set_border_width (GTK_CONTAINER (information_panel->details->container), 0); gtk_widget_show (information_panel->details->container); gtk_container_add (GTK_CONTAINER (information_panel), information_panel->details->container); /* allocate and install the index title widget */ information_panel->details->title = CAJA_SIDEBAR_TITLE (caja_sidebar_title_new ()); gtk_widget_show (GTK_WIDGET (information_panel->details->title)); gtk_box_pack_start (GTK_BOX (information_panel->details->container), GTK_WIDGET (information_panel->details->title), FALSE, FALSE, 8); /* allocate and install the command button container */ make_button_box (information_panel); /* add a callback for when the theme changes */ g_signal_connect (caja_preferences, "changed::" CAJA_PREFERENCES_SIDE_PANE_BACKGROUND_SET, G_CALLBACK(caja_information_panel_theme_changed), information_panel); g_signal_connect (caja_preferences, "changed::" CAJA_PREFERENCES_SIDE_PANE_BACKGROUND_COLOR, G_CALLBACK(caja_information_panel_theme_changed), information_panel); g_signal_connect (caja_preferences, "changed::" CAJA_PREFERENCES_SIDE_PANE_BACKGROUND_URI, G_CALLBACK(caja_information_panel_theme_changed), information_panel); /* prepare ourselves to receive dropped objects */ gtk_drag_dest_set (GTK_WIDGET (information_panel), GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_HIGHLIGHT | GTK_DEST_DEFAULT_DROP, target_table, G_N_ELEMENTS (target_table), GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_ASK); } static void caja_information_panel_finalize (GObject *object) { CajaInformationPanel *information_panel; information_panel = CAJA_INFORMATION_PANEL (object); if (information_panel->details->file != NULL) { caja_file_monitor_remove (information_panel->details->file, information_panel); caja_file_unref (information_panel->details->file); } g_free (information_panel->details->default_background_color); g_free (information_panel->details->default_background_image); g_free (information_panel->details->current_background_color); g_free (information_panel->details->current_background_image); g_signal_handlers_disconnect_by_func (caja_preferences, caja_information_panel_theme_changed, information_panel); G_OBJECT_CLASS (caja_information_panel_parent_class)->finalize (object); } /* callback to handle resetting the background */ static void reset_background_callback (GtkWidget *menu_item, GtkWidget *information_panel) { EelBackground *background; background = eel_get_widget_background (information_panel); if (background != NULL) { eel_background_reset (background); } } static gboolean information_panel_has_background (CajaInformationPanel *information_panel) { EelBackground *background; gboolean has_background; char *color; char *image; background = eel_get_widget_background (GTK_WIDGET(information_panel)); color = eel_background_get_color (background); image = eel_background_get_image_uri (background); has_background = (color || image); return has_background; } /* create the context menu */ static GtkWidget * caja_information_panel_create_context_menu (CajaInformationPanel *information_panel) { GtkWidget *menu, *menu_item; menu = gtk_menu_new (); gtk_menu_set_screen (GTK_MENU (menu), gtk_widget_get_screen (GTK_WIDGET (information_panel))); /* add the reset background item, possibly disabled */ menu_item = gtk_menu_item_new_with_mnemonic (_("Use _Default Background")); gtk_widget_show (menu_item); gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item); gtk_widget_set_sensitive (menu_item, information_panel_has_background (information_panel)); g_signal_connect_object (menu_item, "activate", G_CALLBACK (reset_background_callback), information_panel, 0); return menu; } /* set up the default backgrounds and images */ static void caja_information_panel_read_defaults (CajaInformationPanel *information_panel) { gboolean background_set; char *background_color, *background_image; background_set = g_settings_get_boolean (caja_preferences, CAJA_PREFERENCES_SIDE_PANE_BACKGROUND_SET); background_color = NULL; background_image = NULL; if (background_set) { background_color = g_settings_get_string (caja_preferences, CAJA_PREFERENCES_SIDE_PANE_BACKGROUND_COLOR); background_image = g_settings_get_string (caja_preferences, CAJA_PREFERENCES_SIDE_PANE_BACKGROUND_URI); } g_free (information_panel->details->default_background_color); information_panel->details->default_background_color = NULL; g_free (information_panel->details->default_background_image); information_panel->details->default_background_image = NULL; if (background_color && strlen (background_color)) { information_panel->details->default_background_color = g_strdup (background_color); } /* set up the default background image */ if (background_image && strlen (background_image)) { information_panel->details->default_background_image = g_strdup (background_image); } g_free (background_color); g_free (background_image); } /* handler for handling theme changes */ static void caja_information_panel_theme_changed (GSettings *settings, const gchar *key, gpointer user_data) { CajaInformationPanel *information_panel; information_panel = CAJA_INFORMATION_PANEL (user_data); caja_information_panel_read_defaults (information_panel); caja_information_panel_update_appearance (information_panel); gtk_widget_queue_draw (GTK_WIDGET (information_panel)) ; } /* hit testing */ static InformationPanelPart hit_test (CajaInformationPanel *information_panel, int x, int y) { GtkAllocation *allocation; gboolean bg_hit; if (caja_sidebar_title_hit_test_icon (information_panel->details->title, x, y)) { return ICON_PART; } allocation = g_new0 (GtkAllocation, 1); gtk_widget_get_allocation (GTK_WIDGET (information_panel), allocation); bg_hit = allocation != NULL && x >= allocation->x && y >= allocation->y && x < allocation->x + allocation->width && y < allocation->y + allocation->height; g_free (allocation); if (bg_hit) { return BACKGROUND_PART; } return NO_PART; } /* utility to test if a uri refers to a local image */ static gboolean uri_is_local_image (const char *uri) { GdkPixbuf *pixbuf; char *image_path; image_path = g_filename_from_uri (uri, NULL, NULL); if (image_path == NULL) { return FALSE; } pixbuf = gdk_pixbuf_new_from_file (image_path, NULL); g_free (image_path); if (pixbuf == NULL) { return FALSE; } g_object_unref (pixbuf); return TRUE; } static void receive_dropped_uri_list (CajaInformationPanel *information_panel, GdkDragAction action, int x, int y, GtkSelectionData *selection_data) { char **uris; gboolean exactly_one; GtkWindow *window; EelBackground *background; uris = g_uri_list_extract_uris ((gchar *) gtk_selection_data_get_data (selection_data)); exactly_one = uris[0] != NULL && (uris[1] == NULL || uris[1][0] == '\0'); window = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (information_panel))); switch (hit_test (information_panel, x, y)) { case NO_PART: case BACKGROUND_PART: /* FIXME bugzilla.gnome.org 42507: Does this work for all images, or only background images? * Other views handle background images differently from other URIs. */ if (exactly_one && uri_is_local_image (uris[0])) { if (action == GDK_ACTION_ASK) { action = caja_drag_drop_background_ask (GTK_WIDGET (information_panel), CAJA_DND_ACTION_SET_AS_BACKGROUND | CAJA_DND_ACTION_SET_AS_GLOBAL_BACKGROUND); } if (action > 0) { background = eel_get_widget_background (GTK_WIDGET (information_panel)); eel_background_set_dropped_image (background, action, uris[0]); } } else if (exactly_one) { g_signal_emit (information_panel, signals[LOCATION_CHANGED], 0, uris[0]); } break; case ICON_PART: /* handle images dropped on the logo specially */ if (!exactly_one) { eel_show_error_dialog ( _("You cannot assign more than one custom icon at a time."), _("Please drag just one image to set a custom icon."), window); break; } if (uri_is_local_image (uris[0])) { if (information_panel->details->file != NULL) { caja_file_set_metadata (information_panel->details->file, CAJA_METADATA_KEY_CUSTOM_ICON, NULL, uris[0]); caja_file_set_metadata (information_panel->details->file, CAJA_METADATA_KEY_ICON_SCALE, NULL, NULL); } } else { GFile *f; f = g_file_new_for_uri (uris[0]); if (!g_file_is_native (f)) { eel_show_error_dialog ( _("The file that you dropped is not local."), _("You can only use local images as custom icons."), window); } else { eel_show_error_dialog ( _("The file that you dropped is not an image."), _("You can only use images as custom icons."), window); } g_object_unref (f); } break; } g_strfreev (uris); } static void receive_dropped_color (CajaInformationPanel *information_panel, GdkDragAction action, int x, int y, GtkSelectionData *selection_data) { guint16 *channels; char color_spec[8]; EelBackground *background; if (gtk_selection_data_get_length (selection_data) != 8 || gtk_selection_data_get_format (selection_data) != 16) { g_warning ("received invalid color data"); return; } channels = (guint16 *) gtk_selection_data_get_data (selection_data); g_snprintf (color_spec, sizeof (color_spec), "#%02X%02X%02X", channels[0] >> 8, channels[1] >> 8, channels[2] >> 8); switch (hit_test (information_panel, x, y)) { case NO_PART: g_warning ("dropped color, but not on any part of information_panel"); break; case ICON_PART: case BACKGROUND_PART: if (action == GDK_ACTION_ASK) { action = caja_drag_drop_background_ask (GTK_WIDGET (information_panel), CAJA_DND_ACTION_SET_AS_BACKGROUND | CAJA_DND_ACTION_SET_AS_GLOBAL_BACKGROUND); } if (action > 0) { background = eel_get_widget_background (GTK_WIDGET (information_panel)); eel_background_set_dropped_color (background, GTK_WIDGET (information_panel), action, x, y, selection_data); } break; } } /* handle receiving a dropped keyword */ static void receive_dropped_keyword (CajaInformationPanel *information_panel, int x, int y, GtkSelectionData *selection_data) { caja_drag_file_receive_dropped_keyword (information_panel->details->file, gtk_selection_data_get_data (selection_data)); /* regenerate the display */ caja_information_panel_update_appearance (information_panel); } static void caja_information_panel_drag_data_received (GtkWidget *widget, GdkDragContext *context, int x, int y, GtkSelectionData *selection_data, guint info, guint time) { CajaInformationPanel *information_panel; EelBackground *background; g_return_if_fail (CAJA_IS_INFORMATION_PANEL (widget)); information_panel = CAJA_INFORMATION_PANEL (widget); switch (info) { case TARGET_MATE_URI_LIST: case TARGET_URI_LIST: receive_dropped_uri_list (information_panel, gdk_drag_context_get_selected_action (context), x, y, selection_data); break; case TARGET_COLOR: receive_dropped_color (information_panel, gdk_drag_context_get_selected_action (context), x, y, selection_data); break; case TARGET_BGIMAGE: if (hit_test (information_panel, x, y) == BACKGROUND_PART) receive_dropped_uri_list (information_panel, gdk_drag_context_get_selected_action (context), x, y, selection_data); break; case TARGET_BACKGROUND_RESET: background = eel_get_widget_background ( GTK_WIDGET (information_panel)); if (background != NULL) { eel_background_reset (background); } break; case TARGET_KEYWORD: receive_dropped_keyword (information_panel, x, y, selection_data); break; default: g_warning ("unknown drop type"); } } /* handle the context menu if necessary */ static gboolean caja_information_panel_press_event (GtkWidget *widget, GdkEventButton *event) { CajaInformationPanel *information_panel; GtkWidget *menu; if (gtk_widget_get_window (widget) != event->window) { return FALSE; } information_panel = CAJA_INFORMATION_PANEL (widget); /* handle the context menu */ if (event->button == CONTEXTUAL_MENU_BUTTON) { menu = caja_information_panel_create_context_menu (information_panel); eel_pop_up_context_menu (GTK_MENU(menu), EEL_DEFAULT_POPUP_MENU_DISPLACEMENT, EEL_DEFAULT_POPUP_MENU_DISPLACEMENT, event); } return TRUE; } static gboolean value_different (const char *a, const char *b) { if (!a && !b) return FALSE; if (!a || !b) return TRUE; return strcmp (a, b); } /* Handle the background changed signal by writing out the settings to metadata. */ static void background_settings_changed_callback (EelBackground *background, GdkDragAction action, CajaInformationPanel *information_panel) { char *image; char *color; g_assert (EEL_IS_BACKGROUND (background)); g_assert (CAJA_IS_INFORMATION_PANEL (information_panel)); if (information_panel->details->file == NULL) { return; } /* Block so we don't respond to our own metadata changes. */ g_signal_handlers_block_by_func (information_panel->details->file, G_CALLBACK (background_metadata_changed_callback), information_panel); color = eel_background_get_color (background); image = eel_background_get_image_uri (background); if (action != (GdkDragAction) CAJA_DND_ACTION_SET_AS_BACKGROUND) { caja_file_set_metadata (information_panel->details->file, CAJA_METADATA_KEY_SIDEBAR_BACKGROUND_COLOR, NULL, NULL); caja_file_set_metadata (information_panel->details->file, CAJA_METADATA_KEY_SIDEBAR_BACKGROUND_IMAGE, NULL, NULL); g_signal_handlers_block_by_func (caja_preferences, G_CALLBACK(caja_information_panel_theme_changed), information_panel); g_settings_set_string (caja_preferences, CAJA_PREFERENCES_SIDE_PANE_BACKGROUND_COLOR, color ? color : ""); g_settings_set_string (caja_preferences, CAJA_PREFERENCES_SIDE_PANE_BACKGROUND_URI, image ? image : ""); g_settings_set_boolean (caja_preferences, CAJA_PREFERENCES_SIDE_PANE_BACKGROUND_SET, TRUE); g_signal_handlers_unblock_by_func (caja_preferences, G_CALLBACK(caja_information_panel_theme_changed), information_panel); } else { caja_file_set_metadata (information_panel->details->file, CAJA_METADATA_KEY_SIDEBAR_BACKGROUND_COLOR, NULL, color); caja_file_set_metadata (information_panel->details->file, CAJA_METADATA_KEY_SIDEBAR_BACKGROUND_IMAGE, NULL, image); } if (value_different (information_panel->details->current_background_color, color)) { g_free (information_panel->details->current_background_color); information_panel->details->current_background_color = g_strdup (color); } if (value_different (information_panel->details->current_background_image, image)) { g_free (information_panel->details->current_background_image); information_panel->details->current_background_image = g_strdup (image); } g_free (color); g_free (image); g_signal_handlers_unblock_by_func (information_panel->details->file, G_CALLBACK (background_metadata_changed_callback), information_panel); } /* handle the background reset signal by writing out NULL to metadata and setting the backgrounds fields to their default values */ static void background_reset_callback (EelBackground *background, CajaInformationPanel *information_panel) { char *color; char *image; g_assert (EEL_IS_BACKGROUND (background)); g_assert (CAJA_IS_INFORMATION_PANEL (information_panel)); if (information_panel->details->file == NULL) { return; } /* Block so we don't respond to our own metadata changes. */ g_signal_handlers_block_by_func (information_panel->details->file, G_CALLBACK (background_metadata_changed_callback), information_panel); color = caja_file_get_metadata (information_panel->details->file, CAJA_METADATA_KEY_SIDEBAR_BACKGROUND_COLOR, NULL); image = caja_file_get_metadata (information_panel->details->file, CAJA_METADATA_KEY_SIDEBAR_BACKGROUND_IMAGE, NULL); if (color || image) { caja_file_set_metadata (information_panel->details->file, CAJA_METADATA_KEY_SIDEBAR_BACKGROUND_COLOR, NULL, NULL); caja_file_set_metadata (information_panel->details->file, CAJA_METADATA_KEY_SIDEBAR_BACKGROUND_IMAGE, NULL, NULL); } else { g_signal_handlers_block_by_func (caja_preferences, G_CALLBACK(caja_information_panel_theme_changed), information_panel); g_settings_set_boolean (caja_preferences, CAJA_PREFERENCES_SIDE_PANE_BACKGROUND_SET, FALSE); g_signal_handlers_unblock_by_func (caja_preferences, G_CALLBACK(caja_information_panel_theme_changed), information_panel); } g_signal_handlers_unblock_by_func (information_panel->details->file, G_CALLBACK (background_metadata_changed_callback), information_panel); /* Force a read from the metadata to set the defaults */ background_metadata_changed_callback (information_panel); } static GtkWindow * caja_information_panel_get_window (CajaInformationPanel *information_panel) { GtkWidget *result; result = gtk_widget_get_ancestor (GTK_WIDGET (information_panel), GTK_TYPE_WINDOW); return result == NULL ? NULL : GTK_WINDOW (result); } static void command_button_callback (GtkWidget *button, GAppInfo *application) { CajaInformationPanel *information_panel; GList files; information_panel = CAJA_INFORMATION_PANEL (g_object_get_data (G_OBJECT (button), "user_data")); files.next = NULL; files.prev = NULL; files.data = information_panel->details->file; caja_launch_application (application, &files, caja_information_panel_get_window (information_panel)); } /* interpret commands for buttons specified by metadata. Handle some built-in ones explicitly, or fork a shell to handle general ones */ /* for now, we don't have any of these */ static void metadata_button_callback (GtkWidget *button, const char *command_str) { //CajaInformationPanel *self = CAJA_INFORMATION_PANEL (g_object_get_data (G_OBJECT (button), "user_data")); } /* utility routine that allocates the command buttons from the command list */ static void add_command_button (CajaInformationPanel *information_panel, GAppInfo *application) { char *temp_str; GtkWidget *temp_button, *label; /* There's always at least the "Open with..." button */ information_panel->details->has_buttons = TRUE; temp_str = g_strdup_printf (_("Open With %s"), g_app_info_get_display_name (application)); temp_button = gtk_button_new_with_label (temp_str); label = gtk_bin_get_child (GTK_BIN (temp_button)); gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_START); g_free (temp_str); gtk_box_pack_start (GTK_BOX (information_panel->details->button_box), temp_button, FALSE, FALSE, 0); g_signal_connect_data (temp_button, "clicked", G_CALLBACK (command_button_callback), g_object_ref (application), (GClosureNotify)g_object_unref, 0); g_object_set_data (G_OBJECT (temp_button), "user_data", information_panel); gtk_widget_show (temp_button); } /* utility to construct command buttons for the information_panel from the passed in metadata string */ static void add_buttons_from_metadata (CajaInformationPanel *information_panel, const char *button_data) { char **terms; char *current_term, *temp_str; char *button_name, *command_string; const char *term; int index; GtkWidget *temp_button; /* split the button specification into a set of terms */ button_name = NULL; terms = g_strsplit (button_data, ";", 0); /* for each term, either create a button or attach a property to one */ for (index = 0; (term = terms[index]) != NULL; index++) { current_term = g_strdup (term); temp_str = strchr (current_term, '='); if (temp_str) { *temp_str = '\0'; if (!g_ascii_strcasecmp (current_term, "button")) { button_name = g_strdup (temp_str + 1); } else if (!g_ascii_strcasecmp (current_term, "script")) { if (button_name != NULL) { temp_button = gtk_button_new_with_label (button_name); gtk_box_pack_start (GTK_BOX (information_panel->details->button_box), temp_button, FALSE, FALSE, 0); information_panel->details->has_buttons = TRUE; command_string = g_strdup (temp_str + 1); g_free (button_name); g_signal_connect_data (temp_button, "clicked", G_CALLBACK (metadata_button_callback), command_string, (GClosureNotify)g_free, 0); g_object_set_data (G_OBJECT (temp_button), "user_data", information_panel); gtk_widget_show (temp_button); } } } g_free(current_term); } g_strfreev (terms); } /* * caja_information_panel_update_buttons: * * Update the list of program-launching buttons based on the current uri. */ static void caja_information_panel_update_buttons (CajaInformationPanel *information_panel) { char *button_data; GAppInfo *default_app; /* dispose of any existing buttons */ if (information_panel->details->has_buttons) { gtk_container_remove (GTK_CONTAINER (information_panel->details->container), information_panel->details->button_box_centerer); make_button_box (information_panel); } /* create buttons from file metadata if necessary */ button_data = caja_file_get_metadata (information_panel->details->file, CAJA_METADATA_KEY_SIDEBAR_BUTTONS, NULL); if (button_data) { add_buttons_from_metadata (information_panel, button_data); g_free(button_data); } /* Make a button for the default application */ if (caja_mime_has_any_applications_for_file (information_panel->details->file) && !caja_file_is_directory (information_panel->details->file)) { default_app = caja_mime_get_default_application_for_file (information_panel->details->file); add_command_button (information_panel, default_app); g_object_unref (default_app); } gtk_widget_show (information_panel->details->button_box_centerer); } static void caja_information_panel_update_appearance (CajaInformationPanel *information_panel) { #if GTK_CHECK_VERSION(3,0,0) gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET (information_panel)), GTK_STYLE_CLASS_VIEW); #else EelBackground *background; char *background_color; char *background_image; g_return_if_fail (CAJA_IS_INFORMATION_PANEL (information_panel)); /* Connect the background changed signal to code that writes the color. */ background = eel_get_widget_background (GTK_WIDGET (information_panel)); if (!information_panel->details->background_connected) { information_panel->details->background_connected = TRUE; g_signal_connect_object (background,"settings_changed", G_CALLBACK (background_settings_changed_callback), information_panel, 0); g_signal_connect_object (background, "reset", G_CALLBACK (background_reset_callback), information_panel, 0); } /* Set up the background color and image from the metadata. */ background_color = caja_file_get_metadata (information_panel->details->file, CAJA_METADATA_KEY_SIDEBAR_BACKGROUND_COLOR, NULL); background_image = caja_file_get_metadata (information_panel->details->file, CAJA_METADATA_KEY_SIDEBAR_BACKGROUND_IMAGE, NULL); if (background_color == NULL && background_image == NULL) { background_color = g_strdup (information_panel->details->default_background_color); background_image = g_strdup (information_panel->details->default_background_image); } /* Block so we don't write these settings out in response to our set calls below */ g_signal_handlers_block_by_func (background, G_CALLBACK (background_settings_changed_callback), information_panel); if (value_different (information_panel->details->current_background_color, background_color) || value_different (information_panel->details->current_background_image, background_image)) { g_free (information_panel->details->current_background_color); information_panel->details->current_background_color = g_strdup (background_color); g_free (information_panel->details->current_background_image); information_panel->details->current_background_image = g_strdup (background_image); eel_background_set_image_uri (background, background_image); eel_background_set_color (background, background_color); caja_sidebar_title_select_text_color (information_panel->details->title, background); } g_free (background_color); g_free (background_image); g_signal_handlers_unblock_by_func (background, G_CALLBACK (background_settings_changed_callback), information_panel); #endif } static void background_metadata_changed_callback (CajaInformationPanel *information_panel) { CajaFileAttributes attributes; gboolean ready; attributes = caja_mime_actions_get_required_file_attributes (); ready = caja_file_check_if_ready (information_panel->details->file, attributes); if (ready) { caja_information_panel_update_appearance (information_panel); /* set up the command buttons */ caja_information_panel_update_buttons (information_panel); } } /* here is the key routine that populates the information_panel with the appropriate information when the uri changes */ static void caja_information_panel_set_uri (CajaInformationPanel *information_panel, const char* new_uri, const char* initial_title) { CajaFile *file; CajaFileAttributes attributes; g_return_if_fail (CAJA_IS_INFORMATION_PANEL (information_panel)); g_return_if_fail (new_uri != NULL); g_return_if_fail (initial_title != NULL); /* there's nothing to do if the uri is the same as the current one */ if (information_panel->details->file != NULL && caja_file_matches_uri (information_panel->details->file, new_uri)) { return; } if (information_panel->details->file != NULL) { g_signal_handler_disconnect (information_panel->details->file, information_panel->details->file_changed_connection); caja_file_monitor_remove (information_panel->details->file, information_panel); } file = caja_file_get_by_uri (new_uri); caja_file_unref (information_panel->details->file); information_panel->details->file = file; information_panel->details->file_changed_connection = g_signal_connect_object (information_panel->details->file, "changed", G_CALLBACK (background_metadata_changed_callback), information_panel, G_CONNECT_SWAPPED); attributes = caja_mime_actions_get_required_file_attributes (); caja_file_monitor_add (information_panel->details->file, information_panel, attributes); background_metadata_changed_callback (information_panel); /* tell the title widget about it */ caja_sidebar_title_set_file (information_panel->details->title, information_panel->details->file, initial_title); } static void title_changed_callback (CajaWindowInfo *window, char *new_title, CajaInformationPanel *panel) { caja_sidebar_title_set_text (panel->details->title, new_title); } /* ::style_set handler for the information_panel */ static void #if GTK_CHECK_VERSION (3, 0, 0) caja_information_panel_style_updated (GtkWidget *widget) #else caja_information_panel_style_set (GtkWidget *widget, GtkStyle *previous_style) #endif { CajaInformationPanel *information_panel; information_panel = CAJA_INFORMATION_PANEL (widget); caja_information_panel_theme_changed (NULL, NULL, information_panel); } static void loading_uri_callback (CajaWindowInfo *window, char *uri, CajaInformationPanel *panel) { CajaWindowSlotInfo *slot; char *title; slot = caja_window_info_get_active_slot (window); title = caja_window_slot_info_get_title (slot); caja_information_panel_set_uri (panel, uri, title); g_free (title); } static void selection_changed_callback (CajaWindowInfo *window, CajaInformationPanel *panel) { int selection_count; GList *selection; GFile *selected; CajaFile *file; char *uri, *name; selection = caja_window_info_get_selection (window); selection_count = g_list_length (selection); if (selection_count == 1) { selection = caja_window_info_get_selection (window); selected = selection->data; /* this should never fail here, as we're displaying the file */ file = caja_file_get_existing (selected); uri = caja_file_get_uri (file); name = caja_file_get_display_name (file); caja_file_unref (file); } else { uri = caja_window_info_get_current_location (window); name = caja_window_info_get_title (window); } caja_information_panel_set_uri (panel, uri, name); g_list_free_full (selection, g_object_unref); g_free (uri); g_free (name); } static void caja_information_panel_set_parent_window (CajaInformationPanel *panel, CajaWindowInfo *window) { gpointer slot; char *title, *location; panel->details->window = window; g_signal_connect_object (window, "loading_uri", G_CALLBACK (loading_uri_callback), panel, 0); g_signal_connect_object (window, "title_changed", G_CALLBACK (title_changed_callback), panel, 0); g_signal_connect_object (window, "selection-changed", G_CALLBACK (selection_changed_callback), panel, 0); slot = caja_window_info_get_active_slot (window); title = caja_window_slot_info_get_title (slot); location = caja_window_slot_info_get_current_location (slot); caja_information_panel_set_uri (panel, location, title); g_free (location); g_free (title); } static CajaSidebar * caja_information_panel_create (CajaSidebarProvider *provider, CajaWindowInfo *window) { CajaInformationPanel *panel; panel = g_object_new (caja_information_panel_get_type (), NULL); caja_information_panel_set_parent_window (panel, window); g_object_ref_sink (panel); return CAJA_SIDEBAR (panel); } static void sidebar_provider_iface_init (CajaSidebarProviderIface *iface) { iface->create = caja_information_panel_create; } static void caja_information_panel_provider_init (CajaInformationPanelProvider *sidebar) { } static void caja_information_panel_provider_class_init (CajaInformationPanelProviderClass *class) { } void caja_information_panel_register (void) { caja_module_add_type (caja_information_panel_provider_get_type ()); }