Use Windows shell icons. Icons are stored in the current theme with the

2007-03-08  Mathias Hasselmann <mathias.hasselmann@gmx.de>

	* gtk/gtkfilesystemwin32.c: Use Windows shell icons. Icons are
	stored in the current theme with the name
	"gtk-win32-shell-icon;PATH;INDEX". PATH and INDEX reference shell
	icons as reported by SHGetFileInfoW. (#412221)


svn path=/trunk/; revision=17421
This commit is contained in:
Mathias Hasselmann 2007-03-07 23:17:38 +00:00 committed by Tor Lillqvist
parent 9aad97770a
commit 68d11dd108
2 changed files with 198 additions and 356 deletions

View File

@ -1,3 +1,10 @@
2007-03-08 Mathias Hasselmann <mathias.hasselmann@gmx.de>
* gtk/gtkfilesystemwin32.c: Use Windows shell icons. Icons are
stored in the current theme with the name
"gtk-win32-shell-icon;PATH;INDEX". PATH and INDEX reference shell
icons as reported by SHGetFileInfoW. (#412221)
2007-03-07 Matthias Clasen <mclasen@redhat.com> 2007-03-07 Matthias Clasen <mclasen@redhat.com>
* gtk/gtkexpander.c (gtk_expander_paint_focus): Draw the * gtk/gtkexpander.c (gtk_expander_paint_focus): Draw the

View File

@ -85,7 +85,8 @@ typedef enum {
ICON_NONE, /* "Could not compute the icon type" */ ICON_NONE, /* "Could not compute the icon type" */
ICON_REGULAR, /* Use mime type for icon */ ICON_REGULAR, /* Use mime type for icon */
ICON_DIRECTORY, ICON_DIRECTORY,
ICON_EXECUTABLE ICON_EXECUTABLE,
ICON_VOLUME
} IconType; } IconType;
@ -1331,28 +1332,165 @@ get_icon_type_from_stat (WIN32_FILE_ATTRIBUTE_DATA *wfad)
return ICON_REGULAR; return ICON_REGULAR;
} }
/* Renders a fallback icon from the stock system */ /* Computes our internal icon type based on a path name; also returns the MIME
* type in case we come up with ICON_REGULAR.
*/
static IconType
get_icon_type_from_path (GtkFileFolderWin32 *folder_win32,
WIN32_FILE_ATTRIBUTE_DATA *wfad,
const char *filename)
{
IconType icon_type;
if (folder_win32 && folder_win32->have_stat)
{
char *basename;
struct stat_info_entry *entry;
g_assert (folder_win32->stat_info != NULL);
basename = g_path_get_basename (filename);
entry = g_hash_table_lookup (folder_win32->stat_info, basename);
g_free (basename);
if (entry)
{
if (entry->icon_type == ICON_UNDECIDED)
{
entry->icon_type = get_icon_type_from_stat (&entry->wfad);
g_assert (entry->icon_type != ICON_UNDECIDED);
}
icon_type = entry->icon_type;
if (icon_type == ICON_REGULAR)
{
fill_in_mime_type (folder_win32);
}
return icon_type;
}
}
icon_type = get_icon_type_from_stat (wfad);
return icon_type;
}
static const gchar * static const gchar *
get_fallback_icon_name (IconType icon_type) get_fallback_icon_name (IconType icon_type)
{ {
const char *stock_name;
switch (icon_type) switch (icon_type)
{ {
case ICON_VOLUME:
return GTK_STOCK_HARDDISK;
case ICON_DIRECTORY: case ICON_DIRECTORY:
stock_name = GTK_STOCK_DIRECTORY; return GTK_STOCK_DIRECTORY;
break;
case ICON_EXECUTABLE: case ICON_EXECUTABLE:
stock_name = GTK_STOCK_EXECUTE; return GTK_STOCK_EXECUTE;
break;
default: default:
stock_name = GTK_STOCK_FILE; return GTK_STOCK_FILE;
break; }
}
static gchar *
get_icon_path (const gchar *filename,
IconType icon_type,
gint *index)
{
gchar *path = NULL;
g_return_val_if_fail (NULL != filename, NULL);
g_return_val_if_fail ('\0' != *filename, NULL);
g_return_val_if_fail (NULL != index, NULL);
SHFILEINFOW shfi;
wchar_t *wfn;
wfn = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
if (SHGetFileInfoW (wfn, 0, &shfi, sizeof (shfi), SHGFI_ICONLOCATION))
{
path = g_utf16_to_utf8 (shfi.szDisplayName, -1, NULL, NULL, NULL);
*index = shfi.iIcon;
} }
return stock_name; g_free (wfn);
return path;
}
static gboolean
create_builtin_icon (const gchar *filename,
const gchar *icon_name,
IconType icon_type)
{
static const DWORD attributes[] =
{
SHGFI_ICON | SHGFI_LARGEICON,
SHGFI_ICON | SHGFI_SMALLICON,
0
};
GdkPixbuf *pixbuf = NULL;
SHFILEINFOW shfi;
DWORD_PTR rc;
wchar_t *wfn;
int i;
g_return_val_if_fail (NULL != filename, FALSE);
g_return_val_if_fail ('\0' != *filename, FALSE);
wfn = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
for(i = 0; attributes[i]; ++i)
{
rc = SHGetFileInfoW (wfn, 0, &shfi, sizeof (shfi), attributes[i]);
if (rc && shfi.hIcon)
{
pixbuf = gdk_win32_icon_to_pixbuf_libgtk_only (shfi.hIcon);
if (!DestroyIcon (shfi.hIcon))
g_warning (G_STRLOC ": DestroyIcon failed: %s\n",
g_win32_error_message (GetLastError ()));
gtk_icon_theme_add_builtin_icon (icon_name,
gdk_pixbuf_get_height (pixbuf),
pixbuf);
g_object_unref (pixbuf);
}
}
g_free (wfn);
return (NULL != pixbuf); /* at least one icon was created */
}
gchar *
get_icon_name (const gchar *filename,
IconType icon_type)
{
gchar *icon_name = NULL;
gchar *icon_path = NULL;
gint icon_index = -1;
icon_path = get_icon_path(filename, icon_type, &icon_index);
if (icon_path)
icon_name = g_strdup_printf ("gtk-win32-shell-icon;%s;%d",
icon_path, icon_index);
else
icon_name = g_strdup_printf ("gtk-win32-shell-icon;%s",
filename);
if (!gtk_icon_theme_has_icon (gtk_icon_theme_get_default (), icon_name) &&
!create_builtin_icon (filename, icon_name, icon_type))
{
g_free (icon_name);
icon_name = g_strdup (get_fallback_icon_name (icon_type));
}
g_free (icon_path);
return icon_name;
} }
static gchar * static gchar *
@ -1360,10 +1498,7 @@ gtk_file_system_win32_volume_get_icon_name (GtkFileSystem *file_system,
GtkFileSystemVolume *volume, GtkFileSystemVolume *volume,
GError **error) GError **error)
{ {
/* FIXME: maybe we just always want to return GTK_STOCK_HARDDISK here? return get_icon_name (volume->drive, ICON_VOLUME);
* or the new tango icon name?
*/
return g_strdup ("gnome-dev-harddisk");
} }
#if 0 /* Unused, see below */ #if 0 /* Unused, see below */
@ -1675,280 +1810,6 @@ gtk_file_system_win32_filename_to_path (GtkFileSystem *file_system,
return filename_to_path (filename); return filename_to_path (filename);
} }
#if 0
/* These are unused currently, hmm */
static GdkPixbuf*
extract_icon (const char* filename)
{
#if 0
WORD iicon;
wchar_t *wfn;
wchar_t filename_copy[MAX_PATH];
#else
SHFILEINFOW shfi;
wchar_t *wfn = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
int rc;
#endif
GdkPixbuf *pixbuf = NULL;
HICON hicon;
if (!filename || !filename[0])
return NULL;
#if 0
/* ExtractAssociatedIconW() is about twice as slow as SHGetFileInfoW() */
/* The ugly ExtractAssociatedIcon modifies filename in place. It
* doesn't even take any argument saying how large the buffer is?
* Let's hope MAX_PATH will be large enough. What dork designed that
* API?
*/
wfn = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
if (wcslen (wfn) >= MAX_PATH)
{
g_free (wfn);
return NULL;
}
wcscpy (filename_copy, wfn);
g_free (wfn);
hicon = ExtractAssociatedIconW (GetModuleHandle (NULL), filename_copy, &iicon);
if (!hicon)
{
g_warning (G_STRLOC ":ExtractAssociatedIcon(%s) failed: %s", filename, g_win32_error_message (GetLastError ()));
return NULL;
}
#else
rc = (int) SHGetFileInfoW (wfn, 0, &shfi, sizeof (shfi),
SHGFI_ICON|SHGFI_LARGEICON);
g_free (wfn);
if (!rc)
return NULL;
hicon = shfi.hIcon;
#endif
pixbuf = gdk_win32_icon_to_pixbuf_libgtk_only (hicon);
if (!DestroyIcon (hicon))
g_warning (G_STRLOC ": DestroyIcon failed: %s\n", g_win32_error_message (GetLastError ()));
return pixbuf;
}
static GtkIconSet *
win32_pseudo_mime_lookup (const char* name)
{
gboolean use_cache = TRUE;
static GHashTable *mime_hash = NULL;
GtkIconSet *is = NULL;
char *p = strrchr(name, '.');
char *extension = p ? g_utf8_casefold (p, -1) : g_strdup ("");
GdkPixbuf *pixbuf;
/* Don't cache icons for files that might have embedded icons */
if (strcmp (extension, ".lnk") == 0 ||
strcmp (extension, ".exe") == 0 ||
strcmp (extension, ".dll") == 0)
{
use_cache = FALSE;
g_free (extension);
}
else
{
if (!mime_hash)
mime_hash = g_hash_table_new (g_str_hash, g_str_equal);
/* do we already have it ? */
is = g_hash_table_lookup (mime_hash, extension);
if (is)
{
g_free (extension);
return is;
}
}
/* create icon and set */
pixbuf = extract_icon (name);
if (pixbuf)
{
GtkIconSource* source = gtk_icon_source_new ();
is = gtk_icon_set_new_from_pixbuf (pixbuf);
gtk_icon_source_set_pixbuf (source, pixbuf);
gtk_icon_set_add_source (is, source);
gtk_icon_source_free (source);
}
if (use_cache)
g_hash_table_insert (mime_hash, extension, is);
return is;
}
#endif
/* Returns the name of the icon to be used for a path which is known to be a
* directory. This can vary for Home, Desktop, etc.
*/
static const char *
get_icon_name_for_directory (const char *path)
{
static char *desktop_path = NULL;
if (!g_get_home_dir ())
return "gnome-fs-directory";
if (!desktop_path)
desktop_path = g_build_filename (g_get_home_dir (), "Desktop", NULL);
if (strcmp (g_get_home_dir (), path) == 0)
return "gnome-fs-home";
else if (strcmp (desktop_path, path) == 0)
return "gnome-fs-desktop";
else
return "gnome-fs-directory";
return NULL;
}
/* Computes our internal icon type based on a path name; also returns the MIME
* type in case we come up with ICON_REGULAR.
*/
static IconType
get_icon_type_from_path (GtkFileFolderWin32 *folder_win32,
WIN32_FILE_ATTRIBUTE_DATA *wfad,
const char *filename,
const char **mime_type)
{
IconType icon_type;
*mime_type = NULL;
if (folder_win32 && folder_win32->have_stat)
{
char *basename;
struct stat_info_entry *entry;
g_assert (folder_win32->stat_info != NULL);
basename = g_path_get_basename (filename);
entry = g_hash_table_lookup (folder_win32->stat_info, basename);
g_free (basename);
if (entry)
{
if (entry->icon_type == ICON_UNDECIDED)
{
entry->icon_type = get_icon_type_from_stat (&entry->wfad);
g_assert (entry->icon_type != ICON_UNDECIDED);
}
icon_type = entry->icon_type;
if (icon_type == ICON_REGULAR)
{
fill_in_mime_type (folder_win32);
*mime_type = entry->mime_type;
}
return icon_type;
}
}
icon_type = get_icon_type_from_stat (wfad);
if (icon_type == ICON_REGULAR)
*mime_type = get_mime_type_for_file (filename, wfad);
return icon_type;
}
/* Renders an icon for a non-ICON_REGULAR file */
static const gchar *
get_special_icon_name (IconType icon_type,
const gchar *filename)
{
const char *name;
g_assert (icon_type != ICON_REGULAR);
switch (icon_type)
{
case ICON_DIRECTORY:
/* get_icon_name_for_directory() returns a dupped string */
return get_icon_name_for_directory (filename);
case ICON_EXECUTABLE:
name ="gnome-fs-executable";
break;
default:
g_assert_not_reached ();
return NULL;
}
return name;
}
static gchar *
get_icon_name_for_mime_type (const char *mime_type)
{
char *name;
const char *separator;
GString *icon_name;
if (!mime_type)
return NULL;
separator = strchr (mime_type, '/');
if (!separator)
return NULL; /* maybe we should return a GError with "invalid MIME-type" */
/* FIXME: we default to the gnome icon naming for now. Some question
* as below, how are we going to handle a second attempt?
*/
#if 0
icon_name = g_string_new ("");
g_string_append_len (icon_name, mime_type, separator - mime_type);
g_string_append_c (icon_name, '-');
g_string_append (icon_name, separator + 1);
pixbuf = get_cached_icon (widget, icon_name->str, pixel_size);
g_string_free (icon_name, TRUE);
if (pixbuf)
return pixbuf;
icon_name = g_string_new ("");
g_string_append_len (icon_name, mime_type, separator - mime_type);
g_string_append (icon_name, "-x-generic");
pixbuf = get_cached_icon (widget, icon_name->str, pixel_size);
g_string_free (icon_name, TRUE);
if (pixbuf)
return pixbuf;
#endif
icon_name = g_string_new ("gnome-mime-");
g_string_append_len (icon_name, mime_type, separator - mime_type);
g_string_append_c (icon_name, '-');
g_string_append (icon_name, separator + 1);
name = icon_name->str;
g_string_free (icon_name, FALSE);
return name;
/* FIXME: how are we going to implement a second attempt? */
#if 0
if (pixbuf)
return pixbuf;
icon_name = g_string_new ("gnome-mime-");
g_string_append_len (icon_name, mime_type, separator - mime_type);
pixbuf = get_cached_icon (widget, icon_name->str, pixel_size);
g_string_free (icon_name, TRUE);
return pixbuf;
#endif
}
static void static void
bookmark_list_free (GSList *list) bookmark_list_free (GSList *list)
{ {
@ -2499,36 +2360,10 @@ create_file_info (GtkFileFolderWin32 *folder_win32,
if (types & GTK_FILE_INFO_ICON) if (types & GTK_FILE_INFO_ICON)
{ {
IconType icon_type; IconType icon_type = get_icon_type_from_path (folder_win32, wfad, filename);
gboolean free_icon_name = FALSE; gchar *icon_name = get_icon_name (filename, icon_type);
const char *icon_name;
const char *icon_mime_type;
icon_type = get_icon_type_from_path (folder_win32, wfad, filename, &icon_mime_type);
switch (icon_type)
{
case ICON_NONE:
icon_name = get_fallback_icon_name (icon_type);
break;
case ICON_REGULAR:
free_icon_name = TRUE;
if (icon_mime_type)
icon_name = get_icon_name_for_mime_type (icon_mime_type);
else
icon_name = get_icon_name_for_mime_type (mime_type);
break;
default:
icon_name = get_special_icon_name (icon_type, filename);
break;
}
gtk_file_info_set_icon_name (info, icon_name); gtk_file_info_set_icon_name (info, icon_name);
g_free (icon_name);
if (free_icon_name)
g_free ((char *) icon_name);
} }
return info; return info;