medit/moo/moofileview/moofolder.c

1182 lines
31 KiB
C

/*
* moofolder.c
*
* Copyright (C) 2004-2006 by Yevgen Muntyan <muntyan@math.tamu.edu>
*
* 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 <glib/gstdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <errno.h>
#include <time.h>
#include <gtk/gtkicontheme.h>
#include <gtk/gtkiconfactory.h>
#include <gtk/gtkstock.h>
#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);
impl->reload_idle = 0;
if (impl->files)
g_hash_table_destroy (impl->files);
impl->files = NULL;
if (impl->dir)
g_dir_close (impl->dir);
impl->dir = NULL;
if (impl->populate_idle_id)
g_source_remove (impl->populate_idle_id);
impl->populate_idle_id = 0;
if (impl->timer)
g_timer_destroy (impl->timer);
impl->timer = NULL;
}
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;
}
#if 0
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);
}
#endif
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_CREATED:
file_created (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);
}
}
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);
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);
}
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);
}