diff options
Diffstat (limited to 'libnautilus-private')
22 files changed, 3575 insertions, 969 deletions
diff --git a/libnautilus-private/Makefile.am b/libnautilus-private/Makefile.am index 29ac4a442..ce52aa572 100644 --- a/libnautilus-private/Makefile.am +++ b/libnautilus-private/Makefile.am @@ -48,6 +48,7 @@ libnautilus_private_la_SOURCES = \ nautilus-bookmark.c \ nautilus-customization-data.c \ nautilus-default-file-icon.c \ + nautilus-desktop-file-loader.c \ nautilus-directory-async.c \ nautilus-directory-background.c \ nautilus-directory-metafile-monitor.c \ @@ -60,6 +61,7 @@ libnautilus_private_la_SOURCES = \ nautilus-file-changes-queue.c \ nautilus-file-dnd.c \ nautilus-file-operations-progress.c \ + nautilus-file-queue.c \ nautilus-file-operations.c \ nautilus-file-utilities.c \ nautilus-file.c \ @@ -75,6 +77,8 @@ libnautilus_private_la_SOURCES = \ nautilus-lib-self-check-functions.c \ nautilus-link-set.c \ nautilus-link.c \ + nautilus-link-desktop-file.c \ + nautilus-link-historical.c \ nautilus-medusa-support.c \ nautilus-merged-directory.c \ nautilus-metafile-factory.c \ @@ -106,6 +110,7 @@ libnautilus_private_la_SOURCES = \ nautilus-cdrom-extensions.h \ nautilus-customization-data.h \ nautilus-default-file-icon.h \ + nautilus-desktop-file-loader.h \ nautilus-directory-background.h \ nautilus-directory-metafile-monitor.h \ nautilus-directory-metafile.h \ @@ -124,6 +129,7 @@ libnautilus_private_la_SOURCES = \ nautilus-file-private.h \ nautilus-file-utilities.h \ nautilus-file.h \ + nautilus-file-queue.h \ nautilus-font-factory.h \ nautilus-global-preferences.h \ nautilus-horizontal-splitter.h \ @@ -139,6 +145,8 @@ libnautilus_private_la_SOURCES = \ nautilus-lib-self-check-functions.h \ nautilus-link-set.h \ nautilus-link.h \ + nautilus-link-desktop-file.h \ + nautilus-link-historical.h \ nautilus-medusa-support.h \ nautilus-merged-directory.h \ nautilus-metadata.h \ diff --git a/libnautilus-private/nautilus-desktop-file-loader.c b/libnautilus-private/nautilus-desktop-file-loader.c new file mode 100644 index 000000000..734afdf9f --- /dev/null +++ b/libnautilus-private/nautilus-desktop-file-loader.c @@ -0,0 +1,1223 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: 8; c-basic-offset: 8 -*- */ + +/* nautilus-desktop-file-loader.c + + Copyright (C) 2001 Red Hat, Inc. + + Developers: Havoc Pennington <hp@redhat.com> + + 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. + + The 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; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - + Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include <config.h> +#include "nautilus-desktop-file-loader.h" +#include "nautilus-program-choosing.h" + +#include <libgnome/libgnome.h> +#include <libgnomevfs/gnome-vfs-ops.h> +#include <eel/eel-gnome-extensions.h> +#include <eel/eel-vfs-extensions.h> + +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <ctype.h> +#include <locale.h> +#include <iconv.h> +#include <langinfo.h> + +typedef struct NautilusDesktopFileSection NautilusDesktopFileSection; +typedef struct NautilusDesktopFileAddition NautilusDesktopFileAddition; +typedef struct NautilusDesktopFileForeachData NautilusDesktopFileForeachData; + +typedef void (* NautilusDesktopFileForeachFunc) (NautilusDesktopFile *df, + const char *name, + gpointer data); + + +struct NautilusDesktopFile { + char **lines; + + /* hash of section names, from pointer to start of name (just after + * bracket) to NautilusDesktopFileSection structs + */ + GHashTable *section_hash; + GList *addition_list; + + NautilusDesktopFileSection *main_section; +}; + +struct NautilusDesktopFileSection { + /* pointer into a line of df->lines for start of name */ + const char *name; + + /* hash of keys in the section, + * from pointer to start of key name to + * pointer to start of value, not copied + * from df->lines + */ + GHashTable *key_hash; + + /* First line in the section (not the [section name] line, + * but the one after that) + */ + char **start_line; +}; + +struct NautilusDesktopFileAddition { + NautilusDesktopFileSection *section; + char *name; + char *value; + + /* used for saving */ + gboolean saving_section; +}; + +struct NautilusDesktopFileForeachData { + NautilusDesktopFile *df; + NautilusDesktopFileForeachFunc func; + gpointer user_data; + gboolean include_localized; +}; + +char** nautilus_desktop_file_get_lines (NautilusDesktopFile *df); +void nautilus_desktop_file_foreach_section (NautilusDesktopFile *df, + NautilusDesktopFileForeachFunc func, + gpointer user_data); +void nautilus_desktop_file_foreach_key (NautilusDesktopFile *df, + const char *section, + gboolean include_localized, + NautilusDesktopFileForeachFunc func, + gpointer user_data); + +static NautilusDesktopFile * nautilus_desktop_file_new (void); +static void hash_lines (NautilusDesktopFile *df); +static NautilusDesktopFileSection* section_new (const char *name, + char **start_line); +static void section_free (NautilusDesktopFileSection *sect); +static char* section_dup_name (NautilusDesktopFileSection *sect); +static char* validated_strdup (const char *str); +static NautilusDesktopFileAddition* addition_new (NautilusDesktopFileSection *section, + const char *name, + const char *value); +static void addition_free (NautilusDesktopFileAddition *addition); + + +static NautilusDesktopFile* +nautilus_desktop_file_new (void) +{ + NautilusDesktopFile *df; + + df = g_new0 (NautilusDesktopFile, 1); + + return df; +} + +GnomeVFSResult +nautilus_desktop_file_load (const char *uri, + NautilusDesktopFile **desktop_file) +{ + char *contents; + NautilusDesktopFile *df; + GnomeVFSResult result; + int file_size; + + *desktop_file = NULL; + result = eel_read_entire_file (uri, &file_size, + &contents); + if (result != GNOME_VFS_OK) { + return result; + } + + df = nautilus_desktop_file_from_string (contents); + + g_free (contents); + + *desktop_file = df; + + return GNOME_VFS_OK; +} + +NautilusDesktopFile* +nautilus_desktop_file_from_string (const char *data) +{ + NautilusDesktopFile *df; + + df = nautilus_desktop_file_new (); + + df->lines = g_strsplit (data, "\n", G_MAXINT); + + hash_lines (df); + + return df; +} + +static GnomeVFSResult +write_all (GnomeVFSHandle *handle, + gconstpointer buffer, + GnomeVFSFileSize bytes) +{ + GnomeVFSFileSize bytes_written; + GnomeVFSResult result; + + while (bytes > 0) { + result = gnome_vfs_write (handle, buffer, + bytes, &bytes_written); + if (result != GNOME_VFS_OK) { + return result; + } + bytes -= bytes_written; + } + + return GNOME_VFS_OK; +} + +GnomeVFSResult +nautilus_desktop_file_save (NautilusDesktopFile *df, + const char *uri) +{ + NautilusDesktopFileAddition *addition; + GnomeVFSHandle *handle; + GnomeVFSResult result; + char *old_val; + GList *list; + gint i; + + g_return_val_if_fail (df != NULL, FALSE); + g_return_val_if_fail (df->lines != NULL, FALSE); + + result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_WRITE); + if (result != GNOME_VFS_OK) { + return result; + } + + for (i = 0; df->lines[i] != NULL; i++) { + gboolean handled_line; + + handled_line = FALSE; + for (list = df->addition_list; list; list = list->next) { + addition = (NautilusDesktopFileAddition *)list->data; + + if (addition->section->start_line[0] == df->lines[i]) { + addition->saving_section = TRUE; + } + + if (addition->saving_section && df->lines[i][0] == '[') { + addition->saving_section = FALSE; + } + + if (addition->saving_section && + (strncmp (addition->name, df->lines[i], strlen (addition->name)) == 0)) { + old_val = strstr (df->lines[i], "="); + if (old_val == NULL) { + continue; + } + result = write_all (handle, + df->lines[i], + old_val + 1 - df->lines[i]); + if (result != GNOME_VFS_OK) { + gnome_vfs_close (handle); + return result; + } + result = write_all (handle, + addition->value, + strlen (addition->value)); + if (result != GNOME_VFS_OK) { + gnome_vfs_close (handle); + return result; + } + result = write_all (handle, "\n", 1); + if (result != GNOME_VFS_OK) { + gnome_vfs_close (handle); + return result; + } + + handled_line = TRUE; + } + } + if (handled_line == FALSE) { + result = write_all (handle, + df->lines[i], + strlen (df->lines[i])); + if (result != GNOME_VFS_OK) { + gnome_vfs_close (handle); + return result; + } + result = write_all (handle, "\n", 1); + if (result != GNOME_VFS_OK) { + gnome_vfs_close (handle); + return result; + } + } + } + + gnome_vfs_close (handle); + return GNOME_VFS_OK; +} + +static void +destroy_foreach (gpointer key, gpointer value, gpointer data) +{ + section_free (value); +} + + +void +nautilus_desktop_file_free (NautilusDesktopFile *df) +{ + if (df->section_hash != NULL) { + g_hash_table_foreach (df->section_hash, destroy_foreach, NULL); + g_hash_table_destroy (df->section_hash); + } + + g_list_foreach (df->addition_list, (GFunc) addition_free, NULL); + g_list_free (df->addition_list); + if (df->lines != NULL) + g_strfreev (df->lines); + + g_free (df); +} + + +/* This is cut and pasted from glib 1.3.x + * It should be removed when porting to glib 2 + */ +/** + * g_strdupv: + * @str_array: %NULL-terminated array of strings + * + * Copies %NULL-terminated array of strings. The copy is a deep copy; + * the new array should be freed by first freeing each string, then + * the array itself. g_strfreev() does this for you. If called + * on a %NULL value, g_strdupv() simply returns %NULL. + * + * Return value: a new %NULL-terminated array of strings + **/ +static gchar** +cnp_g_strdupv (gchar **str_array) +{ + if (str_array) { + gint i; + gchar **retval; + + i = 0; + while (str_array[i]) + ++i; + + retval = g_new (gchar*, i + 1); + + i = 0; + while (str_array[i]) { + retval[i] = g_strdup (str_array[i]); + ++i; + } + retval[i] = NULL; + + return retval; + } else { + return NULL; + } +} + +char** +nautilus_desktop_file_get_lines (NautilusDesktopFile *df) +{ + return cnp_g_strdupv (df->lines); +} + +/* Custom hash functions allow us to avoid strdups */ +static gboolean +key_equal (gconstpointer v1, + gconstpointer v2) +{ + const gchar *p1 = v1; + const gchar *p2 = v2; + + /* we count '=' and ' ' as terminator + * and don't count leading/trailing spaces + */ + + while (isspace ((guchar)*p1)) { + ++p1; + } + + while (isspace ((guchar)*p2)) { + ++p2; + } + + while (*p1 && *p2 && + *p1 != '=' && *p2 != '=' && + !isspace ((guchar)*p1) && !isspace ((guchar)*p2)) { + if (*p1 != *p2) { + return FALSE; + } + + ++p1; + ++p2; + } + + if (*p1 && *p1 != '=' && !isspace ((guchar)*p1)) { + return FALSE; + } + + if (*p2 && *p2 != '=' && !isspace ((guchar)*p2)) { + return FALSE; + } + + return TRUE; +} + +static guint +key_hash (gconstpointer key) +{ + const char *p = key; + guint h = *p; + + /* we count '=' and ' ' as terminator + * and don't count leading/trailing spaces + */ + + while (isspace ((guchar)*p)) { + ++p; + } + + if (h) { + for (p += 1; *p != '\0' && *p != '=' && !isspace ((guchar)*p); p++) { + h = (h << 5) - h + *p; + } + } + + return h; +} + +static gboolean +section_equal (gconstpointer v1, + gconstpointer v2) +{ + const gchar *p1 = v1; + const gchar *p2 = v2; + + /* we count ']' as terminator */ + + while (*p1 && *p2 && + *p1 != ']' && *p2 != ']') { + if (*p1 != *p2) { + return FALSE; + } + + ++p1; + ++p2; + } + + if (*p1 && *p1 != ']') { + return FALSE; + } + + if (*p2 && *p2 != ']') { + return FALSE; + } + + return TRUE; +} + +static guint +section_hash (gconstpointer key) +{ + const char *p = key; + guint h = *p; + + /* we count ']' as terminator */ + + if (h) { + for (p += 1; *p != '\0' && *p != ']'; p++) { + h = (h << 5) - h + *p; + } + } + + return h; +} + +static void +hash_lines (NautilusDesktopFile *df) +{ + NautilusDesktopFileSection *current_sect; + const char *eq; + const char *p; + char **iter; + + if (df->section_hash == NULL) { + df->section_hash = g_hash_table_new (section_hash, section_equal); + } + + current_sect = NULL; + iter = df->lines; + + while (iter != NULL && *iter != NULL) { + p = *iter; + + while (isspace ((guchar)*p)) { + ++p; + } + + /* blank or comment lines */ + if (*p == '\0' || *p == '#') { + goto next; + } + + if (*p == '[') { + /* Begin a section */ + ++p; + + if (*p != ']' && + strchr (p, ']') != NULL) { + current_sect = section_new (p, iter + 1); + + g_hash_table_insert (df->section_hash, + (char*) current_sect->name, + current_sect); + + if (df->main_section == NULL && + (section_equal (current_sect->name, "Desktop Entry") || + section_equal (current_sect->name, "KDE Desktop Entry"))) { + df->main_section = current_sect; + } + } + } else { + /* should be a key=value line, if not + * it's some invalid crap + */ + eq = strchr (p, '='); + if (eq == NULL) { + goto next; + } else { + if (current_sect) { + ++eq; + while (isspace ((guchar)*eq)) { + ++eq; + } + /* could overwrite an earlier copy of + * the same key name in this section + */ + g_hash_table_insert (current_sect->key_hash, + (char*) p, (char*) eq); + } + } + } + + next: + ++iter; + } +} + +static NautilusDesktopFileSection* +section_new (const char *name, + char **start_line) +{ + NautilusDesktopFileSection *sect; + + sect = g_new (NautilusDesktopFileSection, 1); + + sect->name = name; + sect->start_line = start_line; + sect->key_hash = g_hash_table_new (key_hash, key_equal); + + return sect; +} + +static void +section_free (NautilusDesktopFileSection *sect) +{ + g_hash_table_destroy (sect->key_hash); + g_free (sect); +} + + +static NautilusDesktopFileAddition * +addition_new (NautilusDesktopFileSection *section, + const char *name, + const char *value) +{ + NautilusDesktopFileAddition *addition; + + addition = g_new (NautilusDesktopFileAddition, 1); + + addition->section = section; + addition->name = g_strdup (name); + addition->value = g_strdup (value); + + return addition; +} + +static void +addition_free (NautilusDesktopFileAddition *addition) +{ + g_free (addition->name); + g_free (addition->value); + g_free (addition); +} + +static char* +section_dup_name (NautilusDesktopFileSection *sect) +{ + const char *name_end; + + name_end = strchr (sect->name, ']'); + + g_assert (name_end); /* we were supposed to verify this on initial parse */ + + return g_strndup (sect->name, name_end - sect->name); +} + +static NautilusDesktopFileSection* +get_section (NautilusDesktopFile *df, + const char *section) +{ + if (df->section_hash == NULL) { + return NULL; + } + + if (section == NULL) { + return df->main_section; + } else { + return g_hash_table_lookup (df->section_hash, section); + } +} + +static void +section_foreach (gpointer key, gpointer value, gpointer data) +{ + NautilusDesktopFileForeachData *fd; + NautilusDesktopFileSection *sect; + char *name; + + fd = data; + sect = value; + + name = section_dup_name (sect); + + (* fd->func) (fd->df, name, fd->user_data); + + g_free (name); +} + +void +nautilus_desktop_file_foreach_section (NautilusDesktopFile *df, + NautilusDesktopFileForeachFunc func, + gpointer user_data) +{ + if (df->section_hash != NULL) { + NautilusDesktopFileForeachData fd; + + fd.df = df; + fd.func = func; + fd.user_data = user_data; + fd.include_localized = FALSE; /* not used */ + + g_hash_table_foreach (df->section_hash, section_foreach, &fd); + } +} + + +static void +key_foreach (gpointer key, gpointer value, gpointer data) +{ + NautilusDesktopFileForeachData *fd; + char *key_end; + char *name; + + fd = data; + + key_end = (char*) key; + while (*key_end && + !isspace ((guchar)*key_end) && + *key_end != '=') { + ++key_end; + } + + name = g_strndup (key, key_end - (char*)key); + + if (fd->include_localized || + (!fd->include_localized && strchr (name, '[') == NULL)) { + (* fd->func) (fd->df, name, fd->user_data); + } + + g_free (name); +} + +void +nautilus_desktop_file_foreach_key (NautilusDesktopFile *df, + const char *section, + gboolean include_localized, + NautilusDesktopFileForeachFunc func, + gpointer user_data) +{ + NautilusDesktopFileSection *sect; + + sect = get_section (df, section); + + if (sect) { + NautilusDesktopFileForeachData fd; + + fd.df = df; + fd.func = func; + fd.user_data = user_data; + fd.include_localized = include_localized; + + g_hash_table_foreach (sect->key_hash, key_foreach, &fd); + } +} + +static const char* +get_keyval (NautilusDesktopFile *df, + const char *section, + const char *keyname) +{ + NautilusDesktopFileSection *sect; + const char *strval; + + sect = get_section (df, section); + + if (sect == NULL) { + return FALSE; + } + + strval = g_hash_table_lookup (sect->key_hash, + keyname); + + return strval; +} + +static gboolean +parse_boolean (const char *strval, + int len, + gboolean *val) +{ + if (len < 0) { + len = strlen (strval); + } + + if (*strval == '1') { + *val = TRUE; + return TRUE; + } else if (len > 3 && + strval[0] == 't' && strval[1] == 'r' && + strval[2] == 'u' && strval[3] == 'e') { + *val = TRUE; + return TRUE; + } else if (*strval == '0') { + *val = FALSE; + return TRUE; + } else if (len > 4 && + strval[0] == 'f' && strval[1] == 'a' && + strval[2] == 'l' && strval[3] == 's' && + strval[4] == 'e') { + *val = FALSE; + return TRUE; + } + + return FALSE; +} + +static gboolean +parse_number (const char *strval, + int len, + double *val) +{ + char *end; + double tmp; + + if (len < 0) { + len = strlen (strval); + } + + tmp = strtod (strval, &end); + if (strval == end) { + return FALSE; + } + + *val = tmp; + return TRUE; +} + + +static void +get_locale (char **lang, + char **lang_country) +{ + const char *uscore_pos; + const char *at_pos; + const char *dot_pos; + const char *end_pos; + const char *locale; + const char *start_lang; + const char *end_lang; + const char *end_country; + + *lang = NULL; + *lang_country = NULL; + + locale = setlocale (LC_MESSAGES, NULL); + + if (locale == NULL) { + return; + } + + /* lang_country.encoding@modifier */ + + uscore_pos = strchr (locale, '_'); + dot_pos = strchr (uscore_pos ? uscore_pos : locale, '.'); + at_pos = strchr ((dot_pos != NULL) ? dot_pos : ((uscore_pos != NULL) ? uscore_pos : locale), '@'); + end_pos = locale + strlen (locale); + + start_lang = locale; + end_lang = ((uscore_pos != NULL) ? uscore_pos : + ((dot_pos != NULL) ? dot_pos : + ((at_pos != NULL) ? at_pos : end_pos))); + end_country = ((dot_pos != NULL) ? dot_pos : + ((at_pos != NULL) ? at_pos : end_pos)); + + if (uscore_pos == NULL) { + *lang = g_strndup (start_lang, end_lang - start_lang); + } else { + *lang = g_strndup (start_lang, end_lang - start_lang); + *lang_country = g_strndup (start_lang, + end_country - start_lang); + } +} + +gboolean +nautilus_desktop_file_get_boolean (NautilusDesktopFile *df, + const char *section, + const char *keyname, + gboolean *val) +{ + const char *strval; + + strval = get_keyval (df, section, keyname); + + if (strval == NULL) { + return FALSE; + } + + return parse_boolean (strval, -1, val); +} + +gboolean +nautilus_desktop_file_get_number (NautilusDesktopFile *df, + const char *section, + const char *keyname, + double *val) +{ + const char *strval; + + strval = get_keyval (df, section, keyname); + + if (strval == NULL) { + return FALSE; + } + + return parse_number (strval, -1, val); +} + +/* Totally bogus UTF-8 stuff */ +gboolean +nautilus_desktop_file_get_string (NautilusDesktopFile *df, + const char *section, + const char *keyname, + char **val) +{ + const char *strval; + char *tmp; + + strval = get_keyval (df, section, keyname); + + if (strval == NULL) { + return FALSE; + } + + tmp = validated_strdup (strval); + + if (tmp != NULL) { + *val = tmp; + } + + return tmp != NULL; +} + +gboolean +nautilus_desktop_file_get_locale_string (NautilusDesktopFile *df, + const char *section, + const char *keyname, + char **val) +{ + const char *strval; + char *lang; + char *lang_country; + char *s; + + strval = NULL; + get_locale (&lang, &lang_country); + + /* FIXME - we need to try de_DE.ENCODING in addition to what + * we are trying here. + */ + + /* Try "Foo[de_DE]" */ + if (lang_country) { + s = g_strconcat (keyname, "[", lang_country, "]", NULL); + strval = get_keyval (df, section, s); + g_free (s); + if (strval != NULL) + goto done; + } + + /* Try "Foo[de]" */ + if (lang) { + s = g_strconcat (keyname, "[", lang, "]", NULL); + strval = get_keyval (df, section, s); + g_free (s); + if (strval != NULL) + goto done; + } + + /* Fall back to not localized */ + strval = get_keyval (df, section, keyname); + + done: + g_free (lang); + g_free (lang_country); + + if (strval == NULL) { + return FALSE; + } else { + char *tmp = validated_strdup (strval); + + if (tmp != NULL) { + *val = tmp; + } + return tmp != NULL; + } +} + +gboolean +nautilus_desktop_file_get_regexp (NautilusDesktopFile *df, + const char *section, + const char *keyname, + char **val) +{ + return nautilus_desktop_file_get_string (df, section, keyname, val); +} + +/* This is extremely broken */ + +#define F 0 /* character never appears in text */ +#define T 1 /* character appears in plain ASCII text */ +#define I 2 /* character appears in ISO-8859 text */ +#define X 3 /* character appears in non-ISO extended ASCII (Mac, IBM PC) */ + +static char text_chars[256] = { + /* BEL BS HT LF FF CR */ + F, F, F, F, F, F, F, T, T, T, T, F, T, T, F, F, /* 0x0X */ + /* ESC */ + F, F, F, F, F, F, F, F, F, F, F, T, F, F, F, F, /* 0x1X */ + T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, /* 0x2X */ + T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, /* 0x3X */ + T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, /* 0x4X */ + T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, /* 0x5X */ + T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, /* 0x6X */ + T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, F, /* 0x7X */ + /* NEL */ + X, X, X, X, X, T, X, X, X, X, X, X, X, X, X, X, /* 0x8X */ + X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, /* 0x9X */ + I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, /* 0xaX */ + I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, /* 0xbX */ + I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, /* 0xcX */ + I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, /* 0xdX */ + I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, /* 0xeX */ + I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I /* 0xfX */ +}; + +static int +looks_utf8 (const unsigned char *buf, + int nbytes, + unsigned long *ubuf, + int *ulen) +{ + int i, n; + unsigned long c; + int gotone = 0; + + *ulen = 0; + + for (i = 0; i < nbytes; i++) { + if ((buf[i] & 0x80) == 0) { /* 0xxxxxxx is plain ASCII */ + /* + * Even if the whole file is valid UTF-8 sequences, + * still reject it if it uses weird control characters. + */ + if (text_chars[buf[i]] != T) { + return 0; + } + + if (ubuf != NULL) { + ubuf[(*ulen)++] = buf[i]; + } + } else if ((buf[i] & 0x40) == 0) { /* 10xxxxxx never 1st byte */ + + return 0; + } else { /* 11xxxxxx begins UTF-8 */ + int following; + + if ((buf[i] & 0x20) == 0) { /* 110xxxxx */ + c = buf[i] & 0x1f; + following = 1; + } else if ((buf[i] & 0x10) == 0) { /* 1110xxxx */ + c = buf[i] & 0x0f; + following = 2; + } else if ((buf[i] & 0x08) == 0) { /* 11110xxx */ + c = buf[i] & 0x07; + following = 3; + } else if ((buf[i] & 0x04) == 0) { /* 111110xx */ + c = buf[i] & 0x03; + following = 4; + } else if ((buf[i] & 0x02) == 0) { /* 1111110x */ + c = buf[i] & 0x01; + following = 5; + } else { + return 0; + } + + for (n = 0; n < following; n++) { + i++; + if (i >= nbytes) { + goto done; + } + + if ((buf[i] & 0x80) == 0 || (buf[i] & 0x40)) { + return 0; + } + + c = (c << 6) + (buf[i] & 0x3f); + } + + if (ubuf != NULL) { + ubuf[(*ulen)++] = c; + } + gotone = 1; + } + } + done: + return gotone; /* don't claim it's UTF-8 if it's all 7-bit */ +} + +G_LOCK_DEFINE_STATIC (init_validate); + +static char* +validated_strdup (const char *str) +{ + static gchar *locale; + static gboolean initialized = FALSE; + gchar *pout, *pin, *buf; + gint len, ulen = 0, ib, ob; + iconv_t fd; + + G_LOCK (init_validate); + if (!initialized) { + /* whee, we are totally unportable (broken too) */ + setlocale (LC_CTYPE, ""); + locale = nl_langinfo (CODESET); + initialized = TRUE; + } + G_UNLOCK (init_validate); + + buf = NULL; + + len = strlen (str); + if (looks_utf8 (str, len, NULL, &ulen)) { + if ((fd = iconv_open (locale, "UTF-8")) != (iconv_t)-1) { + ib = len; + ob = ib * 3; + pout = buf = g_new0 (gchar, ob); + pin = (char*) str; + + /* not portable either */ + + if (iconv (fd, &pin, &ib, &pout, &ob) == (size_t)-1) { + g_free (buf); + buf = NULL; + } + + iconv_close (fd); + } + } else { + buf = g_strdup (str); + } + + return buf; +} + +/* This is cut and pasted from glib 1.3.x + * It should be removed when porting to glib 2 + */ +static void +cnp_g_string_append_len (GString *str, + const char *s, + int len) +{ + if (len < 0) { + g_string_append (str, s); + } else { + char *tmp; + tmp = g_strndup (s, len); + g_string_append (str, tmp); + g_free (tmp); + } +} + +static char* +nautilus_desktop_file_sub_formats (NautilusDesktopFile *df, + const char *src) +{ + GString *new; + const char *p; + const char *end; + char *retval; + + new = g_string_new (""); + + p = src; + end = src; + + p = strchr (p, '%'); + while (p) { + if (p != end) { + /* Append what we just scanned over */ + cnp_g_string_append_len (new, end, p - end); + } + + end = p; + + ++p; /* past the % */ + switch (*p) { + case 'f': + case 'F': + case 'u': + case 'U': + case 'd': + case 'D': + case 'n': + case 'N': + case 'i': + case 'm': + case 'c': + case 'k': + case 'v': + /* We don't actually sub anything for now */ + ++p; + break; + case '%': + /* Escaped % */ + g_string_append (new, "%"); + ++p; + break; + default: + /* some broken .desktop-spec-incompliant crack; + * try just skipping it. + */ + ++p; + break; + } + + p = strchr (p, '%'); + } + + g_string_append (new, end); + + retval = new->str; + g_string_free (new, FALSE); + return retval; +} + +void +nautilus_desktop_file_launch (NautilusDesktopFile *df) +{ + char *type; + char *url; + char *exec; + char *subst; + + + + if (!nautilus_desktop_file_get_string (df, NULL, "Type", &type)) { + return; + } + + if (strcmp (type, "Link") == 0) { + url = NULL; + nautilus_desktop_file_get_string (df, NULL, "URL", &url); + + if (url != NULL) { + gnome_url_show (url); + } + + g_free (url); + } else if (strcmp (type, "Application") == 0) { + exec = NULL; + nautilus_desktop_file_get_string (df, NULL, "Exec", &exec); + + if (exec != NULL) { + gboolean in_terminal; + + subst = nautilus_desktop_file_sub_formats (df, exec); + + in_terminal = FALSE; + nautilus_desktop_file_get_boolean (df, NULL, "Terminal", &in_terminal); + + nautilus_launch_application_from_command ("", + subst, + NULL, + in_terminal); + g_free (subst); + } + + g_free (exec); + } + + g_free (type); +} + + +gboolean +nautilus_desktop_file_set_string (NautilusDesktopFile *df, + const char *section, + const char *keyname, + const char *value) +{ + NautilusDesktopFileSection *sect; + NautilusDesktopFileAddition *addition; + + sect = get_section (df, section); + if (sect == NULL) { + return FALSE; + } + + addition = addition_new (sect, keyname, value); + df->addition_list = g_list_append (df->addition_list, addition); + + return TRUE; +} diff --git a/libnautilus-private/nautilus-desktop-file-loader.h b/libnautilus-private/nautilus-desktop-file-loader.h new file mode 100644 index 000000000..e82303707 --- /dev/null +++ b/libnautilus-private/nautilus-desktop-file-loader.h @@ -0,0 +1,85 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: 8; c-basic-offset: 8 -*- */ + +/* eel-desktop-file-loader.h + + Copyright (C) 2001 Red Hat, Inc. + + Developers: Havoc Pennington <hp@redhat.com> + Alexander Larsson <alexl@redhat.com> + + 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. + + The 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; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - + Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#ifndef NAUTILUS_DESKTOP_FILE_LOADER_H +#define NAUTILUS_DESKTOP_FILE_LOADER_H + +#include <glib.h> +#include <libgnomevfs/gnome-vfs-result.h> + +typedef struct NautilusDesktopFile NautilusDesktopFile; + +/* This is a quick-hack to read and modify .desktop files. + * It has severe limitations, but does what nautilus + * needs right now. You cannot create new sections or add non-existing keys. + * + * The right way to solve this is to write a good desktop file parser + * and put it in another library for use by the panel, nautilus etc. + */ + +GnomeVFSResult nautilus_desktop_file_load (const char *uri, + NautilusDesktopFile **desktop_file); +NautilusDesktopFile *nautilus_desktop_file_from_string (const char *data); +GnomeVFSResult nautilus_desktop_file_save (NautilusDesktopFile *df, + const char *uri); +void nautilus_desktop_file_free (NautilusDesktopFile *df); + +/* This is crap, it just ignores the %f etc. in the exec string, + * and has no error handling. + */ +void nautilus_desktop_file_launch (NautilusDesktopFile *df); + + +gboolean nautilus_desktop_file_get_boolean (NautilusDesktopFile *df, + const char *section, + const char *keyname, + gboolean *val); +gboolean nautilus_desktop_file_get_number (NautilusDesktopFile *df, + const char *section, + const char *keyname, + double *val); +gboolean nautilus_desktop_file_get_string (NautilusDesktopFile *df, + const char *section, + const char *keyname, + char **val); +gboolean nautilus_desktop_file_get_locale_string (NautilusDesktopFile *df, + const char *section, + const char *keyname, + char **val); +gboolean nautilus_desktop_file_get_regexp (NautilusDesktopFile *df, + const char *section, + const char *keyname, + char **val); +gboolean nautilus_desktop_file_set_string (NautilusDesktopFile *df, + const char *section, + const char *keyname, + const char *value); + + +/* Some getters and setters are missing, they should be added as needed */ + + +#endif /* NAUTILUS_DESKTOP_FILE_LOADER_H */ diff --git a/libnautilus-private/nautilus-directory-async.c b/libnautilus-private/nautilus-directory-async.c index 4e2fbd212..5ed4ee009 100644 --- a/libnautilus-private/nautilus-directory-async.c +++ b/libnautilus-private/nautilus-directory-async.c @@ -38,6 +38,8 @@ #include <ctype.h> #include <libxml/parser.h> #include <libxml/xmlmemory.h> +#include <libgnome/gnome-metadata.h> +#include <libgnome/gnome-mime-info.h> #include <gtk/gtkmain.h> #include <stdlib.h> #include <stdio.h> @@ -68,7 +70,7 @@ struct TopLeftTextReadState { EelReadFileHandle *handle; }; -struct ActivationURIReadState { +struct LinkInfoReadState { NautilusFile *file; EelReadFileHandle *handle; }; @@ -102,13 +104,27 @@ static GHashTable *async_jobs; #endif /* Forward declarations for functions that need them. */ -static void deep_count_load (NautilusDirectory *directory, - const char *uri); -static gboolean request_is_satisfied (NautilusDirectory *directory, - NautilusFile *file, - Request *request); -static void cancel_loading_attributes (NautilusDirectory *directory, - GList *file_attributes); +static void deep_count_load (NautilusDirectory *directory, + const char *uri); +static gboolean request_is_satisfied (NautilusDirectory *directory, + NautilusFile *file, + Request *request); +static void cancel_loading_attributes (NautilusDirectory *directory, + GList *file_attributes); +static void add_all_files_to_work_queue (NautilusDirectory *directory); +static void link_info_done (NautilusDirectory *directory, + NautilusFile *file, + const char *uri, + const char *name, + const char *icon); +static gboolean file_needs_high_priority_work_done (NautilusDirectory *directory, + NautilusFile *file); +static gboolean file_needs_low_priority_work_done (NautilusDirectory *directory, + NautilusFile *file); +static void move_file_to_low_priority_queue (NautilusDirectory *directory, + NautilusFile *file); + + /* Some helpers for case-insensitive strings. * Move to nautilus-glib-extensions? @@ -376,14 +392,13 @@ top_left_cancel (NautilusDirectory *directory) } static void -activation_uri_cancel (NautilusDirectory *directory) +link_info_cancel (NautilusDirectory *directory) { - if (directory->details->activation_uri_read_state != NULL) { - eel_read_file_cancel (directory->details->activation_uri_read_state->handle); - g_free (directory->details->activation_uri_read_state); - directory->details->activation_uri_read_state = NULL; - - async_job_end (directory, "activation URI"); + if (directory->details->link_info_read_state != NULL) { + eel_read_file_cancel (directory->details->link_info_read_state->handle); + g_free (directory->details->link_info_read_state); + directory->details->link_info_read_state = NULL; + async_job_end (directory, "link info"); } } @@ -536,13 +551,15 @@ nautilus_directory_set_up_request (Request *request, NAUTILUS_FILE_ATTRIBUTE_ACTIVATION_URI, eel_strcmp_compare_func) != NULL) { request->file_info = TRUE; - request->activation_uri = TRUE; + request->link_info = TRUE; + } + + if (g_list_find_custom (file_attributes, + NAUTILUS_FILE_ATTRIBUTE_CUSTOM_NAME, + eel_strcmp_compare_func) != NULL) { + request->file_info = TRUE; + request->link_info = TRUE; } - - request->metafile |= g_list_find_custom - (file_attributes, - NAUTILUS_FILE_ATTRIBUTE_METADATA, - eel_strcmp_compare_func) != NULL; /* FIXME bugzilla.gnome.org 42435: * Some file attributes are really pieces of metadata. @@ -555,10 +572,19 @@ nautilus_directory_set_up_request (Request *request, * directly (would need some funky char trick to prevent * namespace collisions). */ + if (g_list_find_custom (file_attributes, + NAUTILUS_FILE_ATTRIBUTE_CUSTOM_ICON, + eel_strcmp_compare_func) != NULL) { + request->metafile = TRUE; + request->file_info = TRUE; + request->link_info = TRUE; + } + request->metafile |= g_list_find_custom (file_attributes, - NAUTILUS_FILE_ATTRIBUTE_CUSTOM_ICON, + NAUTILUS_FILE_ATTRIBUTE_METADATA, eel_strcmp_compare_func) != NULL; + } void @@ -618,6 +644,13 @@ nautilus_directory_monitor_add_internal (NautilusDirectory *directory, nautilus_directory_register_metadata_monitor (directory); } + /* Put the monitor file or all the files on the work queue. */ + if (file != NULL) { + nautilus_directory_add_file_to_work_queue (directory, file); + } else { + add_all_files_to_work_queue (directory); + } + /* Kick off I/O. */ nautilus_directory_async_state_changed (directory); } @@ -1052,6 +1085,8 @@ nautilus_directory_monitor_remove_internal (NautilusDirectory *directory, update_metadata_monitors (directory); + /* XXX - do we need to remove anything from the work queue? */ + nautilus_directory_async_state_changed (directory); } @@ -1080,6 +1115,9 @@ nautilus_directory_remove_file_monitors (NautilusDirectory *directory, } update_metadata_monitors (directory); + + /* XXX - do we need to remove anything from the work queue? */ + nautilus_directory_async_state_changed (directory); return (FileMonitors *) result; @@ -1103,6 +1141,8 @@ nautilus_directory_add_file_monitors (NautilusDirectory *directory, list = &directory->details->monitor_list; *list = g_list_concat (*list, (GList *) monitors); + nautilus_directory_add_file_to_work_queue (directory, file); + update_metadata_monitors (directory); nautilus_directory_async_state_changed (directory); } @@ -1224,6 +1264,13 @@ nautilus_directory_call_when_ready_internal (NautilusDirectory *directory, nautilus_directory_register_metadata_monitor (directory); } + /* Put the callback file or all the files on the work queue. */ + if (file != NULL) { + nautilus_directory_add_file_to_work_queue (directory, file); + } else { + add_all_files_to_work_queue (directory); + } + nautilus_directory_async_state_changed (directory); } @@ -1325,7 +1372,10 @@ directory_count_callback (GnomeVFSAsyncHandle *handle, /* Record either a failure or success. */ if (result != GNOME_VFS_ERROR_EOF) { count_file->details->directory_count_failed = TRUE; + count_file->details->got_directory_count = FALSE; + count_file->details->directory_count = 0; } else { + count_file->details->directory_count_failed = FALSE; count_file->details->got_directory_count = TRUE; count_file->details->directory_count = entries_read; } @@ -1459,9 +1509,9 @@ nautilus_async_destroying_file (NautilusFile *file) directory->details->top_left_read_state->file = NULL; changed = TRUE; } - if (directory->details->activation_uri_read_state != NULL - && directory->details->activation_uri_read_state->file == file) { - directory->details->activation_uri_read_state->file = NULL; + if (directory->details->link_info_read_state != NULL + && directory->details->link_info_read_state->file == file) { + directory->details->link_info_read_state->file = NULL; changed = TRUE; } @@ -1474,9 +1524,8 @@ nautilus_async_destroying_file (NautilusFile *file) static gboolean lacks_directory_count (NautilusFile *file) { - return nautilus_file_is_directory (file) - && nautilus_file_should_show_directory_item_count (file) - && !file->details->directory_count_is_up_to_date; + return !file->details->directory_count_is_up_to_date + && nautilus_file_should_show_directory_item_count (file); } static gboolean @@ -1495,9 +1544,9 @@ wants_directory_count (const Request *request) static gboolean lacks_top_left (NautilusFile *file) { - return nautilus_file_should_get_top_left_text (file) - && nautilus_file_contains_text (file) - && !file->details->top_left_text_is_up_to_date; + return file->details->file_info_is_up_to_date && + !file->details->top_left_text_is_up_to_date + && nautilus_file_should_get_top_left_text (file); } static gboolean @@ -1522,8 +1571,7 @@ wants_info (const Request *request) static gboolean lacks_deep_count (NautilusFile *file) { - return nautilus_file_is_directory (file) - && file->details->deep_counts_status != NAUTILUS_REQUEST_DONE; + return file->details->deep_counts_status != NAUTILUS_REQUEST_DONE; } static gboolean @@ -1535,8 +1583,7 @@ wants_deep_count (const Request *request) static gboolean lacks_mime_list (NautilusFile *file) { - return nautilus_file_is_directory (file) - && !file->details->mime_list_is_up_to_date; + return !file->details->mime_list_is_up_to_date; } static gboolean @@ -1553,16 +1600,28 @@ wants_mime_list (const Request *request) } static gboolean -lacks_activation_uri (NautilusFile *file) -{ - return file->details->info != NULL - && !file->details->activation_uri_is_up_to_date; +lacks_link_info (NautilusFile *file) +{ + if (file->details->file_info_is_up_to_date && + !file->details->link_info_is_up_to_date) { + if ((nautilus_file_is_mime_type (file, "application/x-gmc-link") && + nautilus_file_is_in_desktop (file)) || + nautilus_file_is_nautilus_link (file) || + nautilus_file_is_directory (file)) { + return TRUE; + } else { + link_info_done (file->details->directory, file, NULL, NULL, NULL); + return FALSE; + } + } else { + return FALSE; + } } static gboolean -wants_activation_uri (const Request *request) +wants_link_info (const Request *request) { - return request->activation_uri; + return request->link_info; } @@ -1628,8 +1687,8 @@ request_is_satisfied (NautilusDirectory *directory, } } - if (request->activation_uri) { - if (has_problem (directory, file, lacks_activation_uri)) { + if (request->link_info) { + if (has_problem (directory, file, lacks_link_info)) { return FALSE; } } @@ -1845,7 +1904,7 @@ nautilus_directory_stop_monitoring_file_list (NautilusDirectory *directory) } static void -file_list_start (NautilusDirectory *directory) +file_list_start_or_stop (NautilusDirectory *directory) { if (nautilus_directory_is_anyone_monitoring_file_list (directory)) { start_monitoring_file_list (directory); @@ -1857,21 +1916,12 @@ file_list_start (NautilusDirectory *directory) void nautilus_file_invalidate_count_and_mime_list (NautilusFile *file) { - NautilusDirectory *parent_directory; + GList *attributes = NULL; - parent_directory = file->details->directory; - - if (parent_directory->details->count_file == file) { - directory_count_cancel (parent_directory); - } - if (parent_directory->details->mime_list_file == file) { - mime_list_cancel (parent_directory); - } - - file->details->directory_count_is_up_to_date = FALSE; - file->details->mime_list_is_up_to_date = FALSE; + attributes = g_list_prepend (attributes, NAUTILUS_FILE_ATTRIBUTE_DIRECTORY_ITEM_COUNT); + attributes = g_list_prepend (attributes, NAUTILUS_FILE_ATTRIBUTE_DIRECTORY_ITEM_MIME_TYPES); - nautilus_directory_async_state_changed (parent_directory); + nautilus_file_invalidate_attributes (file, attributes); } @@ -1927,6 +1977,7 @@ nautilus_directory_force_reload_internal (NautilusDirectory *directory, /* Start a new directory count. */ nautilus_directory_invalidate_count_and_mime_list (directory); + add_all_files_to_work_queue (directory); nautilus_directory_async_state_changed (directory); } @@ -1990,108 +2041,11 @@ is_needy (NautilusFile *file, return FALSE; } -static NautilusFile * -select_needy_file (NautilusDirectory *directory, - FileCheck check_missing, - RequestCheck check_wanted) -{ - GList *node, *node_2; - ReadyCallback *callback; - Monitor *monitor; - NautilusFile *file; - - /* Quick out if no one is interested. */ - for (node = directory->details->call_when_ready_list; - node != NULL; node = node->next) { - callback = node->data; - if ((* check_wanted) (&callback->request)) { - break; - } - } - if (node == NULL) { - for (node = directory->details->monitor_list; - node != NULL; node = node->next) { - monitor = node->data; - if ((* check_wanted) (&monitor->request)) { - break; - } - } - if (node == NULL) { - return NULL; - } - } - - /* Search for a file that has an unfulfilled request. */ - for (node = directory->details->file_list; - node != NULL; node = node->next) { - file = node->data; - if ((* check_missing) (file)) { - for (node_2 = directory->details->call_when_ready_list; - node_2 != NULL; node_2 = node_2->next) { - callback = node_2->data; - if ((callback->file == NULL || callback->file == file) - && (* check_wanted) (&callback->request)) { - break; - } - } - if (node_2 != NULL) { - return file; - } - for (node_2 = directory->details->monitor_list; - node_2 != NULL; node_2 = node_2->next) { - monitor = node_2->data; - if (monitor_includes_file (monitor, file) - && (* check_wanted) (&monitor->request)) { - break; - } - } - if (node_2 != NULL) { - return file; - } - } - } - - /* Finally, check the file for the directory itself. */ - file = directory->details->as_file; - if (file != NULL) { - if ((* check_missing) (file)) { - for (node_2 = directory->details->call_when_ready_list; - node_2 != NULL; node_2 = node_2->next) { - callback = node_2->data; - if (callback->file == file - && (* check_wanted) (&callback->request)) { - break; - } - } - if (node_2 != NULL) { - return file; - } - for (node_2 = directory->details->monitor_list; - node_2 != NULL; node_2 = node_2->next) { - monitor = node_2->data; - if (monitor->file == file - && (* check_wanted) (&monitor->request)) { - break; - } - } - if (node_2 != NULL) { - return file; - } - } - } - - return NULL; -} - static void -directory_count_start (NautilusDirectory *directory) +directory_count_stop (NautilusDirectory *directory) { NautilusFile *file; - char *uri; - /* If there's already a count in progress, check to be sure - * it's still wanted. - */ if (directory->details->count_in_progress != NULL) { file = directory->details->count_file; if (file != NULL) { @@ -2107,12 +2061,30 @@ directory_count_start (NautilusDirectory *directory) /* The count is not wanted, so stop it. */ directory_count_cancel (directory); } +} - /* Figure out which file to get a count for. */ - file = select_needy_file (directory, - should_get_directory_count_now, - wants_directory_count); - if (file == NULL) { +static void +directory_count_start (NautilusDirectory *directory, + NautilusFile *file) +{ + char *uri; + + if (directory->details->count_in_progress != NULL) { + return; + } + + if (!is_needy (file, + should_get_directory_count_now, + wants_directory_count)) { + return; + } + + if (!nautilus_file_is_directory (file)) { + file->details->directory_count_is_up_to_date = TRUE; + file->details->directory_count_failed = FALSE; + file->details->got_directory_count = FALSE; + + nautilus_directory_async_state_changed (directory); return; } @@ -2251,14 +2223,10 @@ deep_count_load (NautilusDirectory *directory, const char *uri) } static void -deep_count_start (NautilusDirectory *directory) +deep_count_stop (NautilusDirectory *directory) { NautilusFile *file; - char *uri; - /* If there's already a count in progress, check to be sure - * it's still wanted. - */ if (directory->details->deep_count_in_progress != NULL) { file = directory->details->deep_count_file; if (file != NULL) { @@ -2274,12 +2242,28 @@ deep_count_start (NautilusDirectory *directory) /* The count is not wanted, so stop it. */ deep_count_cancel (directory); } +} - /* Figure out which file to get a count for. */ - file = select_needy_file (directory, - lacks_deep_count, - wants_deep_count); - if (file == NULL) { +static void +deep_count_start (NautilusDirectory *directory, + NautilusFile *file) +{ + char *uri; + + if (directory->details->deep_count_in_progress != NULL) { + return; + } + + if (!is_needy (file, + lacks_deep_count, + wants_deep_count)) { + return; + } + + if (!nautilus_file_is_directory (file)) { + file->details->deep_counts_status = NAUTILUS_REQUEST_NOT_STARTED; + + nautilus_directory_async_state_changed (directory); return; } @@ -2386,14 +2370,10 @@ mime_list_load (NautilusDirectory *directory, const char *uri) } static void -mime_list_start (NautilusDirectory *directory) +mime_list_stop (NautilusDirectory *directory) { NautilusFile *file; - char *uri; - /* If there's already a count in progress, check to be sure - * it's still wanted. - */ if (directory->details->mime_list_in_progress != NULL) { file = directory->details->mime_list_file; if (file != NULL) { @@ -2409,12 +2389,34 @@ mime_list_start (NautilusDirectory *directory) /* The count is not wanted, so stop it. */ mime_list_cancel (directory); } +} + +static void +mime_list_start (NautilusDirectory *directory, + NautilusFile *file) +{ + char *uri; + + mime_list_stop (directory); + + if (directory->details->mime_list_in_progress != NULL) { + return; + } /* Figure out which file to get a mime list for. */ - file = select_needy_file (directory, - should_get_mime_list, - wants_mime_list); - if (file == NULL) { + if (!is_needy (file, + should_get_mime_list, + wants_mime_list)) { + return; + } + + if (!nautilus_file_is_directory (file)) { + g_list_free (file->details->mime_list); + file->details->mime_list_failed = FALSE; + file->details->got_directory_count = FALSE; + file->details->mime_list_is_up_to_date = TRUE; + + nautilus_directory_async_state_changed (directory); return; } @@ -2446,8 +2448,6 @@ top_left_read_done (NautilusDirectory *directory) g_assert (directory->details->top_left_read_state->handle == NULL); g_assert (NAUTILUS_IS_FILE (directory->details->top_left_read_state->file)); - directory->details->top_left_read_state->file->details->got_top_left_text = TRUE; - g_free (directory->details->top_left_read_state); directory->details->top_left_read_state = NULL; @@ -2466,10 +2466,7 @@ top_left_read_callback (GnomeVFSResult result, directory = NAUTILUS_DIRECTORY (callback_data); - nautilus_directory_ref (directory); - directory->details->top_left_read_state->handle = NULL; - directory->details->top_left_read_state->file->details->top_left_text_is_up_to_date = TRUE; changed_file = NULL; @@ -2479,21 +2476,16 @@ top_left_read_callback (GnomeVFSResult result, nautilus_extract_top_left_text (file_contents, bytes_read); directory->details->top_left_read_state->file->details->got_top_left_text = TRUE; - - changed_file = directory->details->top_left_read_state->file; - nautilus_file_ref (changed_file); - - g_free (file_contents); + } else { + g_free (directory->details->top_left_read_state->file->details->top_left_text); + directory->details->top_left_read_state->file->details->got_top_left_text = FALSE; } - - top_left_read_done (directory); - if (changed_file != NULL) { - nautilus_file_changed (changed_file); - nautilus_file_unref (changed_file); - } + g_free (file_contents); - nautilus_directory_unref (directory); + nautilus_file_changed (directory->details->top_left_read_state->file); + + top_left_read_done (directory); } static gboolean @@ -2509,14 +2501,10 @@ top_left_read_more_callback (GnomeVFSFileSize bytes_read, } static void -top_left_start (NautilusDirectory *directory) +top_left_stop (NautilusDirectory *directory) { NautilusFile *file; - char *uri; - /* If there's already a read in progress, check to be sure - * it's still wanted. - */ if (directory->details->top_left_read_state != NULL) { file = directory->details->top_left_read_state->file; if (file != NULL) { @@ -2532,12 +2520,31 @@ top_left_start (NautilusDirectory *directory) /* The top left is not wanted, so stop it. */ top_left_cancel (directory); } +} + +static void +top_left_start (NautilusDirectory *directory, + NautilusFile *file) +{ + char *uri; + + if (directory->details->top_left_read_state != NULL) { + return; + } /* Figure out which file to read the top left for. */ - file = select_needy_file (directory, - lacks_top_left, - wants_top_left); - if (file == NULL) { + if (!is_needy (file, + lacks_top_left, + wants_top_left)) { + return; + } + + if (!nautilus_file_contains_text (file)) { + g_free (file->details->top_left_text); + file->details->got_top_left_text = FALSE; + file->details->top_left_text_is_up_to_date = TRUE; + + nautilus_directory_async_state_changed (directory); return; } @@ -2615,16 +2622,10 @@ get_info_callback (GnomeVFSAsyncHandle *handle, } static void -file_info_start (NautilusDirectory *directory) +file_info_stop (NautilusDirectory *directory) { NautilusFile *file; - char *uri; - GnomeVFSURI *vfs_uri; - GList fake_list; - /* If there's already a file info fetch in progress, check to - * be sure it's still wanted. - */ if (directory->details->get_info_in_progress != NULL) { file = directory->details->get_info_file; if (file != NULL) { @@ -2638,27 +2639,42 @@ file_info_start (NautilusDirectory *directory) /* The info is not wanted, so stop it. */ file_info_cancel (directory); } +} + +static void +file_info_start (NautilusDirectory *directory, + NautilusFile *file) +{ + char *uri; + GnomeVFSURI *vfs_uri; + GList fake_list; + + file_info_stop (directory); + + if (directory->details->get_info_in_progress != NULL) { + return; + } - /* Figure out which file to get file info for. */ - do { - file = select_needy_file (directory, lacks_info, wants_info); - if (file == NULL) { - return; - } - - uri = nautilus_file_get_uri (file); - vfs_uri = gnome_vfs_uri_new (uri); - g_free (uri); - - if (vfs_uri == NULL) { - file->details->file_info_is_up_to_date = TRUE; - file->details->get_info_failed = TRUE; - file->details->get_info_error = GNOME_VFS_ERROR_INVALID_URI; - nautilus_file_changed (file); - } - } while (vfs_uri == NULL); + if (!is_needy (file, lacks_info, wants_info)) { + return; + } + + uri = nautilus_file_get_uri (file); + vfs_uri = gnome_vfs_uri_new (uri); + g_free (uri); + + /* If we can't even get info, fill in the info and go on. + */ + + if (vfs_uri == NULL) { + file->details->file_info_is_up_to_date = TRUE; + file->details->get_info_failed = TRUE; + file->details->get_info_error = GNOME_VFS_ERROR_INVALID_URI; + + nautilus_directory_async_state_changed (directory); + return; + } - /* Found one we need to get the info for. */ if (!async_job_start (directory, "file info")) { return; } @@ -2676,44 +2692,56 @@ file_info_start (NautilusDirectory *directory) gnome_vfs_uri_unref (vfs_uri); } + static void -activation_uri_done (NautilusDirectory *directory, - NautilusFile *file, - const char *uri) +link_info_done (NautilusDirectory *directory, + NautilusFile *file, + const char *uri, + const char *name, + const char *icon) { - file->details->activation_uri_is_up_to_date = TRUE; + file->details->link_info_is_up_to_date = TRUE; - file->details->got_activation_uri = TRUE; + file->details->got_link_info = TRUE; g_free (file->details->activation_uri); + g_free (file->details->custom_name); + g_free (file->details->custom_icon_uri); file->details->activation_uri = g_strdup (uri); + file->details->custom_name = g_strdup (name); + file->details->custom_icon_uri = g_strdup (icon); - nautilus_file_changed (file); - - async_job_end (directory, "activation URI"); nautilus_directory_async_state_changed (directory); } + static void -activation_uri_read_done (NautilusDirectory *directory, - const char *uri) +link_info_read_done (NautilusDirectory *directory, + const char *uri, + const char *name, + const char *icon) { NautilusFile *file; - file = directory->details->activation_uri_read_state->file; - g_free (directory->details->activation_uri_read_state); - directory->details->activation_uri_read_state = NULL; + file = directory->details->link_info_read_state->file; + g_free (directory->details->link_info_read_state); + directory->details->link_info_read_state = NULL; - activation_uri_done (directory, file, uri); + nautilus_file_ref (file); + link_info_done (directory, file, uri, name, icon); + nautilus_file_changed (file); + nautilus_file_unref (file); + async_job_end (directory, "link info"); } + static void -activation_uri_nautilus_link_read_callback (GnomeVFSResult result, - GnomeVFSFileSize bytes_read, - char *file_contents, - gpointer callback_data) +link_info_nautilus_link_read_callback (GnomeVFSResult result, + GnomeVFSFileSize bytes_read, + char *file_contents, + gpointer callback_data) { NautilusDirectory *directory; - char *buffer, *uri; + char *buffer, *uri, *name, *icon; directory = NAUTILUS_DIRECTORY (callback_data); @@ -2724,29 +2752,39 @@ activation_uri_nautilus_link_read_callback (GnomeVFSResult result, /* FIXME bugzilla.gnome.org 42433: We should report this error to the user. */ g_free (file_contents); uri = NULL; + name = NULL; + icon = NULL; } else { /* The gnome-xml parser requires a zero-terminated array. */ buffer = g_realloc (file_contents, bytes_read + 1); buffer[bytes_read] = '\0'; uri = nautilus_link_get_link_uri_given_file_contents (buffer, bytes_read); + name = nautilus_link_get_link_name_given_file_contents (buffer, bytes_read); + icon = nautilus_link_get_link_icon_given_file_contents (buffer, bytes_read); g_free (buffer); } - activation_uri_read_done (directory, uri); + link_info_read_done (directory, uri, name, icon); g_free (uri); + g_free (name); + g_free (icon); nautilus_directory_unref (directory); } + + + static void -activation_uri_gmc_link_read_callback (GnomeVFSResult result, - GnomeVFSFileSize bytes_read, - char *file_contents, - gpointer callback_data) +link_info_gmc_link_read_callback (GnomeVFSResult result, + GnomeVFSFileSize bytes_read, + char *file_contents, + gpointer callback_data) { NautilusDirectory *directory; - char *end_of_line, *uri; - + char *end_of_line, *uri, *name, *path, *icon, *icon_path; + int size, res; + directory = NAUTILUS_DIRECTORY (callback_data); nautilus_directory_ref (directory); @@ -2755,6 +2793,8 @@ activation_uri_gmc_link_read_callback (GnomeVFSResult result, if (result != GNOME_VFS_OK || !eel_str_has_prefix (file_contents, "URL: ")) { /* FIXME bugzilla.gnome.org 42433: We should report this error to the user. */ uri = NULL; + name = NULL; + icon = NULL; } else { /* Make sure we don't run off the end of the buffer. */ end_of_line = memchr (file_contents, '\n', bytes_read); @@ -2763,19 +2803,51 @@ activation_uri_gmc_link_read_callback (GnomeVFSResult result, } else { uri = g_strndup (file_contents, bytes_read); } + + path = gnome_vfs_get_local_path_from_uri (uri); + + if (path != NULL) { + /* FIXME: this gnome_metata_get call is synchronous, but better to + * have it here where the results will at least be cached than in + * nautilus_file_get_name. + */ + res = gnome_metadata_get (path, "icon-name", &size, &name); + } else { + res = -1; + } + + if (res == 0) { + name = NULL; + } + + if (path != NULL) { + res = gnome_metadata_get (path, "icon-filename", &size, &icon_path); + } else { + res = -1; + } + + if (res == 0 && icon_path != NULL) { + icon = gnome_vfs_get_uri_from_local_path (icon_path); + g_free (icon_path); + } else { + icon = NULL; + } + } g_free (file_contents); - activation_uri_read_done (directory, uri ? uri + 5 : NULL); + link_info_read_done (directory, uri ? uri + 5 : NULL, name, icon); g_free (uri); + g_free (name); + g_free (icon); nautilus_directory_unref (directory); } static gboolean -activation_uri_gmc_link_read_more_callback (GnomeVFSFileSize bytes_read, - const char *file_contents, - gpointer callback_data) +link_info_gmc_link_read_more_callback (GnomeVFSFileSize bytes_read, + const char *file_contents, + gpointer callback_data) { g_assert (NAUTILUS_IS_DIRECTORY (callback_data)); @@ -2783,96 +2855,165 @@ activation_uri_gmc_link_read_more_callback (GnomeVFSFileSize bytes_read, return bytes_read < 512; } +static char * +make_dot_directory_uri (const char *uri) +{ + char *dot_directory_uri; + GnomeVFSURI *vfs_uri; + GnomeVFSURI *dot_dir_vfs_uri; + + /* FIXME: what we really need is a uri_append_file_name call + * that works on strings, so we can avoid the VFS parsing step. + */ + + vfs_uri = gnome_vfs_uri_new (uri); + if (vfs_uri == NULL) { + return NULL; + } + + dot_dir_vfs_uri = gnome_vfs_uri_append_file_name (vfs_uri, ".directory"); + dot_directory_uri = gnome_vfs_uri_to_string (dot_dir_vfs_uri, GNOME_VFS_URI_HIDE_NONE); + + gnome_vfs_uri_unref (vfs_uri); + gnome_vfs_uri_unref (dot_dir_vfs_uri); + + return dot_directory_uri; +} + + static void -activation_uri_start (NautilusDirectory *directory) +link_info_stop (NautilusDirectory *directory) { NautilusFile *file; - char *mime_type, *uri; - gboolean gmc_style_link, nautilus_style_link; - /* If there's already a activation URI read in progress, check - * to be sure it's still wanted. - */ - if (directory->details->activation_uri_read_state != NULL) { - file = directory->details->activation_uri_read_state->file; + if (directory->details->link_info_read_state != NULL) { + file = directory->details->link_info_read_state->file; + if (file != NULL) { g_assert (NAUTILUS_IS_FILE (file)); g_assert (file->details->directory == directory); if (is_needy (file, - lacks_activation_uri, - wants_activation_uri)) { + lacks_link_info, + wants_link_info)) { return; } } - /* The count is not wanted, so stop it. */ - activation_uri_cancel (directory); + /* The link info is not wanted, so stop it. */ + link_info_cancel (directory); } +} - /* Figure out which file to get activation_uri for. */ - file = select_needy_file (directory, - lacks_activation_uri, - wants_activation_uri); - if (file == NULL) { + +static void +link_info_start (NautilusDirectory *directory, + NautilusFile *file) +{ + char *uri, *dot_directory_uri = NULL; + gboolean gmc_style_link, nautilus_style_link, is_directory; + + if (directory->details->link_info_read_state != NULL) { return; } - if (!async_job_start (directory, "activation URI")) { + if (!is_needy (file, + lacks_link_info, + wants_link_info)) { return; } /* Figure out if it is a link. */ - mime_type = nautilus_file_get_mime_type (file); - gmc_style_link = eel_strcasecmp (mime_type, "application/x-gmc-link") == 0; - g_free (mime_type); + gmc_style_link = nautilus_file_is_mime_type (file, "application/x-gmc-link") && + nautilus_file_is_in_desktop (file); nautilus_style_link = nautilus_file_is_nautilus_link (file); + is_directory = nautilus_file_is_directory (file); + + uri = nautilus_file_get_uri (file); + + if (is_directory) { + dot_directory_uri = make_dot_directory_uri (uri); + } /* If it's not a link we are done. If it is, we need to read it. */ - if (!(gmc_style_link || nautilus_style_link)) { - activation_uri_done (directory, file, NULL); + if (!(gmc_style_link || nautilus_style_link || (is_directory && dot_directory_uri != NULL) )) { + link_info_done (directory, file, NULL, NULL, NULL); } else { - directory->details->activation_uri_read_state = g_new0 (ActivationURIReadState, 1); - directory->details->activation_uri_read_state->file = file; - uri = nautilus_file_get_uri (file); + if (!async_job_start (directory, "link info")) { + g_free (dot_directory_uri); + g_free (uri); + return; + } + + directory->details->link_info_read_state = g_new0 (LinkInfoReadState, 1); + directory->details->link_info_read_state->file = file; if (gmc_style_link) { - directory->details->activation_uri_read_state->handle = eel_read_file_async + directory->details->link_info_read_state->handle = eel_read_file_async (uri, - activation_uri_gmc_link_read_callback, - activation_uri_gmc_link_read_more_callback, + link_info_gmc_link_read_callback, + link_info_gmc_link_read_more_callback, directory); + } else if (is_directory) { + directory->details->link_info_read_state->handle = eel_read_entire_file_async + (dot_directory_uri, + link_info_nautilus_link_read_callback, + directory); + g_free (dot_directory_uri); } else { - directory->details->activation_uri_read_state->handle = eel_read_entire_file_async + directory->details->link_info_read_state->handle = eel_read_entire_file_async (uri, - activation_uri_nautilus_link_read_callback, + link_info_nautilus_link_read_callback, directory); } - g_free (uri); } + g_free (uri); } static void start_or_stop_io (NautilusDirectory *directory) { - /* Start or stop getting file info. */ - file_info_start (directory); + NautilusFile *file; /* Start or stop reading files. */ - file_list_start (directory); - - /* Start or stop getting directory counts. */ - directory_count_start (directory); - deep_count_start (directory); + file_list_start_or_stop (directory); + + /* Stop any no longer wanted attribute fetches. */ + file_info_stop (directory); + directory_count_stop (directory); + deep_count_stop (directory); + mime_list_stop (directory); + top_left_stop (directory); + link_info_stop (directory); + + /* Take files that are all done off the queue. */ + while (!nautilus_file_queue_is_empty (directory->details->high_priority_queue)) { + file = nautilus_file_queue_head (directory->details->high_priority_queue); + + if (file_needs_high_priority_work_done (directory, file)) { + /* Start getting attributes if possible */ + file_info_start (directory, file); + link_info_start (directory, file); + return; + } else { + move_file_to_low_priority_queue (directory, file); + } + } - /* Start or stop getting mime lists. */ - mime_list_start (directory); + /* High priority queue must be empty */ + while (!nautilus_file_queue_is_empty (directory->details->low_priority_queue)) { + file = nautilus_file_queue_head (directory->details->low_priority_queue); - /* Start or stop getting top left pieces of files. */ - top_left_start (directory); + if (file_needs_low_priority_work_done (directory, file)) { + /* Start getting attributes if possible */ + directory_count_start (directory, file); + deep_count_start (directory, file); + mime_list_start (directory, file); + top_left_start (directory, file); + return; + } else { + nautilus_directory_remove_file_from_work_queue (directory, file); - /* Start or stop getting activation URIs, which includes - * reading the contents of Nautilus and GMC link files. - */ - activation_uri_start (directory); + } + } } /* Call this when the monitor or call when ready list changes, @@ -2916,11 +3057,11 @@ void nautilus_directory_cancel (NautilusDirectory *directory) { /* Arbitrary order (kept alphabetical). */ - activation_uri_cancel (directory); deep_count_cancel (directory); directory_count_cancel (directory); file_info_cancel (directory); file_list_cancel (directory); + link_info_cancel (directory); mime_list_cancel (directory); top_left_cancel (directory); @@ -2980,15 +3121,16 @@ cancel_file_info_for_file (NautilusDirectory *directory, } static void -cancel_activation_uri_for_file (NautilusDirectory *directory, +cancel_link_info_for_file (NautilusDirectory *directory, NautilusFile *file) { - if (directory->details->activation_uri_read_state != NULL && - directory->details->activation_uri_read_state->file == file) { - activation_uri_cancel (directory); + if (directory->details->link_info_read_state != NULL && + directory->details->link_info_read_state->file == file) { + link_info_cancel (directory); } } + static void cancel_loading_attributes (NautilusDirectory *directory, GList *file_attributes) @@ -3013,8 +3155,8 @@ cancel_loading_attributes (NautilusDirectory *directory, if (request.file_info) { file_info_cancel (directory); } - if (request.activation_uri) { - file_info_cancel (directory); + if (request.link_info) { + link_info_cancel (directory); } /* FIXME bugzilla.gnome.org 45064: implement cancelling metadata when we @@ -3048,8 +3190,8 @@ nautilus_directory_cancel_loading_file_attributes (NautilusDirectory *directory, if (request.file_info) { cancel_file_info_for_file (directory, file); } - if (request.activation_uri) { - cancel_activation_uri_for_file (directory, file); + if (request.link_info) { + cancel_link_info_for_file (directory, file); } /* FIXME bugzilla.gnome.org 45064: implement cancelling metadata when we @@ -3057,3 +3199,106 @@ nautilus_directory_cancel_loading_file_attributes (NautilusDirectory *directory, nautilus_directory_async_state_changed (directory); } + + + +static gboolean +file_needs_high_priority_work_done (NautilusDirectory *directory, + NautilusFile *file) +{ + if (is_needy (file, lacks_info, wants_info)) { + return TRUE; + } + + if (is_needy (file, lacks_link_info, wants_link_info)) { + return TRUE; + } + + return FALSE; +} + +static gboolean +file_needs_low_priority_work_done (NautilusDirectory *directory, + NautilusFile *file) +{ + if (is_needy (file, lacks_directory_count, wants_directory_count)) { + return TRUE; + } + + if (is_needy (file, lacks_deep_count, wants_deep_count)) { + return TRUE; + } + + if (is_needy (file, lacks_mime_list, wants_mime_list)) { + return TRUE; + } + + if (is_needy (file, lacks_top_left, wants_top_left)) { + return TRUE; + } + + return FALSE; +} + +static gboolean +file_needs_work_done (NautilusDirectory *directory, + NautilusFile *file) +{ + return (file_needs_high_priority_work_done (directory, file) || + file_needs_low_priority_work_done (directory, file)); +} + + +void +nautilus_directory_add_file_to_work_queue (NautilusDirectory *directory, + NautilusFile *file) +{ + if (!file_needs_work_done (directory, file)) { + return; + } + + nautilus_file_queue_enqueue (directory->details->high_priority_queue, + file); +} + + +static void +add_all_files_to_work_queue (NautilusDirectory *directory) +{ + GList *node; + NautilusFile *file; + + for (node = directory->details->file_list; node != NULL; node = node->next) { + file = NAUTILUS_FILE (node->data); + + nautilus_directory_add_file_to_work_queue (directory, file); + } +} + +void +nautilus_directory_remove_file_from_work_queue (NautilusDirectory *directory, + NautilusFile *file) +{ + nautilus_file_queue_remove (directory->details->high_priority_queue, + file); + nautilus_file_queue_remove (directory->details->low_priority_queue, + file); +} + + +static void +move_file_to_low_priority_queue (NautilusDirectory *directory, + NautilusFile *file) +{ + if (!file_needs_low_priority_work_done (directory, file)) { + nautilus_file_queue_remove (directory->details->high_priority_queue, + file); + return; + } + + /* Must add before removing to avoid ref underflow */ + nautilus_file_queue_enqueue (directory->details->low_priority_queue, + file); + nautilus_file_queue_remove (directory->details->high_priority_queue, + file); +} diff --git a/libnautilus-private/nautilus-directory-private.h b/libnautilus-private/nautilus-directory-private.h index b7742d4d1..0546990cf 100644 --- a/libnautilus-private/nautilus-directory-private.h +++ b/libnautilus-private/nautilus-directory-private.h @@ -27,6 +27,7 @@ #include "nautilus-file.h" #include "nautilus-metafile-server.h" #include "nautilus-monitor.h" +#include "nautilus-file-queue.h" #include <eel/eel-vfs-extensions.h> #include <libxml/tree.h> #include <libgnomevfs/gnome-vfs-file-info.h> @@ -35,7 +36,7 @@ #include <libgnomevfs/gnome-vfs-utils.h> #include <libnautilus/nautilus-idle-queue.h> -typedef struct ActivationURIReadState ActivationURIReadState; +typedef struct LinkInfoReadState LinkInfoReadState; typedef struct TopLeftTextReadState TopLeftTextReadState; typedef struct FileMonitors FileMonitors; @@ -50,6 +51,10 @@ struct NautilusDirectoryDetails GList *file_list; GHashTable *file_hash; + /* Queues of files needing some I/O done. */ + NautilusFileQueue *high_priority_queue; + NautilusFileQueue *low_priority_queue; + /* These lists are going to be pretty short. If we think they * are going to get big, we can use hash tables instead. */ @@ -97,14 +102,15 @@ struct NautilusDirectoryDetails GnomeVFSAsyncHandle *get_info_in_progress; TopLeftTextReadState *top_left_read_state; - ActivationURIReadState *activation_uri_read_state; + + LinkInfoReadState *link_info_read_state; GList *file_operations_in_progress; /* list of FileOperation * */ }; /* A request for information about one or more files. */ typedef struct { - gboolean activation_uri; + gboolean link_info; gboolean deep_count; gboolean directory_count; gboolean file_info; @@ -205,6 +211,12 @@ void nautilus_directory_end_file_name_change (NautilusD GList *node); void nautilus_directory_moved (const char *from_uri, const char *to_uri); +/* Interface to the work queue. */ + +void nautilus_directory_add_file_to_work_queue (NautilusDirectory *directory, + NautilusFile *file); +void nautilus_directory_remove_file_from_work_queue (NautilusDirectory *directory, + NautilusFile *file); /* debugging functions */ int nautilus_directory_number_outstanding (void); diff --git a/libnautilus-private/nautilus-directory.c b/libnautilus-private/nautilus-directory.c index da5a91cc8..8fd819f59 100644 --- a/libnautilus-private/nautilus-directory.c +++ b/libnautilus-private/nautilus-directory.c @@ -128,6 +128,8 @@ nautilus_directory_initialize (gpointer object, gpointer klass) directory->details = g_new0 (NautilusDirectoryDetails, 1); directory->details->file_hash = g_hash_table_new (g_str_hash, g_str_equal); + directory->details->high_priority_queue = nautilus_file_queue_new (); + directory->details->low_priority_queue = nautilus_file_queue_new (); directory->details->idle_queue = nautilus_idle_queue_new (); } @@ -195,6 +197,8 @@ nautilus_directory_destroy (GtkObject *object) } g_assert (directory->details->file_list == NULL); g_hash_table_destroy (directory->details->file_hash); + nautilus_file_queue_destroy (directory->details->high_priority_queue); + nautilus_file_queue_destroy (directory->details->low_priority_queue); nautilus_idle_queue_destroy (directory->details->idle_queue); g_assert (directory->details->directory_load_in_progress == NULL); g_assert (directory->details->count_in_progress == NULL); @@ -309,7 +313,8 @@ nautilus_directory_make_uri_canonical (const char *uri) * created. (See bugzilla.gnome.org 43322 for an example.) */ canonical = eel_str_strip_trailing_chr (canonical_maybe_trailing_slash, '/'); - if (strcmp (canonical, canonical_maybe_trailing_slash) != 0) { + if (strcmp (canonical, canonical_maybe_trailing_slash) != 0 && + strcmp (canonical, "favorites:") != 0) { /* If some trailing '/' were stripped, there's the possibility, * that we stripped away all the '/' from a uri that has only * '/' characters. If you change this code, check to make sure @@ -561,6 +566,7 @@ nautilus_directory_add_file (NautilusDirectory *directory, NautilusFile *file) /* Ref if we are monitoring. */ if (nautilus_directory_is_file_list_monitored (directory)) { nautilus_file_ref (file); + nautilus_directory_add_file_to_work_queue (directory, file); } } @@ -583,6 +589,8 @@ nautilus_directory_remove_file (NautilusDirectory *directory, NautilusFile *file (directory->details->file_list, node); g_list_free_1 (node); + nautilus_directory_remove_file_from_work_queue (directory, file); + if (!file->details->unconfirmed) { directory->details->confirmed_file_count--; } diff --git a/libnautilus-private/nautilus-file-attributes.h b/libnautilus-private/nautilus-file-attributes.h index c71e8caf6..df28ad33b 100644 --- a/libnautilus-private/nautilus-file-attributes.h +++ b/libnautilus-private/nautilus-file-attributes.h @@ -29,16 +29,17 @@ * interest in changes to the attributes or when waiting for them. */ -#define NAUTILUS_FILE_ATTRIBUTE_ACTIVATION_URI "activation URI" -#define NAUTILUS_FILE_ATTRIBUTE_CAPABILITIES "capabilities" -#define NAUTILUS_FILE_ATTRIBUTE_CUSTOM_ICON "custom icon" -#define NAUTILUS_FILE_ATTRIBUTE_DEEP_COUNTS "deep counts" -#define NAUTILUS_FILE_ATTRIBUTE_DIRECTORY_ITEM_COUNT "directory item count" -#define NAUTILUS_FILE_ATTRIBUTE_DIRECTORY_ITEM_MIME_TYPES "directory item MIME types" -#define NAUTILUS_FILE_ATTRIBUTE_FILE_TYPE "file type" -#define NAUTILUS_FILE_ATTRIBUTE_IS_DIRECTORY "is directory" -#define NAUTILUS_FILE_ATTRIBUTE_METADATA "metadata" -#define NAUTILUS_FILE_ATTRIBUTE_MIME_TYPE "MIME type" -#define NAUTILUS_FILE_ATTRIBUTE_TOP_LEFT_TEXT "top left text" +#define NAUTILUS_FILE_ATTRIBUTE_ACTIVATION_URI "activation URI" +#define NAUTILUS_FILE_ATTRIBUTE_CAPABILITIES "capabilities" +#define NAUTILUS_FILE_ATTRIBUTE_CUSTOM_ICON "custom icon" +#define NAUTILUS_FILE_ATTRIBUTE_DEEP_COUNTS "deep counts" +#define NAUTILUS_FILE_ATTRIBUTE_DIRECTORY_ITEM_COUNT "directory item count" +#define NAUTILUS_FILE_ATTRIBUTE_DIRECTORY_ITEM_MIME_TYPES "directory item MIME types" +#define NAUTILUS_FILE_ATTRIBUTE_FILE_TYPE "file type" +#define NAUTILUS_FILE_ATTRIBUTE_IS_DIRECTORY "is directory" +#define NAUTILUS_FILE_ATTRIBUTE_METADATA "metadata" +#define NAUTILUS_FILE_ATTRIBUTE_MIME_TYPE "MIME type" +#define NAUTILUS_FILE_ATTRIBUTE_TOP_LEFT_TEXT "top left text" +#define NAUTILUS_FILE_ATTRIBUTE_CUSTOM_NAME "custom name" #endif /* NAUTILUS_FILE_ATTRIBUTES_H */ diff --git a/libnautilus-private/nautilus-file-private.h b/libnautilus-private/nautilus-file-private.h index 3df51e700..0e09a4bbf 100644 --- a/libnautilus-private/nautilus-file-private.h +++ b/libnautilus-private/nautilus-file-private.h @@ -67,6 +67,10 @@ struct NautilusFileDetails GList *mime_list; /* If this is a directory, the list of MIME types in it. */ char *top_left_text; + + /* Info you might get from a link (.desktop, .directory or nautilus link) */ + char *custom_name; + char *custom_icon_uri; char *activation_uri; /* The following is for file operations in progress. Since @@ -109,8 +113,8 @@ struct NautilusFileDetails eel_boolean_bit got_top_left_text : 1; eel_boolean_bit top_left_text_is_up_to_date : 1; - eel_boolean_bit got_activation_uri : 1; - eel_boolean_bit activation_uri_is_up_to_date : 1; + eel_boolean_bit got_link_info : 1; + eel_boolean_bit link_info_is_up_to_date : 1; }; NautilusFile *nautilus_file_new_from_info (NautilusDirectory *directory, diff --git a/libnautilus-private/nautilus-file-queue.c b/libnautilus-private/nautilus-file-queue.c new file mode 100644 index 000000000..a7ea8fdf2 --- /dev/null +++ b/libnautilus-private/nautilus-file-queue.c @@ -0,0 +1,125 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- + + Copyright (C) 2001 Maciej Stachowiak + + This program 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. + + This program 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + Author: Maciej Stachowiak <mjs@noisehavoc.org> +*/ + +#include <config.h> +#include "nautilus-file-queue.h" + +#include <glib.h> + +struct NautilusFileQueue { + GList *head; + GList *tail; + GHashTable *item_to_link_map; +}; + +NautilusFileQueue * +nautilus_file_queue_new (void) +{ + NautilusFileQueue *queue; + + queue = g_new0 (NautilusFileQueue, 1); + queue->item_to_link_map = g_hash_table_new (g_direct_hash, g_direct_equal); + + return queue; +} + +void +nautilus_file_queue_destroy (NautilusFileQueue *queue) +{ + g_hash_table_destroy (queue->item_to_link_map); + nautilus_file_list_free (queue->head); + g_free (queue); +} + +void +nautilus_file_queue_enqueue (NautilusFileQueue *queue, + NautilusFile *file) +{ + if (g_hash_table_lookup (queue->item_to_link_map, file) != NULL) { + /* It's already on the queue. */ + return; + } + + if (queue->tail == NULL) { + queue->head = g_list_append (NULL, file); + queue->tail = queue->head; + } else { + g_list_append (queue->tail, file); + queue->tail = queue->tail->next; + } + + nautilus_file_ref (file); + g_hash_table_insert (queue->item_to_link_map, file, queue->tail); +} + +NautilusFile * +nautilus_file_queue_dequeue (NautilusFileQueue *queue) +{ + NautilusFile *file; + + file = nautilus_file_queue_head (queue); + nautilus_file_queue_remove (queue, file); + + return file; +} + + +void +nautilus_file_queue_remove (NautilusFileQueue *queue, + NautilusFile *file) +{ + GList *link; + + link = g_hash_table_lookup (queue->item_to_link_map, file); + + if (link == NULL) { + /* It's not on the queue */ + return; + } + + if (link == queue->tail) { + /* Need to special-case removing the tail. */ + queue->tail = queue->tail->prev; + } + + queue->head = g_list_remove_link (queue->head, link); + g_list_free (link); + g_hash_table_remove (queue->item_to_link_map, file); + + nautilus_file_unref (file); +} + +NautilusFile * +nautilus_file_queue_head (NautilusFileQueue *queue) +{ + if (queue->head == NULL) { + return NULL; + } + + return NAUTILUS_FILE (queue->head->data); +} + +gboolean +nautilus_file_queue_is_empty (NautilusFileQueue *queue) +{ + return (queue->head == NULL); +} diff --git a/libnautilus-private/nautilus-file-queue.h b/libnautilus-private/nautilus-file-queue.h new file mode 100644 index 000000000..92331d46f --- /dev/null +++ b/libnautilus-private/nautilus-file-queue.h @@ -0,0 +1,52 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- + + Copyright (C) 2001 Maciej Stachowiak + + This program 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. + + This program 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + Author: Maciej Stachowiak <mjs@noisehavoc.org> +*/ + +#ifndef NAUTILUS_FILE_QUEUE_H +#define NAUTILUS_FILE_QUEUE_H + +#include <libnautilus-private/nautilus-file.h> + +typedef struct NautilusFileQueue NautilusFileQueue; + +NautilusFileQueue *nautilus_file_queue_new (void); +void nautilus_file_queue_destroy (NautilusFileQueue *queue); + +/* Add a file to the tail of the queue, unless it's already in the queue */ +void nautilus_file_queue_enqueue (NautilusFileQueue *queue, + NautilusFile *file); + +/* Return the file at the head of the queue after removing it from the + * queue. This is dangerous unless you have another ref to the file, + * since it will unref it. + */ +NautilusFile * nautilus_file_queue_dequeue (NautilusFileQueue *queue); + +/* Remove a file from an arbitrary point in the queue in constant time. */ +void nautilus_file_queue_remove (NautilusFileQueue *queue, + NautilusFile *file); + +/* Get the file at the head of the queue without removing or unrefing it. */ +NautilusFile * nautilus_file_queue_head (NautilusFileQueue *queue); + +gboolean nautilus_file_queue_is_empty (NautilusFileQueue *queue); + +#endif /* NAUTILUS_FILE_CHANGES_QUEUE_H */ diff --git a/libnautilus-private/nautilus-file.c b/libnautilus-private/nautilus-file.c index 918c34af6..1f9208b25 100644 --- a/libnautilus-private/nautilus-file.c +++ b/libnautilus-private/nautilus-file.c @@ -50,7 +50,6 @@ #include <libgnome/gnome-dentry.h> #include <libgnome/gnome-i18n.h> #include <libgnome/gnome-metadata.h> -#include <libgnome/gnome-mime-info.h> #include <libgnome/gnome-mime.h> #include <libgnomevfs/gnome-vfs-file-info.h> #include <libgnomevfs/gnome-vfs-mime-handlers.h> @@ -449,6 +448,8 @@ destroy (GtkObject *object) gnome_vfs_file_info_unref (file->details->info); } g_free (file->details->top_left_text); + g_free (file->details->custom_name); + g_free (file->details->custom_icon_uri); g_free (file->details->activation_uri); g_free (file->details->compare_by_emblem_cache); @@ -2251,81 +2252,18 @@ nautilus_file_set_integer_metadata (NautilusFile *file, metadata); } - - - char * nautilus_file_get_name (NautilusFile *file) { char *name; - GnomeDesktopEntry *entry; - char *path, *uri; - char *caption; - int size, res; if (file == NULL) { return NULL; } g_return_val_if_fail (NAUTILUS_IS_FILE (file), NULL); - - /* FIXME: It's very bad to do this I/O here. For one thing, - * this means that a file's name can change when the MIME - * type is fetched. For another, it means that there's I/O - * done every single time nautilus_file_get_name is called. - * This goes against the design of NautilusFile. The proper - * way to do this is to do this I/O in an async. way when - * the object is created, but at the very least we need to - * change this so it only does the I/O once! - */ - if (nautilus_file_is_mime_type (file, "application/x-gnome-app-info")) { - uri = nautilus_file_get_uri (file); - path = gnome_vfs_get_local_path_from_uri (uri); - - name = NULL; - if (path != NULL) { - entry = gnome_desktop_entry_load (path); - if (entry != NULL) { - name = g_strdup (entry->name); - gnome_desktop_entry_free (entry); - } - } - - g_free (path); - g_free (uri); - - if (name != NULL) { - return name; - } - } - - /* FIXME: It's very bad to do this I/O here. For one thing, - * this means that a file's name can change when the MIME - * type is fetched. For another, it means that there's I/O - * done every single time nautilus_file_get_name is called. - * This goes against the design of NautilusFile. The proper - * way to do this is to do this I/O in an async. way when - * the object is created, but at the very least we need to - * change this so it only does the I/O once! - */ - /* Desktop directories contain special "URL" files, handle - * those by using the gnome metadata caption. - */ - if (nautilus_file_is_gmc_url (file)) { - uri = nautilus_file_get_uri (file); - path = gnome_vfs_get_local_path_from_uri (uri); - - if (path != NULL) { - res = gnome_metadata_get (path, "icon-caption", &size, &caption); - } else { - res = -1; - } - - g_free (path); - g_free (uri); - - if (res == 0 && caption != NULL) { - return caption; - } + + if (file->details->got_link_info && file->details->custom_name != NULL) { + return g_strdup (file->details->custom_name); } name = gnome_vfs_unescape_string (file->details->relative_uri, "/"); @@ -2375,7 +2313,7 @@ nautilus_file_get_activation_uri (NautilusFile *file) { g_return_val_if_fail (NAUTILUS_IS_FILE (file), NULL); - if (!file->details->got_activation_uri) { + if (!file->details->got_link_info) { return NULL; } return file->details->activation_uri == NULL @@ -2383,6 +2321,26 @@ nautilus_file_get_activation_uri (NautilusFile *file) : g_strdup (file->details->activation_uri); } +char * +nautilus_file_get_custom_icon_uri (NautilusFile *file) +{ + char *uri; + + g_return_val_if_fail (NAUTILUS_IS_FILE (file), NULL); + + uri = NULL; + + /* Metadata takes precedence */ + uri = nautilus_file_get_metadata (file, NAUTILUS_METADATA_KEY_CUSTOM_ICON, NULL); + + if (uri == NULL && file->details->got_link_info) { + uri = g_strdup (file->details->custom_icon_uri); + } + + return uri; +} + + /* Return the actual uri associated with the passed-in file. */ char * nautilus_file_get_uri (NautilusFile *file) @@ -4469,7 +4427,8 @@ nautilus_file_get_symbolic_link_target_path (NautilusFile *file) /** * nautilus_file_is_nautilus_link * - * Check if this file is a nautilus link. + * Check if this file is a "nautilus link", meaning a historical + * nautilus xml link file or a desktop file. * @file: NautilusFile representing the file in question. * * Returns: True if the file is a nautilus link. @@ -4478,7 +4437,8 @@ nautilus_file_get_symbolic_link_target_path (NautilusFile *file) gboolean nautilus_file_is_nautilus_link (NautilusFile *file) { - return nautilus_file_is_mime_type (file, "application/x-nautilus-link"); + return nautilus_file_is_mime_type (file, "application/x-nautilus-link") || + nautilus_file_is_mime_type (file, "application/x-gnome-app-info"); } /** @@ -4552,20 +4512,17 @@ nautilus_file_get_file_info_result (NautilusFile *file) gboolean nautilus_file_contains_text (NautilusFile *file) { - char *mime_type; - gboolean contains_text; - if (file == NULL) { return FALSE; } g_return_val_if_fail (NAUTILUS_IS_FILE (file), FALSE); - - mime_type = nautilus_file_get_mime_type (file); - contains_text = eel_istr_has_prefix (mime_type, "text/"); - g_free (mime_type); - - return contains_text; + + if (file->details->info == NULL || file->details->info->mime_type == NULL) { + return FALSE; + } + + return eel_istr_has_prefix (file->details->info->mime_type, "text/"); } /** @@ -4875,12 +4832,11 @@ invalidate_file_info (NautilusFile *file) } static void -invalidate_activation_uri (NautilusFile *file) +invalidate_link_info (NautilusFile *file) { - file->details->activation_uri_is_up_to_date = FALSE; + file->details->link_info_is_up_to_date = FALSE; } - void nautilus_file_invalidate_attributes_internal (NautilusFile *file, GList *file_attributes) @@ -4908,8 +4864,8 @@ nautilus_file_invalidate_attributes_internal (NautilusFile *file, if (request.top_left_text) { invalidate_top_left_text (file); } - if (request.activation_uri) { - invalidate_activation_uri (file); + if (request.link_info) { + invalidate_link_info (file); } /* FIXME bugzilla.gnome.org 45075: implement invalidating metadata */ @@ -4935,6 +4891,8 @@ nautilus_file_invalidate_attributes (NautilusFile *file, /* Actually invalidate the values */ nautilus_file_invalidate_attributes_internal (file, file_attributes); + + nautilus_directory_add_file_to_work_queue (file->details->directory, file); /* Kick off I/O if necessary */ nautilus_directory_async_state_changed (file->details->directory); @@ -4958,6 +4916,7 @@ nautilus_file_get_all_attributes (void) attributes = g_list_prepend (attributes, NAUTILUS_FILE_ATTRIBUTE_METADATA); attributes = g_list_prepend (attributes, NAUTILUS_FILE_ATTRIBUTE_MIME_TYPE); attributes = g_list_prepend (attributes, NAUTILUS_FILE_ATTRIBUTE_TOP_LEFT_TEXT); + attributes = g_list_prepend (attributes, NAUTILUS_FILE_ATTRIBUTE_CUSTOM_NAME); return attributes; } diff --git a/libnautilus-private/nautilus-file.h b/libnautilus-private/nautilus-file.h index b7b034e74..e9bed7db8 100644 --- a/libnautilus-private/nautilus-file.h +++ b/libnautilus-private/nautilus-file.h @@ -290,6 +290,9 @@ GList *nautilus_file_list_filter_hidden_and_backup (GList */ char * nautilus_file_get_activation_uri (NautilusFile *file); +/* Get custom icon (if specified by metadata or link contents) */ +char * nautilus_file_get_custom_icon_uri (NautilusFile *file); + /* Convenience functions for dealing with a list of NautilusFile objects that each have a ref. * These are just convenient names for functions that work on lists of GtkObject *. */ diff --git a/libnautilus-private/nautilus-icon-container.c b/libnautilus-private/nautilus-icon-container.c index 3eefcadae..e736edf51 100644 --- a/libnautilus-private/nautilus-icon-container.c +++ b/libnautilus-private/nautilus-icon-container.c @@ -2339,6 +2339,9 @@ destroy (GtkObject *object) container); nautilus_icon_container_flush_typeselect_state (container); + + g_hash_table_destroy (container->details->icon_set); + container->details->icon_set = NULL; g_free (container->details); @@ -3331,6 +3334,8 @@ nautilus_icon_container_initialize (NautilusIconContainer *container) details = g_new0 (NautilusIconContainerDetails, 1); + details->icon_set = g_hash_table_new (g_direct_hash, g_direct_equal); + details->zoom_level = NAUTILUS_ZOOM_LEVEL_STANDARD; /* font table - this isn't exactly proportional, but it looks better than computed */ @@ -3597,6 +3602,9 @@ nautilus_icon_container_clear (NautilusIconContainer *container) g_list_free (details->new_icons); details->new_icons = NULL; + g_hash_table_destroy (details->icon_set); + details->icon_set = g_hash_table_new (g_direct_hash, g_direct_equal); + nautilus_icon_container_update_scroll_region (container); } @@ -3652,7 +3660,8 @@ icon_destroy (NautilusIconContainer *container, details->icons = g_list_remove (details->icons, icon); details->new_icons = g_list_remove (details->new_icons, icon); - + g_hash_table_remove (details->icon_set, icon->data); + was_selected = icon->is_selected; if (details->keyboard_focus == icon) { @@ -3928,7 +3937,6 @@ nautilus_icon_container_add (NautilusIconContainer *container, NautilusIconData *data) { NautilusIconContainerDetails *details; - GList *p; NautilusIcon *icon; g_return_val_if_fail (NAUTILUS_IS_ICON_CONTAINER (container), FALSE); @@ -3936,14 +3944,8 @@ nautilus_icon_container_add (NautilusIconContainer *container, details = container->details; - /* FIXME bugzilla.gnome.org 41288: - * I guess we need to use an indexed data structure to avoid this loop. - */ - for (p = details->icons; p != NULL; p = p->next) { - icon = p->data; - if (icon->data == data) { - return FALSE; - } + if (g_hash_table_lookup (details->icon_set, data) != NULL) { + return FALSE; } /* Create the new icon, including the canvas item. */ @@ -3964,6 +3966,8 @@ nautilus_icon_container_add (NautilusIconContainer *container, details->icons = g_list_prepend (details->icons, icon); details->new_icons = g_list_prepend (details->new_icons, icon); + g_hash_table_insert (details->icon_set, data, icon); + /* Run an idle function to add the icons. */ schedule_redo_layout (container); @@ -3982,26 +3986,22 @@ nautilus_icon_container_remove (NautilusIconContainer *container, NautilusIconData *data) { NautilusIcon *icon; - GList *p; g_return_val_if_fail (NAUTILUS_IS_ICON_CONTAINER (container), FALSE); g_return_val_if_fail (data != NULL, FALSE); end_renaming_mode (container, FALSE); - /* FIXME bugzilla.gnome.org 41288: - * I guess we need to use an indexed data structure to avoid this loop. - */ - for (p = container->details->icons; p != NULL; p = p->next) { - icon = p->data; - if (icon->data == data) { - icon_destroy (container, icon); - schedule_redo_layout (container); - return TRUE; - } + icon = g_hash_table_lookup (container->details->icon_set, data); + + if (icon == NULL) { + return FALSE; } - return FALSE; + icon_destroy (container, icon); + schedule_redo_layout (container); + + return TRUE; } /** @@ -4016,18 +4016,15 @@ nautilus_icon_container_request_update (NautilusIconContainer *container, NautilusIconData *data) { NautilusIcon *icon; - GList *p; g_return_if_fail (NAUTILUS_IS_ICON_CONTAINER (container)); g_return_if_fail (data != NULL); - for (p = container->details->icons; p != NULL; p = p->next) { - icon = p->data; - if (icon->data == data) { - nautilus_icon_container_update_icon (container, icon); - schedule_redo_layout (container); - return; - } + icon = g_hash_table_lookup (container->details->icon_set, data); + + if (icon != NULL) { + nautilus_icon_container_update_icon (container, icon); + schedule_redo_layout (container); } } @@ -4097,20 +4094,15 @@ nautilus_icon_container_request_update_all (NautilusIconContainer *container) void nautilus_icon_container_reveal (NautilusIconContainer *container, NautilusIconData *data) { - GList *p; NautilusIcon *icon; g_return_if_fail (NAUTILUS_IS_ICON_CONTAINER (container)); g_return_if_fail (data != NULL); - /* FIXME bugzilla.gnome.org 41288: - * I guess we need to use an indexed data structure to avoid this loop. - */ - for (p = container->details->icons; p != NULL; p = p->next) { - icon = p->data; - if (icon->data == data) { - reveal_icon (container, icon); - } + icon = g_hash_table_lookup (container->details->icon_set, data); + + if (icon != NULL) { + reveal_icon (container, icon); } } diff --git a/libnautilus-private/nautilus-icon-factory.c b/libnautilus-private/nautilus-icon-factory.c index b28ee2881..ea9967465 100644 --- a/libnautilus-private/nautilus-icon-factory.c +++ b/libnautilus-private/nautilus-icon-factory.c @@ -35,7 +35,6 @@ #include "nautilus-icon-factory-private.h" #include "nautilus-lib-self-check-functions.h" #include "nautilus-link.h" -#include "nautilus-metadata.h" #include "nautilus-theme.h" #include "nautilus-thumbnails.h" #include "nautilus-trash-monitor.h" @@ -54,12 +53,13 @@ #include <gtk/gtksignal.h> #include <libgnome/gnome-dentry.h> #include <libgnome/gnome-i18n.h> -#include <libgnome/gnome-metadata.h> #include <libgnome/gnome-util.h> #include <libgnomevfs/gnome-vfs-file-info.h> +#include <libgnomevfs/gnome-vfs-mime.h> #include <libgnomevfs/gnome-vfs-mime-handlers.h> #include <libgnomevfs/gnome-vfs-mime-info.h> #include <libgnomevfs/gnome-vfs-mime-monitor.h> +#include <libgnomevfs/gnome-vfs-ops.h> #include <libgnomevfs/gnome-vfs-types.h> #include <librsvg/rsvg.h> #include <stdio.h> @@ -1458,26 +1458,53 @@ is_supported_mime_type (const char *mime_type) return TRUE; } +static void +image_uri_to_name_or_uri (const char *image_uri, + char **icon_name, + char **uri) +{ + char *icon_path; + + if (image_uri == NULL) { + return; + } + + /* FIXME bugzilla.eazel.com 2564: All custom icons must be in file:. */ + icon_path = gnome_vfs_get_local_path_from_uri (image_uri); + if (icon_path == NULL && image_uri[0] == '/') { + icon_path = g_strdup (image_uri); + } + if (icon_path != NULL) { + if (*uri == NULL) { + *uri = gnome_vfs_get_uri_from_local_path (icon_path); + } + g_free (icon_path); + } else if (strpbrk (image_uri, ":/") == NULL) { + *icon_name = remove_icon_name_suffix (image_uri); + } +} + /* key routine to get the scalable icon for a file */ NautilusScalableIcon * nautilus_icon_factory_get_icon_for_file (NautilusFile *file, const char *modifier) { - char *uri, *file_uri, *file_path, *image_uri, *icon_name, *mime_type, *top_left_text; - char *directory, *desktop_directory, *buf, *icon_path; + char *uri, *custom_uri, *file_uri, *icon_name, *mime_type, *top_left_text; + int file_size; gboolean is_local; - int file_size, size, res; NautilusScalableIcon *scalable_icon; - char *directory_uri; - GnomeDesktopEntry *entry; if (file == NULL) { return NULL; } icon_name = NULL; + uri = NULL; + + /* if there is a custom image in the metadata or link info, use that. */ + custom_uri = nautilus_file_get_custom_icon_uri (file); + image_uri_to_name_or_uri (custom_uri, &icon_name, &uri); + g_free (custom_uri); - /* if there is a custom image in the metadata, use that. */ - uri = nautilus_file_get_metadata (file, NAUTILUS_METADATA_KEY_CUSTOM_ICON, NULL); file_uri = nautilus_file_get_uri (file); is_local = nautilus_file_is_local (file); mime_type = nautilus_file_get_mime_type (file); @@ -1486,37 +1513,6 @@ nautilus_icon_factory_get_icon_for_file (NautilusFile *file, const char *modifie or use a thumbnail if one exists. If it's too large, don't try to thumbnail it at all. If a thumbnail is required, but does not yet exist, put an entry on the thumbnail queue so we eventually make one */ - - if (uri == NULL) { - /* Do we have to check the gnome metadata? - * - * Do this only for the ~/.gnome-desktop directory, as it was - * the only place where GMC used it (since everywhere else we could - * not do it because of the imlib leaks). - */ - desktop_directory = nautilus_get_gmc_desktop_directory (); - directory_uri = nautilus_file_get_parent_uri (file); - directory = gnome_vfs_get_local_path_from_uri (directory_uri); - if (directory != NULL && strcmp (directory, desktop_directory) == 0) { - file_path = gnome_vfs_get_local_path_from_uri (file_uri); - - if (file_path != NULL) { - res = gnome_metadata_get (file_path, "icon-filename", &size, &buf); - } else { - res = -1; - } - - if (res == 0 && buf != NULL) { - uri = gnome_vfs_get_uri_from_local_path (buf); - g_free (buf); - } - - g_free (file_path); - } - g_free (directory); - g_free (directory_uri); - g_free (desktop_directory); - } /* also, dont make thumbnails for images in the thumbnails directory */ if (uri == NULL) { @@ -1539,50 +1535,7 @@ nautilus_icon_factory_get_icon_for_file (NautilusFile *file, const char *modifie } } } - - /* Handle nautilus link xml files, which may specify their own image */ - if (nautilus_file_is_nautilus_link (file)) { - /* FIXME bugzilla.gnome.org 42563: This does sync. I/O and only works for local paths. */ - file_path = gnome_vfs_get_local_path_from_uri (file_uri); - if (file_path != NULL) { - image_uri = nautilus_link_local_get_image_uri (file_path); - if (image_uri != NULL) { - /* FIXME bugzilla.gnome.org 42564: All custom icons must be in file:. */ - icon_path = gnome_vfs_get_local_path_from_uri (image_uri); - if (icon_path == NULL && image_uri[0] == '/') { - icon_path = g_strdup (image_uri); - } - if (icon_path != NULL) { - if (uri == NULL) { - uri = gnome_vfs_get_uri_from_local_path (icon_path); - } - g_free (icon_path); - } else if (strpbrk (image_uri, ":/") == NULL) { - icon_name = remove_icon_name_suffix (image_uri); - } - g_free (image_uri); - } - g_free (file_path); - } - } - /* Handle .desktop files. */ - if (uri == NULL - && nautilus_file_is_mime_type (file, "application/x-gnome-app-info")) { - /* FIXME bugzilla.gnome.org 42563: This does sync. I/O and only works for local paths. */ - file_path = gnome_vfs_get_local_path_from_uri (file_uri); - if (file_path != NULL) { - entry = gnome_desktop_entry_load (file_path); - if (entry != NULL) { - if (entry->icon != NULL) { - uri = gnome_vfs_get_uri_from_local_path (entry->icon); - } - gnome_desktop_entry_free (entry); - } - g_free (file_path); - } - } - /* handle SVG files */ if (uri == NULL && icon_name == NULL && nautilus_file_is_mime_type (file, "image/svg")) { @@ -1600,7 +1553,7 @@ nautilus_icon_factory_get_icon_for_file (NautilusFile *file, const char *modifie /* Create the icon or find it in the cache if it's already there. */ scalable_icon = nautilus_scalable_icon_new_from_text_pieces (uri, mime_type, icon_name, modifier, top_left_text); - + g_free (uri); g_free (mime_type); g_free (icon_name); @@ -1609,26 +1562,46 @@ nautilus_icon_factory_get_icon_for_file (NautilusFile *file, const char *modifie return scalable_icon; } + + /** - * nautilus_icon_factory_get_required_file_attributes + * nautilus_icon_factory_get_basic_file_attributes * - * Get the list of file attributes required to obtain a file's icon. + * Get the list of file attributes required to obtain a file's basic icon. + * This includes attributes needed to get a custom icon, but not those needed + * for text preview. * Callers must free this list. */ GList * -nautilus_icon_factory_get_required_file_attributes (void) +nautilus_icon_factory_get_basic_file_attributes (void) { GList *attributes; attributes = g_list_prepend (NULL, NAUTILUS_FILE_ATTRIBUTE_CUSTOM_ICON); attributes = g_list_prepend (attributes, NAUTILUS_FILE_ATTRIBUTE_MIME_TYPE); + return attributes; +} + +/** + * nautilus_icon_factory_get_required_file_attributes + * + * Get the list of file attributes required to obtain a file's icon. + * Callers must free this list. + */ +GList * +nautilus_icon_factory_get_required_file_attributes (void) +{ + GList *attributes; + + attributes = nautilus_icon_factory_get_basic_file_attributes (); attributes = g_list_prepend (attributes, NAUTILUS_FILE_ATTRIBUTE_TOP_LEFT_TEXT); return attributes; } + /** * nautilus_icon_factory_is_icon_ready_for_file * @@ -1650,6 +1623,31 @@ nautilus_icon_factory_is_icon_ready_for_file (NautilusFile *file) return result; } + + +/** + * nautilus_icon_factory_is_basic_icon_ready_for_file + * + * Check whether a NautilusFile has enough information to report + * what its basic icon should be. This will account for custom icons + * but not text preview. + * + * @file: The NautilusFile in question. + */ +gboolean +nautilus_icon_factory_is_basic_icon_ready_for_file (NautilusFile *file) +{ + GList *attributes; + gboolean result; + + attributes = nautilus_icon_factory_get_basic_file_attributes (); + result = nautilus_file_check_if_ready (file, attributes); + g_list_free (attributes); + + return result; +} + + NautilusScalableIcon * nautilus_icon_factory_get_emblem_icon_by_name (const char *emblem_name) { @@ -1916,6 +1914,7 @@ load_named_icon (const char *name, path = get_icon_file_path (name, modifier, size_in_pixels, optimized_for_aa, details); + pixbuf = load_icon_from_path (path, size_in_pixels, FALSE, eel_str_has_prefix (name, EMBLEM_NAME_PREFIX), optimized_for_aa); diff --git a/libnautilus-private/nautilus-icon-factory.h b/libnautilus-private/nautilus-icon-factory.h index 2cc4319d0..4286d69dc 100644 --- a/libnautilus-private/nautilus-icon-factory.h +++ b/libnautilus-private/nautilus-icon-factory.h @@ -105,6 +105,11 @@ NautilusScalableIcon *nautilus_icon_factory_get_icon_for_file (Nautil const char *modifier); gboolean nautilus_icon_factory_is_icon_ready_for_file (NautilusFile *file); GList * nautilus_icon_factory_get_required_file_attributes (void); + +/* The calls below do not account for top-left text, allowing it to be loaded progressively. */ +gboolean nautilus_icon_factory_is_basic_icon_ready_for_file (NautilusFile *file); +GList * nautilus_icon_factory_get_basic_file_attributes (void); + GList * nautilus_icon_factory_get_emblem_icons_for_file (NautilusFile *file, EelStringList *exclude); NautilusScalableIcon *nautilus_icon_factory_get_emblem_icon_by_name (const char *emblem_name); diff --git a/libnautilus-private/nautilus-icon-private.h b/libnautilus-private/nautilus-icon-private.h index e46d7447b..a7a98942a 100644 --- a/libnautilus-private/nautilus-icon-private.h +++ b/libnautilus-private/nautilus-icon-private.h @@ -102,6 +102,7 @@ struct NautilusIconContainerDetails { /* List of icons. */ GList *icons; GList *new_icons; + GHashTable *icon_set; /* Current icon for keyboard navigation. */ NautilusIcon *keyboard_focus; diff --git a/libnautilus-private/nautilus-link-desktop-file.c b/libnautilus-private/nautilus-link-desktop-file.c new file mode 100644 index 000000000..70d3adb18 --- /dev/null +++ b/libnautilus-private/nautilus-link-desktop-file.c @@ -0,0 +1,532 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- + + nautilus-link-desktop-file.c: .desktop link files. + + Copyright (C) 2001 Red Hat, Inc. + + This program 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the historicalied 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + Authors: Jonathan Blandford <jrb@redhat.com> + Alexander Larsson <alexl@redhat.com> +*/ +#include <config.h> +#include "nautilus-link.h" +#include "nautilus-link-desktop-file.h" +#include "nautilus-directory-notify.h" +#include "nautilus-directory.h" +#include "nautilus-file-attributes.h" +#include "nautilus-file.h" +#include "nautilus-metadata.h" +#include "nautilus-file-utilities.h" +#include "nautilus-desktop-file-loader.h" +#include <eel/eel-glib-extensions.h> +#include <eel/eel-gnome-extensions.h> +#include <eel/eel-stock-dialogs.h> +#include <eel/eel-string.h> +#include <eel/eel-xml-extensions.h> +#include <gnome-xml/parser.h> +#include <gnome-xml/xmlmemory.h> +#include <libgnome/gnome-i18n.h> +#include <libgnome/gnome-util.h> +#include <libgnomevfs/gnome-vfs-mime.h> +#include <libgnomevfs/gnome-vfs.h> +#include <stdlib.h> + +#define NAUTILUS_LINK_GENERIC_TAG "Link" +#define NAUTILUS_LINK_TRASH_TAG "X-nautilus-trash" +#define NAUTILUS_LINK_MOUNT_TAG "FSDevice" +#define NAUTILUS_LINK_HOME_TAG "X-nautilus-home" + +static const char * +get_tag (NautilusLinkType type) +{ + switch (type) { + default: + g_assert_not_reached (); + /* fall through */ + case NAUTILUS_LINK_GENERIC: + return NAUTILUS_LINK_GENERIC_TAG; + case NAUTILUS_LINK_TRASH: + return NAUTILUS_LINK_TRASH_TAG; + case NAUTILUS_LINK_MOUNT: + return NAUTILUS_LINK_MOUNT_TAG; + case NAUTILUS_LINK_HOME: + return NAUTILUS_LINK_HOME_TAG; + } +} + +static gchar * +slurp_key_string (const char *path, + const char *keyname, + gboolean localize) +{ + NautilusDesktopFile *desktop_file = NULL; + gchar *text; + gboolean set; + GnomeVFSResult result; + gchar *uri; + + uri = gnome_vfs_get_uri_from_local_path (path); + if (uri == NULL) { + return NULL; + } + + result = nautilus_desktop_file_load (uri, &desktop_file); + + g_free (uri); + + if (result != GNOME_VFS_OK) { + return NULL; + } + + if (localize) { + set = nautilus_desktop_file_get_locale_string (desktop_file, + "Desktop Entry", + keyname, + &text); + } else { + set = nautilus_desktop_file_get_string (desktop_file, + "Desktop Entry", + keyname, + &text); + } + + nautilus_desktop_file_free (desktop_file); + + if (set == FALSE) { + return NULL; + } + + return text; +} + +gboolean +nautilus_link_desktop_file_local_create (const char *directory_path, + const char *name, + const char *image, + const char *target_uri, + const GdkPoint *point, + NautilusLinkType type) +{ + gchar *path; + FILE *file; + char *uri; + GList dummy_list; + NautilusFileChangesQueuePosition item; + GnomeVFSHandle *handle; + + g_return_val_if_fail (directory_path != NULL, FALSE); + g_return_val_if_fail (name != NULL, FALSE); + g_return_val_if_fail (image != NULL, FALSE); + g_return_val_if_fail (target_uri != NULL, FALSE); + + path = nautilus_make_path (directory_path, name); + handle = NULL; + + file = fopen (path, "w"); + + if (file == NULL) { + g_free (path); + return FALSE; + } + + fputs ("[Desktop Entry]\nEncoding=Legacy-Mixed\nName=", file); + fputs (name, file); + fputs ("\nType=", file); + fputs (get_tag (type), file); + fputs ("\nX-Nautilus-Icon=", file); + fputs (image, file); + fputs ("\nURL=", file); + fputs (target_uri, file); + fputs ("\n", file); + /* ... */ + fclose (file); + + uri = gnome_vfs_get_uri_from_local_path (path); + dummy_list.data = uri; + dummy_list.next = NULL; + dummy_list.prev = NULL; + nautilus_directory_notify_files_added (&dummy_list); + nautilus_directory_schedule_metadata_remove (&dummy_list); + + if (point != NULL) { + item.uri = uri; + item.set = TRUE; + item.point.x = point->x; + item.point.y = point->y; + + dummy_list.data = &item; + dummy_list.next = NULL; + dummy_list.prev = NULL; + + nautilus_directory_schedule_position_set (&dummy_list); + } + + g_free (uri); + g_free (path); + return TRUE; +} + +gboolean +nautilus_link_desktop_file_local_set_icon (const char *path, + const char *icon_name) +{ + NautilusDesktopFile *desktop_file; + GnomeVFSResult result; + char *uri; + + uri = gnome_vfs_get_uri_from_local_path (path); + if (uri == NULL) { + return FALSE; + } + + result = nautilus_desktop_file_load (uri, &desktop_file); + + if (result != GNOME_VFS_OK) { + g_free (uri); + return FALSE; + } + + nautilus_desktop_file_set_string (desktop_file, "Desktop Entry", "X-Nautilus-Icon", icon_name); + + result = nautilus_desktop_file_save (desktop_file, uri); + nautilus_desktop_file_free (desktop_file); + + g_free (uri); + + if (result != GNOME_VFS_OK) { + return FALSE; + } + + return TRUE; +} + +char * +nautilus_link_desktop_file_local_get_text (const char *path) +{ + return slurp_key_string (path, "Name", TRUE); +} + +char * +nautilus_link_desktop_file_local_get_additional_text (const char *path) +{ + /* The comment field of current .desktop files is often bad. + * It just contains a copy of the name. This is probably because the + * panel shows the comment field as a tooltip. + */ + return NULL; +#ifdef THIS_IS_NOT_USED_RIGHT_NOW + gchar *type; + gchar *retval; + + type = slurp_key_string (path, "Type", FALSE); + retval = NULL; + if (type == NULL) { + return NULL; + } + + if (strcmp (type, "Application") == 0) { + retval = slurp_key_string (path, "Comment", TRUE); + } + + g_free (type); + + return retval; +#endif +} + +NautilusLinkType +nautilus_link_desktop_file_local_get_link_type (const char *path) +{ + gchar *type; + NautilusLinkType retval; + + type = slurp_key_string (path, "Type", FALSE); + + if (type == NULL) { + return NAUTILUS_LINK_GENERIC; + } + if (strcmp (type, NAUTILUS_LINK_HOME_TAG) == 0) { + retval = NAUTILUS_LINK_HOME; + } else if (strcmp (type, NAUTILUS_LINK_MOUNT_TAG) == 0) { + retval = NAUTILUS_LINK_MOUNT; + } else if (strcmp (type, NAUTILUS_LINK_TRASH_TAG) == 0) { + retval = NAUTILUS_LINK_TRASH; + } else { + retval = NAUTILUS_LINK_GENERIC; + } + + g_free (type); + return retval; +} + +gboolean +nautilus_link_desktop_file_local_is_volume_link (const char *path) +{ + return (nautilus_link_desktop_file_local_get_link_type (path) == NAUTILUS_LINK_MOUNT); +} + +gboolean +nautilus_link_desktop_file_local_is_home_link (const char *path) +{ + return (nautilus_link_desktop_file_local_get_link_type (path) == NAUTILUS_LINK_HOME); +} + +gboolean +nautilus_link_desktop_file_local_is_trash_link (const char *path) +{ + return (nautilus_link_desktop_file_local_get_link_type (path) == NAUTILUS_LINK_TRASH); +} + +static gchar * +nautilus_link_desktop_file_get_link_uri_from_desktop (NautilusDesktopFile *desktop_file) +{ + gchar *terminal_command; + gchar *launch_string; + gboolean need_term; + gchar *type; + gchar *retval; + + retval = NULL; + + type = NULL; + if (! nautilus_desktop_file_get_string (desktop_file, + "Desktop Entry", + "Type", + &type)) { + return NULL; + } + + if (strcmp (type, "Application") == 0) { + if (! nautilus_desktop_file_get_string (desktop_file, + "Desktop Entry", + "Exec", + &launch_string)) { + return NULL; + } + + need_term = FALSE; + nautilus_desktop_file_get_boolean (desktop_file, + "Desktop Entry", + "Terminal", + &need_term); + if (need_term) { + terminal_command = eel_gnome_make_terminal_command (launch_string); + retval = g_strconcat ("command:", terminal_command, NULL); + g_free (terminal_command); + } else { + retval = g_strconcat ("command:", launch_string, NULL); + } + g_free (launch_string); + } else if (strcmp (type, "URL") == 0) { + /* Some old broken desktop files use this nonstandard feature, we need handle it though */ + nautilus_desktop_file_get_string (desktop_file, + "Desktop Entry", + "Exec", + &retval); + } else if ((strcmp (type, NAUTILUS_LINK_GENERIC_TAG) == 0) || + (strcmp (type, NAUTILUS_LINK_MOUNT_TAG) == 0) || + (strcmp (type, NAUTILUS_LINK_TRASH_TAG) == 0) || + (strcmp (type, NAUTILUS_LINK_HOME_TAG) == 0)) { + nautilus_desktop_file_get_string (desktop_file, + "Desktop Entry", + "URL", + &retval); + } + return retval; +} + +static gchar * +nautilus_link_desktop_file_get_link_name_from_desktop (NautilusDesktopFile *desktop_file) +{ + gchar *name; + + name = NULL; + + if (nautilus_desktop_file_get_string (desktop_file, + "Desktop Entry", + "Name", + &name)) { + return name; + } else { + return NULL; + } +} + +static gchar * +nautilus_link_desktop_file_get_link_icon_from_desktop (NautilusDesktopFile *desktop_file) +{ + char *icon_uri; + gchar *absolute; + gchar *icon_name; + + if (nautilus_desktop_file_get_string (desktop_file, "Desktop Entry", "X-Nautilus-Icon", &icon_uri)) { + return icon_uri; + } + + /* Fall back to a standard icon. */ + if (nautilus_desktop_file_get_string (desktop_file, "Desktop Entry", "Icon", &icon_name)) { + if (icon_name == NULL) { + return NULL; + } + + absolute = gnome_pixmap_file (icon_name); + if (absolute != NULL) { + g_free (icon_name); + icon_name = absolute; + } + if (icon_name[0] == '/') { + icon_uri = gnome_vfs_get_uri_from_local_path (icon_name); + } else { + icon_uri = NULL; + } + g_free (icon_name); + + return icon_uri; + } + + return NULL; +} + +char * +nautilus_link_desktop_file_local_get_link_uri (const char *path) +{ + NautilusDesktopFile *desktop_file = NULL; + gchar *retval; + GnomeVFSResult result; + char *uri; + + uri = gnome_vfs_get_uri_from_local_path (path); + if (uri == NULL) { + return FALSE; + } + + result = nautilus_desktop_file_load (uri, &desktop_file); + + g_free (uri); + + if (result != GNOME_VFS_OK) { + return NULL; + } + + retval = nautilus_link_desktop_file_get_link_uri_from_desktop (desktop_file); + + nautilus_desktop_file_free (desktop_file); + return retval; +} + +char * +nautilus_link_desktop_file_get_link_uri_given_file_contents (const char *link_file_contents, + int link_file_size) +{ + NautilusDesktopFile *desktop_file; + gchar *slurp; + gchar *retval; + + slurp = g_strndup (link_file_contents, link_file_size); + desktop_file = nautilus_desktop_file_from_string (slurp); + g_free (slurp); + if (desktop_file == NULL) { + return NULL; + } + retval = nautilus_link_desktop_file_get_link_uri_from_desktop (desktop_file); + + nautilus_desktop_file_free (desktop_file); + return retval; +} + +char * +nautilus_link_desktop_file_get_link_name_given_file_contents (const char *link_file_contents, + int link_file_size) +{ + NautilusDesktopFile *desktop_file; + gchar *slurp; + gchar *retval; + + slurp = g_strndup (link_file_contents, link_file_size); + desktop_file = nautilus_desktop_file_from_string (slurp); + g_free (slurp); + if (desktop_file == NULL) { + return NULL; + } + retval = nautilus_link_desktop_file_get_link_name_from_desktop (desktop_file); + + nautilus_desktop_file_free (desktop_file); + return retval; +} + + +char * +nautilus_link_desktop_file_get_link_icon_given_file_contents (const char *link_file_contents, + int link_file_size) +{ + NautilusDesktopFile *desktop_file; + gchar *slurp; + gchar *retval; + + slurp = g_strndup (link_file_contents, link_file_size); + desktop_file = nautilus_desktop_file_from_string (slurp); + g_free (slurp); + if (desktop_file == NULL) { + return NULL; + } + retval = nautilus_link_desktop_file_get_link_icon_from_desktop (desktop_file); + + nautilus_desktop_file_free (desktop_file); + return retval; +} + + +void +nautilus_link_desktop_file_local_create_from_gnome_entry (GnomeDesktopEntry *entry, + const char *dest_path, + const GdkPoint *position) +{ + char *uri; + GList dummy_list; + NautilusFileChangesQueuePosition item; + GnomeDesktopEntry *new_entry; + char *file_name; + + new_entry = gnome_desktop_entry_copy (entry); + g_free (new_entry->location); + file_name = g_strdup_printf ("%s.desktop", entry->name); + new_entry->location = nautilus_make_path (dest_path, file_name); + g_free (file_name); + gnome_desktop_entry_save (new_entry); + + uri = gnome_vfs_get_uri_from_local_path (dest_path); + dummy_list.data = uri; + dummy_list.next = NULL; + dummy_list.prev = NULL; + nautilus_directory_notify_files_added (&dummy_list); + nautilus_directory_schedule_metadata_remove (&dummy_list); + + if (position != NULL) { + item.uri = uri; + item.set = TRUE; + item.point.x = position->x; + item.point.y = position->y; + + dummy_list.data = &item; + dummy_list.next = NULL; + dummy_list.prev = NULL; + + nautilus_directory_schedule_position_set (&dummy_list); + } + gnome_desktop_entry_free (new_entry); +} + diff --git a/libnautilus-private/nautilus-link-desktop-file.h b/libnautilus-private/nautilus-link-desktop-file.h new file mode 100644 index 000000000..4a4d8fb22 --- /dev/null +++ b/libnautilus-private/nautilus-link-desktop-file.h @@ -0,0 +1,61 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- + + nautilus-link-desktop-file.h: . + + Copyright (C) 2001 Red Hat, Inc. + + This program 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. + + This program 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + Authors: Jonathan Blandford <jrb@redhat.com> +*/ + +#ifndef NAUTILUS_LINK_DESKTOP_FILE_H +#define NAUTILUS_LINK_DESKTOP_FILE_H + +#include "nautilus-file.h" +#include <gdk/gdk.h> +#include <libgnome/gnome-defs.h> +#include <libgnome/gnome-dentry.h> + + +gboolean nautilus_link_desktop_file_local_create (const char *directory_path, + const char *name, + const char *image, + const char *target_uri, + const GdkPoint *point, + NautilusLinkType type); +gboolean nautilus_link_desktop_file_local_set_icon (const char *path, + const char *icon_name); +char * nautilus_link_desktop_file_local_get_text (const char *path); +char * nautilus_link_desktop_file_local_get_additional_text (const char *path); +NautilusLinkType nautilus_link_desktop_file_local_get_link_type (const char *path); +gboolean nautilus_link_desktop_file_local_is_volume_link (const char *path); +gboolean nautilus_link_desktop_file_local_is_home_link (const char *path); +gboolean nautilus_link_desktop_file_local_is_trash_link (const char *path); +char * nautilus_link_desktop_file_local_get_link_uri (const char *path); +char * nautilus_link_desktop_file_get_link_uri_given_file_contents (const char *link_file_contents, + int link_file_size); +char * nautilus_link_desktop_file_get_link_name_given_file_contents (const char *link_file_contents, + int link_file_size); +char * nautilus_link_desktop_file_get_link_icon_given_file_contents (const char *link_file_contents, + int link_file_size); +void nautilus_link_desktop_file_local_create_from_gnome_entry (GnomeDesktopEntry *entry, + const char *dest_path, + const GdkPoint *position); + + +#endif /* NAUTILUS_LINK_DESKTOP_FILE_H */ + diff --git a/libnautilus-private/nautilus-link-historical.c b/libnautilus-private/nautilus-link-historical.c new file mode 100644 index 000000000..4640f6600 --- /dev/null +++ b/libnautilus-private/nautilus-link-historical.c @@ -0,0 +1,435 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- + + nautilus-link-historical.c: xml-based link files. + + Copyright (C) 1999, 2000, 2001 Eazel, Inc. + + This program 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the historicalied 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + Author: Andy Hertzfeld <andy@eazel.com> +*/ + +#include <config.h> +#include "nautilus-link.h" +#include "nautilus-link-historical.h" + +#include "nautilus-directory-notify.h" +#include "nautilus-directory.h" +#include "nautilus-file-attributes.h" +#include "nautilus-file.h" +#include "nautilus-metadata.h" +#include "nautilus-file-utilities.h" +#include <eel/eel-glib-extensions.h> +#include <eel/eel-gnome-extensions.h> +#include <eel/eel-stock-dialogs.h> +#include <eel/eel-string.h> +#include <eel/eel-vfs-extensions.h> +#include <eel/eel-xml-extensions.h> +#include <gnome-xml/parser.h> +#include <gnome-xml/xmlmemory.h> +#include <libgnome/gnome-i18n.h> +#include <libgnome/gnome-util.h> +#include <libgnomevfs/gnome-vfs-mime.h> +#include <libgnomevfs/gnome-vfs.h> +#include <stdlib.h> + + +#define NAUTILUS_LINK_GENERIC_TAG "Generic Link" +#define NAUTILUS_LINK_TRASH_TAG "Trash Link" +#define NAUTILUS_LINK_MOUNT_TAG "Mount Link" +#define NAUTILUS_LINK_HOME_TAG "Home Link" + +#define REMOTE_ICON_DIR_PERMISSIONS (GNOME_VFS_PERM_USER_ALL \ + | GNOME_VFS_PERM_GROUP_ALL \ + | GNOME_VFS_PERM_OTHER_ALL) + +typedef void (* NautilusFileFunction) (NautilusFile *file); + +static const char * +get_tag (NautilusLinkType type) +{ + switch (type) { + default: + g_assert_not_reached (); + /* fall through */ + case NAUTILUS_LINK_GENERIC: + return NAUTILUS_LINK_GENERIC_TAG; + case NAUTILUS_LINK_TRASH: + return NAUTILUS_LINK_TRASH_TAG; + case NAUTILUS_LINK_MOUNT: + return NAUTILUS_LINK_MOUNT_TAG; + case NAUTILUS_LINK_HOME: + return NAUTILUS_LINK_HOME_TAG; + } +} + +static NautilusLinkType +get_link_type (const char *tag) +{ + if (tag != NULL) { + if (strcmp (tag, NAUTILUS_LINK_TRASH_TAG) == 0) { + return NAUTILUS_LINK_TRASH; + } + if (strcmp (tag, NAUTILUS_LINK_MOUNT_TAG) == 0) { + return NAUTILUS_LINK_MOUNT; + } + if (strcmp (tag, NAUTILUS_LINK_HOME_TAG) == 0) { + return NAUTILUS_LINK_HOME; + } + } + return NAUTILUS_LINK_GENERIC; +} + +gboolean +nautilus_link_historical_local_create (const char *directory_path, + const char *name, + const char *image, + const char *target_uri, + const GdkPoint *point, + NautilusLinkType type) +{ + xmlDocPtr output_document; + xmlNodePtr root_node; + char *path; + int result; + char *uri; + GList dummy_list; + NautilusFileChangesQueuePosition item; + + + g_return_val_if_fail (directory_path != NULL, FALSE); + g_return_val_if_fail (name != NULL, FALSE); + g_return_val_if_fail (image != NULL, FALSE); + g_return_val_if_fail (target_uri != NULL, FALSE); + + /* create a new xml document */ + output_document = xmlNewDoc ("1.0"); + + /* add the root node to the output document */ + root_node = xmlNewDocNode (output_document, NULL, "nautilus_object", NULL); + xmlDocSetRootElement (output_document, root_node); + + /* Add mime magic string so that the mime sniffer can recognize us. + * Note: The value of the tag identfies what type of link this. */ + xmlSetProp (root_node, "nautilus_link", get_tag (type)); + + /* Add link and custom icon tags */ + xmlSetProp (root_node, "custom_icon", image); + xmlSetProp (root_node, "link", target_uri); + + /* all done, so save the xml document as a link file */ + path = nautilus_make_path (directory_path, name); + result = xmlSaveFile (path, output_document); + + xmlFreeDoc (output_document); + + if (result <= 0) { + g_free (path); + return FALSE; + } + + /* Notify that this new file has been created. */ + uri = gnome_vfs_get_uri_from_local_path (path); + dummy_list.data = uri; + dummy_list.next = NULL; + dummy_list.prev = NULL; + nautilus_directory_notify_files_added (&dummy_list); + nautilus_directory_schedule_metadata_remove (&dummy_list); + + if (point != NULL) { + item.uri = uri; + item.set = TRUE; + item.point.x = point->x; + item.point.y = point->y; + + dummy_list.data = &item; + dummy_list.next = NULL; + dummy_list.prev = NULL; + + nautilus_directory_schedule_position_set (&dummy_list); + } + + g_free (uri); + + g_free (path); + + return TRUE; +} + +static char * +xml_get_root_property (xmlDoc *doc, + const char *key) +{ + char *property, *duplicate; + + /* Need to g_strdup so we can free with g_free instead of xmlFree. */ + property = xmlGetProp (xmlDocGetRootElement (doc), key); + duplicate = g_strdup (property); + xmlFree (property); + return duplicate; +} + +static char * +local_get_root_property (const char *path, + const char *key) +{ + xmlDoc *document; + char *property; + const char *mime_type; + + property = NULL; + + /* Check mime type. Exit if it is not a nautilus link */ + mime_type = gnome_vfs_get_file_mime_type (path, NULL, FALSE); + if (strcmp (mime_type, "application/x-nautilus-link") != 0) { + return NULL; + } + + document = xmlParseFile (path); + if (document != NULL) { + property = xml_get_root_property (document, key); + xmlFreeDoc (document); + } + + return property; +} + +static gboolean +local_set_root_property (const char *uri, + const char *key, + const char *value, + NautilusFileFunction extra_notify) +{ + xmlDocPtr document; + xmlNodePtr root; + xmlChar *old_value; + char *path; + NautilusFile *file; + + path = gnome_vfs_get_local_path_from_uri (uri); + document = xmlParseFile (path); + if (document == NULL) { + return FALSE; + } + root = xmlDocGetRootElement (document); + if (root == NULL) { + xmlFreeDoc (document); + return FALSE; + } + + /* Check if the property value is already correct. */ + old_value = xmlGetProp (root, key); + if (old_value != NULL && strcmp (old_value, value) == 0) { + xmlFreeDoc (document); + xmlFree (old_value); + return TRUE; + } + + xmlFree (old_value); + + /* Change and write the property. */ + xmlSetProp (root, key, value); + xmlSaveFile (path, document); + xmlFreeDoc (document); + + /* Notify about the change. */ + file = nautilus_file_get (uri); + if (file != NULL) { + if (extra_notify != NULL) { + (* extra_notify) (file); + } + nautilus_file_changed (file); + nautilus_file_unref (file); + } + g_free (path); + + return TRUE; +} + + +/* Set the icon for a link file. This can only be called on local + * paths, and only on files known to be link files. + */ +gboolean +nautilus_link_historical_local_set_icon (const char *path, const char *icon_name) +{ + return local_set_root_property (path, + NAUTILUS_METADATA_KEY_CUSTOM_ICON, + icon_name, + NULL); +} + + +/* Set the link uri for a link file. This can only be called on local + * paths, and only on files known to be link files. + */ +gboolean +nautilus_link_historical_local_set_link_uri (const char *path, const char *link_uri) +{ + return local_set_root_property (path, + "link", + link_uri, + NULL); +} + +gboolean +nautilus_link_historical_local_set_type (const char *path, + NautilusLinkType type) +{ + return local_set_root_property (path, + "nautilus_link", + get_tag (type), + NULL); +} + +/* returns additional text to display under the name, NULL if none */ +char * +nautilus_link_historical_local_get_additional_text (const char *path) +{ + return local_get_root_property + (path, NAUTILUS_METADATA_KEY_EXTRA_TEXT); +} + + +/* Returns the link uri associated with a link file. */ +char * +nautilus_link_historical_local_get_link_uri (const char *path) +{ + return local_get_root_property (path, "link"); +} + +/* Returns the link type of the link file. */ +NautilusLinkType +nautilus_link_historical_local_get_link_type (const char *path) +{ + char *property; + NautilusLinkType type; + + property = local_get_root_property (path, "nautilus_link"); + type = get_link_type (property); + g_free (property); + + return type; +} + +/* FIXME bugzilla.eazel.com 2495: + * Caller has to know to pass in a file with a NUL character at the end. + */ +char * +nautilus_link_historical_get_link_uri_given_file_contents (const char *file_contents, + int file_size) +{ + xmlDoc *doc; + char *property; + + doc = xmlParseMemory ((char *) file_contents, file_size); + property = xml_get_root_property (doc, "link"); + xmlFreeDoc (doc); + return property; +} + + +char * +nautilus_link_historical_get_link_icon_given_file_contents (const char *file_contents, + int file_size) +{ + xmlDoc *doc; + char *property; + + doc = xmlParseMemory ((char *) file_contents, file_size); + property = xml_get_root_property (doc, NAUTILUS_METADATA_KEY_CUSTOM_ICON); + xmlFreeDoc (doc); + return property; +} + + +gboolean +nautilus_link_historical_local_is_volume_link (const char *path) +{ + return nautilus_link_historical_local_get_link_type (path) == NAUTILUS_LINK_MOUNT; +} + +gboolean +nautilus_link_historical_local_is_home_link (const char *path) +{ + return nautilus_link_historical_local_get_link_type (path) == NAUTILUS_LINK_HOME; +} + +gboolean +nautilus_link_historical_local_is_trash_link (const char *path) +{ + return nautilus_link_historical_local_get_link_type (path) == NAUTILUS_LINK_TRASH; +} + + +void +nautilus_link_historical_local_create_from_gnome_entry (GnomeDesktopEntry *entry, const char *dest_path, const GdkPoint *position) +{ + char *icon_name; + char *launch_string, *terminal_command; + char *quoted, *arguments, *temp_str; + int i; + + if (entry == NULL || dest_path == NULL) { + return; + } + + /* Extract arguments from exec array */ + arguments = NULL; + for (i = 0; i < entry->exec_length; ++i) { + quoted = eel_shell_quote (entry->exec[i]); + if (arguments == NULL) { + arguments = quoted; + } else { + temp_str = arguments; + arguments = g_strconcat (arguments, " ", quoted, NULL); + g_free (temp_str); + g_free (quoted); + } + } + + if (strcmp (entry->type, "Application") == 0) { + if (entry->terminal) { + terminal_command = eel_gnome_make_terminal_command (arguments); + launch_string = g_strconcat ("command:", terminal_command, NULL); + g_free (terminal_command); + } else { + launch_string = g_strconcat ("command:", arguments, NULL); + } + } else if (strcmp (entry->type, "URL") == 0) { + launch_string = g_strdup (arguments); + } else { + /* Unknown .desktop file type */ + launch_string = NULL; + } + + if (entry->icon != NULL) { + icon_name = eel_make_uri_from_half_baked_uri (entry->icon); + } else { + icon_name = g_strdup ("gnome-unknown.png"); + } + + if (launch_string != NULL) { + nautilus_link_historical_local_create (dest_path, entry->name, icon_name, + launch_string, position, NAUTILUS_LINK_GENERIC); + } + + g_free (icon_name); + g_free (launch_string); + g_free (arguments); +} + + diff --git a/libnautilus-private/nautilus-link-historical.h b/libnautilus-private/nautilus-link-historical.h new file mode 100644 index 000000000..b181ec7af --- /dev/null +++ b/libnautilus-private/nautilus-link-historical.h @@ -0,0 +1,60 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- + + nautilus-link-historical.h: xml-based link files that control their appearance + and behavior. + + Copyright (C) 2000 Eazel, Inc. + + This program 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. + + This program 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + Authors: Andy Hertzfeld <andy@eazel.com> +*/ + +#ifndef NAUTILUS_LINK_HISTORICAL_H +#define NAUTILUS_LINK_HISTORICAL_H + +#include "nautilus-file.h" +#include <gdk/gdk.h> +#include <libgnome/gnome-defs.h> +#include <libgnome/gnome-dentry.h> + +gboolean nautilus_link_historical_local_create (const char *directory_path, + const char *name, + const char *image, + const char *target_uri, + const GdkPoint *point, + NautilusLinkType type); +gboolean nautilus_link_historical_local_set_icon (const char *path, + const char *icon_name); +gboolean nautilus_link_historical_local_set_type (const char *path, + NautilusLinkType type); +gboolean nautilus_link_historical_local_set_link_uri (const char *path, + const char *uri); +char * nautilus_link_historical_local_get_additional_text (const char *path); +NautilusLinkType nautilus_link_historical_local_get_link_type (const char *path); +gboolean nautilus_link_historical_local_is_volume_link (const char *path); +gboolean nautilus_link_historical_local_is_home_link (const char *path); +gboolean nautilus_link_historical_local_is_trash_link (const char *path); +char * nautilus_link_historical_local_get_link_uri (const char *path); +char * nautilus_link_historical_get_link_uri_given_file_contents (const char *link_file_contents, + int link_file_size); +char * nautilus_link_historical_get_link_icon_given_file_contents (const char *link_file_contents, + int link_file_size); +void nautilus_link_historical_local_create_from_gnome_entry (GnomeDesktopEntry *entry, + const char *dest_path, + const GdkPoint *position); + +#endif /* NAUTILUS_LINK_HISTORICAL_H */ diff --git a/libnautilus-private/nautilus-link.c b/libnautilus-private/nautilus-link.c index 47b92a292..bedf5954c 100644 --- a/libnautilus-private/nautilus-link.c +++ b/libnautilus-private/nautilus-link.c @@ -24,6 +24,8 @@ #include <config.h> #include "nautilus-link.h" +#include "nautilus-link-historical.h" +#include "nautilus-link-desktop-file.h" #include "nautilus-directory-notify.h" #include "nautilus-directory.h" @@ -45,56 +47,19 @@ #include <libgnomevfs/gnome-vfs.h> #include <stdlib.h> -/* Link type XML tags */ -#define NAUTILUS_LINK_GENERIC_TAG "Generic Link" -#define NAUTILUS_LINK_TRASH_TAG "Trash Link" -#define NAUTILUS_LINK_MOUNT_TAG "Mount Link" -#define NAUTILUS_LINK_HOME_TAG "Home Link" +const char *get_uri_mime_type_full (const gchar *uri_path); -#define REMOTE_ICON_DIR_PERMISSIONS (GNOME_VFS_PERM_USER_ALL \ - | GNOME_VFS_PERM_GROUP_ALL \ - | GNOME_VFS_PERM_OTHER_ALL) - -typedef struct { - char *link_uri; - char *file_path; -} NautilusLinkIconNotificationInfo; - -typedef void (* NautilusFileFunction) (NautilusFile *file); - -static const char * -get_tag (NautilusLinkType type) +const char * +get_uri_mime_type_full (const gchar *uri_path) { - switch (type) { - default: - g_assert_not_reached (); - /* fall through */ - case NAUTILUS_LINK_GENERIC: - return NAUTILUS_LINK_GENERIC_TAG; - case NAUTILUS_LINK_TRASH: - return NAUTILUS_LINK_TRASH_TAG; - case NAUTILUS_LINK_MOUNT: - return NAUTILUS_LINK_MOUNT_TAG; - case NAUTILUS_LINK_HOME: - return NAUTILUS_LINK_HOME_TAG; - } -} + const gchar *retval; + GnomeVFSURI *uri; -static NautilusLinkType -get_link_type (const char *tag) -{ - if (tag != NULL) { - if (strcmp (tag, NAUTILUS_LINK_TRASH_TAG) == 0) { - return NAUTILUS_LINK_TRASH; - } - if (strcmp (tag, NAUTILUS_LINK_MOUNT_TAG) == 0) { - return NAUTILUS_LINK_MOUNT; - } - if (strcmp (tag, NAUTILUS_LINK_HOME_TAG) == 0) { - return NAUTILUS_LINK_HOME; - } - } - return NAUTILUS_LINK_GENERIC; + uri = gnome_vfs_uri_new (uri_path); + retval = gnome_vfs_get_mime_type (uri); + gnome_vfs_uri_unref (uri); + + return retval; } gboolean @@ -105,457 +70,292 @@ nautilus_link_local_create (const char *directory_path, const GdkPoint *point, NautilusLinkType type) { - xmlDocPtr output_document; - xmlNodePtr root_node; - char *path; - int result; - char *uri; - GList dummy_list; - NautilusFileChangesQueuePosition item; + gboolean retval; - - g_return_val_if_fail (directory_path != NULL, FALSE); - g_return_val_if_fail (name != NULL, FALSE); - g_return_val_if_fail (image != NULL, FALSE); - g_return_val_if_fail (target_uri != NULL, FALSE); - - /* create a new xml document */ - output_document = xmlNewDoc ("1.0"); - - /* add the root node to the output document */ - root_node = xmlNewDocNode (output_document, NULL, "nautilus_object", NULL); - xmlDocSetRootElement (output_document, root_node); - - /* Add mime magic string so that the mime sniffer can recognize us. - * Note: The value of the tag identfies what type of link this. */ - xmlSetProp (root_node, "nautilus_link", get_tag (type)); - - /* Add link and custom icon tags */ - xmlSetProp (root_node, "custom_icon", image); - xmlSetProp (root_node, "link", target_uri); - - /* all done, so save the xml document as a link file */ - path = nautilus_make_path (directory_path, name); - result = xmlSaveFile (path, output_document); - - xmlFreeDoc (output_document); + retval = nautilus_link_desktop_file_local_create (directory_path, + name, image, + target_uri, point, + type); - if (result <= 0) { - g_free (path); - return FALSE; - } - - /* Notify that this new file has been created. */ - uri = gnome_vfs_get_uri_from_local_path (path); - dummy_list.data = uri; - dummy_list.next = NULL; - dummy_list.prev = NULL; - nautilus_directory_notify_files_added (&dummy_list); - nautilus_directory_schedule_metadata_remove (&dummy_list); - - if (point != NULL) { - item.uri = uri; - item.set = TRUE; - item.point.x = point->x; - item.point.y = point->y; - - dummy_list.data = &item; - dummy_list.next = NULL; - dummy_list.prev = NULL; - - nautilus_directory_schedule_position_set (&dummy_list); - } - - g_free (uri); - - g_free (path); - - return TRUE; + return retval; } -static char * -xml_get_root_property (xmlDoc *doc, - const char *key) +gboolean +nautilus_link_local_set_icon (const char *path, const char *icon_name) { - char *property, *duplicate; - - /* Need to g_strdup so we can free with g_free instead of xmlFree. */ - property = xmlGetProp (xmlDocGetRootElement (doc), key); - duplicate = g_strdup (property); - xmlFree (property); - return duplicate; -} + const gchar *mime_type; + gboolean retval; + NautilusFile *file; + GList *attributes; -static char * -local_get_root_property (const char *path, - const char *key) -{ - xmlDoc *document; - char *property; - const char *mime_type; - - property = NULL; + mime_type = get_uri_mime_type_full (path); + retval = FALSE; - /* Check mime type. Exit if it is not a nautilus link */ - mime_type = gnome_vfs_get_file_mime_type (path, NULL, FALSE); - if (strcmp (mime_type, "application/x-nautilus-link") != 0) { - return NULL; + if (mime_type == NULL) { + return retval; } - - document = xmlParseFile (path); - if (document != NULL) { - property = xml_get_root_property (document, key); - xmlFreeDoc (document); - } - - return property; -} - -static gboolean -local_set_root_property (const char *path, - const char *key, - const char *value, - NautilusFileFunction extra_notify) -{ - xmlDocPtr document; - xmlNodePtr root; - xmlChar *old_value; - char *uri; - NautilusFile *file; - document = xmlParseFile (path); - if (document == NULL) { - return FALSE; - } - root = xmlDocGetRootElement (document); - if (root == NULL) { - xmlFreeDoc (document); - return FALSE; + if (strcmp (mime_type, "application/x-nautilus-link") == 0) { + retval = nautilus_link_historical_local_set_icon (path, icon_name); + } else if (strcmp (mime_type, "application/x-gnome-app-info") == 0) { + retval = nautilus_link_desktop_file_local_set_icon (path, icon_name); } - /* Check if the property value is already correct. */ - old_value = xmlGetProp (root, key); - if (old_value != NULL && strcmp (old_value, value) == 0) { - xmlFreeDoc (document); - xmlFree (old_value); - return TRUE; - } + file = nautilus_file_get (path); + attributes = g_list_prepend (NULL, NAUTILUS_FILE_ATTRIBUTE_ACTIVATION_URI); + nautilus_file_invalidate_attributes (file, attributes); + nautilus_file_unref (file); + g_list_free (attributes); - xmlFree (old_value); - - /* Change and write the property. */ - xmlSetProp (root, key, value); - xmlSaveFile (path, document); - xmlFreeDoc (document); - - /* Notify about the change. */ - uri = gnome_vfs_get_uri_from_local_path (path); - file = nautilus_file_get (uri); - if (file != NULL) { - if (extra_notify != NULL) { - (* extra_notify) (file); - } - nautilus_file_changed (file); - nautilus_file_unref (file); - } - g_free (uri); - - return TRUE; + return retval; } -/* Set the icon for a link file. This can only be called on local - * paths, and only on files known to be link files. - */ gboolean -nautilus_link_local_set_icon (const char *path, const char *icon_name) +nautilus_link_local_set_link_uri (const char *path, const char *link_uri) { - return local_set_root_property (path, - NAUTILUS_METADATA_KEY_CUSTOM_ICON, - icon_name, - NULL); -} + const gchar *mime_type; + gboolean retval; + NautilusFile *file; + GList *attributes; + mime_type = get_uri_mime_type_full (path); + retval = FALSE; -static void -forget_file_activation_uri (NautilusFile *file) -{ - GList *attributes; + if (mime_type == NULL) { + return retval; + } + if (strcmp (mime_type, "application/x-nautilus-link") == 0) { + retval = nautilus_link_historical_local_set_link_uri (path, link_uri); + } + /* FIXME: May want to implement this for desktop files too */ + + file = nautilus_file_get (path); attributes = g_list_prepend (NULL, NAUTILUS_FILE_ATTRIBUTE_ACTIVATION_URI); nautilus_file_invalidate_attributes (file, attributes); + nautilus_file_unref (file); g_list_free (attributes); -} -/* Set the link uri for a link file. This can only be called on local - * paths, and only on files known to be link files. - */ -gboolean -nautilus_link_local_set_link_uri (const char *path, const char *link_uri) -{ - return local_set_root_property (path, - "link", - link_uri, - forget_file_activation_uri); + return retval; } gboolean nautilus_link_local_set_type (const char *path, NautilusLinkType type) { - return local_set_root_property (path, - "nautilus_link", - get_tag (type), - NULL); + const gchar *mime_type; + gboolean retval; + + mime_type = get_uri_mime_type_full (path); + retval = FALSE; + + if (mime_type == NULL) { + return retval; + } + + if (strcmp (mime_type, "application/x-nautilus-link") == 0) { + retval = nautilus_link_historical_local_set_type (path, type); + } + /* FIXME: May want to implement this for desktop files too */ + + return retval; } /* returns additional text to display under the name, NULL if none */ char * nautilus_link_local_get_additional_text (const char *path) { - return local_get_root_property - (path, NAUTILUS_METADATA_KEY_EXTRA_TEXT); -} + const gchar *mime_type; + gchar *retval; -/* utility to return the local pathname of a cached icon, given the leaf name */ -/* if the icons directory hasn't been created yet, create it */ -static char * -make_local_path (const char *image_uri) -{ - GnomeVFSResult result; - - char *escaped_uri, *local_directory_path, *local_directory_uri, *local_file_path; - - escaped_uri = gnome_vfs_escape_slashes (image_uri); - - local_directory_path = g_strconcat - (g_get_home_dir (), - "/.nautilus/remote_icons", - NULL); - - /* We must create the directory if it doesn't exist. */ - local_directory_uri = gnome_vfs_get_uri_from_local_path (local_directory_path); - result = gnome_vfs_make_directory (local_directory_uri, REMOTE_ICON_DIR_PERMISSIONS); - if (result != GNOME_VFS_OK) { - g_free (local_directory_uri); - g_free (escaped_uri); - g_free (local_directory_path); - return NULL; - } - - local_file_path = nautilus_make_path (local_directory_path, escaped_uri); - g_free (local_directory_uri); - g_free (escaped_uri); - g_free (local_directory_path); + mime_type = get_uri_mime_type_full (path); + retval = NULL; - return local_file_path; -} + if (mime_type == NULL) { + return retval; + } -/* utility to free the icon notification info */ + if (strcmp (mime_type, "application/x-nautilus-link") == 0) { + retval = nautilus_link_historical_local_get_additional_text (path); + } else if (strcmp (mime_type, "application/x-gnome-app-info") == 0) { + retval = nautilus_link_desktop_file_local_get_additional_text (path); + } -static void -free_icon_notification_info (NautilusLinkIconNotificationInfo *info) -{ - g_free (info->link_uri); - g_free (info->file_path); - g_free (info); + return retval; } -/* callback to handle the asynchronous reading of icons */ -static void -icon_read_done_callback (GnomeVFSResult result, - GnomeVFSFileSize file_size, - char *file_contents, - gpointer callback_data) +/* Returns the link uri associated with a link file. */ +char * +nautilus_link_local_get_link_uri (const char *path) { - int size; - FILE* outfile; - NautilusFile *file; - NautilusLinkIconNotificationInfo *info; - - info = (NautilusLinkIconNotificationInfo *) callback_data; + const gchar *mime_type; + gchar *retval; - if (result != GNOME_VFS_OK) { - g_assert (file_contents == NULL); - free_icon_notification_info (info); - return; - } + mime_type = get_uri_mime_type_full (path); + retval = NULL; - /* write out the file into the cache area */ - size = file_size; - outfile = fopen (info->file_path, "wb"); - fwrite (file_contents, size, 1, outfile); - fclose (outfile); - - g_free (file_contents); + if (mime_type == NULL) { + return retval; + } - /* tell the world that the file has changed */ - file = nautilus_file_get (info->link_uri); - if (file != NULL) { - nautilus_file_changed (file); - nautilus_file_unref (file); + if (strcmp (mime_type, "application/x-nautilus-link") == 0) { + retval = nautilus_link_historical_local_get_link_uri (path); + } else if (strcmp (mime_type, "application/x-gnome-app-info") == 0) { + retval = nautilus_link_desktop_file_local_get_link_uri (path); } - - /* free up the notification info */ - free_icon_notification_info (info); + + return retval; } -/* returns the image associated with a link file */ -char * -nautilus_link_local_get_image_uri (const char *path) +/* Returns the link type of the link file. */ +NautilusLinkType +nautilus_link_local_get_link_type (const char *path) { - xmlDoc *doc; - char *icon_uri; - char *local_path, *local_uri; - NautilusLinkIconNotificationInfo *info; - - doc = xmlParseFile (path); - if (doc == NULL) { - return NULL; - } - - icon_uri = xml_get_root_property (doc, NAUTILUS_METADATA_KEY_CUSTOM_ICON); - xmlFreeDoc (doc); + const gchar *mime_type; + NautilusLinkType retval; - if (icon_uri == NULL) { - return NULL; + mime_type = get_uri_mime_type_full (path); + retval = NAUTILUS_LINK_GENERIC; + + if (mime_type == NULL) { + return retval; } - - /* if the image is remote, see if we can find it in our local cache */ - if (eel_is_remote_uri (icon_uri)) { - local_path = make_local_path (icon_uri); - if (local_path == NULL) { - g_free (icon_uri); - return NULL; - } - if (g_file_exists (local_path)) { - g_free (icon_uri); - local_uri = gnome_vfs_get_uri_from_local_path (local_path); - g_free (local_path); - return local_uri; - } - - /* load it asynchronously through gnome-vfs */ - info = g_new0 (NautilusLinkIconNotificationInfo, 1); - info->link_uri = gnome_vfs_get_uri_from_local_path (path); - info->file_path = local_path; - eel_read_entire_file_async (icon_uri, icon_read_done_callback, info); - - g_free (icon_uri); - return NULL; /* return NULL since the icon is still loading - it will get correctly set by the callback */ + + if (strcmp (mime_type, "application/x-nautilus-link") == 0) { + retval = nautilus_link_historical_local_get_link_type (path); + } else if (strcmp (mime_type, "application/x-gnome-app-info") == 0) { + retval = nautilus_link_desktop_file_local_get_link_type (path); } - - return icon_uri; + + return retval; } -/* Returns the link uri associated with a link file. */ char * -nautilus_link_local_get_link_uri (const char *path) +nautilus_link_get_link_uri_given_file_contents (const char *file_contents, + int file_size) { - return local_get_root_property (path, "link"); + const gchar *mime_type; + gchar *retval; + + mime_type = gnome_vfs_get_mime_type_for_data (file_contents, file_size); + retval = NULL; + + if (strcmp (mime_type, "application/x-nautilus-link") == 0) { + retval = nautilus_link_historical_get_link_uri_given_file_contents (file_contents, file_size); + } else if (strcmp (mime_type, "application/x-gnome-app-info") == 0) { + retval = nautilus_link_desktop_file_get_link_uri_given_file_contents (file_contents, file_size); + } + + return retval; } -/* Returns the link type of the link file. */ -NautilusLinkType -nautilus_link_local_get_link_type (const char *path) +char * +nautilus_link_get_link_name_given_file_contents (const char *file_contents, + int file_size) { - char *property; - NautilusLinkType type; - - property = local_get_root_property (path, "nautilus_link"); - type = get_link_type (property); - g_free (property); + const gchar *mime_type; + gchar *retval; + + mime_type = gnome_vfs_get_mime_type_for_data (file_contents, file_size); + retval = NULL; - return type; + if (strcmp (mime_type, "application/x-nautilus-link") == 0) { + retval = NULL; + } else if (strcmp (mime_type, "application/x-gnome-app-info") == 0) { + retval = nautilus_link_desktop_file_get_link_name_given_file_contents (file_contents, file_size); + } + + return retval; } -/* FIXME bugzilla.gnome.org 42495: - * Caller has to know to pass in a file with a NUL character at the end. - */ char * -nautilus_link_get_link_uri_given_file_contents (const char *file_contents, +nautilus_link_get_link_icon_given_file_contents (const char *file_contents, int file_size) { - xmlDoc *doc; - char *property; - - doc = xmlParseMemory ((char *) file_contents, file_size); - property = xml_get_root_property (doc, "link"); - xmlFreeDoc (doc); - return property; + const gchar *mime_type; + gchar *retval; + + mime_type = gnome_vfs_get_mime_type_for_data (file_contents, file_size); + retval = NULL; + + if (strcmp (mime_type, "application/x-nautilus-link") == 0) { + retval = nautilus_link_historical_get_link_icon_given_file_contents (file_contents, file_size); + } else if (strcmp (mime_type, "application/x-gnome-app-info") == 0) { + retval = nautilus_link_desktop_file_get_link_icon_given_file_contents (file_contents, file_size); + } + + return retval; } gboolean nautilus_link_local_is_volume_link (const char *path) { - return nautilus_link_local_get_link_type (path) == NAUTILUS_LINK_MOUNT; + const gchar *mime_type; + gboolean retval; + + mime_type = get_uri_mime_type_full (path); + retval = FALSE; + + if (mime_type == NULL) { + return retval; + } + + if (strcmp (mime_type, "application/x-nautilus-link") == 0) { + retval = nautilus_link_historical_local_is_volume_link (path); + } else if (strcmp (mime_type, "application/x-gnome-app-info") == 0) { + retval = nautilus_link_desktop_file_local_is_volume_link (path); + } + + return retval; } gboolean nautilus_link_local_is_home_link (const char *path) { - return nautilus_link_local_get_link_type (path) == NAUTILUS_LINK_HOME; + const gchar *mime_type; + gboolean retval; + + mime_type = get_uri_mime_type_full (path); + retval = FALSE; + + if (mime_type == NULL) { + return retval; + } + + if (strcmp (mime_type, "application/x-nautilus-link") == 0) { + retval = nautilus_link_historical_local_is_home_link (path); + } else if (strcmp (mime_type, "application/x-gnome-app-info") == 0) { + retval = nautilus_link_desktop_file_local_is_home_link (path); + } + + return retval; } gboolean nautilus_link_local_is_trash_link (const char *path) { - return nautilus_link_local_get_link_type (path) == NAUTILUS_LINK_TRASH; + const gchar *mime_type; + gboolean retval; + + mime_type = get_uri_mime_type_full (path); + retval = FALSE; + + if (mime_type == NULL) { + return retval; + } + + if (strcmp (mime_type, "application/x-nautilus-link") == 0) { + retval = nautilus_link_historical_local_is_trash_link (path); + } else if (strcmp (mime_type, "application/x-gnome-app-info") == 0) { + retval = nautilus_link_desktop_file_local_is_trash_link (path); + } + + return retval; } void nautilus_link_local_create_from_gnome_entry (GnomeDesktopEntry *entry, const char *dest_path, const GdkPoint *position) { - char *icon_name; - char *launch_string, *terminal_command; - char *quoted, *arguments, *temp_str; - int i; - - if (entry == NULL || dest_path == NULL) { - return; - } - - /* Extract arguments from exec array */ - arguments = NULL; - for (i = 0; i < entry->exec_length; ++i) { - quoted = eel_shell_quote (entry->exec[i]); - if (arguments == NULL) { - arguments = quoted; - } else { - temp_str = arguments; - arguments = g_strconcat (arguments, " ", quoted, NULL); - g_free (temp_str); - g_free (quoted); - } - } - - if (strcmp (entry->type, "Application") == 0) { - if (entry->terminal) { - terminal_command = eel_gnome_make_terminal_command (arguments); - launch_string = g_strconcat ("command:", terminal_command, NULL); - g_free (terminal_command); - } else { - launch_string = g_strconcat ("command:", arguments, NULL); - } - } else if (strcmp (entry->type, "URL") == 0) { - launch_string = g_strdup (arguments); - } else { - /* Unknown .desktop file type */ - launch_string = NULL; - } - - if (entry->icon != NULL) { - icon_name = eel_make_uri_from_half_baked_uri (entry->icon); - } else { - icon_name = g_strdup ("gnome-unknown.png"); - } - - if (launch_string != NULL) { - nautilus_link_local_create (dest_path, entry->name, icon_name, - launch_string, position, NAUTILUS_LINK_GENERIC); - } - - g_free (icon_name); - g_free (launch_string); - g_free (arguments); + nautilus_link_desktop_file_local_create_from_gnome_entry (entry, dest_path, position); } diff --git a/libnautilus-private/nautilus-link.h b/libnautilus-private/nautilus-link.h index 88882ccb5..a1864b91d 100644 --- a/libnautilus-private/nautilus-link.h +++ b/libnautilus-private/nautilus-link.h @@ -78,13 +78,6 @@ gboolean nautilus_link_local_set_link_uri (const char */ char * nautilus_link_local_get_additional_text (const char *path); -/* Returns the image associated with a link file. Despite the fact - * that it takes a URI parameter, works only if the file is local and - * does sync. I/O on the link, although it does async. on the image - * and caches if the image is remote. - */ -char * nautilus_link_local_get_image_uri (const char *path); - /* Returns the link type of a link file. * Works only if the file is local and does sync. I/O */ @@ -115,9 +108,12 @@ gboolean nautilus_link_local_is_trash_link (const char char * nautilus_link_local_get_link_uri (const char *path); char * nautilus_link_get_link_uri_given_file_contents (const char *link_file_contents, int link_file_size); - -void nautilus_link_local_create_from_gnome_entry (GnomeDesktopEntry *entry, - const char *dest_path, - const GdkPoint *position); +char * nautilus_link_get_link_name_given_file_contents (const char *file_contents, + int link_file_size); +char * nautilus_link_get_link_icon_given_file_contents (const char *file_contents, + int link_file_size); +void nautilus_link_local_create_from_gnome_entry (GnomeDesktopEntry *entry, + const char *dest_path, + const GdkPoint *position); #endif /* NAUTILUS_LINK_H */ |