Compute lucene-style scores for our hits.
[beagle.git] / chooser-fu / gtkfilesystembeagle.c
blob06cc47d3c6a6ddacb98745029e7457928b42fbec
1 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
3 /* GTK - The GIMP Toolkit
4 * gtkfilesystemunix.c: Default implementation of GtkFileSystem for UNIX-like systems
5 * Copyright (C) 2003, Red Hat, Inc.
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 * Boston, MA 02111-1307, USA.
23 #ifdef CONFIG_H
24 #include <config.h>
25 #endif
27 #include "beaglequery.h"
29 #include "gtkfilesystembeagle.h"
30 #define GTK_FILE_SYSTEM_ENABLE_UNSUPPORTED
31 #include "gtk/gtkfilesystem.h"
32 #include "gtk/gtkicontheme.h"
33 #include "gtk/gtkdialog.h"
34 #include "gtk/gtkstock.h"
35 #include "gtk/gtkbox.h"
36 #include "gtk/gtkhbox.h"
37 #include "gtk/gtklabel.h"
38 #include "gtk/gtkentry.h"
40 #define _(x) (x)
42 #define XDG_PREFIX _beagle_xdg
43 #include "xdgmime.h"
45 #include <errno.h>
46 #include <string.h>
47 #include <sys/stat.h>
48 #include <sys/types.h>
49 #include <pwd.h>
50 #include <unistd.h>
51 #include <stdio.h>
52 #include <time.h>
54 #define SEARCH_MAGIC ":search:"
56 #define BOOKMARKS_FILENAME ".gtk-bookmarks"
57 #define BOOKMARKS_TMP_FILENAME ".gtk-bookmarks-XXXXXX"
59 #define FOLDER_CACHE_LIFETIME 2 /* seconds */
61 typedef struct _GtkFileSystemUnixClass GtkFileSystemUnixClass;
63 #define GTK_FILE_SYSTEM_UNIX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_FILE_SYSTEM_UNIX, GtkFileSystemUnixClass))
64 #define GTK_IS_FILE_SYSTEM_UNIX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_FILE_SYSTEM_UNIX))
65 #define GTK_FILE_SYSTEM_UNIX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_FILE_SYSTEM_UNIX, GtkFileSystemUnixClass))
67 struct _GtkFileSystemUnixClass
69 GObjectClass parent_class;
72 struct _GtkFileSystemUnix
74 GObject parent_instance;
76 GHashTable *folder_hash;
79 /* Icon type, supplemented by MIME type
81 typedef enum {
82 ICON_UNDECIDED,
83 ICON_NONE,
84 ICON_REGULAR, /* Use mime type for icon */
85 ICON_BLOCK_DEVICE,
86 ICON_BROKEN_SYMBOLIC_LINK,
87 ICON_CHARACTER_DEVICE,
88 ICON_DIRECTORY,
89 ICON_EXECUTABLE,
90 ICON_FIFO,
91 ICON_SOCKET
92 } IconType;
95 #define GTK_TYPE_FILE_FOLDER_UNIX (gtk_file_folder_unix_get_type ())
96 #define GTK_FILE_FOLDER_UNIX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_FILE_FOLDER_UNIX, GtkFileFolderUnix))
97 #define GTK_IS_FILE_FOLDER_UNIX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_FILE_FOLDER_UNIX))
98 #define GTK_FILE_FOLDER_UNIX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_FILE_FOLDER_UNIX, GtkFileFolderUnixClass))
99 #define GTK_IS_FILE_FOLDER_UNIX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_FILE_FOLDER_UNIX))
100 #define GTK_FILE_FOLDER_UNIX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_FILE_FOLDER_UNIX, GtkFileFolderUnixClass))
102 typedef struct _GtkFileFolderUnix GtkFileFolderUnix;
103 typedef struct _GtkFileFolderUnixClass GtkFileFolderUnixClass;
105 struct _GtkFileFolderUnixClass
107 GObjectClass parent_class;
110 static time_t search_string_time = 0;
111 static gchar *search_string = NULL;
113 struct _GtkFileFolderUnix
115 GObject parent_instance;
117 gboolean search_magic;
118 BeagleQueryResult *query_result;
120 GtkFileSystemUnix *system_unix;
121 GtkFileInfoType types;
122 gchar *filename;
123 GHashTable *stat_info;
124 unsigned int have_stat : 1;
125 unsigned int have_mime_type : 1;
126 time_t asof;
129 struct stat_info_entry {
130 struct stat statbuf;
131 char *mime_type;
132 IconType icon_type;
135 static const GtkFileInfoType STAT_NEEDED_MASK = (GTK_FILE_INFO_IS_FOLDER |
136 GTK_FILE_INFO_IS_HIDDEN |
137 GTK_FILE_INFO_MODIFICATION_TIME |
138 GTK_FILE_INFO_SIZE);
140 static GObjectClass *system_parent_class;
141 static GObjectClass *folder_parent_class;
143 static void gtk_file_system_unix_class_init (GtkFileSystemUnixClass *class);
144 static void gtk_file_system_unix_iface_init (GtkFileSystemIface *iface);
145 static void gtk_file_system_unix_init (GtkFileSystemUnix *impl);
146 static void gtk_file_system_unix_finalize (GObject *object);
148 static GSList * gtk_file_system_unix_list_volumes (GtkFileSystem *file_system);
149 static GtkFileSystemVolume *gtk_file_system_unix_get_volume_for_path (GtkFileSystem *file_system,
150 const GtkFilePath *path);
152 static GtkFileFolder *gtk_file_system_unix_get_folder (GtkFileSystem *file_system,
153 const GtkFilePath *path,
154 GtkFileInfoType types,
155 GError **error);
156 static gboolean gtk_file_system_unix_create_folder (GtkFileSystem *file_system,
157 const GtkFilePath *path,
158 GError **error);
160 static void gtk_file_system_unix_volume_free (GtkFileSystem *file_system,
161 GtkFileSystemVolume *volume);
162 static GtkFilePath *gtk_file_system_unix_volume_get_base_path (GtkFileSystem *file_system,
163 GtkFileSystemVolume *volume);
164 static gboolean gtk_file_system_unix_volume_get_is_mounted (GtkFileSystem *file_system,
165 GtkFileSystemVolume *volume);
166 static gboolean gtk_file_system_unix_volume_mount (GtkFileSystem *file_system,
167 GtkFileSystemVolume *volume,
168 GError **error);
169 static gchar * gtk_file_system_unix_volume_get_display_name (GtkFileSystem *file_system,
170 GtkFileSystemVolume *volume);
171 static GdkPixbuf * gtk_file_system_unix_volume_render_icon (GtkFileSystem *file_system,
172 GtkFileSystemVolume *volume,
173 GtkWidget *widget,
174 gint pixel_size,
175 GError **error);
177 static gboolean gtk_file_system_unix_get_parent (GtkFileSystem *file_system,
178 const GtkFilePath *path,
179 GtkFilePath **parent,
180 GError **error);
181 static GtkFilePath * gtk_file_system_unix_make_path (GtkFileSystem *file_system,
182 const GtkFilePath *base_path,
183 const gchar *display_name,
184 GError **error);
185 static gboolean gtk_file_system_unix_parse (GtkFileSystem *file_system,
186 const GtkFilePath *base_path,
187 const gchar *str,
188 GtkFilePath **folder,
189 gchar **file_part,
190 GError **error);
192 static gchar * gtk_file_system_unix_path_to_uri (GtkFileSystem *file_system,
193 const GtkFilePath *path);
194 static gchar * gtk_file_system_unix_path_to_filename (GtkFileSystem *file_system,
195 const GtkFilePath *path);
196 static GtkFilePath *gtk_file_system_unix_uri_to_path (GtkFileSystem *file_system,
197 const gchar *uri);
198 static GtkFilePath *gtk_file_system_unix_filename_to_path (GtkFileSystem *file_system,
199 const gchar *filename);
201 static GdkPixbuf *gtk_file_system_unix_render_icon (GtkFileSystem *file_system,
202 const GtkFilePath *path,
203 GtkWidget *widget,
204 gint pixel_size,
205 GError **error);
207 static gboolean gtk_file_system_unix_insert_bookmark (GtkFileSystem *file_system,
208 const GtkFilePath *path,
209 gint position,
210 GError **error);
211 static gboolean gtk_file_system_unix_remove_bookmark (GtkFileSystem *file_system,
212 const GtkFilePath *path,
213 GError **error);
214 static GSList * gtk_file_system_unix_list_bookmarks (GtkFileSystem *file_system);
216 static GType gtk_file_folder_unix_get_type (void);
217 static void gtk_file_folder_unix_class_init (GtkFileFolderUnixClass *class);
218 static void gtk_file_folder_unix_iface_init (GtkFileFolderIface *iface);
219 static void gtk_file_folder_unix_init (GtkFileFolderUnix *impl);
220 static void gtk_file_folder_unix_finalize (GObject *object);
222 static GtkFileInfo *gtk_file_folder_unix_get_info (GtkFileFolder *folder,
223 const GtkFilePath *path,
224 GError **error);
225 static gboolean gtk_file_folder_unix_list_children (GtkFileFolder *folder,
226 GSList **children,
227 GError **error);
229 static gboolean gtk_file_folder_unix_is_finished_loading (GtkFileFolder *folder);
231 static GtkFilePath *filename_to_path (const gchar *filename);
233 static gboolean filename_is_root (const char *filename);
235 static gboolean fill_in_names (GtkFileFolderUnix *folder_unix, GError **error);
236 static gboolean fill_in_stats (GtkFileFolderUnix *folder_unix, GError **error);
237 static gboolean fill_in_mime_type (GtkFileFolderUnix *folder_unix, GError **error);
239 static char * get_parent_dir (const char *filename);
242 * GtkFileSystemUnix
244 static GType file_system_beagle_type = 0;
246 GType
247 gtk_file_system_beagle_get_type (void)
249 return file_system_beagle_type;
252 void
253 gtk_file_system_beagle_register_type (GTypeModule *module)
256 if (!file_system_beagle_type)
258 static const GTypeInfo file_system_beagle_info =
260 sizeof (GtkFileSystemUnixClass),
261 NULL, /* base_init */
262 NULL, /* base_finalize */
263 (GClassInitFunc) gtk_file_system_unix_class_init,
264 NULL, /* class_finalize */
265 NULL, /* class_data */
266 sizeof (GtkFileSystemUnix),
267 0, /* n_preallocs */
268 (GInstanceInitFunc) gtk_file_system_unix_init,
271 static const GInterfaceInfo file_system_info =
273 (GInterfaceInitFunc) gtk_file_system_unix_iface_init, /* interface_init */
274 NULL, /* interface_finalize */
275 NULL /* interface_data */
278 file_system_beagle_type = g_type_module_register_type (module,
279 G_TYPE_OBJECT,
280 "GtkFileSystemBeagle",
281 &file_system_beagle_info, 0);
282 g_type_module_add_interface (module,
283 file_system_beagle_type,
284 GTK_TYPE_FILE_SYSTEM,
285 &file_system_info);
288 return;
292 * gtk_file_system_unix_new:
294 * Creates a new #GtkFileSystemUnix object. #GtkFileSystemUnix
295 * implements the #GtkFileSystem interface with direct access to
296 * the filesystem using Unix/Linux API calls
298 * Return value: the new #GtkFileSystemUnix object
300 GtkFileSystem *
301 gtk_file_system_beagle_new (void)
303 return g_object_new (GTK_TYPE_FILE_SYSTEM_UNIX, NULL);
306 static void
307 gtk_file_system_unix_class_init (GtkFileSystemUnixClass *class)
309 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
311 system_parent_class = g_type_class_peek_parent (class);
313 gobject_class->finalize = gtk_file_system_unix_finalize;
316 static void
317 gtk_file_system_unix_iface_init (GtkFileSystemIface *iface)
319 iface->list_volumes = gtk_file_system_unix_list_volumes;
320 iface->get_volume_for_path = gtk_file_system_unix_get_volume_for_path;
321 iface->get_folder = gtk_file_system_unix_get_folder;
322 iface->create_folder = gtk_file_system_unix_create_folder;
323 iface->volume_free = gtk_file_system_unix_volume_free;
324 iface->volume_get_base_path = gtk_file_system_unix_volume_get_base_path;
325 iface->volume_get_is_mounted = gtk_file_system_unix_volume_get_is_mounted;
326 iface->volume_mount = gtk_file_system_unix_volume_mount;
327 iface->volume_get_display_name = gtk_file_system_unix_volume_get_display_name;
328 iface->volume_render_icon = gtk_file_system_unix_volume_render_icon;
329 iface->get_parent = gtk_file_system_unix_get_parent;
330 iface->make_path = gtk_file_system_unix_make_path;
331 iface->parse = gtk_file_system_unix_parse;
332 iface->path_to_uri = gtk_file_system_unix_path_to_uri;
333 iface->path_to_filename = gtk_file_system_unix_path_to_filename;
334 iface->uri_to_path = gtk_file_system_unix_uri_to_path;
335 iface->filename_to_path = gtk_file_system_unix_filename_to_path;
336 iface->render_icon = gtk_file_system_unix_render_icon;
337 iface->insert_bookmark = gtk_file_system_unix_insert_bookmark;
338 iface->remove_bookmark = gtk_file_system_unix_remove_bookmark;
339 iface->list_bookmarks = gtk_file_system_unix_list_bookmarks;
342 static void
343 gtk_file_system_unix_init (GtkFileSystemUnix *system_unix)
345 system_unix->folder_hash = g_hash_table_new (g_str_hash, g_str_equal);
348 static void
349 gtk_file_system_unix_finalize (GObject *object)
351 GtkFileSystemUnix *system_unix;
353 system_unix = GTK_FILE_SYSTEM_UNIX (object);
355 /* FIXME: assert that the hash is empty? */
356 g_hash_table_destroy (system_unix->folder_hash);
358 system_parent_class->finalize (object);
361 /* Returns our single root volume */
362 static GtkFileSystemVolume *
363 get_root_volume (void)
365 return (GtkFileSystemVolume *) gtk_file_path_new_dup ("/");
368 static GtkFileSystemVolume *
369 get_search_volume (void)
371 return (GtkFileSystemVolume *) gtk_file_path_new_dup (SEARCH_MAGIC);
374 static gboolean
375 is_search_volume (GtkFileSystemVolume *volume)
377 return ! strcmp (gtk_file_path_get_string ((GtkFilePath *) volume), SEARCH_MAGIC);
380 static gboolean
381 is_search_path (const GtkFilePath *path)
383 return ! strncmp (gtk_file_path_get_string (path), SEARCH_MAGIC, strlen (SEARCH_MAGIC));
386 static GSList *
387 gtk_file_system_unix_list_volumes (GtkFileSystem *file_system)
389 GSList *foo = g_slist_append (NULL, get_root_volume ());
390 foo = g_slist_append (foo, get_search_volume ());
391 return foo;
394 static GtkFileSystemVolume *
395 gtk_file_system_unix_get_volume_for_path (GtkFileSystem *file_system,
396 const GtkFilePath *path)
398 if (is_search_path (path))
399 return get_search_volume ();
400 else
401 return get_root_volume ();
404 static char *
405 remove_trailing_slash (const char *filename)
407 int len;
409 len = strlen (filename);
411 if (len > 1 && filename[len - 1] == '/')
412 return g_strndup (filename, len - 1);
413 else
414 return g_memdup (filename, len + 1);
417 static gchar *
418 get_search_string (void)
420 gchar *results = NULL;
421 GtkWidget *dialog;
422 GtkWindow *toplevel;
423 GtkWidget *hbox;
424 GtkWidget *label;
425 GtkWidget *entry;
426 gboolean refocus;
427 const char *title;
428 const char *accept_stock;
430 dialog = gtk_dialog_new_with_buttons ("Search",
431 NULL,
432 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_NO_SEPARATOR,
433 //GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
434 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
435 NULL);
436 gtk_window_set_default_size (GTK_WINDOW (dialog), 300, -1);
437 gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
438 gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog)->vbox), 2);
439 gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
441 hbox = gtk_hbox_new (FALSE, 12);
442 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), hbox, FALSE, FALSE, 0);
443 gtk_container_set_border_width (GTK_CONTAINER (hbox), 5);
445 label = gtk_label_new_with_mnemonic (_("Search For:"));
446 gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
448 entry = gtk_entry_new ();
449 gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE);
450 gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
451 gtk_label_set_mnemonic_widget (GTK_LABEL (label), entry);
453 /* Run */
455 gtk_widget_show_all (dialog);
457 refocus = TRUE;
459 gtk_dialog_run (GTK_DIALOG (dialog));
461 results = g_strdup (gtk_entry_get_text (GTK_ENTRY (entry)));
463 gtk_widget_destroy (dialog);
465 return results;
468 static GtkFileFolder *
469 gtk_file_system_unix_get_folder (GtkFileSystem *file_system,
470 const GtkFilePath *path,
471 GtkFileInfoType types,
472 GError **error)
474 GtkFileSystemUnix *system_unix;
475 GtkFileFolderUnix *folder_unix;
476 const char *filename;
477 char *filename_copy;
478 time_t now = time (NULL);
480 g_print ("Get folder [%s]\n", gtk_file_path_get_string (path));
482 system_unix = GTK_FILE_SYSTEM_UNIX (file_system);
484 if (is_search_path (path))
486 time_t now;
487 time (&now);
488 if (search_string == NULL || difftime (now, search_string_time) > 2) {
489 g_free (search_string);
490 search_string = get_search_string ();
491 time (&search_string_time);
494 folder_unix = g_object_new (GTK_TYPE_FILE_FOLDER_UNIX, NULL);
495 folder_unix->search_magic = TRUE;
496 return GTK_FILE_FOLDER (folder_unix);
499 filename = gtk_file_path_get_string (path);
500 g_return_val_if_fail (filename != NULL, NULL);
501 g_return_val_if_fail (g_path_is_absolute (filename), NULL);
503 filename_copy = remove_trailing_slash (filename);
504 folder_unix = g_hash_table_lookup (system_unix->folder_hash, filename_copy);
506 if (folder_unix)
508 g_free (filename_copy);
509 if (now - folder_unix->asof >= FOLDER_CACHE_LIFETIME &&
510 folder_unix->stat_info)
512 #if 0
513 g_print ("Cleaning out cached directory %s\n", filename);
514 #endif
515 g_hash_table_destroy (folder_unix->stat_info);
516 folder_unix->stat_info = NULL;
517 folder_unix->have_mime_type = FALSE;
518 folder_unix->have_stat = FALSE;
521 g_object_ref (folder_unix);
522 folder_unix->types |= types;
523 types = folder_unix->types;
525 else
527 if (!g_file_test (filename, G_FILE_TEST_IS_DIR))
529 int save_errno = errno;
530 gchar *filename_utf8 = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
532 /* If g_file_test() returned FALSE but not due to an error, it means
533 * that the filename is not a directory.
535 if (save_errno == 0)
536 /* ENOTDIR */
537 g_set_error (error,
538 GTK_FILE_SYSTEM_ERROR,
539 GTK_FILE_SYSTEM_ERROR_NOT_FOLDER,
540 _("%s: %s"),
541 filename_utf8 ? filename_utf8 : "???",
542 g_strerror (ENOTDIR));
543 else
544 g_set_error (error,
545 GTK_FILE_SYSTEM_ERROR,
546 GTK_FILE_SYSTEM_ERROR_NONEXISTENT,
547 _("error getting information for '%s': %s"),
548 filename_utf8 ? filename_utf8 : "???",
549 g_strerror (save_errno));
551 g_free (filename_utf8);
552 g_free (filename_copy);
553 return NULL;
556 folder_unix = g_object_new (GTK_TYPE_FILE_FOLDER_UNIX, NULL);
557 folder_unix->system_unix = system_unix;
558 folder_unix->filename = filename_copy;
559 folder_unix->types = types;
560 folder_unix->stat_info = NULL;
561 folder_unix->asof = now;
562 folder_unix->have_mime_type = FALSE;
563 folder_unix->have_stat = FALSE;
565 g_hash_table_insert (system_unix->folder_hash,
566 folder_unix->filename,
567 folder_unix);
570 if ((types & STAT_NEEDED_MASK) && !fill_in_stats (folder_unix, error))
572 g_object_unref (folder_unix);
573 return NULL;
575 if ((types & GTK_FILE_INFO_MIME_TYPE) && !fill_in_mime_type (folder_unix, error))
577 g_object_unref (folder_unix);
578 return NULL;
581 return GTK_FILE_FOLDER (folder_unix);
584 static gboolean
585 gtk_file_system_unix_create_folder (GtkFileSystem *file_system,
586 const GtkFilePath *path,
587 GError **error)
589 GtkFileSystemUnix *system_unix;
590 const char *filename;
591 gboolean result;
592 char *parent, *tmp;
594 system_unix = GTK_FILE_SYSTEM_UNIX (file_system);
596 filename = gtk_file_path_get_string (path);
597 g_return_val_if_fail (filename != NULL, FALSE);
598 g_return_val_if_fail (g_path_is_absolute (filename), FALSE);
600 tmp = remove_trailing_slash (filename);
601 result = mkdir (tmp, 0777) == 0;
602 g_free (tmp);
604 if (!result)
606 int save_errno = errno;
607 gchar *filename_utf8 = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
608 g_set_error (error,
609 GTK_FILE_SYSTEM_ERROR,
610 GTK_FILE_SYSTEM_ERROR_NONEXISTENT,
611 _("error creating directory '%s': %s"),
612 filename_utf8 ? filename_utf8 : "???",
613 g_strerror (save_errno));
614 g_free (filename_utf8);
615 return FALSE;
618 if (filename_is_root (filename))
619 return TRUE; /* hmmm, but with no notification */
621 parent = get_parent_dir (filename);
622 if (parent)
624 GtkFileFolderUnix *folder_unix;
626 folder_unix = g_hash_table_lookup (system_unix->folder_hash, parent);
627 if (folder_unix)
629 GtkFileInfoType types;
630 GtkFilePath *parent_path;
631 GSList *paths;
632 GtkFileFolder *folder;
634 /* This is sort of a hack. We re-get the folder, to ensure that the
635 * newly-created directory gets read into the folder's info hash table.
638 types = folder_unix->types;
640 parent_path = gtk_file_path_new_dup (parent);
641 folder = gtk_file_system_get_folder (file_system, parent_path, types, NULL);
642 gtk_file_path_free (parent_path);
644 if (folder)
646 paths = g_slist_append (NULL, (GtkFilePath *) path);
647 g_signal_emit_by_name (folder, "files-added", paths);
648 g_slist_free (paths);
649 g_object_unref (folder);
653 g_free (parent);
656 return TRUE;
659 static void
660 gtk_file_system_unix_volume_free (GtkFileSystem *file_system,
661 GtkFileSystemVolume *volume)
663 GtkFilePath *path;
665 path = (GtkFilePath *) volume;
666 gtk_file_path_free (path);
669 static GtkFilePath *
670 gtk_file_system_unix_volume_get_base_path (GtkFileSystem *file_system,
671 GtkFileSystemVolume *volume)
673 if (is_search_volume (volume)) {
674 /* Yet another ugly hack: by having the magic search volume
675 return a differently-named folder every time, it forces the
676 path-navigation button in the chooser to be refreshed
677 if you conduct multiple searches from inside of one chooser. */
678 static int magic_counter = 0;
679 char *path = g_strdup_printf ("%s:%d", SEARCH_MAGIC, magic_counter);
680 ++magic_counter;
681 return gtk_file_path_new_steal (path);
682 } else {
683 return gtk_file_path_new_dup ("/");
687 static gboolean
688 gtk_file_system_unix_volume_get_is_mounted (GtkFileSystem *file_system,
689 GtkFileSystemVolume *volume)
691 return TRUE;
694 static gboolean
695 gtk_file_system_unix_volume_mount (GtkFileSystem *file_system,
696 GtkFileSystemVolume *volume,
697 GError **error)
699 g_set_error (error,
700 GTK_FILE_SYSTEM_ERROR,
701 GTK_FILE_SYSTEM_ERROR_FAILED,
702 _("This file system does not support mounting"));
703 return FALSE;
706 static gchar *
707 gtk_file_system_unix_volume_get_display_name (GtkFileSystem *file_system,
708 GtkFileSystemVolume *volume)
711 if (is_search_volume (volume))
712 return g_strdup (_("Search"));
713 else
714 return g_strdup (_("Filesystem")); /* Same as Nautilus */
717 static IconType
718 get_icon_type_from_stat (struct stat *statp)
720 if (S_ISBLK (statp->st_mode))
721 return ICON_BLOCK_DEVICE;
722 else if (S_ISLNK (statp->st_mode))
723 return ICON_BROKEN_SYMBOLIC_LINK; /* See get_icon_type */
724 else if (S_ISCHR (statp->st_mode))
725 return ICON_CHARACTER_DEVICE;
726 else if (S_ISDIR (statp->st_mode))
727 return ICON_DIRECTORY;
728 else if (S_ISFIFO (statp->st_mode))
729 return ICON_FIFO;
730 else if (S_ISSOCK (statp->st_mode))
731 return ICON_SOCKET;
732 else
733 return ICON_REGULAR;
736 static IconType
737 get_icon_type (const char *filename,
738 GError **error)
740 struct stat statbuf;
742 /* If stat fails, try to fall back to lstat to catch broken links
744 if (stat (filename, &statbuf) != 0)
746 if (errno != ENOENT || lstat (filename, &statbuf) != 0)
748 int save_errno = errno;
749 gchar *filename_utf8 = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
750 g_set_error (error,
751 GTK_FILE_SYSTEM_ERROR,
752 GTK_FILE_SYSTEM_ERROR_NONEXISTENT,
753 _("error getting information for '%s': %s"),
754 filename_utf8 ? filename_utf8 : "???",
755 g_strerror (save_errno));
756 g_free (filename_utf8);
758 return ICON_NONE;
762 return get_icon_type_from_stat (&statbuf);
765 typedef struct
767 gint size;
768 GdkPixbuf *pixbuf;
769 } IconCacheElement;
771 static void
772 icon_cache_element_free (IconCacheElement *element)
774 if (element->pixbuf)
775 g_object_unref (element->pixbuf);
776 g_free (element);
779 static void
780 icon_theme_changed (GtkIconTheme *icon_theme)
782 GHashTable *cache;
784 /* Difference from the initial creation is that we don't
785 * reconnect the signal
787 cache = g_hash_table_new_full (g_str_hash, g_str_equal,
788 (GDestroyNotify)g_free,
789 (GDestroyNotify)icon_cache_element_free);
790 g_object_set_data_full (G_OBJECT (icon_theme), "gtk-file-icon-cache",
791 cache, (GDestroyNotify)g_hash_table_destroy);
794 static GdkPixbuf *
795 get_cached_icon (GtkWidget *widget,
796 const gchar *name,
797 gint pixel_size)
799 GtkIconTheme *icon_theme = gtk_icon_theme_get_for_screen (gtk_widget_get_screen (widget));
800 GHashTable *cache = g_object_get_data (G_OBJECT (icon_theme), "gtk-file-icon-cache");
801 IconCacheElement *element;
803 if (!cache)
805 cache = g_hash_table_new_full (g_str_hash, g_str_equal,
806 (GDestroyNotify)g_free,
807 (GDestroyNotify)icon_cache_element_free);
809 g_object_set_data_full (G_OBJECT (icon_theme), "gtk-file-icon-cache",
810 cache, (GDestroyNotify)g_hash_table_destroy);
811 g_signal_connect (icon_theme, "changed",
812 G_CALLBACK (icon_theme_changed), NULL);
815 element = g_hash_table_lookup (cache, name);
816 if (!element)
818 element = g_new0 (IconCacheElement, 1);
819 g_hash_table_insert (cache, g_strdup (name), element);
822 if (element->size != pixel_size)
824 if (element->pixbuf)
825 g_object_unref (element->pixbuf);
826 element->size = pixel_size;
827 element->pixbuf = gtk_icon_theme_load_icon (icon_theme, name,
828 pixel_size, 0, NULL);
831 return element->pixbuf ? g_object_ref (element->pixbuf) : NULL;
834 static GdkPixbuf *
835 gtk_file_system_unix_volume_render_icon (GtkFileSystem *file_system,
836 GtkFileSystemVolume *volume,
837 GtkWidget *widget,
838 gint pixel_size,
839 GError **error)
841 /* FIXME: set the GError if we can't load the icon */
842 if (is_search_volume (volume))
843 return get_cached_icon (widget, "gtk-find", pixel_size);
844 else
845 return get_cached_icon (widget, "gnome-fs-blockdev", pixel_size);
848 static char *
849 get_parent_dir (const char *filename)
851 int len;
853 len = strlen (filename);
855 /* Ignore trailing slashes */
856 if (len > 1 && filename[len - 1] == '/')
858 char *tmp, *parent;
860 tmp = g_strndup (filename, len - 1);
862 parent = g_path_get_dirname (tmp);
863 g_free (tmp);
865 return parent;
867 else
868 return g_path_get_dirname (filename);
871 static gboolean
872 gtk_file_system_unix_get_parent (GtkFileSystem *file_system,
873 const GtkFilePath *path,
874 GtkFilePath **parent,
875 GError **error)
877 const char *filename;
879 if (is_search_path (path))
881 *parent = NULL;
882 return TRUE;
885 filename = gtk_file_path_get_string (path);
886 g_return_val_if_fail (filename != NULL, FALSE);
887 g_return_val_if_fail (g_path_is_absolute (filename), FALSE);
889 if (filename_is_root (filename))
891 *parent = NULL;
893 else
895 gchar *parent_filename = get_parent_dir (filename);
896 *parent = filename_to_path (parent_filename);
897 g_free (parent_filename);
900 return TRUE;
903 static GtkFilePath *
904 gtk_file_system_unix_make_path (GtkFileSystem *file_system,
905 const GtkFilePath *base_path,
906 const gchar *display_name,
907 GError **error)
909 const char *base_filename;
910 gchar *filename;
911 gchar *full_filename;
912 GError *tmp_error = NULL;
913 GtkFilePath *result;
915 base_filename = gtk_file_path_get_string (base_path);
916 g_return_val_if_fail (base_filename != NULL, NULL);
917 g_return_val_if_fail (g_path_is_absolute (base_filename), NULL);
919 if (strchr (display_name, G_DIR_SEPARATOR))
921 g_set_error (error,
922 GTK_FILE_SYSTEM_ERROR,
923 GTK_FILE_SYSTEM_ERROR_BAD_FILENAME,
924 _("The name \"%s\" is not valid because it contains the character \"%s\". "
925 "Please use a different name."),
926 display_name,
927 G_DIR_SEPARATOR_S);
928 return NULL;
931 filename = g_filename_from_utf8 (display_name, -1, NULL, NULL, &tmp_error);
932 if (!filename)
934 g_set_error (error,
935 GTK_FILE_SYSTEM_ERROR,
936 GTK_FILE_SYSTEM_ERROR_BAD_FILENAME,
937 "%s",
938 tmp_error->message);
940 g_error_free (tmp_error);
942 return NULL;
945 full_filename = g_build_filename (base_filename, filename, NULL);
946 result = filename_to_path (full_filename);
947 g_free (filename);
948 g_free (full_filename);
950 return result;
953 /* If this was a publically exported function, it should return
954 * a dup'ed result, but we make it modify-in-place for efficiency
955 * here, and because it works for us.
957 static void
958 canonicalize_filename (gchar *filename)
960 gchar *p, *q;
961 gboolean last_was_slash = FALSE;
963 p = filename;
964 q = filename;
966 while (*p)
968 if (*p == G_DIR_SEPARATOR)
970 if (!last_was_slash)
971 *q++ = G_DIR_SEPARATOR;
973 last_was_slash = TRUE;
975 else
977 if (last_was_slash && *p == '.')
979 if (*(p + 1) == G_DIR_SEPARATOR ||
980 *(p + 1) == '\0')
982 if (*(p + 1) == '\0')
983 break;
985 p += 1;
987 else if (*(p + 1) == '.' &&
988 (*(p + 2) == G_DIR_SEPARATOR ||
989 *(p + 2) == '\0'))
991 if (q > filename + 1)
993 q--;
994 while (q > filename + 1 &&
995 *(q - 1) != G_DIR_SEPARATOR)
996 q--;
999 if (*(p + 2) == '\0')
1000 break;
1002 p += 2;
1004 else
1006 *q++ = *p;
1007 last_was_slash = FALSE;
1010 else
1012 *q++ = *p;
1013 last_was_slash = FALSE;
1017 p++;
1020 if (q > filename + 1 && *(q - 1) == G_DIR_SEPARATOR)
1021 q--;
1023 *q = '\0';
1026 /* Takes a user-typed filename and expands a tilde at the beginning of the string */
1027 static char *
1028 expand_tilde (const char *filename)
1030 const char *notilde;
1031 const char *slash;
1032 const char *home;
1034 if (filename[0] != '~')
1035 return g_strdup (filename);
1037 notilde = filename + 1;
1039 slash = strchr (notilde, G_DIR_SEPARATOR);
1040 if (!slash)
1041 return NULL;
1043 if (slash == notilde)
1045 home = g_get_home_dir ();
1047 if (!home)
1048 return g_strdup (filename);
1050 else
1052 char *username;
1053 struct passwd *passwd;
1055 username = g_strndup (notilde, slash - notilde);
1056 passwd = getpwnam (username);
1057 g_free (username);
1059 if (!passwd)
1060 return g_strdup (filename);
1062 home = passwd->pw_dir;
1065 return g_build_filename (home, G_DIR_SEPARATOR_S, slash + 1, NULL);
1068 static gboolean
1069 gtk_file_system_unix_parse (GtkFileSystem *file_system,
1070 const GtkFilePath *base_path,
1071 const gchar *str,
1072 GtkFilePath **folder,
1073 gchar **file_part,
1074 GError **error)
1076 const char *base_filename;
1077 gchar *filename;
1078 gchar *last_slash;
1079 gboolean result = FALSE;
1081 base_filename = gtk_file_path_get_string (base_path);
1082 g_return_val_if_fail (base_filename != NULL, FALSE);
1083 g_return_val_if_fail (g_path_is_absolute (base_filename), FALSE);
1085 filename = expand_tilde (str);
1086 if (!filename)
1088 g_set_error (error,
1089 GTK_FILE_SYSTEM_ERROR,
1090 GTK_FILE_SYSTEM_ERROR_BAD_FILENAME,
1091 "%s", ""); /* nothing for now, as we are string-frozen */
1092 return FALSE;
1095 last_slash = strrchr (filename, G_DIR_SEPARATOR);
1096 if (!last_slash)
1098 *folder = gtk_file_path_copy (base_path);
1099 *file_part = g_strdup (filename);
1100 result = TRUE;
1102 else
1104 gchar *folder_part;
1105 gchar *folder_path;
1106 GError *tmp_error = NULL;
1108 if (last_slash == filename)
1109 folder_part = g_strdup ("/");
1110 else
1111 folder_part = g_filename_from_utf8 (filename, last_slash - filename,
1112 NULL, NULL, &tmp_error);
1114 if (!folder_part)
1116 g_set_error (error,
1117 GTK_FILE_SYSTEM_ERROR,
1118 GTK_FILE_SYSTEM_ERROR_BAD_FILENAME,
1119 "%s",
1120 tmp_error->message);
1121 g_error_free (tmp_error);
1123 else
1125 if (folder_part[0] == G_DIR_SEPARATOR)
1126 folder_path = folder_part;
1127 else
1129 folder_path = g_build_filename (base_filename, folder_part, NULL);
1130 g_free (folder_part);
1133 canonicalize_filename (folder_path);
1135 *folder = filename_to_path (folder_path);
1136 *file_part = g_strdup (last_slash + 1);
1138 g_free (folder_path);
1140 result = TRUE;
1144 g_free (filename);
1146 return result;
1149 static gchar *
1150 gtk_file_system_unix_path_to_uri (GtkFileSystem *file_system,
1151 const GtkFilePath *path)
1153 return g_filename_to_uri (gtk_file_path_get_string (path), NULL, NULL);
1156 static gchar *
1157 gtk_file_system_unix_path_to_filename (GtkFileSystem *file_system,
1158 const GtkFilePath *path)
1160 return g_strdup (gtk_file_path_get_string (path));
1163 static GtkFilePath *
1164 gtk_file_system_unix_uri_to_path (GtkFileSystem *file_system,
1165 const gchar *uri)
1167 GtkFilePath *path;
1168 gchar *filename = g_filename_from_uri (uri, NULL, NULL);
1170 if (filename)
1172 path = filename_to_path (filename);
1173 g_free (filename);
1175 else
1176 path = NULL;
1178 return path;
1181 static GtkFilePath *
1182 gtk_file_system_unix_filename_to_path (GtkFileSystem *file_system,
1183 const gchar *filename)
1185 return filename_to_path (filename);
1188 static const char *
1189 get_icon_for_directory (const char *path)
1191 static char *desktop_path = NULL;
1193 if (!g_get_home_dir ())
1194 return "gnome-fs-directory";
1196 if (!desktop_path)
1197 desktop_path = g_build_filename (g_get_home_dir (), "Desktop", NULL);
1199 if (strcmp (g_get_home_dir (), path) == 0)
1200 return "gnome-fs-home";
1201 else if (strcmp (desktop_path, path) == 0)
1202 return "gnome-fs-desktop";
1203 else
1204 return "gnome-fs-directory";
1207 static GdkPixbuf *
1208 gtk_file_system_unix_render_icon (GtkFileSystem *file_system,
1209 const GtkFilePath *path,
1210 GtkWidget *widget,
1211 gint pixel_size,
1212 GError **error)
1214 const char *filename;
1215 IconType icon_type;
1216 const char *mime_type = NULL;
1217 char *dirname;
1218 GtkFileSystemUnix *system_unix;
1219 GtkFileFolderUnix *folder_unix;
1221 system_unix = GTK_FILE_SYSTEM_UNIX (file_system);
1222 filename = gtk_file_path_get_string (path);
1223 dirname = g_path_get_dirname (filename);
1224 folder_unix = g_hash_table_lookup (system_unix->folder_hash, dirname);
1225 g_free (dirname);
1227 if (folder_unix)
1229 char *basename;
1230 struct stat_info_entry *entry;
1232 if (!fill_in_stats (folder_unix, error))
1233 return NULL;
1235 basename = g_path_get_basename (filename);
1236 entry = g_hash_table_lookup (folder_unix->stat_info, basename);
1237 g_free (basename);
1238 if (entry)
1240 if (entry->icon_type == ICON_UNDECIDED)
1241 entry->icon_type = get_icon_type_from_stat (&entry->statbuf);
1242 icon_type = entry->icon_type;
1243 if (icon_type == ICON_REGULAR)
1245 (void)fill_in_mime_type (folder_unix, NULL);
1246 mime_type = entry->mime_type;
1249 else
1250 icon_type = ICON_NONE;
1252 else
1254 #if 0
1255 g_print ("No folder open for %s\n", filename);
1256 #endif
1258 icon_type = get_icon_type (filename, error);
1259 if (icon_type == ICON_REGULAR)
1260 mime_type = xdg_mime_get_mime_type_for_file (filename);
1264 /* FIXME: this function should not return NULL without setting the GError; we
1265 * should perhaps provide a "never fails" generic stock icon for when all else
1266 * fails.
1269 if (icon_type == ICON_NONE)
1270 return NULL;
1272 if (icon_type != ICON_REGULAR)
1274 const char *name;
1276 switch (icon_type)
1278 case ICON_BLOCK_DEVICE:
1279 name = "gnome-fs-blockdev";
1280 break;
1281 case ICON_BROKEN_SYMBOLIC_LINK:
1282 name = "gnome-fs-symlink";
1283 break;
1284 case ICON_CHARACTER_DEVICE:
1285 name = "gnome-fs-chardev";
1286 break;
1287 case ICON_DIRECTORY:
1288 name = get_icon_for_directory (filename);
1289 break;
1290 case ICON_EXECUTABLE:
1291 name ="gnome-fs-executable";
1292 break;
1293 case ICON_FIFO:
1294 name = "gnome-fs-fifo";
1295 break;
1296 case ICON_SOCKET:
1297 name = "gnome-fs-socket";
1298 break;
1299 default:
1300 g_assert_not_reached ();
1301 return NULL;
1304 return get_cached_icon (widget, name, pixel_size);
1307 if (mime_type)
1309 const char *separator;
1310 GString *icon_name;
1311 GdkPixbuf *pixbuf;
1313 separator = strchr (mime_type, '/');
1314 if (!separator)
1315 return NULL;
1317 icon_name = g_string_new ("gnome-mime-");
1318 g_string_append_len (icon_name, mime_type, separator - mime_type);
1319 g_string_append_c (icon_name, '-');
1320 g_string_append (icon_name, separator + 1);
1321 pixbuf = get_cached_icon (widget, icon_name->str, pixel_size);
1322 g_string_free (icon_name, TRUE);
1323 if (pixbuf)
1324 return pixbuf;
1326 icon_name = g_string_new ("gnome-mime-");
1327 g_string_append_len (icon_name, mime_type, separator - mime_type);
1328 pixbuf = get_cached_icon (widget, icon_name->str, pixel_size);
1329 g_string_free (icon_name, TRUE);
1330 if (pixbuf)
1331 return pixbuf;
1334 return get_cached_icon (widget, "gnome-fs-regular", pixel_size);
1337 static void
1338 bookmark_list_free (GSList *list)
1340 GSList *l;
1342 for (l = list; l; l = l->next)
1343 g_free (l->data);
1345 g_slist_free (list);
1348 /* Returns whether a URI is a local file:// */
1349 static gboolean
1350 is_local_uri (const char *uri)
1352 char *filename;
1353 char *hostname;
1354 gboolean result;
1356 /* This is rather crude, but hey */
1357 filename = g_filename_from_uri (uri, &hostname, NULL);
1359 result = (filename && !hostname);
1361 g_free (filename);
1362 g_free (hostname);
1364 return result;
1367 static char *
1368 bookmark_get_filename (gboolean tmp_file)
1370 char *filename;
1372 filename = g_build_filename (g_get_home_dir (),
1373 tmp_file ? BOOKMARKS_TMP_FILENAME : BOOKMARKS_FILENAME,
1374 NULL);
1375 g_assert (filename != NULL);
1376 return filename;
1379 static gboolean
1380 bookmark_list_read (GSList **bookmarks, GError **error)
1382 gchar *filename;
1383 gchar *contents;
1384 gboolean result = FALSE;
1386 filename = bookmark_get_filename (FALSE);
1387 *bookmarks = NULL;
1389 if (g_file_get_contents (filename, &contents, NULL, error))
1391 gchar **lines = g_strsplit (contents, "\n", -1);
1392 int i;
1393 GHashTable *table;
1395 table = g_hash_table_new (g_str_hash, g_str_equal);
1397 for (i = 0; lines[i]; i++)
1399 if (lines[i][0] && !g_hash_table_lookup (table, lines[i]))
1401 *bookmarks = g_slist_prepend (*bookmarks, g_strdup (lines[i]));
1402 g_hash_table_insert (table, lines[i], lines[i]);
1406 g_free (contents);
1407 g_hash_table_destroy (table);
1408 g_strfreev (lines);
1410 *bookmarks = g_slist_reverse (*bookmarks);
1411 result = TRUE;
1414 g_free (filename);
1416 return result;
1419 static gboolean
1420 bookmark_list_write (GSList *bookmarks, GError **error)
1422 char *tmp_filename;
1423 char *filename;
1424 gboolean result = TRUE;
1425 FILE *file;
1426 int fd;
1427 int saved_errno;
1429 /* First, write a temporary file */
1431 tmp_filename = bookmark_get_filename (TRUE);
1432 filename = bookmark_get_filename (FALSE);
1434 fd = g_mkstemp (tmp_filename);
1435 if (fd == -1)
1437 saved_errno = errno;
1438 goto io_error;
1441 if ((file = fdopen (fd, "w")) != NULL)
1443 GSList *l;
1445 for (l = bookmarks; l; l = l->next)
1446 if (fputs (l->data, file) == EOF
1447 || fputs ("\n", file) == EOF)
1449 saved_errno = errno;
1450 goto io_error;
1453 if (fclose (file) == EOF)
1455 saved_errno = errno;
1456 goto io_error;
1459 if (rename (tmp_filename, filename) == -1)
1461 saved_errno = errno;
1462 goto io_error;
1465 result = TRUE;
1466 goto out;
1468 else
1470 saved_errno = errno;
1472 /* fdopen() failed, so we can't do much error checking here anyway */
1473 close (fd);
1476 io_error:
1478 g_set_error (error,
1479 GTK_FILE_SYSTEM_ERROR,
1480 GTK_FILE_SYSTEM_ERROR_FAILED,
1481 _("Bookmark saving failed (%s)"),
1482 g_strerror (saved_errno));
1483 result = FALSE;
1485 if (fd != -1)
1486 unlink (tmp_filename); /* again, not much error checking we can do here */
1488 out:
1490 g_free (filename);
1491 g_free (tmp_filename);
1493 return result;
1496 static gboolean
1497 gtk_file_system_unix_insert_bookmark (GtkFileSystem *file_system,
1498 const GtkFilePath *path,
1499 gint position,
1500 GError **error)
1502 GSList *bookmarks;
1503 int num_bookmarks;
1504 GSList *l;
1505 char *uri;
1506 gboolean result;
1507 GError *err;
1509 err = NULL;
1510 if (!bookmark_list_read (&bookmarks, &err) && err->code != G_FILE_ERROR_NOENT)
1512 g_propagate_error (error, err);
1513 return FALSE;
1516 num_bookmarks = g_slist_length (bookmarks);
1517 g_return_val_if_fail (position >= -1 && position <= num_bookmarks, FALSE);
1519 result = FALSE;
1521 uri = gtk_file_system_unix_path_to_uri (file_system, path);
1523 for (l = bookmarks; l; l = l->next)
1525 const char *bookmark;
1527 bookmark = l->data;
1528 if (strcmp (bookmark, uri) == 0)
1530 g_set_error (error,
1531 GTK_FILE_SYSTEM_ERROR,
1532 GTK_FILE_SYSTEM_ERROR_ALREADY_EXISTS,
1533 "%s already exists in the bookmarks list",
1534 uri);
1535 goto out;
1539 bookmarks = g_slist_insert (bookmarks, g_strdup (uri), position);
1540 if (bookmark_list_write (bookmarks, error))
1542 result = TRUE;
1543 g_signal_emit_by_name (file_system, "bookmarks-changed", 0);
1546 out:
1548 g_free (uri);
1549 bookmark_list_free (bookmarks);
1551 return result;
1554 static gboolean
1555 gtk_file_system_unix_remove_bookmark (GtkFileSystem *file_system,
1556 const GtkFilePath *path,
1557 GError **error)
1559 GSList *bookmarks;
1560 char *uri;
1561 GSList *l;
1562 gboolean result;
1564 if (!bookmark_list_read (&bookmarks, error))
1565 return FALSE;
1567 result = FALSE;
1569 uri = gtk_file_system_path_to_uri (file_system, path);
1571 for (l = bookmarks; l; l = l->next)
1573 const char *bookmark;
1575 bookmark = l->data;
1576 if (strcmp (bookmark, uri) == 0)
1578 g_free (l->data);
1579 bookmarks = g_slist_remove_link (bookmarks, l);
1580 g_slist_free_1 (l);
1582 if (bookmark_list_write (bookmarks, error))
1584 result = TRUE;
1585 g_signal_emit_by_name (file_system, "bookmarks-changed", 0);
1588 goto out;
1592 g_set_error (error,
1593 GTK_FILE_SYSTEM_ERROR,
1594 GTK_FILE_SYSTEM_ERROR_NONEXISTENT,
1595 "%s does not exist in the bookmarks list",
1596 uri);
1598 out:
1600 g_free (uri);
1601 bookmark_list_free (bookmarks);
1603 return result;
1606 static GSList *
1607 gtk_file_system_unix_list_bookmarks (GtkFileSystem *file_system)
1609 GSList *bookmarks;
1610 GSList *result;
1611 GSList *l;
1613 if (!bookmark_list_read (&bookmarks, NULL))
1614 return NULL;
1616 result = NULL;
1618 for (l = bookmarks; l; l = l->next)
1620 const char *name;
1622 name = l->data;
1624 if (is_local_uri (name))
1625 result = g_slist_prepend (result, gtk_file_system_unix_uri_to_path (file_system, name));
1628 bookmark_list_free (bookmarks);
1630 result = g_slist_reverse (result);
1631 return result;
1635 * GtkFileFolderUnix
1637 static GType file_folder_unix_type = 0;
1639 static GType
1640 gtk_file_folder_unix_get_type (void)
1642 return file_folder_unix_type;
1645 static GType
1646 gtk_file_folder_unix_register_type (GTypeModule *module)
1648 if (!file_folder_unix_type)
1650 static const GTypeInfo file_folder_unix_info =
1652 sizeof (GtkFileFolderUnixClass),
1653 NULL, /* base_init */
1654 NULL, /* base_finalize */
1655 (GClassInitFunc) gtk_file_folder_unix_class_init,
1656 NULL, /* class_finalize */
1657 NULL, /* class_data */
1658 sizeof (GtkFileFolderUnix),
1659 0, /* n_preallocs */
1660 (GInstanceInitFunc) gtk_file_folder_unix_init,
1663 static const GInterfaceInfo file_folder_info =
1665 (GInterfaceInitFunc) gtk_file_folder_unix_iface_init, /* interface_init */
1666 NULL, /* interface_finalize */
1667 NULL /* interface_data */
1670 file_folder_unix_type = g_type_module_register_type (module,
1671 G_TYPE_OBJECT,
1672 "GtkFileFolderBeagle",
1673 &file_folder_unix_info, 0);
1674 g_type_module_add_interface (module,
1675 file_folder_unix_type,
1676 GTK_TYPE_FILE_FOLDER,
1677 &file_folder_info);
1680 return file_folder_unix_type;
1683 static void
1684 gtk_file_folder_unix_class_init (GtkFileFolderUnixClass *class)
1686 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
1688 folder_parent_class = g_type_class_peek_parent (class);
1690 gobject_class->finalize = gtk_file_folder_unix_finalize;
1693 static void
1694 gtk_file_folder_unix_iface_init (GtkFileFolderIface *iface)
1696 iface->get_info = gtk_file_folder_unix_get_info;
1697 iface->list_children = gtk_file_folder_unix_list_children;
1698 /*iface->is_finished_loading = gtk_file_folder_unix_is_finished_loading;*/
1701 static void
1702 gtk_file_folder_unix_init (GtkFileFolderUnix *impl)
1706 static void
1707 gtk_file_folder_unix_finalize (GObject *object)
1709 GtkFileFolderUnix *folder_unix = GTK_FILE_FOLDER_UNIX (object);
1711 if (folder_unix->search_magic)
1713 beagle_query_result_free (folder_unix->query_result);
1714 folder_parent_class->finalize (object);
1715 return;
1718 g_hash_table_remove (folder_unix->system_unix->folder_hash, folder_unix->filename);
1720 if (folder_unix->stat_info)
1722 #if 0
1723 g_print ("Releasing information for directory %s\n", folder_unix->filename);
1724 #endif
1725 g_hash_table_destroy (folder_unix->stat_info);
1728 g_free (folder_unix->filename);
1730 folder_parent_class->finalize (object);
1733 static GtkFileInfo *
1734 gtk_file_folder_unix_get_info (GtkFileFolder *folder,
1735 const GtkFilePath *path,
1736 GError **error)
1738 GtkFileFolderUnix *folder_unix = GTK_FILE_FOLDER_UNIX (folder);
1739 GtkFileInfo *info;
1740 gchar *dirname, *basename;
1741 const char *filename;
1742 struct stat_info_entry *entry;
1743 gboolean file_must_exist;
1744 GtkFileInfoType types;
1746 if (folder_unix->search_magic)
1748 if (! path)
1750 gchar *str;
1751 info = gtk_file_info_new ();
1752 str = g_strdup_printf ("Search Results: %s", search_string);
1753 gtk_file_info_set_display_name (info, str);
1754 g_free (str);
1755 gtk_file_info_set_is_folder (info, TRUE);
1756 gtk_file_info_set_is_hidden (info, FALSE);
1757 gtk_file_info_set_mime_type (info, "x-directory/normal");
1758 return info;
1759 } else {
1760 const char *filename = gtk_file_path_get_string (path);
1761 char *uri = g_strconcat ("file://", filename, NULL);
1762 BeagleHit *hit = beagle_query_result_get_by_uri (folder_unix->query_result, uri);
1763 info = gtk_file_info_new ();
1764 gtk_file_info_set_display_name (info, filename); /* skip over file:// */
1765 gtk_file_info_set_is_hidden (info, FALSE);
1766 gtk_file_info_set_is_folder (info, FALSE);
1767 gtk_file_info_set_mime_type (info, beagle_hit_get_mime_type (hit));
1768 gtk_file_info_set_modification_time (info, beagle_hit_get_timestamp (hit));
1769 g_free (uri);
1770 return info;
1774 if (!path)
1776 struct stat buf;
1778 g_return_val_if_fail (filename_is_root (folder_unix->filename), NULL);
1780 if (stat (folder_unix->filename, &buf) != 0)
1781 return NULL;
1783 info = gtk_file_info_new ();
1784 gtk_file_info_set_display_name (info, "/");
1785 gtk_file_info_set_is_folder (info, TRUE);
1786 gtk_file_info_set_is_hidden (info, FALSE);
1787 gtk_file_info_set_mime_type (info, "x-directory/normal");
1788 gtk_file_info_set_modification_time (info, buf.st_mtime);
1789 gtk_file_info_set_size (info, buf.st_size);
1791 return info;
1794 filename = gtk_file_path_get_string (path);
1795 g_return_val_if_fail (filename != NULL, NULL);
1796 g_return_val_if_fail (g_path_is_absolute (filename), NULL);
1798 if (! folder_unix->search_magic) {
1799 dirname = get_parent_dir (filename);
1800 g_return_val_if_fail (strcmp (dirname, folder_unix->filename) == 0, NULL);
1801 g_free (dirname);
1804 basename = g_path_get_basename (filename);
1805 types = folder_unix->types;
1806 file_must_exist = (types & ~GTK_FILE_INFO_DISPLAY_NAME) != 0;
1807 entry = file_must_exist
1808 ? g_hash_table_lookup (folder_unix->stat_info, basename)
1809 : NULL;
1810 /* basename freed later. */
1812 if (!file_must_exist || entry)
1814 info = gtk_file_info_new ();
1816 else
1818 gchar *filename_utf8 = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
1819 g_set_error (error,
1820 GTK_FILE_SYSTEM_ERROR,
1821 GTK_FILE_SYSTEM_ERROR_NONEXISTENT,
1822 _("error getting information for '%s'"),
1823 filename_utf8 ? filename_utf8 : "???");
1824 g_free (filename_utf8);
1825 info = NULL;
1826 types = 0;
1829 if (types & GTK_FILE_INFO_DISPLAY_NAME)
1831 gchar *display_name = g_filename_to_utf8 (basename, -1, NULL, NULL, NULL);
1832 if (!display_name)
1833 display_name = g_strescape (basename, NULL);
1835 gtk_file_info_set_display_name (info, display_name);
1837 g_free (display_name);
1840 if (types & GTK_FILE_INFO_IS_HIDDEN)
1841 gtk_file_info_set_is_hidden (info, basename[0] == '.');
1843 if (types & GTK_FILE_INFO_IS_FOLDER)
1844 gtk_file_info_set_is_folder (info, S_ISDIR (entry->statbuf.st_mode));
1846 if (types & GTK_FILE_INFO_MIME_TYPE)
1847 gtk_file_info_set_mime_type (info, entry->mime_type);
1849 if (types & GTK_FILE_INFO_MODIFICATION_TIME)
1850 gtk_file_info_set_modification_time (info, entry->statbuf.st_mtime);
1852 if (types & GTK_FILE_INFO_SIZE)
1853 gtk_file_info_set_size (info, (gint64)entry->statbuf.st_size);
1855 g_free (basename);
1857 return info;
1861 static void
1862 cb_list_children (gpointer key, gpointer value, gpointer user_data)
1864 GSList **children = user_data;
1865 *children = g_slist_prepend (*children, key);
1868 static void
1869 beagle_query_result_list_children (BeagleQueryResult *bqr, GSList **children)
1871 GSList *all;
1873 for (all = beagle_query_result_get_all (bqr); all != NULL; all = all->next) {
1874 BeagleHit *hit = all->data;
1876 /* Filter out non-file:// Uris */
1877 if (! strncmp (beagle_hit_get_uri (hit), "file://", 7)) {
1878 *children = g_slist_prepend (*children, filename_to_path (beagle_hit_get_uri (hit) + 7));
1883 static gboolean
1884 gtk_file_folder_unix_list_children (GtkFileFolder *folder,
1885 GSList **children,
1886 GError **error)
1888 GtkFileFolderUnix *folder_unix = GTK_FILE_FOLDER_UNIX (folder);
1889 GSList *l;
1891 g_print ("List Children\n");
1893 if (folder_unix->search_magic)
1895 if (search_string != NULL) {
1896 folder_unix->query_result = beagle_query (search_string);
1897 time (&search_string_time);
1899 *children = NULL;
1900 beagle_query_result_list_children (folder_unix->query_result, children);
1901 return TRUE;
1904 if (!fill_in_names (folder_unix, error))
1905 return FALSE;
1907 *children = NULL;
1909 /* Get the list of basenames. */
1910 g_hash_table_foreach (folder_unix->stat_info, cb_list_children, children);
1912 /* Turn basenames into GFilePaths. */
1913 for (l = *children; l; l = l->next)
1915 const char *basename = l->data;
1916 char *fullname = g_build_filename (folder_unix->filename, basename, NULL);
1917 l->data = filename_to_path (fullname);
1918 g_free (fullname);
1920 return TRUE;
1923 static gboolean
1924 gtk_file_folder_unix_is_finished_loading (GtkFileFolder *folder)
1926 /* Since we don't do asynchronous loads, we are always finished loading */
1927 return TRUE;
1930 static void
1931 free_stat_info_entry (struct stat_info_entry *entry)
1933 g_free (entry->mime_type);
1934 g_free (entry);
1937 static gboolean
1938 fill_in_names (GtkFileFolderUnix *folder_unix, GError **error)
1940 GDir *dir;
1942 if (folder_unix->stat_info)
1943 return TRUE;
1945 #if 0
1946 g_print ("Reading directory %s\n", folder_unix->filename);
1947 #endif
1949 folder_unix->stat_info = g_hash_table_new_full (g_str_hash, g_str_equal,
1950 (GDestroyNotify)g_free,
1951 (GDestroyNotify)free_stat_info_entry);
1952 dir = g_dir_open (folder_unix->filename, 0, error);
1953 if (!dir)
1955 int save_errno = errno;
1956 gchar *filename_utf8 = g_filename_to_utf8 (folder_unix->filename, -1, NULL, NULL, NULL);
1957 g_set_error (error,
1958 GTK_FILE_SYSTEM_ERROR,
1959 GTK_FILE_SYSTEM_ERROR_NONEXISTENT,
1960 _("error getting information for '%s': %s"),
1961 filename_utf8 ? filename_utf8 : "???",
1962 g_strerror (save_errno));
1963 g_free (filename_utf8);
1964 return FALSE;
1967 while (TRUE)
1969 const gchar *basename = g_dir_read_name (dir);
1970 if (!basename)
1971 break;
1973 g_hash_table_insert (folder_unix->stat_info,
1974 g_strdup (basename),
1975 g_new0 (struct stat_info_entry, 1));
1978 g_dir_close (dir);
1980 folder_unix->asof = time (NULL);
1981 return TRUE;
1984 static gboolean
1985 cb_fill_in_stats (gpointer key, gpointer value, gpointer user_data)
1987 const char *basename = key;
1988 struct stat_info_entry *entry = value;
1989 GtkFileFolderUnix *folder_unix = user_data;
1990 char *fullname = g_build_filename (folder_unix->filename, basename, NULL);
1991 gboolean result;
1993 if (stat (fullname, &entry->statbuf) == -1 &&
1994 (errno != ENOENT || lstat (fullname, &entry->statbuf) == -1))
1995 result = TRUE; /* Couldn't stat -- remove from hash. */
1996 else
1997 result = FALSE;
1999 g_free (fullname);
2000 return result;
2004 static gboolean
2005 fill_in_stats (GtkFileFolderUnix *folder_unix, GError **error)
2007 if (folder_unix->have_stat)
2008 return TRUE;
2010 if (!fill_in_names (folder_unix, error))
2011 return FALSE;
2013 #if 0
2014 g_print ("Stating directory %s\n", folder_unix->filename);
2015 #endif
2016 g_hash_table_foreach_remove (folder_unix->stat_info,
2017 cb_fill_in_stats,
2018 folder_unix);
2020 folder_unix->have_stat = TRUE;
2021 return TRUE;
2025 static gboolean
2026 cb_fill_in_mime_type (gpointer key, gpointer value, gpointer user_data)
2028 const char *basename = key;
2029 struct stat_info_entry *entry = value;
2030 GtkFileFolderUnix *folder_unix = user_data;
2031 char *fullname = g_build_filename (folder_unix->filename, basename, NULL);
2033 /* FIXME: Should not need to re-stat. */
2034 const char *mime_type = xdg_mime_get_mime_type_for_file (fullname);
2035 entry->mime_type = g_strdup (mime_type);
2037 g_free (fullname);
2038 /* FIXME: free on NULL? */
2039 return FALSE;
2042 static gboolean
2043 fill_in_mime_type (GtkFileFolderUnix *folder_unix, GError **error)
2045 if (folder_unix->have_mime_type)
2046 return TRUE;
2048 #if 0
2049 g_print ("Getting mime types for directory %s\n", folder_unix->filename);
2050 #endif
2051 g_hash_table_foreach_remove (folder_unix->stat_info,
2052 cb_fill_in_mime_type,
2053 folder_unix);
2055 folder_unix->have_mime_type = TRUE;
2056 return TRUE;
2059 static GtkFilePath *
2060 filename_to_path (const char *filename)
2062 char *tmp;
2064 tmp = remove_trailing_slash (filename);
2065 return gtk_file_path_new_steal (tmp);
2068 static gboolean
2069 filename_is_root (const char *filename)
2071 const gchar *after_root;
2073 after_root = g_path_skip_root (filename);
2075 return (after_root != NULL && *after_root == '\0');
2078 void
2079 fs_module_init(GTypeModule *module)
2081 gtk_file_folder_unix_register_type (module);
2082 gtk_file_system_beagle_register_type (module);
2085 void
2086 fs_module_exit (void)
2090 GtkFileSystem *
2091 fs_module_create (void)
2093 return gtk_file_system_beagle_new ();