From ea6686baf339c41abf5b6cea74e83edb73f2883c Mon Sep 17 00:00:00 2001 From: Benedikt Meurer <benny@xfce.org> Date: Sun, 29 Jan 2006 13:07:15 +0000 Subject: [PATCH] 2006-01-29 Benedikt Meurer <benny@xfce.org> * thunar/thunar-templates-action.c: Add support for sub folders below the ~/Templates folder. This fixes reopened bug #1391. * thunar-vfs/thunar-vfs.symbols, thunar-vfs/thunar-vfs-info.{c,h}: Add thunar_vfs_info_read_link(), which determines the target from a symbolic link. * thunar/thunar-file.h: Add method thunar_file_read_link() as wrapper to thunar_vfs_info_read_link(). * thunar/thunar-properties-dialog.c: Display the "Link Target" for symbolic links. This fixes bug #1394. (Old svn revision: 19656) --- ChangeLog | 12 ++ thunar-vfs/thunar-vfs-info.c | 34 +++- thunar-vfs/thunar-vfs-info.h | 3 + thunar-vfs/thunar-vfs.symbols | 1 + thunar/thunar-file.h | 21 +++ thunar/thunar-properties-dialog.c | 32 +++- thunar/thunar-templates-action.c | 252 +++++++++++++----------------- 7 files changed, 210 insertions(+), 145 deletions(-) diff --git a/ChangeLog b/ChangeLog index 2b81620ba..46b23ff9b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,15 @@ +2006-01-29 Benedikt Meurer <benny@xfce.org> + + * thunar/thunar-templates-action.c: Add support for sub folders below + the ~/Templates folder. This fixes reopened bug #1391. + * thunar-vfs/thunar-vfs.symbols, thunar-vfs/thunar-vfs-info.{c,h}: Add + thunar_vfs_info_read_link(), which determines the target from a + symbolic link. + * thunar/thunar-file.h: Add method thunar_file_read_link() as wrapper + to thunar_vfs_info_read_link(). + * thunar/thunar-properties-dialog.c: Display the "Link Target" for + symbolic links. This fixes bug #1394. + 2006-01-28 Benedikt Meurer <benny@xfce.org> * thunar-vfs/thunar-vfs-chmod-job.c(thunar_vfs_chmod_job_execute), diff --git a/thunar-vfs/thunar-vfs-info.c b/thunar-vfs/thunar-vfs-info.c index 0d122b6a1..1ad343cd4 100644 --- a/thunar-vfs/thunar-vfs-info.c +++ b/thunar-vfs/thunar-vfs-info.c @@ -192,6 +192,38 @@ thunar_vfs_info_copy (const ThunarVfsInfo *info) +/** + * thunar_vfs_info_read_link: + * @info : a #ThunarVfsInfo. + * @error : return location for errors or %NULL. + * + * Reads the contents of the symbolic link to which @info refers to, + * like the POSIX readlink() function. The returned string is in the + * encoding used for filenames. + * + * Return value: a newly allocated string with the contents of the + * symbolic link, or %NULL if an error occurred. + **/ +gchar* +thunar_vfs_info_read_link (const ThunarVfsInfo *info, + GError **error) +{ + gchar absolute_path[THUNAR_VFS_PATH_MAXSTRLEN]; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (info->ref_count > 0, NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + /* determine the absolute path */ + if (thunar_vfs_path_to_string (info->path, absolute_path, sizeof (absolute_path), error) < 0) + return NULL; + + /* determine the link target */ + return g_file_read_link (absolute_path, error); +} + + + /** * thunar_vfs_info_execute: * @info : a #ThunarVfsInfo. @@ -220,7 +252,7 @@ thunar_vfs_info_execute (const ThunarVfsInfo *info, gboolean terminal; gboolean result = FALSE; XfceRc *rc; - gchar absolute_path[PATH_MAX + 1]; + gchar absolute_path[THUNAR_VFS_PATH_MAXSTRLEN]; gchar *working_directory; gchar *path_escaped; gchar **argv = NULL; diff --git a/thunar-vfs/thunar-vfs-info.h b/thunar-vfs/thunar-vfs-info.h index 1fc335cfc..a0dd8d9bf 100644 --- a/thunar-vfs/thunar-vfs-info.h +++ b/thunar-vfs/thunar-vfs-info.h @@ -102,6 +102,9 @@ ThunarVfsInfo *thunar_vfs_info_copy (const ThunarVfsIn G_INLINE_FUNC const gchar *thunar_vfs_info_get_custom_icon (const ThunarVfsInfo *info); +gchar *thunar_vfs_info_read_link (const ThunarVfsInfo *info, + GError **error) G_GNUC_MALLOC; + gboolean thunar_vfs_info_execute (const ThunarVfsInfo *info, GdkScreen *screen, GList *path_list, diff --git a/thunar-vfs/thunar-vfs.symbols b/thunar-vfs/thunar-vfs.symbols index 656361556..a0cfd226a 100644 --- a/thunar-vfs/thunar-vfs.symbols +++ b/thunar-vfs/thunar-vfs.symbols @@ -81,6 +81,7 @@ thunar_vfs_info_copy #ifdef INCLUDE_INTERNAL_SYMBOLS thunar_vfs_info_get_custom_icon #endif +thunar_vfs_info_read_link G_GNUC_MALLOC thunar_vfs_info_execute thunar_vfs_info_rename thunar_vfs_info_matches diff --git a/thunar/thunar-file.h b/thunar/thunar-file.h index 005a0965f..12dc54fc3 100644 --- a/thunar/thunar-file.h +++ b/thunar/thunar-file.h @@ -207,6 +207,9 @@ static inline gboolean thunar_file_is_regular (const ThunarF static inline gboolean thunar_file_is_root (const ThunarFile *file); static inline gboolean thunar_file_is_symlink (const ThunarFile *file); +static inline gchar *thunar_file_read_link (const ThunarFile *file, + GError **error) G_GNUC_MALLOC; + ThunarFile *thunar_file_cache_lookup (const ThunarVfsPath *path); @@ -487,6 +490,24 @@ thunar_file_is_symlink (const ThunarFile *file) return ((file->info->flags & THUNAR_VFS_FILE_FLAGS_SYMLINK) != 0); } +/** + * thunar_file_read_link: + * @file : a #ThunarFile instance. + * @error : return location for errors or %NULL. + * + * Simple wrapper to thunar_vfs_info_read_link(). + * + * Return value: the link target of @file or %NULL + * if an error occurred. + **/ +static inline gchar* +thunar_file_read_link (const ThunarFile *file, + GError **error) +{ + g_return_val_if_fail (THUNAR_IS_FILE (file), NULL); + return thunar_vfs_info_read_link (file->info, error); +} + /** diff --git a/thunar/thunar-properties-dialog.c b/thunar/thunar-properties-dialog.c index 22f8e1ddc..8fac70933 100644 --- a/thunar/thunar-properties-dialog.c +++ b/thunar/thunar-properties-dialog.c @@ -96,6 +96,7 @@ struct _ThunarPropertiesDialog GtkWidget *name_entry; GtkWidget *kind_label; GtkWidget *openwith_chooser; + GtkWidget *link_label; GtkWidget *modified_label; GtkWidget *accessed_label; GtkWidget *volume_image; @@ -249,7 +250,7 @@ thunar_properties_dialog_init (ThunarPropertiesDialog *dialog) /* - Second box (kind, open with) + Second box (kind, open with, link target) */ label = gtk_label_new (_("Kind:")); gtk_label_set_attributes (GTK_LABEL (label), thunar_pango_attr_list_bold ()); @@ -279,6 +280,19 @@ thunar_properties_dialog_init (ThunarPropertiesDialog *dialog) ++row; + label = gtk_label_new (_("Link Target:")); + gtk_label_set_attributes (GTK_LABEL (label), thunar_pango_attr_list_bold ()); + gtk_misc_set_alignment (GTK_MISC (label), 1.0f, 0.5f); + gtk_table_attach (GTK_TABLE (table), label, 0, 1, row, row + 1, GTK_FILL, GTK_FILL, 0, 0); + gtk_widget_show (label); + + dialog->link_label = g_object_new (GTK_TYPE_LABEL, "ellipsize", PANGO_ELLIPSIZE_START, "xalign", 0.0f, NULL); + exo_binding_new (G_OBJECT (dialog->link_label), "visible", G_OBJECT (label), "visible"); + gtk_table_attach (GTK_TABLE (table), dialog->link_label, 1, 2, row, row + 1, GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0); + gtk_widget_show (dialog->link_label); + + ++row; + spacer = g_object_new (GTK_TYPE_ALIGNMENT, "height-request", 12, NULL); gtk_table_attach (GTK_TABLE (table), spacer, 0, 2, row, row + 1, GTK_FILL, GTK_FILL, 0, 0); @@ -599,6 +613,7 @@ thunar_properties_dialog_update (ThunarPropertiesDialog *dialog) const gchar *name; GdkPixbuf *icon; glong offset; + gchar *display_name; gchar *size_string; gchar *str; @@ -655,6 +670,21 @@ thunar_properties_dialog_update (ThunarPropertiesDialog *dialog) "visible", (thunar_file_is_regular (dialog->file) && !thunar_file_is_executable (dialog->file)), NULL); + /* update the link target */ + str = thunar_file_is_symlink (dialog->file) ? thunar_file_read_link (dialog->file, NULL) : NULL; + if (G_UNLIKELY (str != NULL)) + { + display_name = g_filename_display_name (str); + gtk_label_set_text (GTK_LABEL (dialog->link_label), display_name); + gtk_widget_show (dialog->link_label); + g_free (display_name); + g_free (str); + } + else + { + gtk_widget_hide (dialog->link_label); + } + /* update the modified time */ str = thunar_file_get_date_string (dialog->file, THUNAR_FILE_DATE_MODIFIED); if (G_LIKELY (str != NULL)) diff --git a/thunar/thunar-templates-action.c b/thunar/thunar-templates-action.c index dcb9e26c9..1fddde6cd 100644 --- a/thunar/thunar-templates-action.c +++ b/thunar/thunar-templates-action.c @@ -38,19 +38,12 @@ enum static void thunar_templates_action_class_init (ThunarTemplatesActionClass *klass); -static void thunar_templates_action_init (ThunarTemplatesAction *templates_action); -static void thunar_templates_action_finalize (GObject *object); static GtkWidget *thunar_templates_action_create_menu_item (GtkAction *action); -static void thunar_templates_action_build_menu (ThunarTemplatesAction *templates_action, +static void thunar_templates_action_fill_menu (ThunarTemplatesAction *templates_action, + ThunarVfsPath *templates_path, GtkWidget *menu); static void thunar_templates_action_menu_mapped (GtkWidget *menu, ThunarTemplatesAction *templates_action); -static void thunar_templates_action_monitor (ThunarVfsMonitor *monitor, - ThunarVfsMonitorHandle *handle, - ThunarVfsMonitorEvent event, - ThunarVfsPath *handle_path, - ThunarVfsPath *event_path, - gpointer user_data); @@ -66,10 +59,6 @@ struct _ThunarTemplatesActionClass struct _ThunarTemplatesAction { GtkAction __parent__; - - ThunarVfsMonitorHandle *templates_handle; - ThunarVfsMonitor *templates_monitor; - ThunarVfsPath *templates_path; }; @@ -96,7 +85,7 @@ thunar_templates_action_get_type (void) NULL, sizeof (ThunarTemplatesAction), 0, - (GInstanceInitFunc) thunar_templates_action_init, + NULL, NULL, }; @@ -112,14 +101,10 @@ static void thunar_templates_action_class_init (ThunarTemplatesActionClass *klass) { GtkActionClass *gtkaction_class; - GObjectClass *gobject_class; /* determine the parent type class */ thunar_templates_action_parent_class = g_type_class_peek_parent (klass); - gobject_class = G_OBJECT_CLASS (klass); - gobject_class->finalize = thunar_templates_action_finalize; - gtkaction_class = GTK_ACTION_CLASS (klass); gtkaction_class->create_menu_item = thunar_templates_action_create_menu_item; @@ -132,7 +117,7 @@ thunar_templates_action_class_init (ThunarTemplatesActionClass *klass) **/ templates_action_signals[CREATE_EMPTY_FILE] = g_signal_new (I_("create-empty-file"), - G_TYPE_FROM_CLASS (gobject_class), + G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (ThunarTemplatesActionClass, create_empty_file), NULL, NULL, g_cclosure_marshal_VOID__VOID, @@ -149,7 +134,7 @@ thunar_templates_action_class_init (ThunarTemplatesActionClass *klass) **/ templates_action_signals[CREATE_TEMPLATE] = g_signal_new (I_("create-template"), - G_TYPE_FROM_CLASS (gobject_class), + G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (ThunarTemplatesActionClass, create_template), NULL, NULL, g_cclosure_marshal_VOID__BOXED, @@ -158,39 +143,6 @@ thunar_templates_action_class_init (ThunarTemplatesActionClass *klass) -static void -thunar_templates_action_init (ThunarTemplatesAction *templates_action) -{ - ThunarVfsPath *home_path; - - /* determine the path to the ~/Templates directory */ - home_path = thunar_vfs_path_get_for_home (); - templates_action->templates_path = thunar_vfs_path_relative (home_path, "Templates"); - thunar_vfs_path_unref (home_path); -} - - - -static void -thunar_templates_action_finalize (GObject *object) -{ - ThunarTemplatesAction *templates_action = THUNAR_TEMPLATES_ACTION (object); - - /* unregister from the monitor (if any) */ - if (G_LIKELY (templates_action->templates_monitor != NULL)) - { - thunar_vfs_monitor_remove (templates_action->templates_monitor, templates_action->templates_handle); - g_object_unref (G_OBJECT (templates_action->templates_monitor)); - } - - /* release the ~/Templates path */ - thunar_vfs_path_unref (templates_action->templates_path); - - (*G_OBJECT_CLASS (thunar_templates_action_parent_class)->finalize) (object); -} - - - static GtkWidget* thunar_templates_action_create_menu_item (GtkAction *action) { @@ -213,15 +165,24 @@ thunar_templates_action_create_menu_item (GtkAction *action) static gint -info_compare (gconstpointer info_a, - gconstpointer info_b) +info_compare (gconstpointer a, + gconstpointer b) { - gchar *name_a; - gchar *name_b; - gint result; - - name_a = g_utf8_casefold (((ThunarVfsInfo *) info_a)->display_name, -1); - name_b = g_utf8_casefold (((ThunarVfsInfo *) info_b)->display_name, -1); + const ThunarVfsInfo *info_a = a; + const ThunarVfsInfo *info_b = b; + gchar *name_a; + gchar *name_b; + gint result; + + /* sort folders before files */ + if (info_a->type == THUNAR_VFS_FILE_TYPE_DIRECTORY && info_b->type != THUNAR_VFS_FILE_TYPE_DIRECTORY) + return -1; + else if (info_a->type != THUNAR_VFS_FILE_TYPE_DIRECTORY && info_b->type == THUNAR_VFS_FILE_TYPE_DIRECTORY) + return 1; + + /* compare by name */ + name_a = g_utf8_casefold (info_a->display_name, -1); + name_b = g_utf8_casefold (info_b->display_name, -1); result = g_utf8_collate (name_a, name_b); g_free (name_b); g_free (name_a); @@ -251,14 +212,16 @@ item_activated (GtkWidget *item, static void -thunar_templates_action_build_menu (ThunarTemplatesAction *templates_action, - GtkWidget *menu) +thunar_templates_action_fill_menu (ThunarTemplatesAction *templates_action, + ThunarVfsPath *templates_path, + GtkWidget *menu) { ThunarVfsInfo *info; ThunarVfsPath *path; GtkIconTheme *icon_theme; const gchar *icon_name; const gchar *name; + GtkWidget *submenu; GtkWidget *image; GtkWidget *item; gchar *absolute_path; @@ -268,8 +231,8 @@ thunar_templates_action_build_menu (ThunarTemplatesAction *templates_action, GList *lp; GDir *dp; - /* try to open the ~/Templates directory */ - absolute_path = thunar_vfs_path_dup_string (templates_action->templates_path); + /* try to open the templates (sub)directory */ + absolute_path = thunar_vfs_path_dup_string (templates_path); dp = g_dir_open (absolute_path, 0, NULL); g_free (absolute_path); @@ -287,7 +250,7 @@ thunar_templates_action_build_menu (ThunarTemplatesAction *templates_action, continue; /* determine the info for that file */ - path = thunar_vfs_path_relative (templates_action->templates_path, name); + path = thunar_vfs_path_relative (templates_path, name); info = thunar_vfs_info_new_for_path (path, NULL); thunar_vfs_path_unref (path); @@ -301,17 +264,21 @@ thunar_templates_action_build_menu (ThunarTemplatesAction *templates_action, } /* check if we have any infos */ - if (G_LIKELY (info_list != NULL)) + if (G_UNLIKELY (info_list == NULL)) + return; + + /* determine the icon theme for the menu */ + icon_theme = gtk_icon_theme_get_for_screen (gtk_widget_get_screen (menu)); + + /* add menu items for all infos */ + for (lp = info_list; lp != NULL; lp = lp->next) { - /* determine the icon theme for the mapped menu */ - icon_theme = gtk_icon_theme_get_for_screen (gtk_widget_get_screen (menu)); + /* determine the info */ + info = lp->data; - /* add menu items for all infos */ - for (lp = info_list; lp != NULL; lp = lp->next) + /* check if we have a regular file or a directory here */ + if (G_LIKELY (info->type == THUNAR_VFS_FILE_TYPE_REGULAR)) { - /* determine the info */ - info = lp->data; - /* generate a label by stripping off the extension */ label = g_strdup (info->display_name); dot = g_utf8_strrchr (label, -1, '.'); @@ -320,7 +287,7 @@ thunar_templates_action_build_menu (ThunarTemplatesAction *templates_action, /* allocate a new menu item */ item = gtk_image_menu_item_new_with_label (label); - g_object_set_data_full (G_OBJECT (item), I_("thunar-vfs-info"), info, (GDestroyNotify) thunar_vfs_info_unref); + g_object_set_data_full (G_OBJECT (item), I_("thunar-vfs-info"), thunar_vfs_info_ref (info), (GDestroyNotify) thunar_vfs_info_unref); g_signal_connect (G_OBJECT (item), "activate", G_CALLBACK (item_activated), templates_action); gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); gtk_widget_show (item); @@ -336,11 +303,73 @@ thunar_templates_action_build_menu (ThunarTemplatesAction *templates_action, /* cleanup */ g_free (label); } + else if (info->type == THUNAR_VFS_FILE_TYPE_DIRECTORY) + { + /* allocate a new submenu for the directory */ + submenu = gtk_menu_new (); + exo_gtk_object_ref_sink (GTK_OBJECT (submenu)); + gtk_menu_set_screen (GTK_MENU (submenu), gtk_widget_get_screen (menu)); + + /* fill the submenu from the folder contents */ + thunar_templates_action_fill_menu (templates_action, info->path, submenu); + + /* check if any items were added to the submenu */ + if (G_LIKELY (GTK_MENU_SHELL (submenu)->children != NULL)) + { + /* hook up the submenu */ + item = gtk_image_menu_item_new_with_label (info->display_name); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + gtk_widget_show (item); + + /* lookup the icon for the mime type of that file */ + icon_name = thunar_vfs_mime_info_lookup_icon_name (info->mime_info, icon_theme); + + /* generate an image based on the named icon */ + image = gtk_image_new_from_icon_name (icon_name, GTK_ICON_SIZE_MENU); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image); + gtk_widget_show (image); + } - /* release the info list itself */ - g_list_free (info_list); + /* cleanup */ + g_object_unref (G_OBJECT (submenu)); + } } - else + + /* release the info list */ + thunar_vfs_info_list_free (info_list); +} + + + +static void +thunar_templates_action_menu_mapped (GtkWidget *menu, + ThunarTemplatesAction *templates_action) +{ + ThunarVfsPath *templates_path; + ThunarVfsPath *home_path; + GtkWidget *image; + GtkWidget *item; + GList *children; + + g_return_if_fail (THUNAR_IS_TEMPLATES_ACTION (templates_action)); + g_return_if_fail (GTK_IS_MENU_SHELL (menu)); + + /* drop all existing children of the menu first */ + children = gtk_container_get_children (GTK_CONTAINER (menu)); + g_list_foreach (children, (GFunc) gtk_widget_destroy, NULL); + g_list_free (children); + + /* determine the path to the ~/Templates folder */ + home_path = thunar_vfs_path_get_for_home (); + templates_path = thunar_vfs_path_relative (home_path, "Templates"); + thunar_vfs_path_unref (home_path); + + /* fill the menu with files/folders from the ~/Templates folder */ + thunar_templates_action_fill_menu (templates_action, templates_path, menu); + + /* check if any items were added to the menu */ + if (G_UNLIKELY (GTK_MENU_SHELL (menu)->children == NULL)) { /* tell the user that no templates were found */ item = gtk_menu_item_new_with_label (_("No Templates installed")); @@ -364,72 +393,9 @@ thunar_templates_action_build_menu (ThunarTemplatesAction *templates_action, image = gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU); gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image); gtk_widget_show (image); -} - - -static void -thunar_templates_action_menu_mapped (GtkWidget *menu, - ThunarTemplatesAction *templates_action) -{ - g_return_if_fail (THUNAR_IS_TEMPLATES_ACTION (templates_action)); - g_return_if_fail (GTK_IS_MENU_SHELL (menu)); - - /* check if we already added our children to the submenu */ - if (G_LIKELY (GTK_MENU_SHELL (menu)->children != NULL)) - return; - - /* generate the menu */ - thunar_templates_action_build_menu (templates_action, menu); - - /* check if we're already monitoring ~/Templates for changes */ - if (G_UNLIKELY (templates_action->templates_monitor == NULL)) - { - /* grab a reference on the VFS monitor */ - templates_action->templates_monitor = thunar_vfs_monitor_get_default (); - templates_action->templates_handle = thunar_vfs_monitor_add_directory (templates_action->templates_monitor, - templates_action->templates_path, - thunar_templates_action_monitor, - templates_action); - } -} - - - -static void -thunar_templates_action_monitor (ThunarVfsMonitor *monitor, - ThunarVfsMonitorHandle *handle, - ThunarVfsMonitorEvent event, - ThunarVfsPath *handle_path, - ThunarVfsPath *event_path, - gpointer user_data) -{ - ThunarTemplatesAction *templates_action = THUNAR_TEMPLATES_ACTION (user_data); - GtkWidget *menu; - GSList *lp; - GList *children; - - g_return_if_fail (THUNAR_IS_TEMPLATES_ACTION (templates_action)); - g_return_if_fail (templates_action->templates_monitor == monitor); - g_return_if_fail (templates_action->templates_handle == handle); - - /* clear all menus for all menu proxies (will be rebuild on-demand) */ - for (lp = gtk_action_get_proxies (GTK_ACTION (templates_action)); lp != NULL; lp = lp->next) - if (G_LIKELY (GTK_IS_MENU_ITEM (lp->data))) - { - menu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (lp->data)); - if (G_UNLIKELY (menu == NULL)) - continue; - - /* we clear the menu */ - children = gtk_container_get_children (GTK_CONTAINER (menu)); - g_list_foreach (children, (GFunc) gtk_widget_destroy, NULL); - g_list_free (children); - - /* regenerate it if its currently mapped */ - if (G_UNLIKELY (GTK_WIDGET_MAPPED (menu))) - thunar_templates_action_build_menu (templates_action, menu); - } + /* cleanup */ + thunar_vfs_path_unref (templates_path); } -- GitLab