diff --git a/moo/moofileview/Makefile.am b/moo/moofileview/Makefile.am index a3f68a02..1a107b4d 100644 --- a/moo/moofileview/Makefile.am +++ b/moo/moofileview/Makefile.am @@ -27,6 +27,9 @@ moofileview_sources = \ moofileview-tools.c \ moofileview-tools.h \ moofileview-ui.h \ + moofolder-private.h \ + moofolder.c \ + moofolder.h \ moofoldermodel.c \ moofoldermodel.h \ moofoldermodel-private.h\ diff --git a/moo/moofileview/moofile-private.h b/moo/moofileview/moofile-private.h index 86389c45..21d4c309 100644 --- a/moo/moofileview/moofile-private.h +++ b/moo/moofileview/moofile-private.h @@ -28,12 +28,6 @@ G_BEGIN_DECLS #define MOO_TYPE_FILE (_moo_file_get_type ()) #define MOO_TYPE_FILE_INFO (_moo_file_info_get_type ()) #define MOO_TYPE_FILE_FLAGS (_moo_file_flags_get_type ()) -#define MOO_TYPE_FOLDER (_moo_folder_get_type ()) -#define MOO_FOLDER(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), MOO_TYPE_FOLDER, MooFolder)) -#define MOO_FOLDER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MOO_TYPE_FOLDER, MooFolderClass)) -#define MOO_IS_FOLDER(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), MOO_TYPE_FOLDER)) -#define MOO_IS_FOLDER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MOO_TYPE_FOLDER)) -#define MOO_FOLDER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MOO_TYPE_FOLDER, MooFolderClass)) #ifdef __WIN32__ /* FILETIME */ @@ -46,37 +40,6 @@ typedef GTime MooFileTime; typedef gint64 MooFileSize; -typedef struct _MooFolderPrivate MooFolderPrivate; -typedef struct _MooFolderClass MooFolderClass; -typedef struct _MooFileSystem MooFileSystem; - -/* should be ordered TODO why? */ -typedef enum { - MOO_FILE_HAS_STAT = 1 << 1, - MOO_FILE_HAS_MIME_TYPE = 1 << 2, - MOO_FILE_HAS_ICON = 1 << 3, - MOO_FILE_ALL_FLAGS = (1 << 4) - 1 -} MooFileFlags; - -struct _MooFolder -{ - GObject parent; - MooFolderPrivate *priv; -}; - -struct _MooFolderClass -{ - GObjectClass parent_class; - - void (*deleted) (MooFolder *folder); - void (*files_added) (MooFolder *folder, - GSList *files); - void (*files_changed) (MooFolder *folder, - GSList *files); - void (*files_removed) (MooFolder *folder, - GSList *files); -}; - struct _MooFile { char *name; @@ -97,7 +60,9 @@ struct _MooFile GType _moo_file_get_type (void) G_GNUC_CONST; GType _moo_file_flags_get_type (void) G_GNUC_CONST; GType _moo_file_info_get_type (void) G_GNUC_CONST; -GType _moo_folder_get_type (void) G_GNUC_CONST; + +MooFile *_moo_file_new (const char *dirname, + const char *basename); MooFile *_moo_file_ref (MooFile *file); void _moo_file_unref (MooFile *file); @@ -118,36 +83,19 @@ const char *_moo_file_collation_key (const MooFile *file); const char *_moo_file_case_display_name(const MooFile *file); #ifndef __WIN32__ -gconstpointer _moo_file_get_stat (const MooFile *file); +const struct stat *_moo_file_get_stat (const MooFile *file); const char *_moo_file_link_get_target (const MooFile *file); #endif -const char *_moo_folder_get_path (MooFolder *folder); -/* list should be freed and elements unref'ed */ -GSList *_moo_folder_list_files (MooFolder *folder); -MooFile *_moo_folder_get_file (MooFolder *folder, - const char *basename); -char *_moo_folder_get_file_path (MooFolder *folder, - MooFile *file); -char *_moo_folder_get_file_uri (MooFolder *folder, - MooFile *file); -/* result should be unref'ed */ -MooFolder *_moo_folder_get_parent (MooFolder *folder, - MooFileFlags wanted); -char *_moo_folder_get_parent_path(MooFolder *folder); - -char **_moo_folder_get_file_info (MooFolder *folder, - MooFile *file); -void _moo_folder_reload (MooFolder *folder); - - -MooFolder *_moo_folder_new (MooFileSystem *fs, - const char *path, - MooFileFlags wanted, - GError **error); -void _moo_folder_set_wanted (MooFolder *folder, - MooFileFlags wanted, - gboolean bit_now); +guint8 _moo_file_icon_blank (void); +guint8 _moo_file_get_icon_type (MooFile *file, + const char *dirname); +void _moo_file_stat (MooFile *file, + const char *dirname); +#ifndef __WIN32__ +void _moo_file_find_mime_type (MooFile *file, + const char *path); +#endif G_END_DECLS diff --git a/moo/moofileview/moofile.c b/moo/moofileview/moofile.c index 71d59ecf..b52ff369 100644 --- a/moo/moofileview/moofile.c +++ b/moo/moofileview/moofile.c @@ -73,72 +73,6 @@ G_STMT_START { \ g_timer_stop (timer); \ } G_STMT_END -#if 0 -#define PRINT_TIMES g_print -#else -static void PRINT_TIMES (G_GNUC_UNUSED const char *format, ...) -{ -} -#endif - -typedef enum { - STAGE_NAMES = 1, - STAGE_STAT = 2, - STAGE_MIME_TYPE = 3 -} Stage; - -typedef struct { - double names_timer; - double stat_timer; - guint stat_counter; - double icons_timer; - guint icons_counter; -} Debug; - -struct _MooFolderPrivate { - guint deleted : 1; - Stage done; - Stage wanted; - Stage wanted_bg; - MooFileSystem *fs; - GDir *dir; - GHashTable *files; /* basename -> MooFile* */ - GSList *files_copy; - char *path; - GSourceFunc populate_func; - int populate_priority; - guint populate_idle_id; - double populate_timeout; - Debug debug; - GTimer *timer; - MooFileWatch *fam; - guint fam_request; - guint reload_idle; -}; - - -static void moo_folder_finalize (GObject *object); - -static void moo_folder_deleted (MooFolder *folder); - -static void folder_emit_deleted (MooFolder *folder); -static void folder_emit_files (MooFolder *folder, - guint signal, - GSList *files); - -static gboolean moo_folder_do_reload (MooFolder *folder); - -static void stop_populate (MooFolder *folder); - -static void files_list_free (GSList **list); - -static gboolean get_icons_a_bit (MooFolder *folder); -static gboolean get_stat_a_bit (MooFolder *folder); -static double get_names (MooFolder *folder); - -static guint8 get_icon (MooFile *file, - const char *dirname); -static guint8 get_blank_icon (void); static GdkPixbuf *render_icon (const MooFile *file, GtkWidget *widget, GtkIconSize size); @@ -146,510 +80,13 @@ static GdkPixbuf *render_icon_for_path (const char *path, GtkWidget *widget, GtkIconSize size); -#define FILE_PATH(folder,file) g_build_filename (folder->priv->path, file->name, NULL) #define MAKE_PATH(dirname,file) g_build_filename (dirname, file->name, NULL) -static void start_monitor (MooFolder *folder); -static void stop_monitor (MooFolder *folder); - -static GSList *hash_table_to_file_list (GHashTable *files); -static void diff_hash_tables (GHashTable *table1, - GHashTable *table2, - GSList **only_1, - GSList **only_2); - -static MooFile *moo_file_new (const char *dirname, - const char *basename); -#define moo_file_stat moo_file_stat_unix -static void moo_file_stat_unix (MooFile *file, - const char *dirname); - - -/* MOO_TYPE_FOLDER */ -G_DEFINE_TYPE (MooFolder, _moo_folder, G_TYPE_OBJECT) - -enum { - DELETED, - FILES_ADDED, - FILES_REMOVED, - FILES_CHANGED, - NUM_SIGNALS -}; - -static guint signals[NUM_SIGNALS]; - -static void -_moo_folder_class_init (MooFolderClass *klass) -{ - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - - gobject_class->finalize = moo_folder_finalize; - - klass->deleted = moo_folder_deleted; - - signals[DELETED] = - g_signal_new ("deleted", - G_OBJECT_CLASS_TYPE (klass), - G_SIGNAL_RUN_FIRST, - G_STRUCT_OFFSET (MooFolderClass, deleted), - NULL, NULL, - _moo_marshal_VOID__VOID, - G_TYPE_NONE, 0); - - signals[FILES_ADDED] = - g_signal_new ("files-added", - G_OBJECT_CLASS_TYPE (klass), - G_SIGNAL_RUN_FIRST, - G_STRUCT_OFFSET (MooFolderClass, files_added), - NULL, NULL, - _moo_marshal_VOID__POINTER, - G_TYPE_NONE, 1, - G_TYPE_POINTER); - - signals[FILES_REMOVED] = - g_signal_new ("files-removed", - G_OBJECT_CLASS_TYPE (klass), - G_SIGNAL_RUN_FIRST, - G_STRUCT_OFFSET (MooFolderClass, files_removed), - NULL, NULL, - _moo_marshal_VOID__POINTER, - G_TYPE_NONE, 1, - G_TYPE_POINTER); - - signals[FILES_CHANGED] = - g_signal_new ("files-changed", - G_OBJECT_CLASS_TYPE (klass), - G_SIGNAL_RUN_FIRST, - G_STRUCT_OFFSET (MooFolderClass, files_changed), - NULL, NULL, - _moo_marshal_VOID__POINTER, - G_TYPE_NONE, 1, - G_TYPE_POINTER); -} - - -static void -_moo_folder_init (MooFolder *folder) -{ - folder->priv = g_new0 (MooFolderPrivate, 1); - folder->priv->deleted = FALSE; - folder->priv->done = 0; - folder->priv->wanted = 0; - folder->priv->fs = NULL; - folder->priv->dir = NULL; - folder->priv->files_copy = NULL; - folder->priv->path = NULL; - folder->priv->populate_func = NULL; - folder->priv->populate_idle_id = 0; - folder->priv->populate_timeout = BACKGROUND_TIMEOUT; - folder->priv->timer = g_timer_new (); - g_timer_stop (folder->priv->timer); - - folder->priv->files = - g_hash_table_new_full (g_str_hash, g_str_equal, - g_free, (GDestroyNotify) _moo_file_unref); -} - - -static void -moo_folder_finalize (GObject *object) -{ - MooFolder *folder = MOO_FOLDER (object); - - if (folder->priv->files) - g_hash_table_destroy (folder->priv->files); - - files_list_free (&folder->priv->files_copy); - if (folder->priv->dir) - g_dir_close (folder->priv->dir); - g_free (folder->priv->path); - if (folder->priv->populate_idle_id) - g_source_remove (folder->priv->populate_idle_id); - g_timer_destroy (folder->priv->timer); - if (folder->priv->reload_idle) - g_source_remove (folder->priv->reload_idle); - - g_free (folder->priv); - folder->priv = NULL; - - G_OBJECT_CLASS (_moo_folder_parent_class)->finalize (object); -} - - -MooFolder * -_moo_folder_new (MooFileSystem *fs, - const char *path, - MooFileFlags wanted, - GError **error) -{ - GDir *dir; - MooFolder *folder; - GError *file_error = NULL; - - g_return_val_if_fail (MOO_IS_FILE_SYSTEM (fs), NULL); - g_return_val_if_fail (path != NULL, NULL); - - dir = g_dir_open (path, 0, &file_error); - - if (!dir) - { - if (file_error->domain != G_FILE_ERROR) - { - g_set_error (error, MOO_FILE_ERROR, - MOO_FILE_ERROR_FAILED, - "%s", file_error->message); - } - else switch (file_error->code) - { - case G_FILE_ERROR_NOENT: - g_set_error (error, MOO_FILE_ERROR, - MOO_FILE_ERROR_NONEXISTENT, - "%s", file_error->message); - break; - case G_FILE_ERROR_NOTDIR: - g_set_error (error, MOO_FILE_ERROR, - MOO_FILE_ERROR_NOT_FOLDER, - "%s", file_error->message); - break; - case G_FILE_ERROR_NAMETOOLONG: - case G_FILE_ERROR_LOOP: - g_set_error (error, MOO_FILE_ERROR, - MOO_FILE_ERROR_BAD_FILENAME, - "%s", file_error->message); - break; - default: - g_set_error (error, MOO_FILE_ERROR, - MOO_FILE_ERROR_FAILED, - "%s", file_error->message); - break; - } - - g_error_free (file_error); - return NULL; - } - - folder = g_object_new (MOO_TYPE_FOLDER, NULL); - folder->priv->fs = fs; - folder->priv->path = g_strdup (path); - - folder->priv->dir = dir; - - get_names (folder); - _moo_folder_set_wanted (folder, wanted, TRUE); - - return folder; -} - - -static void -moo_folder_deleted (MooFolder *folder) -{ - stop_populate (folder); - stop_monitor (folder); - if (folder->priv->reload_idle) - g_source_remove (folder->priv->reload_idle); - folder->priv->reload_idle = 0; - folder->priv->deleted = TRUE; - g_hash_table_destroy (folder->priv->files); - folder->priv->files = NULL; -} - - -void -_moo_folder_set_wanted (MooFolder *folder, - MooFileFlags wanted, - gboolean bit_now) -{ - Stage wanted_stage = STAGE_NAMES; - - g_return_if_fail (MOO_IS_FOLDER (folder)); - g_return_if_fail (!folder->priv->deleted); - - if (wanted & MOO_FILE_HAS_ICON) - wanted_stage = STAGE_MIME_TYPE; - else if (wanted & MOO_FILE_HAS_MIME_TYPE) - wanted_stage = STAGE_MIME_TYPE; - else if (wanted & MOO_FILE_HAS_STAT) - wanted_stage = STAGE_STAT; - - if (wanted_stage <= folder->priv->done) - return; - - if (folder->priv->wanted > folder->priv->done) - { - g_assert (folder->priv->populate_idle_id != 0); - folder->priv->wanted = MAX (folder->priv->wanted, wanted_stage); - return; - } - - folder->priv->wanted = wanted_stage; - - if (folder->priv->wanted_bg != 0) - { - g_assert (folder->priv->populate_idle_id != 0); - g_assert (folder->priv->populate_func != NULL); - g_assert (folder->priv->populate_priority != 0); - g_source_remove (folder->priv->populate_idle_id); - } - else - { - g_assert (folder->priv->populate_idle_id == 0); - - switch (folder->priv->done) - { - case STAGE_NAMES: - g_assert (folder->priv->dir == NULL); - folder->priv->populate_func = (GSourceFunc) get_stat_a_bit; - break; - case STAGE_STAT: - g_assert (folder->priv->dir == NULL); - folder->priv->populate_func = (GSourceFunc) get_icons_a_bit; - break; - default: - g_assert_not_reached (); - } - } - - folder->priv->wanted_bg = STAGE_MIME_TYPE; - folder->priv->populate_timeout = NORMAL_TIMEOUT; - folder->priv->populate_priority = NORMAL_PRIORITY; - - TIMER_CLEAR (folder->priv->timer); - - if (!bit_now || - folder->priv->populate_func (folder)) - { - folder->priv->populate_idle_id = - g_timeout_add_full (folder->priv->populate_priority, - folder->priv->populate_timeout, - folder->priv->populate_func, - folder, NULL); - } -} - - -static void -stop_populate (MooFolder *folder) -{ - if (folder->priv->populate_idle_id) - g_source_remove (folder->priv->populate_idle_id); - folder->priv->populate_idle_id = 0; - folder->priv->populate_func = 0; - if (folder->priv->dir) - g_dir_close (folder->priv->dir); - folder->priv->dir = NULL; - files_list_free (&folder->priv->files_copy); -} - - -static void -folder_emit_deleted (MooFolder *folder) -{ - g_signal_emit (folder, signals[DELETED], 0); -} - - -static void -folder_emit_files (MooFolder *folder, - guint sig, - GSList *files) -{ - if (files) - g_signal_emit (folder, signals[sig], 0, files); -} - - -GSList * -_moo_folder_list_files (MooFolder *folder) -{ - g_return_val_if_fail (MOO_IS_FOLDER (folder), NULL); - g_return_val_if_fail (!folder->priv->deleted, NULL); - return hash_table_to_file_list (folder->priv->files); -} - - -static double -get_names (MooFolder *folder) -{ - GTimer *timer; - GSList *added = NULL; - const char *name; - MooFile *file; - double elapsed; - - g_assert (folder->priv->path != NULL); - g_assert (folder->priv->dir != NULL); - g_assert (g_hash_table_size (folder->priv->files) == 0); - - timer = g_timer_new (); - - file = moo_file_new (folder->priv->path, ".."); - file->flags = MOO_FILE_HAS_MIME_TYPE | MOO_FILE_HAS_ICON; - file->info = MOO_FILE_INFO_EXISTS | MOO_FILE_INFO_IS_DIR; - file->icon = get_icon (file, folder->priv->path); - - g_hash_table_insert (folder->priv->files, g_strdup (".."), file); - added = g_slist_prepend (added, file); - - for (name = g_dir_read_name (folder->priv->dir); - name != NULL; - name = g_dir_read_name (folder->priv->dir)) - { - file = moo_file_new (folder->priv->path, name); - - if (file) - { - file->icon = get_blank_icon (); - g_hash_table_insert (folder->priv->files, g_strdup (name), file); - added = g_slist_prepend (added, file); - } - else - { - g_critical ("%s: moo_file_new() failed for '%s'", G_STRLOC, name); - } - } - - folder_emit_files (folder, FILES_ADDED, added); - g_slist_free (added); - - elapsed = folder->priv->debug.names_timer = g_timer_elapsed (timer, NULL); - g_timer_destroy (timer); - - g_dir_close (folder->priv->dir); - folder->priv->dir = NULL; - - folder->priv->done = STAGE_NAMES; - - PRINT_TIMES ("names folder %s: %f sec\n", - folder->priv->path, - folder->priv->debug.names_timer); - - return elapsed; -} - - -static gboolean -get_stat_a_bit (MooFolder *folder) -{ - gboolean done = FALSE; - double elapsed; - - g_assert (folder->priv->dir == NULL); - g_assert (folder->priv->done == STAGE_NAMES); - g_assert (folder->priv->path != NULL); - - elapsed = g_timer_elapsed (folder->priv->timer, NULL); - g_timer_continue (folder->priv->timer); - - if (!folder->priv->files_copy) - folder->priv->files_copy = hash_table_to_file_list (folder->priv->files); - if (!folder->priv->files_copy) - done = TRUE; - - while (!done) - { - GSList *changed = folder->priv->files_copy; - MooFile *file = changed->data; - folder->priv->files_copy = g_slist_remove_link (folder->priv->files_copy, - folder->priv->files_copy); - - if (!(file->flags & MOO_FILE_HAS_STAT)) - { - moo_file_stat (file, folder->priv->path); - folder_emit_files (folder, FILES_CHANGED, changed); - } - else - { - _moo_file_unref (file); - } - - g_slist_free_1 (changed); - - if (!folder->priv->files_copy) - done = TRUE; - - if (g_timer_elapsed (folder->priv->timer, NULL) > folder->priv->populate_timeout) - break; - } - - elapsed = g_timer_elapsed (folder->priv->timer, NULL) - elapsed; - folder->priv->debug.stat_timer += elapsed; - folder->priv->debug.stat_counter += 1; - g_timer_stop (folder->priv->timer); - - if (!done) - { - TIMER_CLEAR (folder->priv->timer); - return TRUE; - } - else - { - g_assert (folder->priv->files_copy == NULL); - folder->priv->populate_idle_id = 0; - - PRINT_TIMES ("stat folder %s: %d iterations, %f sec\n", - folder->priv->path, - folder->priv->debug.stat_counter, - folder->priv->debug.stat_timer); - - folder->priv->done = STAGE_STAT; - - if (folder->priv->wanted >= STAGE_MIME_TYPE || folder->priv->wanted_bg >= STAGE_MIME_TYPE) - { - if (folder->priv->wanted >= STAGE_MIME_TYPE) - { - folder->priv->populate_priority = NORMAL_PRIORITY; - folder->priv->populate_timeout = NORMAL_TIMEOUT; - } - else if (folder->priv->wanted_bg >= STAGE_MIME_TYPE) - { - folder->priv->populate_priority = BACKGROUND_PRIORITY; - folder->priv->populate_timeout = BACKGROUND_TIMEOUT; - } - - if (folder->priv->populate_idle_id) - g_source_remove (folder->priv->populate_idle_id); - folder->priv->populate_idle_id = 0; - folder->priv->populate_func = (GSourceFunc) get_icons_a_bit; - - if (g_timer_elapsed (folder->priv->timer, NULL) < folder->priv->populate_timeout) - { - /* in this case we may block for as much as twice TIMEOUT, but usually - it allows stat and loading icons in one iteration */ - TIMER_CLEAR (folder->priv->timer); - if (folder->priv->populate_func (folder)) - folder->priv->populate_idle_id = - g_timeout_add_full (folder->priv->populate_priority, - folder->priv->populate_timeout, - folder->priv->populate_func, - folder, NULL); - } - else - { - TIMER_CLEAR (folder->priv->timer); - folder->priv->populate_idle_id = - g_timeout_add_full (folder->priv->populate_priority, - folder->priv->populate_timeout, - folder->priv->populate_func, - folder, NULL); - } - } - else - { - folder->priv->populate_func = NULL; - folder->priv->populate_priority = 0; - folder->priv->populate_timeout = 0; - } - - return FALSE; - } -} - #ifndef __WIN32__ -inline static void -get_mime_type (MooFile *file, - const char *path) +void +_moo_file_find_mime_type (MooFile *file, + const char *path) { if (file->flags & MOO_FILE_HAS_STAT) file->mime_type = xdg_mime_get_mime_type_for_file (path, &file->statbuf); @@ -669,511 +106,13 @@ get_mime_type (MooFile *file, #endif /* !__WIN32__ */ -static gboolean -get_icons_a_bit (MooFolder *folder) -{ - gboolean done = FALSE; - double elapsed; - - g_assert (folder->priv->dir == NULL); - g_assert (folder->priv->done == STAGE_STAT); - g_assert (folder->priv->path != NULL); - - elapsed = g_timer_elapsed (folder->priv->timer, NULL); - g_timer_continue (folder->priv->timer); - - if (!folder->priv->files_copy) - folder->priv->files_copy = - hash_table_to_file_list (folder->priv->files); - if (!folder->priv->files_copy) - done = TRUE; - - while (!done) - { - GSList *changed = folder->priv->files_copy; - MooFile *file = changed->data; - - folder->priv->files_copy = - g_slist_remove_link (folder->priv->files_copy, - changed); - -#ifndef __WIN32__ - if (file->info & MOO_FILE_INFO_EXISTS && - !(file->flags & MOO_FILE_HAS_MIME_TYPE)) - { - char *path = FILE_PATH (folder, file); - - get_mime_type (file, path); - - file->flags |= MOO_FILE_HAS_ICON; - file->icon = get_icon (file, folder->priv->path); - folder_emit_files (folder, FILES_CHANGED, changed); - g_free (path); - } -#endif - - _moo_file_unref (file); - g_slist_free (changed); - - if (!folder->priv->files_copy) - done = TRUE; - - if (g_timer_elapsed (folder->priv->timer, NULL) > folder->priv->populate_timeout) - break; - } - - elapsed = g_timer_elapsed (folder->priv->timer, NULL) - elapsed; - folder->priv->debug.icons_timer += elapsed; - folder->priv->debug.icons_counter += 1; - TIMER_CLEAR (folder->priv->timer); - - if (done) - { - PRINT_TIMES ("icons folder %s: %d iterations, %f sec\n", - folder->priv->path, - folder->priv->debug.icons_counter, - folder->priv->debug.icons_timer); - - g_assert (folder->priv->files_copy == NULL); - folder->priv->populate_idle_id = 0; - folder->priv->done = STAGE_MIME_TYPE; - folder->priv->populate_func = NULL; - folder->priv->populate_priority = 0; - folder->priv->populate_timeout = 0; - - start_monitor (folder); - } - - return !done; -} - - -MooFile * -_moo_folder_get_file (MooFolder *folder, - const char *basename) -{ - g_return_val_if_fail (MOO_IS_FOLDER (folder), NULL); - g_return_val_if_fail (!folder->priv->deleted, NULL); - g_return_val_if_fail (basename != NULL, NULL); - return g_hash_table_lookup (folder->priv->files, basename); -} - - -char * -_moo_folder_get_file_path (MooFolder *folder, - MooFile *file) -{ - g_return_val_if_fail (MOO_IS_FOLDER (folder), NULL); - g_return_val_if_fail (file != NULL, NULL); - return FILE_PATH (folder, file); -} - - -char * -_moo_folder_get_file_uri (MooFolder *folder, - MooFile *file) -{ - char *path, *uri; - - g_return_val_if_fail (MOO_IS_FOLDER (folder), NULL); - g_return_val_if_fail (file != NULL, NULL); - - path = FILE_PATH (folder, file); - g_return_val_if_fail (path != NULL, NULL); - - uri = g_filename_to_uri (path, NULL, NULL); - - g_free (path); - return uri; -} - - -MooFolder * -_moo_folder_get_parent (MooFolder *folder, - MooFileFlags wanted) -{ - g_return_val_if_fail (MOO_IS_FOLDER (folder), NULL); - g_return_val_if_fail (!folder->priv->deleted, NULL); - return _moo_file_system_get_parent_folder (folder->priv->fs, - folder, wanted); -} - - -MooFileSystem * -_moo_folder_get_file_system (MooFolder *folder) -{ - g_return_val_if_fail (MOO_IS_FOLDER (folder), NULL); - return folder->priv->fs; -} - - -/*****************************************************************************/ -/* Monitoring - */ - -static void file_deleted (MooFolder *folder, - const char *name); -static void file_changed (MooFolder *folder, - const char *name); -static void file_created (MooFolder *folder, - const char *name); - -static void -fam_callback (MooFileWatch *watch, - MooFileEvent *event, - gpointer data) -{ - MooFolder *folder = MOO_FOLDER (data); - - g_return_if_fail (watch == folder->priv->fam); - g_return_if_fail (event->monitor_id == folder->priv->fam_request); - - switch (event->code) - { - case MOO_FILE_EVENT_CHANGED: - file_changed (folder, event->filename); - break; - case MOO_FILE_EVENT_DELETED: - file_deleted (folder, event->filename); - break; - case MOO_FILE_EVENT_ERROR: - stop_monitor (folder); - file_changed (folder, folder->priv->path); - break; - } -} - - -static void -start_monitor (MooFolder *folder) -{ - GError *error = NULL; - - g_return_if_fail (!folder->priv->deleted); - g_return_if_fail (folder->priv->fam_request == 0); - folder->priv->fam = _moo_file_system_get_file_watch (folder->priv->fs); - g_return_if_fail (folder->priv->fam != NULL); - - folder->priv->fam_request = - moo_file_watch_create_monitor (folder->priv->fam, - folder->priv->path, - fam_callback, folder, - NULL, &error); - - if (!folder->priv->fam_request) - { - g_warning ("%s: moo_file_watch_create_monitor failed for path '%s'", G_STRLOC, folder->priv->path); - g_warning ("%s: %s", G_STRLOC, error->message); - g_error_free (error); - return; - } -} - - -static void -stop_monitor (MooFolder *folder) -{ - if (folder->priv->fam_request) - { - moo_file_watch_cancel_monitor (folder->priv->fam, - folder->priv->fam_request); - folder->priv->fam = NULL; - folder->priv->fam_request = 0; - } -} - - -static void -file_deleted (MooFolder *folder, - const char *name) -{ - MooFile *file; - GSList *list; - - g_return_if_fail (!folder->priv->deleted); - - if (!strcmp (name, folder->priv->path)) - return folder_emit_deleted (folder); - - file = g_hash_table_lookup (folder->priv->files, name); - if (!file) return; - - _moo_file_ref (file); - g_hash_table_remove (folder->priv->files, name); - - list = g_slist_append (NULL, file); - folder_emit_files (folder, FILES_REMOVED, list); - - g_slist_free (list); - _moo_file_unref (file); -} - - -void -_moo_folder_reload (MooFolder *folder) -{ - g_return_if_fail (MOO_IS_FOLDER (folder)); - - if (folder->priv->reload_idle) - g_source_remove (folder->priv->reload_idle); - folder->priv->reload_idle = 0; - - moo_folder_do_reload (folder); -} - - -static void -file_changed (MooFolder *folder, - const char *name) -{ - g_return_if_fail (!folder->priv->deleted); - - if (!strcmp (name, folder->priv->path)) - { - if (!folder->priv->reload_idle) - folder->priv->reload_idle = - g_idle_add ((GSourceFunc) moo_folder_do_reload, - folder); - } - else - { - /* XXX */ - g_return_if_reached (); - } -} - - -static void -file_created (MooFolder *folder, - const char *name) -{ - MooFile *file; - GSList *list; - - g_return_if_fail (!folder->priv->deleted); - - file = moo_file_new (folder->priv->path, name); - g_return_if_fail (file != NULL); - - file->icon = get_icon (file, folder->priv->path); - moo_file_stat (file, folder->priv->path); - -#ifndef __WIN32__ - if (file->info & MOO_FILE_INFO_EXISTS && - !(file->flags & MOO_FILE_HAS_MIME_TYPE)) - { - char *path = FILE_PATH (folder, file); - - get_mime_type (file, path); - - file->flags |= MOO_FILE_HAS_ICON; - file->icon = get_icon (file, folder->priv->path); - g_free (path); - } -#endif - - g_hash_table_insert (folder->priv->files, - g_strdup (name), file); - list = g_slist_append (NULL, file); - folder_emit_files (folder, FILES_ADDED, list); - - g_slist_free (list); -} - - -/* TODO */ -static gboolean -moo_folder_do_reload (MooFolder *folder) -{ - GHashTable *files; - GDir *dir; - GError *error = NULL; - const char *name; - GSList *new = NULL, *deleted = NULL, *l; - - g_return_val_if_fail (!folder->priv->deleted, FALSE); - folder->priv->reload_idle = 0; - - dir = g_dir_open (folder->priv->path, 0, &error); - - if (!dir) - { - g_warning ("%s: could not open directory %s", - G_STRLOC, folder->priv->path); - g_warning ("%s: %s", G_STRLOC, error->message); - g_error_free (error); - folder_emit_deleted (folder); - return FALSE; - } - - files = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); - - while ((name = g_dir_read_name (dir))) - g_hash_table_insert (files, g_strdup (name), NULL); - - diff_hash_tables (files, folder->priv->files, &new, &deleted); - - for (l = new; l != NULL; l = l->next) - file_created (folder, l->data); - - for (l = deleted; l != NULL; l = l->next) - file_deleted (folder, l->data); - - g_slist_foreach (new, (GFunc) g_free, NULL); - g_slist_foreach (deleted, (GFunc) g_free, NULL); - g_slist_free (new); - g_slist_free (deleted); - g_hash_table_destroy (files); - g_dir_close (dir); - return FALSE; -} - - -/* XXX */ -static char * -moo_file_get_type_string (MooFile *file) -{ - g_return_val_if_fail (MOO_FILE_EXISTS (file), NULL); - - if (MOO_FILE_IS_DIR (file)) - return g_strdup ("folder"); - else if (file->mime_type) - return g_strdup (file->mime_type); - else - return g_strdup ("file"); -} - - -/* XXX */ -static char * -moo_file_get_size_string (MooFile *file) -{ - return g_strdup_printf ("%" G_GINT64_FORMAT, (MooFileSize) file->statbuf.st_size); -} - - -/* XXX */ -static char * -moo_file_get_mtime_string (MooFile *file) -{ - static char buf[1024]; - - if (!MOO_FILE_EXISTS (file)) - return NULL; - -#ifdef __WIN32__ - if (MOO_FILE_IS_DIR (file)) - return NULL; -#endif - - if (strftime (buf, 1024, "%x %X", localtime ((time_t*)&file->statbuf.st_mtime))) - return g_strdup (buf); - else - return NULL; -} - - -char ** -_moo_folder_get_file_info (MooFolder *folder, - MooFile *file) -{ - GPtrArray *array; - GSList *list; - - g_return_val_if_fail (MOO_IS_FOLDER (folder), NULL); - g_return_val_if_fail (file != NULL, NULL); - g_return_val_if_fail (!folder->priv->deleted, NULL); - g_return_val_if_fail (folder->priv->files != NULL, NULL); - g_return_val_if_fail (g_hash_table_lookup (folder->priv->files, - _moo_file_name (file)) == file, NULL); - - moo_file_stat (file, folder->priv->path); - -#ifndef __WIN32__ - if (file->info & MOO_FILE_INFO_EXISTS && - !(file->flags & MOO_FILE_HAS_MIME_TYPE)) - { - char *path = FILE_PATH (folder, file); - - get_mime_type (file, path); - - file->flags |= MOO_FILE_HAS_ICON; - file->icon = get_icon (file, folder->priv->path); - g_free (path); - } -#endif - - array = g_ptr_array_new (); - - if (file->info & MOO_FILE_INFO_EXISTS) - { - char *type, *mtime, *location; - - g_ptr_array_add (array, g_strdup ("Type:")); - type = moo_file_get_type_string (file); - - if (file->info & MOO_FILE_INFO_IS_LINK) - { - g_ptr_array_add (array, g_strdup_printf ("link to %s", type)); - g_free (type); - } - else - { - g_ptr_array_add (array, type); - } - - location = g_filename_display_name (_moo_folder_get_path (folder)); - g_ptr_array_add (array, g_strdup ("Location:")); - g_ptr_array_add (array, location); - - if (!(file->info & MOO_FILE_INFO_IS_DIR)) - { - g_ptr_array_add (array, g_strdup ("Size:")); - g_ptr_array_add (array, moo_file_get_size_string (file)); - } - - mtime = moo_file_get_mtime_string (file); - - if (mtime) - { - g_ptr_array_add (array, g_strdup ("Modified:")); - g_ptr_array_add (array, mtime); - } - } - else if (file->info & MOO_FILE_INFO_IS_LINK) - { - g_ptr_array_add (array, g_strdup ("Type:")); - g_ptr_array_add (array, g_strdup ("broken symbolic link")); - } - -#ifndef __WIN32__ - if ((file->info & MOO_FILE_INFO_IS_LINK) && - _moo_file_link_get_target (file)) - { - g_ptr_array_add (array, g_strdup ("Points to:")); - g_ptr_array_add (array, g_strdup (_moo_file_link_get_target (file))); - } -#endif - - list = g_slist_append (NULL, _moo_file_ref (file)); - g_object_ref (folder); - folder_emit_files (folder, FILES_CHANGED, list); - g_object_unref (folder); - _moo_file_unref (file); - g_slist_free (list); - - g_ptr_array_add (array, NULL); - return (char**) g_ptr_array_free (array, FALSE); -} - - /********************************************************************/ /* MooFile */ -static MooFile * -moo_file_new (const char *dirname, - const char *basename) +MooFile * +_moo_file_new (const char *dirname, + const char *basename) { MooFile *file = NULL; char *path = NULL; @@ -1239,9 +178,9 @@ _moo_file_unref (MooFile *file) } -static void -moo_file_stat_unix (MooFile *file, - const char *dirname) +void +_moo_file_stat (MooFile *file, + const char *dirname) { char *fullname; @@ -1361,7 +300,7 @@ moo_file_stat_unix (MooFile *file, file->flags |= MOO_FILE_HAS_ICON; } - file->icon = get_icon (file, dirname); + file->icon = _moo_file_get_icon_type (file, dirname); if (file->name[0] == '.') file->info |= MOO_FILE_INFO_IS_HIDDEN; @@ -1417,7 +356,7 @@ _moo_file_get_mime_type (const MooFile *file) #ifndef __WIN32__ -gconstpointer +const struct stat * _moo_file_get_stat (const MooFile *file) { g_return_val_if_fail (file != NULL, NULL); @@ -1529,84 +468,6 @@ _moo_file_info_get_type (void) } -const char * -_moo_folder_get_path (MooFolder *folder) -{ - g_return_val_if_fail (MOO_IS_FOLDER (folder), NULL); - return folder->priv->path; -} - - -static void -files_list_free (GSList **list) -{ - g_slist_foreach (*list, (GFunc) _moo_file_unref, NULL); - g_slist_free (*list); - *list = NULL; -} - - -static void -prepend_file (G_GNUC_UNUSED gpointer key, - MooFile *file, - GSList **list) -{ - *list = g_slist_prepend (*list, _moo_file_ref (file)); -} - -static GSList * -hash_table_to_file_list (GHashTable *files) -{ - GSList *list = NULL; - g_return_val_if_fail (files != NULL, NULL); - g_hash_table_foreach (files, (GHFunc) prepend_file, &list); - return list; -} - - -static void -check_unique (const char *key, - G_GNUC_UNUSED gpointer whatever, - gpointer user_data) -{ - struct { - GSList *list; - GHashTable *table2; - } *data = user_data; - gpointer orig_key, value; - - if (!g_hash_table_lookup_extended (data->table2, key, &orig_key, &value)) - data->list = g_slist_prepend (data->list, g_strdup (key)); -} - -static void -get_unique (GHashTable *table1, - GHashTable *table2, - GSList **only_1) -{ - struct { - GSList *list; - GHashTable *table2; - } data; - - data.list = NULL; - data.table2 = table2; - g_hash_table_foreach (table1, (GHFunc) check_unique, &data); - - *only_1 = data.list; -} - -static void -diff_hash_tables (GHashTable *table1, - GHashTable *table2, - GSList **only_1, - GSList **only_2) -{ - get_unique (table1, table2, only_1); - get_unique (table2, table1, only_2); -} - - #if !GLIB_CHECK_VERSION(2,8,0) /* This is a collation key that is very very likely to sort before any collation key that libc strxfrm generates. We use this before any @@ -2127,9 +988,9 @@ _create_named_icon (GtkIconTheme *icon_theme, } -static guint8 -get_icon (MooFile *file, - const char *dirname) +guint8 +_moo_file_get_icon_type (MooFile *file, + const char *dirname) { if (MOO_FILE_IS_BROKEN_LINK (file)) return MOO_ICON_BROKEN_LINK; @@ -2164,8 +1025,8 @@ get_icon (MooFile *file, } -static guint8 -get_blank_icon (void) +guint8 +_moo_file_icon_blank (void) { return MOO_ICON_BLANK; } diff --git a/moo/moofileview/moofile.h b/moo/moofileview/moofile.h index c1bc6525..bc5c5331 100644 --- a/moo/moofileview/moofile.h +++ b/moo/moofileview/moofile.h @@ -19,8 +19,16 @@ G_BEGIN_DECLS -typedef struct _MooFile MooFile; -typedef struct _MooFolder MooFolder; +typedef struct _MooFile MooFile; +typedef struct _MooFolder MooFolder; + +/* should be ordered TODO why? */ +typedef enum { + MOO_FILE_HAS_STAT = 1 << 1, + MOO_FILE_HAS_MIME_TYPE = 1 << 2, + MOO_FILE_HAS_ICON = 1 << 3, + MOO_FILE_ALL_FLAGS = (1 << 4) - 1 +} MooFileFlags; typedef enum { MOO_FILE_INFO_EXISTS = 1 << 0, diff --git a/moo/moofileview/moofilesystem.c b/moo/moofileview/moofilesystem.c index 2c6d82c6..7ccd233b 100644 --- a/moo/moofileview/moofilesystem.c +++ b/moo/moofileview/moofilesystem.c @@ -13,6 +13,7 @@ #define MOO_FILE_VIEW_COMPILATION #include "moofilesystem.h" +#include "moofolder-private.h" #include "mooutils/mooutils-fs.h" #include "mooutils/moomarshals.h" #include @@ -25,16 +26,23 @@ #endif #define BROKEN_NAME "<" "????" ">" +#define FOLDERS_CACHE_SIZE 10 + +typedef struct { + GQueue *queue; + GHashTable *paths; +} FoldersCache; struct _MooFileSystemPrivate { GHashTable *folders; MooFileWatch *fam; + FoldersCache cache; }; static MooFileSystem *fs_instance = NULL; -static void moo_file_system_finalize (GObject *object); +static void moo_file_system_dispose (GObject *object); static MooFolder *get_folder (MooFileSystem *fs, const char *path, @@ -113,7 +121,7 @@ _moo_file_system_class_init (MooFileSystemClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - gobject_class->finalize = moo_file_system_finalize; + gobject_class->dispose = moo_file_system_dispose; klass->get_folder = get_folder; klass->create_folder = create_folder; @@ -139,30 +147,76 @@ _moo_file_system_class_init (MooFileSystemClass *klass) static void -_moo_file_system_init (MooFileSystem *fs) +add_folder_cache (MooFileSystem *fs, + MooFolderImpl *impl) { - fs->priv = g_new0 (MooFileSystemPrivate, 1); + FoldersCache *cache = &fs->priv->cache; - fs->priv->folders = g_hash_table_new_full (g_str_hash, g_str_equal, - g_free, g_object_unref); + g_queue_push_head (cache->queue, impl); + g_hash_table_insert (cache->paths, impl->path, impl); + + if (cache->queue->length > FOLDERS_CACHE_SIZE) + { + MooFolderImpl *old = g_queue_pop_tail (cache->queue); + g_hash_table_remove (cache->paths, old->path); + _moo_folder_impl_free (old); + } +} + + +void +_moo_file_system_folder_finalized (MooFileSystem *fs, + MooFolder *folder) +{ + MooFolderImpl *impl; + + g_return_if_fail (MOO_IS_FILE_SYSTEM (fs)); + g_return_if_fail (MOO_IS_FOLDER (folder)); + + impl = folder->impl; + folder->impl = NULL; + impl->proxy = NULL; + + g_hash_table_remove (fs->priv->folders, impl->path); + + if (!impl->deleted) + add_folder_cache (fs, impl); + else + _moo_folder_impl_free (impl); } static void -moo_file_system_finalize (GObject *object) +_moo_file_system_init (MooFileSystem *fs) +{ + fs->priv = g_new0 (MooFileSystemPrivate, 1); + fs->priv->folders = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + fs->priv->cache.queue = g_queue_new (); + fs->priv->cache.paths = g_hash_table_new (g_str_hash, g_str_equal); +} + + +static void +moo_file_system_dispose (GObject *object) { MooFileSystem *fs = MOO_FILE_SYSTEM (object); - g_hash_table_destroy (fs->priv->folders); - - if (fs->priv->fam) + if (fs->priv) { - moo_file_watch_close (fs->priv->fam, NULL); - moo_file_watch_unref (fs->priv->fam); - } + g_hash_table_destroy (fs->priv->folders); + g_hash_table_destroy (fs->priv->cache.paths); + g_queue_foreach (fs->priv->cache.queue, (GFunc) _moo_folder_impl_free, NULL); + g_queue_free (fs->priv->cache.queue); - g_free (fs->priv); - fs->priv = NULL; + if (fs->priv->fam) + { + moo_file_watch_close (fs->priv->fam, NULL); + moo_file_watch_unref (fs->priv->fam); + } + + g_free (fs->priv); + fs->priv = NULL; + } G_OBJECT_CLASS (_moo_file_system_parent_class)->finalize (object); } @@ -324,23 +378,31 @@ _moo_file_system_get_file_watch (MooFileSystem *fs) /* TODO what's this? */ -static void -folder_deleted (MooFolder *folder, - MooFileSystem *fs) +void +_moo_file_system_folder_deleted (MooFileSystem *fs, + MooFolderImpl *impl) { - g_signal_handlers_disconnect_by_func (folder, - (gpointer) folder_deleted, fs); - g_hash_table_remove (fs->priv->folders, _moo_folder_get_path (folder)); + if (impl->proxy) + { + g_hash_table_remove (fs->priv->folders, impl->path); + } + else + { + g_hash_table_remove (fs->priv->cache.paths, impl->path); + g_queue_remove (fs->priv->cache.queue, impl); + _moo_folder_impl_free (impl); + } } -MooFolder * +static MooFolder * get_folder (MooFileSystem *fs, const char *path, MooFileFlags wanted, GError **error) { MooFolder *folder; + MooFolderImpl *impl; char *norm_path = NULL; g_return_val_if_fail (path != NULL, NULL); @@ -374,6 +436,19 @@ get_folder (MooFileSystem *fs, goto out; } + impl = g_hash_table_lookup (fs->priv->cache.paths, norm_path); + + if (impl) + { + g_hash_table_remove (fs->priv->cache.paths, impl->path); + g_queue_remove (fs->priv->cache.queue, impl); + folder = _moo_folder_new_with_impl (impl); + g_hash_table_insert (fs->priv->folders, norm_path, folder); + norm_path = NULL; + _moo_folder_set_wanted (folder, wanted, TRUE); + goto out; + } + if (!g_file_test (norm_path, G_FILE_TEST_EXISTS)) { g_set_error (error, MOO_FILE_ERROR, @@ -396,11 +471,7 @@ get_folder (MooFileSystem *fs, if (folder) { - g_hash_table_insert (fs->priv->folders, - norm_path, - g_object_ref (folder)); - g_signal_connect (folder, "deleted", - G_CALLBACK (folder_deleted), fs); + g_hash_table_insert (fs->priv->folders, norm_path, folder); norm_path = NULL; } @@ -411,7 +482,7 @@ out: /* TODO */ -gboolean +static gboolean create_folder (G_GNUC_UNUSED MooFileSystem *fs, const char *path, GError **error) @@ -559,7 +630,7 @@ normalize_path (const char *path) } -gboolean +static gboolean delete_file (G_GNUC_UNUSED MooFileSystem *fs, const char *path, gboolean recursive, diff --git a/moo/moofileview/moofilesystem.h b/moo/moofileview/moofilesystem.h index d140c6a3..a1e80cb6 100644 --- a/moo/moofileview/moofilesystem.h +++ b/moo/moofileview/moofilesystem.h @@ -24,7 +24,7 @@ #ifndef __MOO_FILE_SYSTEM_H__ #define __MOO_FILE_SYSTEM_H__ -#include "moofileview/moofile-private.h" +#include "moofileview/moofolder.h" #include #include @@ -39,6 +39,7 @@ G_BEGIN_DECLS #define MOO_IS_FILE_SYSTEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MOO_TYPE_FILE_SYSTEM)) #define MOO_FILE_SYSTEM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MOO_TYPE_FILE_SYSTEM, MooFileSystemClass)) +typedef struct _MooFileSystem MooFileSystem; typedef struct _MooFileSystemPrivate MooFileSystemPrivate; typedef struct _MooFileSystemClass MooFileSystemClass; diff --git a/moo/moofileview/moofileview-aux.h b/moo/moofileview/moofileview-aux.h index 5377548d..e5dee008 100644 --- a/moo/moofileview/moofileview-aux.h +++ b/moo/moofileview/moofileview-aux.h @@ -15,6 +15,7 @@ #error "This file may not be included" #endif +#include "moofileview/moofile-private.h" #include #include diff --git a/moo/moofileview/moofileview-dialogs.c b/moo/moofileview/moofileview-dialogs.c index e6b1c8af..49b59bc7 100644 --- a/moo/moofileview/moofileview-dialogs.c +++ b/moo/moofileview/moofileview-dialogs.c @@ -14,6 +14,7 @@ #define MOO_FILE_VIEW_COMPILATION #include "moofileview/moofileview-dialogs.h" #include "moofileview/moofilesystem.h" +#include "moofileview/moofile-private.h" #include "moofileview/moofileprops-glade.h" #include "moofileview/moocreatefolder-glade.h" #include "moofileview/moofileviewdrop-glade.h" diff --git a/moo/moofileview/moofileview.c b/moo/moofileview/moofileview.c index 527acd18..4de68b02 100644 --- a/moo/moofileview/moofileview.c +++ b/moo/moofileview/moofileview.c @@ -21,6 +21,7 @@ #include "moofileview/moofileview-dialogs.h" #include "moofileview/moobookmarkmgr.h" #include "moofileview/moofilesystem.h" +#include "moofileview/moofile-private.h" #include "moofileview/moofoldermodel.h" #include "moofileview/moofileentry.h" #include "moofileview/mooiconview.h" diff --git a/moo/moofileview/moofolder-private.h b/moo/moofileview/moofolder-private.h new file mode 100644 index 00000000..79eeb841 --- /dev/null +++ b/moo/moofileview/moofolder-private.h @@ -0,0 +1,110 @@ +/* + * moofolder-private.h + * + * Copyright (C) 2004-2006 by Yevgen Muntyan + * + * 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. + * + * See COPYING file that comes with this distribution. + */ + +#ifndef MOO_FILE_VIEW_COMPILATION +#error "This file may not be included" +#endif + +#ifndef __MOO_FOLDER_PRIVATE_H__ +#define __MOO_FOLDER_PRIVATE_H__ + +#include "moofileview/moofolder.h" +#include "moofileview/moofile-private.h" +#include "moofileview/moofilesystem.h" + +G_BEGIN_DECLS + + +#define MOO_FOLDER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MOO_TYPE_FOLDER, MooFolderClass)) +#define MOO_IS_FOLDER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MOO_TYPE_FOLDER)) +#define MOO_FOLDER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MOO_TYPE_FOLDER, MooFolderClass)) + +typedef struct _MooFolderImpl MooFolderImpl; +typedef struct _MooFolderClass MooFolderClass; + +struct _MooFolder +{ + GObject parent; + MooFolderImpl *impl; +}; + +struct _MooFolderClass +{ + GObjectClass parent_class; + + void (*deleted) (MooFolder *folder); + void (*files_added) (MooFolder *folder, + GSList *files); + void (*files_changed) (MooFolder *folder, + GSList *files); + void (*files_removed) (MooFolder *folder, + GSList *files); +}; + +typedef struct { + double names_timer; + double stat_timer; + guint stat_counter; + double icons_timer; + guint icons_counter; +} Debug; + +typedef enum { + STAGE_NAMES = 1, + STAGE_STAT = 2, + STAGE_MIME_TYPE = 3 +} Stage; + +struct _MooFolderImpl { + MooFolder *proxy; + Stage done; + Stage wanted; + Stage wanted_bg; + MooFileSystem *fs; + GDir *dir; + GHashTable *files; /* basename -> MooFile* */ + GSList *files_copy; + char *path; + GSourceFunc populate_func; + int populate_priority; + guint populate_idle_id; + double populate_timeout; + Debug debug; + GTimer *timer; + MooFileWatch *fam; + guint fam_request; + guint reload_idle; + + guint deleted : 1; +}; + + +MooFolder *_moo_folder_new (MooFileSystem *fs, + const char *path, + MooFileFlags wanted, + GError **error); +MooFolder *_moo_folder_new_with_impl (MooFolderImpl *impl); +void _moo_folder_set_wanted (MooFolder *folder, + MooFileFlags wanted, + gboolean bit_now); +void _moo_folder_impl_free (MooFolderImpl *impl); + +void _moo_file_system_folder_finalized (MooFileSystem *fs, + MooFolder *folder); +void _moo_file_system_folder_deleted (MooFileSystem *fs, + MooFolderImpl *folder); + + +G_END_DECLS + +#endif /* __MOO_FOLDER_PRIVATE_H__ */ diff --git a/moo/moofileview/moofolder.c b/moo/moofileview/moofolder.c new file mode 100644 index 00000000..3cbf5d45 --- /dev/null +++ b/moo/moofileview/moofolder.c @@ -0,0 +1,1184 @@ +/* + * moofolder.c + * + * Copyright (C) 2004-2006 by Yevgen Muntyan + * + * 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. + * + * See COPYING file that comes with this distribution. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#define MOO_FILE_VIEW_COMPILATION +#include "moofileview/moofilesystem.h" +#include "moofileview/moofolder-private.h" +#include "moofileview/symlink.h" +#include "mooutils/mooutils-fs.h" +#include "mooutils/mooutils-misc.h" +#include "mooutils/moomarshals.h" +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include +#include +#include +#include +#include + +#ifndef __WIN32__ +#include "mooutils/xdgmime/xdgmime.h" +#endif + +#define NORMAL_PRIORITY G_PRIORITY_DEFAULT_IDLE +#define NORMAL_TIMEOUT 0.04 +#define BACKGROUND_PRIORITY G_PRIORITY_LOW +#define BACKGROUND_TIMEOUT 0.001 + +#define TIMER_CLEAR(timer) \ +G_STMT_START { \ + g_timer_start (timer); \ + g_timer_stop (timer); \ +} G_STMT_END + +#if 0 +#define PRINT_TIMES g_print +#else +static void PRINT_TIMES (G_GNUC_UNUSED const char *format, ...) +{ +} +#endif + +static void moo_folder_dispose (GObject *object); + +static void moo_folder_deleted (MooFolderImpl *impl); +static gboolean moo_folder_do_reload (MooFolderImpl *impl); + +static void stop_populate (MooFolderImpl *impl); + +static void files_list_free (GSList **list); + +static gboolean get_icons_a_bit (MooFolderImpl *impl); +static gboolean get_stat_a_bit (MooFolderImpl *impl); +static double get_names (MooFolderImpl *impl); + +#define FILE_PATH(folder,file) g_build_filename (folder->impl->path, file->name, NULL) + +static void start_monitor (MooFolderImpl *impl); +static void stop_monitor (MooFolderImpl *impl); + +static GSList *hash_table_to_file_list (GHashTable *files); +static void diff_hash_tables (GHashTable *table1, + GHashTable *table2, + GSList **only_1, + GSList **only_2); + + +/* MOO_TYPE_FOLDER */ +G_DEFINE_TYPE (MooFolder, _moo_folder, G_TYPE_OBJECT) + +enum { + DELETED, + FILES_ADDED, + FILES_REMOVED, + FILES_CHANGED, + NUM_SIGNALS +}; + +static guint signals[NUM_SIGNALS]; + +static void +_moo_folder_class_init (MooFolderClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->dispose = moo_folder_dispose; + + signals[DELETED] = + g_signal_new ("deleted", + G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (MooFolderClass, deleted), + NULL, NULL, + _moo_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + signals[FILES_ADDED] = + g_signal_new ("files-added", + G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (MooFolderClass, files_added), + NULL, NULL, + _moo_marshal_VOID__POINTER, + G_TYPE_NONE, 1, + G_TYPE_POINTER); + + signals[FILES_REMOVED] = + g_signal_new ("files-removed", + G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (MooFolderClass, files_removed), + NULL, NULL, + _moo_marshal_VOID__POINTER, + G_TYPE_NONE, 1, + G_TYPE_POINTER); + + signals[FILES_CHANGED] = + g_signal_new ("files-changed", + G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (MooFolderClass, files_changed), + NULL, NULL, + _moo_marshal_VOID__POINTER, + G_TYPE_NONE, 1, + G_TYPE_POINTER); +} + + +static void +_moo_folder_init (G_GNUC_UNUSED MooFolder *folder) +{ +} + + +static MooFolderImpl * +moo_folder_impl_new (MooFileSystem *fs, + const char *path, + GDir *dir) +{ + MooFolderImpl *impl; + + impl = g_new0 (MooFolderImpl, 1); + impl->deleted = FALSE; + impl->done = 0; + impl->wanted = 0; + impl->fs = fs; + impl->dir = dir; + impl->files_copy = NULL; + impl->path = g_strdup (path); + impl->populate_func = NULL; + impl->populate_idle_id = 0; + impl->populate_timeout = BACKGROUND_TIMEOUT; + impl->timer = g_timer_new (); + g_timer_stop (impl->timer); + + impl->files = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, + (GDestroyNotify) _moo_file_unref); + + return impl; +} + + +static void +folder_shutdown (MooFolderImpl *impl) +{ + stop_populate (impl); + stop_monitor (impl); + files_list_free (&impl->files_copy); + if (impl->reload_idle) + g_source_remove (impl->reload_idle); + if (impl->files) + g_hash_table_destroy (impl->files); + if (impl->dir) + g_dir_close (impl->dir); + if (impl->populate_idle_id) + g_source_remove (impl->populate_idle_id); + if (impl->timer) + g_timer_destroy (impl->timer); + if (impl->reload_idle) + g_source_remove (impl->reload_idle); + impl->populate_idle_id = 0; + impl->timer = NULL; + impl->reload_idle = 0; + impl->files = NULL; + impl->reload_idle = 0; +} + + +void +_moo_folder_impl_free (MooFolderImpl *impl) +{ + g_return_if_fail (impl != NULL); + folder_shutdown (impl); + g_free (impl->path); + g_free (impl); +} + + +static void +moo_folder_dispose (GObject *object) +{ + MooFolder *folder = MOO_FOLDER (object); + + if (folder->impl) + { + MooFileSystem *fs = folder->impl->fs; + folder->impl->proxy = NULL; + _moo_file_system_folder_finalized (fs, folder); + folder->impl = NULL; + g_object_unref (fs); + } + + G_OBJECT_CLASS (_moo_folder_parent_class)->finalize (object); +} + + +MooFolder * +_moo_folder_new_with_impl (MooFolderImpl *impl) +{ + MooFolder *folder = g_object_new (MOO_TYPE_FOLDER, NULL); + folder->impl = impl; + impl->proxy = folder; + g_object_ref (impl->fs); + return folder; +} + + +MooFolder * +_moo_folder_new (MooFileSystem *fs, + const char *path, + MooFileFlags wanted, + GError **error) +{ + GDir *dir; + MooFolderImpl *impl; + MooFolder *folder; + GError *file_error = NULL; + + g_return_val_if_fail (MOO_IS_FILE_SYSTEM (fs), NULL); + g_return_val_if_fail (path != NULL, NULL); + + dir = g_dir_open (path, 0, &file_error); + + if (!dir) + { + if (file_error->domain != G_FILE_ERROR) + { + g_set_error (error, MOO_FILE_ERROR, + MOO_FILE_ERROR_FAILED, + "%s", file_error->message); + } + else switch (file_error->code) + { + case G_FILE_ERROR_NOENT: + g_set_error (error, MOO_FILE_ERROR, + MOO_FILE_ERROR_NONEXISTENT, + "%s", file_error->message); + break; + case G_FILE_ERROR_NOTDIR: + g_set_error (error, MOO_FILE_ERROR, + MOO_FILE_ERROR_NOT_FOLDER, + "%s", file_error->message); + break; + case G_FILE_ERROR_NAMETOOLONG: + case G_FILE_ERROR_LOOP: + g_set_error (error, MOO_FILE_ERROR, + MOO_FILE_ERROR_BAD_FILENAME, + "%s", file_error->message); + break; + default: + g_set_error (error, MOO_FILE_ERROR, + MOO_FILE_ERROR_FAILED, + "%s", file_error->message); + break; + } + + g_error_free (file_error); + return NULL; + } + + impl = moo_folder_impl_new (fs, path, dir); + + folder = _moo_folder_new_with_impl (impl); + get_names (folder->impl); + _moo_folder_set_wanted (folder, wanted, TRUE); + + return folder; +} + + +static void +moo_folder_deleted (MooFolderImpl *impl) +{ + folder_shutdown (impl); + impl->deleted = TRUE; + _moo_file_system_folder_deleted (impl->fs, impl); +} + + +void +_moo_folder_set_wanted (MooFolder *folder, + MooFileFlags wanted, + gboolean bit_now) +{ + Stage wanted_stage = STAGE_NAMES; + + g_return_if_fail (MOO_IS_FOLDER (folder)); + g_return_if_fail (!folder->impl->deleted); + + if (wanted & MOO_FILE_HAS_ICON) + wanted_stage = STAGE_MIME_TYPE; + else if (wanted & MOO_FILE_HAS_MIME_TYPE) + wanted_stage = STAGE_MIME_TYPE; + else if (wanted & MOO_FILE_HAS_STAT) + wanted_stage = STAGE_STAT; + + if (wanted_stage <= folder->impl->done) + return; + + if (folder->impl->wanted > folder->impl->done) + { + g_assert (folder->impl->populate_idle_id != 0); + folder->impl->wanted = MAX (folder->impl->wanted, wanted_stage); + return; + } + + folder->impl->wanted = wanted_stage; + + if (folder->impl->wanted_bg != 0) + { + g_assert (folder->impl->populate_idle_id != 0); + g_assert (folder->impl->populate_func != NULL); + g_assert (folder->impl->populate_priority != 0); + g_source_remove (folder->impl->populate_idle_id); + } + else + { + g_assert (folder->impl->populate_idle_id == 0); + + switch (folder->impl->done) + { + case STAGE_NAMES: + g_assert (folder->impl->dir == NULL); + folder->impl->populate_func = (GSourceFunc) get_stat_a_bit; + break; + case STAGE_STAT: + g_assert (folder->impl->dir == NULL); + folder->impl->populate_func = (GSourceFunc) get_icons_a_bit; + break; + default: + g_assert_not_reached (); + } + } + + folder->impl->wanted_bg = STAGE_MIME_TYPE; + folder->impl->populate_timeout = NORMAL_TIMEOUT; + folder->impl->populate_priority = NORMAL_PRIORITY; + + TIMER_CLEAR (folder->impl->timer); + + g_object_ref (folder); + + if (!bit_now || + folder->impl->populate_func (folder->impl)) + { + folder->impl->populate_idle_id = + g_timeout_add_full (folder->impl->populate_priority, + folder->impl->populate_timeout, + folder->impl->populate_func, + folder->impl, NULL); + } + + g_object_unref (folder); +} + + +static void +stop_populate (MooFolderImpl *impl) +{ + if (impl->populate_idle_id) + g_source_remove (impl->populate_idle_id); + impl->populate_idle_id = 0; + impl->populate_func = NULL; + if (impl->dir) + g_dir_close (impl->dir); + impl->dir = NULL; + files_list_free (&impl->files_copy); +} + + +static void +folder_emit_deleted (MooFolderImpl *impl) +{ + MooFolder *folder; + + /* moo_folder_deleted may call impl_free() */ + folder = impl->proxy; + moo_folder_deleted (impl); + + if (folder) + g_signal_emit (folder, signals[DELETED], 0); +} + + +static void +folder_emit_files (MooFolderImpl *impl, + guint sig, + GSList *files) +{ + if (files && impl->proxy) + g_signal_emit (impl->proxy, signals[sig], 0, files); +} + + +GSList * +_moo_folder_list_files (MooFolder *folder) +{ + g_return_val_if_fail (MOO_IS_FOLDER (folder), NULL); + g_return_val_if_fail (!folder->impl->deleted, NULL); + return hash_table_to_file_list (folder->impl->files); +} + + +static double +get_names (MooFolderImpl *impl) +{ + GTimer *timer; + GSList *added = NULL; + const char *name; + MooFile *file; + double elapsed; + + g_assert (impl->path != NULL); + g_assert (impl->dir != NULL); + g_assert (g_hash_table_size (impl->files) == 0); + + timer = g_timer_new (); + + file = _moo_file_new (impl->path, ".."); + file->flags = MOO_FILE_HAS_MIME_TYPE | MOO_FILE_HAS_ICON; + file->info = MOO_FILE_INFO_EXISTS | MOO_FILE_INFO_IS_DIR; + file->icon = _moo_file_get_icon_type (file, impl->path); + + g_hash_table_insert (impl->files, g_strdup (".."), file); + added = g_slist_prepend (added, file); + + for (name = g_dir_read_name (impl->dir); + name != NULL; + name = g_dir_read_name (impl->dir)) + { + file = _moo_file_new (impl->path, name); + + if (file) + { + file->icon = _moo_file_icon_blank (); + g_hash_table_insert (impl->files, g_strdup (name), file); + added = g_slist_prepend (added, file); + } + else + { + g_critical ("%s: _moo_file_new() failed for '%s'", G_STRLOC, name); + } + } + + folder_emit_files (impl, FILES_ADDED, added); + g_slist_free (added); + + elapsed = impl->debug.names_timer = g_timer_elapsed (timer, NULL); + g_timer_destroy (timer); + + g_dir_close (impl->dir); + impl->dir = NULL; + + impl->done = STAGE_NAMES; + + PRINT_TIMES ("names folder %s: %f sec\n", + impl->path, + impl->debug.names_timer); + + return elapsed; +} + + +static gboolean +get_stat_a_bit (MooFolderImpl *impl) +{ + gboolean done = FALSE; + double elapsed; + + g_assert (impl->dir == NULL); + g_assert (impl->done == STAGE_NAMES); + g_assert (impl->path != NULL); + + elapsed = g_timer_elapsed (impl->timer, NULL); + g_timer_continue (impl->timer); + + if (!impl->files_copy) + impl->files_copy = hash_table_to_file_list (impl->files); + if (!impl->files_copy) + done = TRUE; + + while (!done) + { + GSList *changed = impl->files_copy; + MooFile *file = changed->data; + impl->files_copy = g_slist_remove_link (impl->files_copy, impl->files_copy); + + if (!(file->flags & MOO_FILE_HAS_STAT)) + { + _moo_file_stat (file, impl->path); + folder_emit_files (impl, FILES_CHANGED, changed); + } + else + { + _moo_file_unref (file); + } + + g_slist_free_1 (changed); + + if (!impl->files_copy) + done = TRUE; + + if (g_timer_elapsed (impl->timer, NULL) > impl->populate_timeout) + break; + } + + elapsed = g_timer_elapsed (impl->timer, NULL) - elapsed; + impl->debug.stat_timer += elapsed; + impl->debug.stat_counter += 1; + g_timer_stop (impl->timer); + + if (!done) + { + TIMER_CLEAR (impl->timer); + return TRUE; + } + else + { + g_assert (impl->files_copy == NULL); + impl->populate_idle_id = 0; + + PRINT_TIMES ("stat folder %s: %d iterations, %f sec\n", + impl->path, + impl->debug.stat_counter, + impl->debug.stat_timer); + + impl->done = STAGE_STAT; + + if (impl->wanted >= STAGE_MIME_TYPE || impl->wanted_bg >= STAGE_MIME_TYPE) + { + if (impl->wanted >= STAGE_MIME_TYPE) + { + impl->populate_priority = NORMAL_PRIORITY; + impl->populate_timeout = NORMAL_TIMEOUT; + } + else if (impl->wanted_bg >= STAGE_MIME_TYPE) + { + impl->populate_priority = BACKGROUND_PRIORITY; + impl->populate_timeout = BACKGROUND_TIMEOUT; + } + + if (impl->populate_idle_id) + g_source_remove (impl->populate_idle_id); + impl->populate_idle_id = 0; + impl->populate_func = (GSourceFunc) get_icons_a_bit; + + if (g_timer_elapsed (impl->timer, NULL) < impl->populate_timeout) + { + /* in this case we may block for as much as twice TIMEOUT, but usually + it allows stat and loading icons in one iteration */ + TIMER_CLEAR (impl->timer); + if (impl->populate_func (impl)) + impl->populate_idle_id = + g_timeout_add_full (impl->populate_priority, + impl->populate_timeout, + impl->populate_func, + impl, NULL); + } + else + { + TIMER_CLEAR (impl->timer); + impl->populate_idle_id = + g_timeout_add_full (impl->populate_priority, + impl->populate_timeout, + impl->populate_func, + impl, NULL); + } + } + else + { + impl->populate_func = NULL; + impl->populate_priority = 0; + impl->populate_timeout = 0; + } + + return FALSE; + } +} + + +static gboolean +get_icons_a_bit (MooFolderImpl *impl) +{ + gboolean done = FALSE; + double elapsed; + + g_assert (impl->dir == NULL); + g_assert (impl->done == STAGE_STAT); + g_assert (impl->path != NULL); + + elapsed = g_timer_elapsed (impl->timer, NULL); + g_timer_continue (impl->timer); + + if (!impl->files_copy) + impl->files_copy = hash_table_to_file_list (impl->files); + if (!impl->files_copy) + done = TRUE; + + while (!done) + { + GSList *changed = impl->files_copy; + MooFile *file = changed->data; + + impl->files_copy = g_slist_remove_link (impl->files_copy, changed); + +#ifndef __WIN32__ + if (file->info & MOO_FILE_INFO_EXISTS && + !(file->flags & MOO_FILE_HAS_MIME_TYPE)) + { + char *path = g_build_filename (impl->path, file->name, NULL); + + _moo_file_find_mime_type (file, path); + + file->flags |= MOO_FILE_HAS_ICON; + file->icon = _moo_file_get_icon_type (file, impl->path); + folder_emit_files (impl, FILES_CHANGED, changed); + g_free (path); + } +#endif + + _moo_file_unref (file); + g_slist_free (changed); + + if (!impl->files_copy) + done = TRUE; + + if (g_timer_elapsed (impl->timer, NULL) > impl->populate_timeout) + break; + } + + elapsed = g_timer_elapsed (impl->timer, NULL) - elapsed; + impl->debug.icons_timer += elapsed; + impl->debug.icons_counter += 1; + TIMER_CLEAR (impl->timer); + + if (done) + { + PRINT_TIMES ("icons folder %s: %d iterations, %f sec\n", + impl->path, + impl->debug.icons_counter, + impl->debug.icons_timer); + + g_assert (impl->files_copy == NULL); + impl->populate_idle_id = 0; + impl->done = STAGE_MIME_TYPE; + impl->populate_func = NULL; + impl->populate_priority = 0; + impl->populate_timeout = 0; + + start_monitor (impl); + } + + return !done; +} + + +MooFile * +_moo_folder_get_file (MooFolder *folder, + const char *basename) +{ + g_return_val_if_fail (MOO_IS_FOLDER (folder), NULL); + g_return_val_if_fail (!folder->impl->deleted, NULL); + g_return_val_if_fail (basename != NULL, NULL); + return g_hash_table_lookup (folder->impl->files, basename); +} + + +char * +_moo_folder_get_file_path (MooFolder *folder, + MooFile *file) +{ + g_return_val_if_fail (MOO_IS_FOLDER (folder), NULL); + g_return_val_if_fail (file != NULL, NULL); + return FILE_PATH (folder, file); +} + + +char * +_moo_folder_get_file_uri (MooFolder *folder, + MooFile *file) +{ + char *path, *uri; + + g_return_val_if_fail (MOO_IS_FOLDER (folder), NULL); + g_return_val_if_fail (file != NULL, NULL); + + path = FILE_PATH (folder, file); + g_return_val_if_fail (path != NULL, NULL); + + uri = g_filename_to_uri (path, NULL, NULL); + + g_free (path); + return uri; +} + + +MooFolder * +_moo_folder_get_parent (MooFolder *folder, + MooFileFlags wanted) +{ + g_return_val_if_fail (MOO_IS_FOLDER (folder), NULL); + g_return_val_if_fail (!folder->impl->deleted, NULL); + return _moo_file_system_get_parent_folder (folder->impl->fs, + folder, wanted); +} + + +MooFileSystem * +_moo_folder_get_file_system (MooFolder *folder) +{ + g_return_val_if_fail (MOO_IS_FOLDER (folder), NULL); + return folder->impl->fs; +} + + +/*****************************************************************************/ +/* Monitoring + */ + +static void file_deleted (MooFolderImpl *folder, + const char *name); +static void file_changed (MooFolderImpl *folder, + const char *name); +static void file_created (MooFolderImpl *folder, + const char *name); + +static void +fam_callback (MooFileWatch *watch, + MooFileEvent *event, + gpointer data) +{ + MooFolderImpl *impl = data; + + g_return_if_fail (watch == impl->fam); + g_return_if_fail (event->monitor_id == impl->fam_request); + + switch (event->code) + { + case MOO_FILE_EVENT_CHANGED: + file_changed (impl, event->filename); + break; + case MOO_FILE_EVENT_DELETED: + file_deleted (impl, event->filename); + break; + case MOO_FILE_EVENT_ERROR: + stop_monitor (impl); + file_changed (impl, impl->path); + break; + } +} + + +static void +start_monitor (MooFolderImpl *impl) +{ + GError *error = NULL; + + g_return_if_fail (!impl->deleted); + g_return_if_fail (impl->fam_request == 0); + impl->fam = _moo_file_system_get_file_watch (impl->fs); + g_return_if_fail (impl->fam != NULL); + + impl->fam_request = + moo_file_watch_create_monitor (impl->fam, impl->path, + fam_callback, impl, + NULL, &error); + + if (!impl->fam_request) + { + g_warning ("%s: moo_file_watch_create_monitor failed for path '%s'", G_STRLOC, impl->path); + g_warning ("%s: %s", G_STRLOC, error->message); + g_error_free (error); + return; + } +} + + +static void +stop_monitor (MooFolderImpl *impl) +{ + if (impl->fam_request) + { + moo_file_watch_cancel_monitor (impl->fam, impl->fam_request); + impl->fam = NULL; + impl->fam_request = 0; + } +} + + +static void +file_deleted (MooFolderImpl *impl, + const char *name) +{ + MooFile *file; + GSList *list; + + g_return_if_fail (!impl->deleted); + + if (!strcmp (name, impl->path)) + return folder_emit_deleted (impl); + + file = g_hash_table_lookup (impl->files, name); + if (!file) return; + + _moo_file_ref (file); + g_hash_table_remove (impl->files, name); + + list = g_slist_append (NULL, file); + folder_emit_files (impl, FILES_REMOVED, list); + + g_slist_free (list); + _moo_file_unref (file); +} + + +void +_moo_folder_reload (MooFolder *folder) +{ + g_return_if_fail (MOO_IS_FOLDER (folder)); + + if (folder->impl->reload_idle) + g_source_remove (folder->impl->reload_idle); + folder->impl->reload_idle = 0; + + moo_folder_do_reload (folder->impl); +} + + +static void +file_changed (MooFolderImpl *impl, + const char *name) +{ + g_return_if_fail (!impl->deleted); + + if (!strcmp (name, impl->path)) + { + if (!impl->reload_idle) + impl->reload_idle = g_idle_add ((GSourceFunc) moo_folder_do_reload, impl); + } + else + { + /* XXX */ + g_return_if_reached (); + } +} + + +static void +file_created (MooFolderImpl *impl, + const char *name) +{ + MooFile *file; + GSList *list; + + g_return_if_fail (!impl->deleted); + + file = _moo_file_new (impl->path, name); + g_return_if_fail (file != NULL); + + file->icon = _moo_file_get_icon_type (file, impl->path); + _moo_file_stat (file, impl->path); + +#ifndef __WIN32__ + if (file->info & MOO_FILE_INFO_EXISTS && + !(file->flags & MOO_FILE_HAS_MIME_TYPE)) + { + char *path = g_build_filename (impl->path, file->name, NULL); + + _moo_file_find_mime_type (file, path); + + file->flags |= MOO_FILE_HAS_ICON; + file->icon = _moo_file_get_icon_type (file, impl->path); + g_free (path); + } +#endif + + g_hash_table_insert (impl->files, g_strdup (name), file); + list = g_slist_append (NULL, file); + folder_emit_files (impl, FILES_ADDED, list); + + g_slist_free (list); +} + + +/* TODO */ +static gboolean +moo_folder_do_reload (MooFolderImpl *impl) +{ + GHashTable *files; + GDir *dir; + GError *error = NULL; + const char *name; + GSList *new = NULL, *deleted = NULL, *l; + + g_return_val_if_fail (!impl->deleted, FALSE); + impl->reload_idle = 0; + + dir = g_dir_open (impl->path, 0, &error); + + if (!dir) + { + g_warning ("%s: could not open directory %s", + G_STRLOC, impl->path); + g_warning ("%s: %s", G_STRLOC, error->message); + g_error_free (error); + folder_emit_deleted (impl); + return FALSE; + } + + files = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + + while ((name = g_dir_read_name (dir))) + g_hash_table_insert (files, g_strdup (name), NULL); + + diff_hash_tables (files, impl->files, &new, &deleted); + + for (l = new; l != NULL; l = l->next) + file_created (impl, l->data); + + for (l = deleted; l != NULL; l = l->next) + file_deleted (impl, l->data); + + g_slist_foreach (new, (GFunc) g_free, NULL); + g_slist_foreach (deleted, (GFunc) g_free, NULL); + g_slist_free (new); + g_slist_free (deleted); + g_hash_table_destroy (files); + g_dir_close (dir); + return FALSE; +} + + +/* XXX */ +static char * +moo_file_get_type_string (MooFile *file) +{ + g_return_val_if_fail (MOO_FILE_EXISTS (file), NULL); + + if (MOO_FILE_IS_DIR (file)) + return g_strdup ("folder"); + else if (file->mime_type) + return g_strdup (file->mime_type); + else + return g_strdup ("file"); +} + + +/* XXX */ +static char * +moo_file_get_size_string (MooFile *file) +{ + return g_strdup_printf ("%" G_GINT64_FORMAT, (MooFileSize) file->statbuf.st_size); +} + + +/* XXX */ +static char * +moo_file_get_mtime_string (MooFile *file) +{ + static char buf[1024]; + + if (!MOO_FILE_EXISTS (file)) + return NULL; + +#ifdef __WIN32__ + if (MOO_FILE_IS_DIR (file)) + return NULL; +#endif + + if (strftime (buf, 1024, "%x %X", localtime ((time_t*)&file->statbuf.st_mtime))) + return g_strdup (buf); + else + return NULL; +} + + +char ** +_moo_folder_get_file_info (MooFolder *folder, + MooFile *file) +{ + GPtrArray *array; + GSList *list; + + g_return_val_if_fail (MOO_IS_FOLDER (folder), NULL); + g_return_val_if_fail (file != NULL, NULL); + g_return_val_if_fail (!folder->impl->deleted, NULL); + g_return_val_if_fail (folder->impl->files != NULL, NULL); + g_return_val_if_fail (g_hash_table_lookup (folder->impl->files, + _moo_file_name (file)) == file, NULL); + + _moo_file_stat (file, folder->impl->path); + +#ifndef __WIN32__ + if (file->info & MOO_FILE_INFO_EXISTS && + !(file->flags & MOO_FILE_HAS_MIME_TYPE)) + { + char *path = FILE_PATH (folder, file); + + _moo_file_find_mime_type (file, path); + + file->flags |= MOO_FILE_HAS_ICON; + file->icon = _moo_file_get_icon_type (file, folder->impl->path); + g_free (path); + } +#endif + + array = g_ptr_array_new (); + + if (file->info & MOO_FILE_INFO_EXISTS) + { + char *type, *mtime, *location; + + g_ptr_array_add (array, g_strdup ("Type:")); + type = moo_file_get_type_string (file); + + if (file->info & MOO_FILE_INFO_IS_LINK) + { + g_ptr_array_add (array, g_strdup_printf ("link to %s", type)); + g_free (type); + } + else + { + g_ptr_array_add (array, type); + } + + location = g_filename_display_name (_moo_folder_get_path (folder)); + g_ptr_array_add (array, g_strdup ("Location:")); + g_ptr_array_add (array, location); + + if (!(file->info & MOO_FILE_INFO_IS_DIR)) + { + g_ptr_array_add (array, g_strdup ("Size:")); + g_ptr_array_add (array, moo_file_get_size_string (file)); + } + + mtime = moo_file_get_mtime_string (file); + + if (mtime) + { + g_ptr_array_add (array, g_strdup ("Modified:")); + g_ptr_array_add (array, mtime); + } + } + else if (file->info & MOO_FILE_INFO_IS_LINK) + { + g_ptr_array_add (array, g_strdup ("Type:")); + g_ptr_array_add (array, g_strdup ("broken symbolic link")); + } + +#ifndef __WIN32__ + if ((file->info & MOO_FILE_INFO_IS_LINK) && + _moo_file_link_get_target (file)) + { + g_ptr_array_add (array, g_strdup ("Points to:")); + g_ptr_array_add (array, g_strdup (_moo_file_link_get_target (file))); + } +#endif + + list = g_slist_append (NULL, _moo_file_ref (file)); + g_object_ref (folder); + folder_emit_files (folder->impl, FILES_CHANGED, list); + g_object_unref (folder); + _moo_file_unref (file); + g_slist_free (list); + + g_ptr_array_add (array, NULL); + return (char**) g_ptr_array_free (array, FALSE); +} + + +const char * +_moo_folder_get_path (MooFolder *folder) +{ + g_return_val_if_fail (MOO_IS_FOLDER (folder), NULL); + return folder->impl->path; +} + + +static void +files_list_free (GSList **list) +{ + g_slist_foreach (*list, (GFunc) _moo_file_unref, NULL); + g_slist_free (*list); + *list = NULL; +} + + +static void +prepend_file (G_GNUC_UNUSED gpointer key, + MooFile *file, + GSList **list) +{ + *list = g_slist_prepend (*list, _moo_file_ref (file)); +} + +static GSList * +hash_table_to_file_list (GHashTable *files) +{ + GSList *list = NULL; + g_return_val_if_fail (files != NULL, NULL); + g_hash_table_foreach (files, (GHFunc) prepend_file, &list); + return list; +} + + +static void +check_unique (const char *key, + G_GNUC_UNUSED gpointer whatever, + gpointer user_data) +{ + struct { + GSList *list; + GHashTable *table2; + } *data = user_data; + gpointer orig_key, value; + + if (!g_hash_table_lookup_extended (data->table2, key, &orig_key, &value)) + data->list = g_slist_prepend (data->list, g_strdup (key)); +} + +static void +get_unique (GHashTable *table1, + GHashTable *table2, + GSList **only_1) +{ + struct { + GSList *list; + GHashTable *table2; + } data; + + data.list = NULL; + data.table2 = table2; + g_hash_table_foreach (table1, (GHFunc) check_unique, &data); + + *only_1 = data.list; +} + +static void +diff_hash_tables (GHashTable *table1, + GHashTable *table2, + GSList **only_1, + GSList **only_2) +{ + get_unique (table1, table2, only_1); + get_unique (table2, table1, only_2); +} diff --git a/moo/moofileview/moofolder.h b/moo/moofileview/moofolder.h new file mode 100644 index 00000000..5c765640 --- /dev/null +++ b/moo/moofileview/moofolder.h @@ -0,0 +1,54 @@ +/* + * moofolder.h + * + * Copyright (C) 2004-2006 by Yevgen Muntyan + * + * 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. + * + * See COPYING file that comes with this distribution. + */ + +#ifndef MOO_FILE_VIEW_COMPILATION +#error "This file may not be included" +#endif + +#ifndef __MOO_FOLDER_H__ +#define __MOO_FOLDER_H__ + +#include "moofileview/moofile.h" + +G_BEGIN_DECLS + + +#define MOO_TYPE_FOLDER (_moo_folder_get_type ()) +#define MOO_FOLDER(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), MOO_TYPE_FOLDER, MooFolder)) +#define MOO_IS_FOLDER(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), MOO_TYPE_FOLDER)) + + +GType _moo_folder_get_type (void) G_GNUC_CONST; + +const char *_moo_folder_get_path (MooFolder *folder); +/* list should be freed and elements unref'ed */ +GSList *_moo_folder_list_files (MooFolder *folder); +MooFile *_moo_folder_get_file (MooFolder *folder, + const char *basename); +char *_moo_folder_get_file_path (MooFolder *folder, + MooFile *file); +char *_moo_folder_get_file_uri (MooFolder *folder, + MooFile *file); +/* result should be unref'ed */ +MooFolder *_moo_folder_get_parent (MooFolder *folder, + MooFileFlags wanted); +char *_moo_folder_get_parent_path(MooFolder *folder); + +char **_moo_folder_get_file_info (MooFolder *folder, + MooFile *file); +void _moo_folder_reload (MooFolder *folder); + + +G_END_DECLS + +#endif /* __MOO_FOLDER_H__ */ diff --git a/moo/moofileview/moofoldermodel.c b/moo/moofileview/moofoldermodel.c index b9fa57fe..d9907a31 100644 --- a/moo/moofileview/moofoldermodel.c +++ b/moo/moofileview/moofoldermodel.c @@ -14,6 +14,7 @@ #define MOO_FILE_VIEW_COMPILATION #include "moofoldermodel.h" #include "moofile-private.h" +#include "moofolder-private.h" #include "moofoldermodel-private.h" #include diff --git a/moo/mooutils/moofilewatch.c b/moo/mooutils/moofilewatch.c index a5bab7a7..0e6579cb 100644 --- a/moo/mooutils/moofilewatch.c +++ b/moo/mooutils/moofilewatch.c @@ -44,7 +44,7 @@ #include "mooutils/moocompat.h" -#if 1 || defined(__WIN32__) && 0 +#if 0 || defined(__WIN32__) && 0 #define DEBUG_PRINT g_message #else static void DEBUG_PRINT (G_GNUC_UNUSED const char *format, ...) @@ -1037,6 +1037,7 @@ typedef struct { HANDLE events[MAXIMUM_WAIT_OBJECTS]; FAMThreadWatch watches[MAXIMUM_WAIT_OBJECTS]; guint n_events; + gboolean running; } FAMThread; typedef enum {