Index: src/file-manager/fm-directory-view.c =================================================================== --- src/file-manager/fm-directory-view.c (revision 12790) +++ src/file-manager/fm-directory-view.c (working copy) @@ -151,6 +151,7 @@ #define FM_DIRECTORY_VIEW_POPUP_PATH_LOCATION "/location" #define MAX_MENU_LEVELS 5 +#define TEMPLATE_LIMIT 30 enum { ADD_FILE, @@ -187,9 +188,6 @@ static gboolean confirm_trash_auto_value static char *scripts_directory_uri; static int scripts_directory_uri_length; -static char *templates_directory_uri; -static int templates_directory_uri_length; - struct FMDirectoryViewDetails { NautilusWindowInfo *window; @@ -389,6 +387,8 @@ static gboolean activate_check_mime_type NautilusFile *file, gboolean warn_on_mismatch); static GdkDragAction ask_link_action (FMDirectoryView *view); +static void update_templates_directory (FMDirectoryView *view); +static void user_dirs_changed (FMDirectoryView *view); static void file_get_volume_and_drive (NautilusFile *file, GnomeVFSVolume **volume, @@ -1619,17 +1619,6 @@ set_up_scripts_directory_global (void) } static void -set_up_templates_directory_global (void) -{ - if (templates_directory_uri != NULL) { - return; - } - - templates_directory_uri = nautilus_get_templates_directory_uri (); - templates_directory_uri_length = strlen (templates_directory_uri); -} - -static void create_scripts_directory (void) { char *gnome1_path, *gnome1_uri_str; @@ -1912,6 +1901,7 @@ fm_directory_view_init (FMDirectoryView static gboolean setup_autos = FALSE; NautilusDirectory *scripts_directory; NautilusDirectory *templates_directory; + char *templates_uri; if (!setup_autos) { setup_autos = TRUE; @@ -1940,10 +1930,18 @@ fm_directory_view_init (FMDirectoryView add_directory_to_scripts_directory_list (view, scripts_directory); nautilus_directory_unref (scripts_directory); - set_up_templates_directory_global (); - templates_directory = nautilus_directory_get (templates_directory_uri); - add_directory_to_templates_directory_list (view, templates_directory); - nautilus_directory_unref (templates_directory); + if (nautilus_should_use_templates_directory ()) { + templates_uri = nautilus_get_templates_directory_uri (); + templates_directory = nautilus_directory_get (templates_uri); + g_free (templates_uri); + add_directory_to_templates_directory_list (view, templates_directory); + nautilus_directory_unref (templates_directory); + } + update_templates_directory (view); + g_signal_connect_object (nautilus_signaller_get_current (), + "user_dirs_changed", + G_CALLBACK (user_dirs_changed), + view, G_CONNECT_SWAPPED); view->details->sort_directories_first = eel_preferences_get_boolean (NAUTILUS_PREFERENCES_SORT_DIRECTORIES_FIRST); @@ -5712,19 +5710,52 @@ add_template_to_templates_menus (FMDirec g_free (action_name); } +static void +update_templates_directory (FMDirectoryView *view) +{ + NautilusDirectory *templates_directory; + GList *node, *next; + char *templates_uri; + + for (node = view->details->templates_directory_list; node != NULL; node = next) { + next = node->next; + remove_directory_from_templates_directory_list (view, node->data); + } + + if (nautilus_should_use_templates_directory ()) { + templates_uri = nautilus_get_templates_directory_uri (); + templates_directory = nautilus_directory_get (templates_uri); + g_free (templates_uri); + add_directory_to_templates_directory_list (view, templates_directory); + nautilus_directory_unref (templates_directory); + } +} + +static void +user_dirs_changed (FMDirectoryView *view) +{ + update_templates_directory (view); + view->details->templates_invalid = TRUE; + schedule_update_menus (view); +} static gboolean -directory_belongs_in_templates_menu (const char *uri) +directory_belongs_in_templates_menu (const char *templates_directory_uri, + const char *uri) { int num_levels; int i; - if (!eel_str_has_prefix (uri, templates_directory_uri)) { + if (templates_directory_uri == NULL) { + return FALSE; + } + + if (!g_str_has_prefix (uri, templates_directory_uri)) { return FALSE; } num_levels = 0; - for (i = templates_directory_uri_length; uri[i] != '\0'; i++) { + for (i = strlen (templates_directory_uri); uri[i] != '\0'; i++) { if (uri[i] == '/') { num_levels++; } @@ -5738,7 +5769,9 @@ directory_belongs_in_templates_menu (con } static gboolean -update_directory_in_templates_menu (FMDirectoryView *view, NautilusDirectory *directory) +update_directory_in_templates_menu (FMDirectoryView *view, + const char *templates_directory_uri, + NautilusDirectory *directory) { char *menu_path, *popup_bg_path; GList *file_list, *filtered, *node; @@ -5747,9 +5780,13 @@ update_directory_in_templates_menu (FMDi NautilusDirectory *dir; char *escaped_path; char *uri; + int num; + + /* We know this directory belongs to the template dir, so it must exist */ + g_assert (templates_directory_uri); uri = nautilus_directory_get_uri (directory); - escaped_path = escape_action_path (uri + templates_directory_uri_length); + escaped_path = escape_action_path (uri + strlen (templates_directory_uri)); g_free (uri); menu_path = g_strconcat (FM_DIRECTORY_VIEW_MENU_PATH_NEW_DOCUMENTS_PLACEHOLDER, escaped_path, @@ -5765,13 +5802,14 @@ update_directory_in_templates_menu (FMDi file_list = nautilus_file_list_sort_by_display_name (filtered); + num = 0; any_templates = FALSE; - for (node = file_list; node != NULL; node = node->next) { + for (node = file_list; num < TEMPLATE_LIMIT && node != NULL; node = node->next, num++) { file = node->data; if (nautilus_file_is_directory (file)) { uri = nautilus_file_get_uri (file); - if (directory_belongs_in_templates_menu (uri)) { + if (directory_belongs_in_templates_menu (templates_directory_uri, uri)) { dir = nautilus_directory_get (uri); add_directory_to_templates_directory_list (view, dir); nautilus_directory_unref (dir); @@ -5809,6 +5847,13 @@ update_templates_menu (FMDirectoryView * GtkUIManager *ui_manager; char *uri; GtkAction *action; + char *templates_directory_uri; + + if (nautilus_should_use_templates_directory ()) { + templates_directory_uri = nautilus_get_templates_directory_uri (); + } else { + templates_directory_uri = NULL; + } /* There is a race condition here. If we don't mark the scripts menu as valid before we begin our task then we can lose template menu updates that @@ -5833,9 +5878,11 @@ update_templates_menu (FMDirectoryView * directory = node->data; uri = nautilus_directory_get_uri (directory); - if (!directory_belongs_in_templates_menu (uri)) { + if (!directory_belongs_in_templates_menu (templates_directory_uri, uri)) { remove_directory_from_templates_directory_list (view, directory); - } else if (update_directory_in_templates_menu (view, directory)) { + } else if (update_directory_in_templates_menu (view, + templates_directory_uri, + directory)) { any_templates = TRUE; } g_free (uri); @@ -5844,6 +5891,8 @@ update_templates_menu (FMDirectoryView * action = gtk_action_group_get_action (view->details->dir_action_group, FM_ACTION_NO_TEMPLATES); gtk_action_set_visible (action, !any_templates); + + g_free (templates_directory_uri); } Index: libnautilus-private/nautilus-file.c =================================================================== --- libnautilus-private/nautilus-file.c (revision 12790) +++ libnautilus-private/nautilus-file.c (working copy) @@ -839,7 +839,6 @@ nautilus_file_can_rename (NautilusFile * } if ((is_desktop_file (file) && !can_rename_desktop_file (file)) || - nautilus_file_is_desktop_directory (file) || nautilus_file_is_home (file)) { return FALSE; } Index: libnautilus-private/nautilus-file-utilities.c =================================================================== --- libnautilus-private/nautilus-file-utilities.c (revision 12790) +++ libnautilus-private/nautilus-file-utilities.c (working copy) @@ -31,6 +31,7 @@ #include "nautilus-metafile.h" #include "nautilus-file.h" #include "nautilus-search-directory.h" +#include "nautilus-signaller.h" #include #include #include @@ -49,6 +50,10 @@ #define LEGACY_DESKTOP_DIRECTORY_NAME ".gnome-desktop" #define DEFAULT_DESKTOP_DIRECTORY_MODE (0755) +static void update_xdg_dir_cache (void); +static void schedule_user_dirs_changed (void); +static void desktop_dir_changed (void); + char * nautilus_compute_title_for_uri (const char *text_uri) @@ -148,15 +153,280 @@ nautilus_get_user_directory (void) return user_directory; } +typedef struct { + char *type; + char *path; + NautilusFile *file; +} XdgDirEntry; + + +static XdgDirEntry * +parse_xdg_dirs (const char *config_file) +{ + GArray *array; + char *config_file_free = NULL; + XdgDirEntry dir; + char *data; + char **lines; + char *p, *d; + int i; + char *type_start, *type_end; + char *value, *unescaped; + gboolean relative; + + array = g_array_new (TRUE, TRUE, sizeof (XdgDirEntry)); + + if (config_file == NULL) + { + config_file_free = g_build_filename (g_get_user_config_dir (), + "user-dirs.dirs", NULL); + config_file = (const char *)config_file_free; + } + + if (g_file_get_contents (config_file, &data, NULL, NULL)) + { + lines = g_strsplit (data, "\n", 0); + g_free (data); + for (i = 0; lines[i] != NULL; i++) + { + p = lines[i]; + while (g_ascii_isspace (*p)) + p++; + + if (*p == '#') + continue; + + value = strchr (p, '='); + if (value == NULL) + continue; + *value++ = 0; + + g_strchug (g_strchomp (p)); + if (!g_str_has_prefix (p, "XDG_")) + continue; + if (!g_str_has_suffix (p, "_DIR")) + continue; + type_start = p + 4; + type_end = p + strlen (p) - 4; + + while (g_ascii_isspace (*value)) + value++; + + if (*value != '"') + continue; + value++; + + relative = FALSE; + if (g_str_has_prefix (value, "$HOME")) + { + relative = TRUE; + value += 5; + while (*value == '/') + value++; + } + else if (*value != '/') + continue; + + d = unescaped = g_malloc (strlen (value) + 1); + while (*value && *value != '"') + { + if ((*value == '\\') && (*(value + 1) != 0)) + value++; + *d++ = *value++; + } + *d = 0; + + *type_end = 0; + dir.type = g_strdup (type_start); + if (relative) + { + dir.path = g_build_filename (g_get_home_dir (), unescaped, NULL); + g_free (unescaped); + } + else + dir.path = unescaped; + + g_array_append_val (array, dir); + } + + g_strfreev (lines); + } + + g_free (config_file_free); + + return (XdgDirEntry *)g_array_free (array, FALSE); +} + +static XdgDirEntry *cached_xdg_dirs = NULL; +static GnomeVFSMonitorHandle *cached_xdg_dirs_handle = NULL; + +static void +xdg_dir_changed (NautilusFile *file, + XdgDirEntry *dir) +{ + char *file_uri; + char *dir_uri; + char *path; + + file_uri = nautilus_file_get_uri (file); + dir_uri = gnome_vfs_get_uri_from_local_path (dir->path); + if (file_uri && dir_uri && + !gnome_vfs_uris_match (dir_uri, file_uri)) { + path = gnome_vfs_get_local_path_from_uri (file_uri); + + if (path) { + char *argv[5]; + int i; + + g_free (dir->path); + dir->path = path; + + i = 0; + argv[i++] = "xdg-user-dirs-update"; + argv[i++] = "--set"; + argv[i++] = dir->type; + argv[i++] = dir->path; + argv[i++] = NULL; + + /* We do this sync, to avoid possible race-conditions + if multiple dirs change at the same time. Its + blocking the main thread, but these updates should + be very rare and very fast. */ + g_spawn_sync (NULL, + argv, NULL, + G_SPAWN_SEARCH_PATH | + G_SPAWN_STDOUT_TO_DEV_NULL | + G_SPAWN_STDERR_TO_DEV_NULL, + NULL, NULL, + NULL, NULL, NULL, NULL); + schedule_user_dirs_changed (); + desktop_dir_changed (); + } + } + g_free (file_uri); + g_free (dir_uri); +} + +static void +xdg_dir_cache_changed_cb (GnomeVFSMonitorHandle *handle, + const gchar *monitor_uri, + const gchar *info_uri, + GnomeVFSMonitorEventType event_type, + gpointer user_data) +{ + if (event_type == GNOME_VFS_MONITOR_EVENT_CHANGED || + event_type == GNOME_VFS_MONITOR_EVENT_CREATED) { + update_xdg_dir_cache (); + } +} + +static int user_dirs_changed_tag = 0; + +static gboolean +emit_user_dirs_changed_idle (gpointer data) +{ + g_signal_emit_by_name (nautilus_signaller_get_current (), + "user_dirs_changed"); + user_dirs_changed_tag = 0; + return FALSE; +} + +static void +schedule_user_dirs_changed (void) +{ + if (user_dirs_changed_tag == 0) { + user_dirs_changed_tag = g_idle_add (emit_user_dirs_changed_idle, NULL); + } +} + +static void +update_xdg_dir_cache (void) +{ + static gboolean started_monitor = FALSE; + char *config_file, *uri; + int i; + + if (cached_xdg_dirs) { + for (i = 0 ; cached_xdg_dirs[i].type != NULL; i++) { + if (cached_xdg_dirs[i].file != NULL) { + nautilus_file_monitor_remove (cached_xdg_dirs[i].file, + &cached_xdg_dirs[i]); + g_signal_handlers_disconnect_by_func (cached_xdg_dirs[i].file, + G_CALLBACK (xdg_dir_changed), + &cached_xdg_dirs[i]); + nautilus_file_unref (cached_xdg_dirs[i].file); + } + g_free (cached_xdg_dirs[i].type); + g_free (cached_xdg_dirs[i].path); + } + g_free (cached_xdg_dirs); + + schedule_user_dirs_changed (); + desktop_dir_changed (); + } + + if (!started_monitor) { + config_file = g_build_filename (g_get_user_config_dir (), + "user-dirs.dirs", NULL); + uri = gnome_vfs_get_uri_from_local_path (config_file); + gnome_vfs_monitor_add (&cached_xdg_dirs_handle, + uri, + GNOME_VFS_MONITOR_FILE, + xdg_dir_cache_changed_cb, + NULL); + g_free (uri); + g_free (config_file); + } + + cached_xdg_dirs = parse_xdg_dirs (NULL); + + for (i = 0 ; cached_xdg_dirs[i].type != NULL; i++) { + cached_xdg_dirs[i].file = NULL; + if (strcmp (cached_xdg_dirs[i].path, g_get_home_dir ()) != 0) { + uri = gnome_vfs_get_uri_from_local_path (cached_xdg_dirs[i].path); + cached_xdg_dirs[i].file = nautilus_file_get (uri); + nautilus_file_monitor_add (cached_xdg_dirs[i].file, + &cached_xdg_dirs[i], + NAUTILUS_FILE_ATTRIBUTE_FILE_TYPE); + g_signal_connect (cached_xdg_dirs[i].file, + "changed", G_CALLBACK (xdg_dir_changed), &cached_xdg_dirs[i]); + g_free (uri); + } + } +} + +char * +nautilus_get_xdg_dir (const char *type) +{ + int i; + + if (cached_xdg_dirs == NULL) { + update_xdg_dir_cache (); + } + + for (i = 0 ; cached_xdg_dirs != NULL && cached_xdg_dirs[i].type != NULL; i++) { + if (strcmp (cached_xdg_dirs[i].type, type) == 0) { + return g_strdup (cached_xdg_dirs[i].path); + } + } + if (strcmp ("DESKTOP", type) == 0) { + return g_build_filename (g_get_home_dir (), DESKTOP_DIRECTORY_NAME, NULL); + } + if (strcmp ("TEMPLATES", type) == 0) { + return g_build_filename (g_get_home_dir (), "Templates", NULL); + } + + return g_strdup (g_get_home_dir ()); +} + static char * get_desktop_path (void) { if (eel_preferences_get_boolean (NAUTILUS_PREFERENCES_DESKTOP_IS_HOME_DIR)) { return g_strdup (g_get_home_dir()); } else { - return g_build_filename (g_get_home_dir (), DESKTOP_DIRECTORY_NAME, NULL); + return nautilus_get_xdg_dir ("DESKTOP"); } - } /** @@ -231,11 +501,22 @@ nautilus_get_home_directory_uri (void) } +gboolean +nautilus_should_use_templates_directory (void) +{ + char *dir; + gboolean res; + + dir = nautilus_get_xdg_dir ("TEMPLATES"); + res = strcmp (dir, g_get_home_dir ()) != 0; + g_free (dir); + return res; +} + char * nautilus_get_templates_directory (void) { - return g_build_filename (g_get_home_dir(), - "Templates", NULL); + return nautilus_get_xdg_dir ("TEMPLATES"); } void @@ -283,8 +564,9 @@ static char *escaped_desktop_dir_dirname static char *escaped_desktop_dir_filename = NULL; static gboolean desktop_dir_changed_callback_installed = FALSE; + static void -desktop_dir_changed_callback (gpointer callback_data) +desktop_dir_changed (void) { g_free (escaped_desktop_dir); g_free (escaped_desktop_dir_filename); @@ -295,6 +577,12 @@ desktop_dir_changed_callback (gpointer c } static void +desktop_dir_changed_callback (gpointer callback_data) +{ + desktop_dir_changed (); +} + +static void update_desktop_dir (void) { char *uri, *path; Index: libnautilus-private/nautilus-file-utilities.h =================================================================== --- libnautilus-private/nautilus-file-utilities.h (revision 12790) +++ libnautilus-private/nautilus-file-utilities.h (working copy) @@ -37,6 +37,7 @@ gboolean nautilus_file_name_matches_back /* These functions all return something something that needs to be * freed with g_free, is not NULL, and is guaranteed to exist. */ +char * nautilus_get_xdg_dir (const char *type); char * nautilus_get_user_directory (void); char * nautilus_get_desktop_directory (void); char * nautilus_get_desktop_directory_uri (void); @@ -49,6 +50,7 @@ gboolean nautilus_is_home_directory_file char * nautilus_get_gmc_desktop_directory (void); char * nautilus_get_pixmap_directory (void); +gboolean nautilus_should_use_templates_directory (void); char * nautilus_get_templates_directory (void); char * nautilus_get_templates_directory_uri (void); void nautilus_create_templates_directory (void); Index: libnautilus-private/nautilus-signaller.c =================================================================== --- libnautilus-private/nautilus-signaller.c (revision 12790) +++ libnautilus-private/nautilus-signaller.c (working copy) @@ -39,6 +39,7 @@ enum { HISTORY_LIST_CHANGED, EMBLEMS_CHANGED, POPUP_MENU_CHANGED, + USER_DIRS_CHANGED, LAST_SIGNAL }; @@ -94,4 +95,12 @@ nautilus_signaller_class_init (NautilusS NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); + signals[USER_DIRS_CHANGED] = + g_signal_new ("user_dirs_changed", + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); }