1323 lines
41 KiB
C
1323 lines
41 KiB
C
|
|
/* vi:set et ai sw=2 sts=2 ts=2: */
|
||
|
|
/*-
|
||
|
|
* Copyright (c) 2005-2006 Benedikt Meurer <benny@xfce.org>
|
||
|
|
* Copyright (c) 2009 Jannis Pohlmann <jannis@xfce.org>
|
||
|
|
*
|
||
|
|
* This program is free software; you can redistribute it and/or modify it
|
||
|
|
* under the terms of the GNU General Public License as published by the Free
|
||
|
|
* Software Foundation; either version 2 of the License, or (at your option)
|
||
|
|
* any later version.
|
||
|
|
*
|
||
|
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||
|
|
* more details.
|
||
|
|
*
|
||
|
|
* You should have received a copy of the GNU General Public License along with
|
||
|
|
* this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
||
|
|
* Place, Suite 330, Boston, MA 02111-1307 USA
|
||
|
|
*/
|
||
|
|
|
||
|
|
#ifdef HAVE_CONFIG_H
|
||
|
|
#include "config.h"
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#include "thunar/thunar-folder.h"
|
||
|
|
#include "thunar/thunar-gobject-extensions.h"
|
||
|
|
#include "thunar/thunar-io-jobs.h"
|
||
|
|
#include "thunar/thunar-job.h"
|
||
|
|
#include "thunar/thunar-private.h"
|
||
|
|
|
||
|
|
#define DEBUG_FILE_CHANGES FALSE
|
||
|
|
|
||
|
|
/* The maximum throttle interval (in ms) in which files will be added, removed or notified to be changed */
|
||
|
|
#define THUNAR_FOLDER_UPDATE_TIMEOUT (25)
|
||
|
|
|
||
|
|
/* property identifiers */
|
||
|
|
enum
|
||
|
|
{
|
||
|
|
PROP_0,
|
||
|
|
PROP_CORRESPONDING_FILE,
|
||
|
|
PROP_LOADING,
|
||
|
|
};
|
||
|
|
|
||
|
|
/* signal identifiers */
|
||
|
|
enum
|
||
|
|
{
|
||
|
|
DESTROY,
|
||
|
|
ERROR,
|
||
|
|
FILES_ADDED,
|
||
|
|
FILES_REMOVED,
|
||
|
|
FILES_CHANGED,
|
||
|
|
THUMBNAILS_UPDATED,
|
||
|
|
LAST_SIGNAL,
|
||
|
|
};
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
static void
|
||
|
|
thunar_folder_dispose (GObject *object);
|
||
|
|
static void
|
||
|
|
thunar_folder_finalize (GObject *object);
|
||
|
|
static void
|
||
|
|
thunar_folder_get_property (GObject *object,
|
||
|
|
guint prop_id,
|
||
|
|
GValue *value,
|
||
|
|
GParamSpec *pspec);
|
||
|
|
static void
|
||
|
|
thunar_folder_set_property (GObject *object,
|
||
|
|
guint prop_uid,
|
||
|
|
const GValue *value,
|
||
|
|
GParamSpec *pspec);
|
||
|
|
static void
|
||
|
|
thunar_folder_real_destroy (ThunarFolder *folder);
|
||
|
|
static void
|
||
|
|
thunar_folder_error (ExoJob *job,
|
||
|
|
GError *error,
|
||
|
|
ThunarFolder *folder);
|
||
|
|
static gboolean
|
||
|
|
thunar_folder_files_ready (ThunarJob *job,
|
||
|
|
GList *files,
|
||
|
|
ThunarFolder *folder);
|
||
|
|
static void
|
||
|
|
thunar_folder_finished (ExoJob *job,
|
||
|
|
ThunarFolder *folder);
|
||
|
|
static void
|
||
|
|
thunar_folder_changed (ThunarFile *file,
|
||
|
|
ThunarFolder *folder);
|
||
|
|
static void
|
||
|
|
thunar_folder_destroyed (ThunarFile *file,
|
||
|
|
ThunarFolder *folder);
|
||
|
|
static void
|
||
|
|
thunar_folder_file_changed (ThunarFolder *folder,
|
||
|
|
ThunarFile *file);
|
||
|
|
static void
|
||
|
|
thunar_folder_file_destroyed (ThunarFolder *folder,
|
||
|
|
ThunarFile *file);
|
||
|
|
static void
|
||
|
|
thunar_folder_monitor (GFileMonitor *monitor,
|
||
|
|
GFile *file,
|
||
|
|
GFile *other_file,
|
||
|
|
GFileMonitorEvent event_type,
|
||
|
|
gpointer user_data);
|
||
|
|
static void
|
||
|
|
thunar_folder_load_content_types (ThunarFolder *folder,
|
||
|
|
GHashTable *files);
|
||
|
|
static void
|
||
|
|
thunar_folder_add_file (ThunarFolder *folder,
|
||
|
|
ThunarFile *file);
|
||
|
|
static void
|
||
|
|
thunar_folder_remove_file (ThunarFolder *folder,
|
||
|
|
ThunarFile *file);
|
||
|
|
static void
|
||
|
|
thunar_folder_thumbnail_updated (ThunarFolder *folder,
|
||
|
|
ThunarThumbnailSize size,
|
||
|
|
ThunarFile *file);
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
struct _ThunarFolderClass
|
||
|
|
{
|
||
|
|
GObjectClass __parent__;
|
||
|
|
|
||
|
|
/* signals */
|
||
|
|
void (*destroy) (ThunarFolder *folder);
|
||
|
|
void (*error) (ThunarFolder *folder,
|
||
|
|
const GError *error);
|
||
|
|
void (*files_added) (ThunarFolder *folder,
|
||
|
|
GHashTable *files);
|
||
|
|
void (*files_removed) (ThunarFolder *folder,
|
||
|
|
GHashTable *files);
|
||
|
|
void (*files_changed) (ThunarFolder *folder,
|
||
|
|
GHashTable *files);
|
||
|
|
void (*thumbnails_updated) (ThunarFolder *folder,
|
||
|
|
GList *files);
|
||
|
|
};
|
||
|
|
|
||
|
|
struct _ThunarFolder
|
||
|
|
{
|
||
|
|
GObject __parent__;
|
||
|
|
|
||
|
|
ThunarJob *job;
|
||
|
|
ThunarJob *content_type_job;
|
||
|
|
|
||
|
|
ThunarFile *corresponding_file;
|
||
|
|
|
||
|
|
/* Files which were loaded a list directory jobs. The key is a ThunarFile; value is NULL (unimportant)*/
|
||
|
|
GHashTable *loaded_files_map;
|
||
|
|
|
||
|
|
/* Files which were changed recently. The key is a ThunarFile; value is NULL (unimportant)*/
|
||
|
|
GHashTable *changed_files_map;
|
||
|
|
|
||
|
|
/* Files which were added recently. The key is a ThunarFile; value is NULL (unimportant)*/
|
||
|
|
GHashTable *added_files_map;
|
||
|
|
|
||
|
|
/* Files which were removed recently. The key is a ThunarFile; value is NULL (unimportant)*/
|
||
|
|
GHashTable *removed_files_map;
|
||
|
|
|
||
|
|
/* Files inside this folder. The key is a ThunarFile; value is NULL (unimportant)*/
|
||
|
|
GHashTable *files_map;
|
||
|
|
|
||
|
|
gboolean reload_info;
|
||
|
|
|
||
|
|
guint in_destruction : 1;
|
||
|
|
|
||
|
|
GFileMonitor *monitor;
|
||
|
|
|
||
|
|
/* timeout source ID, used for collecting updates on files before sending the related signal */
|
||
|
|
guint files_update_timeout_source_id;
|
||
|
|
|
||
|
|
/* List of ThunarFiles for which the thumbnail got updated recently */
|
||
|
|
GList *thumbnail_updated_files;
|
||
|
|
|
||
|
|
/* timeout source ID, used for collecting files for which to update the thumbnail before sending the 'thumbnail-updated' signal */
|
||
|
|
guint thumbnail_updated_timeout_source_id;
|
||
|
|
|
||
|
|
/* True if all files of the directory are available as ThunarFiles */
|
||
|
|
gboolean loaded;
|
||
|
|
};
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
static guint folder_signals[LAST_SIGNAL];
|
||
|
|
static GQuark thunar_folder_quark;
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
G_DEFINE_TYPE (ThunarFolder, thunar_folder, G_TYPE_OBJECT)
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
static void
|
||
|
|
thunar_folder_constructed (GObject *object)
|
||
|
|
{
|
||
|
|
ThunarFolder *folder = THUNAR_FOLDER (object);
|
||
|
|
GError *error = NULL;
|
||
|
|
|
||
|
|
folder->monitor = g_file_monitor_directory (thunar_file_get_file (folder->corresponding_file),
|
||
|
|
G_FILE_MONITOR_WATCH_MOVES, NULL, &error);
|
||
|
|
|
||
|
|
if (G_LIKELY (folder->monitor != NULL))
|
||
|
|
g_signal_connect (folder->monitor, "changed", G_CALLBACK (thunar_folder_monitor), folder);
|
||
|
|
else
|
||
|
|
{
|
||
|
|
g_debug ("Could not create folder monitor: %s", error->message);
|
||
|
|
g_error_free (error);
|
||
|
|
}
|
||
|
|
|
||
|
|
G_OBJECT_CLASS (thunar_folder_parent_class)->constructed (object);
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
static void
|
||
|
|
thunar_folder_class_init (ThunarFolderClass *klass)
|
||
|
|
{
|
||
|
|
GObjectClass *gobject_class;
|
||
|
|
|
||
|
|
gobject_class = G_OBJECT_CLASS (klass);
|
||
|
|
gobject_class->dispose = thunar_folder_dispose;
|
||
|
|
gobject_class->finalize = thunar_folder_finalize;
|
||
|
|
gobject_class->get_property = thunar_folder_get_property;
|
||
|
|
gobject_class->set_property = thunar_folder_set_property;
|
||
|
|
gobject_class->constructed = thunar_folder_constructed;
|
||
|
|
|
||
|
|
klass->destroy = thunar_folder_real_destroy;
|
||
|
|
|
||
|
|
/**
|
||
|
|
* ThunarFolder::corresponding-file:
|
||
|
|
*
|
||
|
|
* The #ThunarFile referring to the #ThunarFolder.
|
||
|
|
**/
|
||
|
|
g_object_class_install_property (gobject_class,
|
||
|
|
PROP_CORRESPONDING_FILE,
|
||
|
|
g_param_spec_object ("corresponding-file",
|
||
|
|
"corresponding-file",
|
||
|
|
"corresponding-file",
|
||
|
|
THUNAR_TYPE_FILE,
|
||
|
|
G_PARAM_READABLE
|
||
|
|
| G_PARAM_WRITABLE
|
||
|
|
| G_PARAM_CONSTRUCT_ONLY));
|
||
|
|
|
||
|
|
/**
|
||
|
|
* ThunarFolder::loading:
|
||
|
|
*
|
||
|
|
* Tells whether the contents of the #ThunarFolder are
|
||
|
|
* currently being loaded.
|
||
|
|
**/
|
||
|
|
g_object_class_install_property (gobject_class,
|
||
|
|
PROP_LOADING,
|
||
|
|
g_param_spec_boolean ("loading",
|
||
|
|
"loading",
|
||
|
|
"loading",
|
||
|
|
FALSE,
|
||
|
|
EXO_PARAM_READABLE));
|
||
|
|
/**
|
||
|
|
* ThunarFolder::destroy:
|
||
|
|
* @folder : a #ThunarFolder.
|
||
|
|
*
|
||
|
|
* Emitted when the #ThunarFolder is destroyed.
|
||
|
|
**/
|
||
|
|
folder_signals[DESTROY] =
|
||
|
|
g_signal_new (I_ ("destroy"),
|
||
|
|
G_TYPE_FROM_CLASS (gobject_class),
|
||
|
|
G_SIGNAL_RUN_CLEANUP | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
|
||
|
|
G_STRUCT_OFFSET (ThunarFolderClass, destroy),
|
||
|
|
NULL, NULL,
|
||
|
|
g_cclosure_marshal_VOID__VOID,
|
||
|
|
G_TYPE_NONE, 0);
|
||
|
|
|
||
|
|
/**
|
||
|
|
* ThunarFolder::error:
|
||
|
|
* @folder : a #ThunarFolder.
|
||
|
|
* @error : the #GError describing the problem.
|
||
|
|
*
|
||
|
|
* Emitted when the #ThunarFolder fails to completly
|
||
|
|
* load the directory content because of an error.
|
||
|
|
**/
|
||
|
|
folder_signals[ERROR] =
|
||
|
|
g_signal_new (I_ ("error"),
|
||
|
|
G_TYPE_FROM_CLASS (gobject_class),
|
||
|
|
G_SIGNAL_RUN_LAST,
|
||
|
|
G_STRUCT_OFFSET (ThunarFolderClass, error),
|
||
|
|
NULL, NULL,
|
||
|
|
g_cclosure_marshal_VOID__POINTER,
|
||
|
|
G_TYPE_NONE, 1, G_TYPE_POINTER);
|
||
|
|
|
||
|
|
/**
|
||
|
|
* ThunarFolder::files-added:
|
||
|
|
*
|
||
|
|
* Emitted by the #ThunarFolder whenever new files have
|
||
|
|
* been added to a particular folder.
|
||
|
|
**/
|
||
|
|
folder_signals[FILES_ADDED] =
|
||
|
|
g_signal_new (I_ ("files-added"),
|
||
|
|
G_TYPE_FROM_CLASS (gobject_class),
|
||
|
|
G_SIGNAL_RUN_LAST,
|
||
|
|
G_STRUCT_OFFSET (ThunarFolderClass, files_added),
|
||
|
|
NULL, NULL,
|
||
|
|
g_cclosure_marshal_VOID__POINTER,
|
||
|
|
G_TYPE_NONE, 1, G_TYPE_POINTER);
|
||
|
|
|
||
|
|
/**
|
||
|
|
* ThunarFolder::files-removed:
|
||
|
|
*
|
||
|
|
* Emitted by the #ThunarFolder whenever a bunch of files
|
||
|
|
* is removed from the folder, which means they are not
|
||
|
|
* necessarily deleted from disk. This can be used to implement
|
||
|
|
* the reload of folders, which take longer to load.
|
||
|
|
**/
|
||
|
|
folder_signals[FILES_REMOVED] =
|
||
|
|
g_signal_new (I_ ("files-removed"),
|
||
|
|
G_TYPE_FROM_CLASS (gobject_class),
|
||
|
|
G_SIGNAL_RUN_LAST,
|
||
|
|
G_STRUCT_OFFSET (ThunarFolderClass, files_removed),
|
||
|
|
NULL, NULL,
|
||
|
|
g_cclosure_marshal_VOID__POINTER,
|
||
|
|
G_TYPE_NONE, 1, G_TYPE_POINTER);
|
||
|
|
|
||
|
|
/**
|
||
|
|
* ThunarFolder::file-changed:
|
||
|
|
*
|
||
|
|
* Emitted by the #ThunarFolder whenever at least one file in the folder has changed
|
||
|
|
**/
|
||
|
|
folder_signals[FILES_CHANGED] =
|
||
|
|
g_signal_new (I_ ("files-changed"),
|
||
|
|
G_TYPE_FROM_CLASS (gobject_class),
|
||
|
|
G_SIGNAL_RUN_LAST,
|
||
|
|
G_STRUCT_OFFSET (ThunarFolderClass, files_changed),
|
||
|
|
NULL, NULL,
|
||
|
|
g_cclosure_marshal_VOID__POINTER,
|
||
|
|
G_TYPE_NONE, 1, G_TYPE_POINTER);
|
||
|
|
|
||
|
|
/**
|
||
|
|
* ThunarFolder::thumbnails-updated:
|
||
|
|
*
|
||
|
|
* This signal is emitted on #ThunarFolder whenever for the currently
|
||
|
|
* existing #ThunarFile thumbnail status changes.
|
||
|
|
**/
|
||
|
|
folder_signals[THUMBNAILS_UPDATED] =
|
||
|
|
g_signal_new (I_ ("thumbnails-updated"),
|
||
|
|
G_TYPE_FROM_CLASS (gobject_class),
|
||
|
|
G_SIGNAL_RUN_LAST,
|
||
|
|
G_STRUCT_OFFSET (ThunarFolderClass, thumbnails_updated),
|
||
|
|
NULL, NULL,
|
||
|
|
g_cclosure_marshal_VOID__POINTER,
|
||
|
|
G_TYPE_NONE, 1, G_TYPE_POINTER);
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
static void
|
||
|
|
thunar_folder_init (ThunarFolder *folder)
|
||
|
|
{
|
||
|
|
/* If hashtable is initialized without a key_equal_func (we'd use g_direct_equal here);
|
||
|
|
* then equality is checked similar to g_direct_equal but without the overhead of a function call */
|
||
|
|
folder->files_map = g_hash_table_new_full (g_direct_hash, NULL, g_object_unref, NULL);
|
||
|
|
folder->loaded_files_map = g_hash_table_new_full (g_direct_hash, NULL, g_object_unref, NULL);
|
||
|
|
folder->added_files_map = g_hash_table_new_full (g_direct_hash, NULL, g_object_unref, NULL);
|
||
|
|
folder->removed_files_map = g_hash_table_new_full (g_direct_hash, NULL, g_object_unref, NULL);
|
||
|
|
folder->changed_files_map = g_hash_table_new_full (g_direct_hash, NULL, g_object_unref, NULL);
|
||
|
|
|
||
|
|
folder->loaded = FALSE;
|
||
|
|
folder->reload_info = FALSE;
|
||
|
|
folder->files_update_timeout_source_id = 0;
|
||
|
|
folder->thumbnail_updated_files = NULL;
|
||
|
|
folder->thumbnail_updated_timeout_source_id = 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
static void
|
||
|
|
thunar_folder_dispose (GObject *object)
|
||
|
|
{
|
||
|
|
ThunarFolder *folder = THUNAR_FOLDER (object);
|
||
|
|
|
||
|
|
if (!folder->in_destruction)
|
||
|
|
{
|
||
|
|
folder->in_destruction = TRUE;
|
||
|
|
g_signal_emit (G_OBJECT (folder), folder_signals[DESTROY], 0);
|
||
|
|
folder->in_destruction = FALSE;
|
||
|
|
}
|
||
|
|
|
||
|
|
(*G_OBJECT_CLASS (thunar_folder_parent_class)->dispose) (object);
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
static void
|
||
|
|
thunar_folder_finalize (GObject *object)
|
||
|
|
{
|
||
|
|
ThunarFolder *folder = THUNAR_FOLDER (object);
|
||
|
|
GHashTableIter iter;
|
||
|
|
gpointer key, file;
|
||
|
|
|
||
|
|
/* stop content type loading */
|
||
|
|
if (G_UNLIKELY (folder->content_type_job != NULL))
|
||
|
|
{
|
||
|
|
exo_job_cancel (EXO_JOB (folder->content_type_job));
|
||
|
|
g_object_unref (folder->content_type_job);
|
||
|
|
folder->content_type_job = NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* stop any running tumbnailing timeout source */
|
||
|
|
if (folder->thumbnail_updated_timeout_source_id != 0)
|
||
|
|
g_source_remove (folder->thumbnail_updated_timeout_source_id);
|
||
|
|
|
||
|
|
/* stop any running changed_files timeout source */
|
||
|
|
if (folder->files_update_timeout_source_id != 0)
|
||
|
|
g_source_remove (folder->files_update_timeout_source_id);
|
||
|
|
|
||
|
|
if (folder->monitor != NULL)
|
||
|
|
{
|
||
|
|
g_signal_handlers_disconnect_by_data (folder->monitor, folder);
|
||
|
|
g_object_unref (folder->monitor);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (folder->corresponding_file)
|
||
|
|
{
|
||
|
|
thunar_file_unwatch (folder->corresponding_file);
|
||
|
|
|
||
|
|
/* disconnect from the folder */
|
||
|
|
g_signal_handlers_disconnect_by_data (folder->corresponding_file, folder);
|
||
|
|
}
|
||
|
|
|
||
|
|
/* disconnect ThunarFile signals from files inside the folder */
|
||
|
|
g_hash_table_iter_init (&iter, folder->files_map);
|
||
|
|
while (g_hash_table_iter_next (&iter, &key, &file))
|
||
|
|
g_signal_handlers_disconnect_by_data (file, folder);
|
||
|
|
|
||
|
|
/* release files to thumbnail if any */
|
||
|
|
thunar_g_list_free_full (folder->thumbnail_updated_files);
|
||
|
|
|
||
|
|
/* cancel the pending job (if any) */
|
||
|
|
if (G_UNLIKELY (folder->job != NULL))
|
||
|
|
{
|
||
|
|
g_signal_handlers_disconnect_by_data (folder->job, folder);
|
||
|
|
g_object_unref (folder->job);
|
||
|
|
folder->job = NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* disconnect from the corresponding file */
|
||
|
|
if (G_LIKELY (folder->corresponding_file != NULL))
|
||
|
|
{
|
||
|
|
/* drop the reference */
|
||
|
|
g_object_set_qdata (G_OBJECT (folder->corresponding_file), thunar_folder_quark, NULL);
|
||
|
|
g_object_unref (G_OBJECT (folder->corresponding_file));
|
||
|
|
}
|
||
|
|
|
||
|
|
/* release references to the current files lists */
|
||
|
|
g_hash_table_destroy (folder->files_map);
|
||
|
|
g_hash_table_destroy (folder->loaded_files_map);
|
||
|
|
g_hash_table_destroy (folder->changed_files_map);
|
||
|
|
g_hash_table_destroy (folder->added_files_map);
|
||
|
|
g_hash_table_destroy (folder->removed_files_map);
|
||
|
|
|
||
|
|
(*G_OBJECT_CLASS (thunar_folder_parent_class)->finalize) (object);
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
static void
|
||
|
|
thunar_folder_get_property (GObject *object,
|
||
|
|
guint prop_id,
|
||
|
|
GValue *value,
|
||
|
|
GParamSpec *pspec)
|
||
|
|
{
|
||
|
|
ThunarFolder *folder = THUNAR_FOLDER (object);
|
||
|
|
|
||
|
|
switch (prop_id)
|
||
|
|
{
|
||
|
|
case PROP_CORRESPONDING_FILE:
|
||
|
|
g_value_set_object (value, folder->corresponding_file);
|
||
|
|
break;
|
||
|
|
|
||
|
|
case PROP_LOADING:
|
||
|
|
g_value_set_boolean (value, thunar_folder_get_loading (folder));
|
||
|
|
break;
|
||
|
|
|
||
|
|
default:
|
||
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
static void
|
||
|
|
thunar_folder_set_property (GObject *object,
|
||
|
|
guint prop_id,
|
||
|
|
const GValue *value,
|
||
|
|
GParamSpec *pspec)
|
||
|
|
{
|
||
|
|
ThunarFolder *folder = THUNAR_FOLDER (object);
|
||
|
|
|
||
|
|
switch (prop_id)
|
||
|
|
{
|
||
|
|
case PROP_CORRESPONDING_FILE:
|
||
|
|
folder->corresponding_file = g_value_dup_object (value);
|
||
|
|
if (folder->corresponding_file)
|
||
|
|
{
|
||
|
|
thunar_file_watch (folder->corresponding_file);
|
||
|
|
g_signal_connect (G_OBJECT (folder->corresponding_file), "changed", G_CALLBACK (thunar_folder_changed), folder);
|
||
|
|
g_signal_connect (G_OBJECT (folder->corresponding_file), "destroy", G_CALLBACK (thunar_folder_destroyed), folder);
|
||
|
|
}
|
||
|
|
|
||
|
|
break;
|
||
|
|
|
||
|
|
case PROP_LOADING:
|
||
|
|
_thunar_assert_not_reached ();
|
||
|
|
break;
|
||
|
|
|
||
|
|
default:
|
||
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
static gboolean
|
||
|
|
_thunar_folder_remove_file (ThunarFolder *folder,
|
||
|
|
ThunarFile *file)
|
||
|
|
{
|
||
|
|
/* skip, if we dont have that file */
|
||
|
|
if (!g_hash_table_contains (folder->files_map, file))
|
||
|
|
return FALSE;
|
||
|
|
|
||
|
|
/* disconnect all signals for the file */
|
||
|
|
g_signal_handlers_disconnect_by_data (G_OBJECT (file), folder);
|
||
|
|
|
||
|
|
/* remove the ThunarFile from our map (the destroy method of the hashmap will to the 'g_object_unref')*/
|
||
|
|
g_hash_table_remove (folder->files_map, file);
|
||
|
|
|
||
|
|
return TRUE;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
static gboolean
|
||
|
|
_thunar_folder_add_file (ThunarFolder *folder,
|
||
|
|
ThunarFile *file)
|
||
|
|
{
|
||
|
|
/* skip, if we already have that file */
|
||
|
|
if (g_hash_table_contains (folder->files_map, file))
|
||
|
|
return FALSE;
|
||
|
|
|
||
|
|
/* add the ThunarFile) to the hashmap and keep a reference to it */
|
||
|
|
g_hash_table_add (folder->files_map, g_object_ref (file));
|
||
|
|
|
||
|
|
/* connect relevant signals */
|
||
|
|
g_signal_connect_swapped (G_OBJECT (file), "changed", G_CALLBACK (thunar_folder_file_changed), folder);
|
||
|
|
g_signal_connect_swapped (G_OBJECT (file), "destroy", G_CALLBACK (thunar_folder_file_destroyed), folder);
|
||
|
|
g_signal_connect_swapped (G_OBJECT (file), "thumbnail-updated", G_CALLBACK (thunar_folder_thumbnail_updated), folder);
|
||
|
|
|
||
|
|
return TRUE;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
static gboolean
|
||
|
|
_thunar_folder_files_update_timeout (gpointer data)
|
||
|
|
{
|
||
|
|
ThunarFolder *folder = THUNAR_FOLDER (data);
|
||
|
|
ThunarFile *file;
|
||
|
|
GHashTable *files = g_hash_table_new_full (g_direct_hash, NULL, g_object_unref, NULL);
|
||
|
|
GHashTableIter iter;
|
||
|
|
gpointer key;
|
||
|
|
|
||
|
|
/* send a 'files-removed' signal for all files which were removed */
|
||
|
|
g_hash_table_iter_init (&iter, folder->removed_files_map);
|
||
|
|
while (g_hash_table_iter_next (&iter, &key, NULL))
|
||
|
|
{
|
||
|
|
/* prevent destruction by adding a temporary reference to the file */
|
||
|
|
file = g_object_ref (THUNAR_FILE (key));
|
||
|
|
if (_thunar_folder_remove_file (folder, file))
|
||
|
|
g_hash_table_add (files, g_object_ref (file));
|
||
|
|
g_object_unref (file);
|
||
|
|
}
|
||
|
|
|
||
|
|
g_signal_emit (G_OBJECT (folder), folder_signals[FILES_REMOVED], 0, files);
|
||
|
|
|
||
|
|
g_hash_table_remove_all (files);
|
||
|
|
g_hash_table_remove_all (folder->removed_files_map);
|
||
|
|
|
||
|
|
/* send a 'files-added' signal for all files which were added */
|
||
|
|
g_hash_table_iter_init (&iter, folder->added_files_map);
|
||
|
|
while (g_hash_table_iter_next (&iter, &key, NULL))
|
||
|
|
{
|
||
|
|
file = THUNAR_FILE (key);
|
||
|
|
if (_thunar_folder_add_file (folder, file))
|
||
|
|
g_hash_table_add (files, g_object_ref (file));
|
||
|
|
}
|
||
|
|
|
||
|
|
/* start loading the content types of all added files */
|
||
|
|
thunar_folder_load_content_types (folder, files);
|
||
|
|
|
||
|
|
g_signal_emit (G_OBJECT (folder), folder_signals[FILES_ADDED], 0, files);
|
||
|
|
|
||
|
|
g_hash_table_remove_all (files);
|
||
|
|
g_hash_table_remove_all (folder->added_files_map);
|
||
|
|
|
||
|
|
/* reload files which were changed and send a 'changed' signal for them, if required */
|
||
|
|
g_hash_table_iter_init (&iter, folder->changed_files_map);
|
||
|
|
while (g_hash_table_iter_next (&iter, &key, NULL))
|
||
|
|
{
|
||
|
|
/* block 'changed' signals for this file until reload is done, in order to prevent recursion */
|
||
|
|
g_signal_handlers_block_by_func (G_OBJECT (key), G_CALLBACK (thunar_folder_file_changed), folder);
|
||
|
|
thunar_file_reload (THUNAR_FILE (key));
|
||
|
|
g_signal_handlers_unblock_by_func (G_OBJECT (key), G_CALLBACK (thunar_folder_file_changed), folder);
|
||
|
|
|
||
|
|
/* only send the 'changed' signal for files which are already part of this folder */
|
||
|
|
if (!g_hash_table_contains (folder->files_map, key))
|
||
|
|
continue;
|
||
|
|
|
||
|
|
g_hash_table_add (files, g_object_ref (key));
|
||
|
|
}
|
||
|
|
|
||
|
|
if (g_hash_table_size (files) > 0)
|
||
|
|
g_signal_emit (G_OBJECT (folder), folder_signals[FILES_CHANGED], 0, files);
|
||
|
|
|
||
|
|
g_hash_table_destroy (files);
|
||
|
|
g_hash_table_remove_all (folder->changed_files_map);
|
||
|
|
|
||
|
|
/* Loading is done for this folder */
|
||
|
|
if (folder->loaded == FALSE && folder->job == NULL)
|
||
|
|
{
|
||
|
|
folder->loaded = TRUE;
|
||
|
|
g_object_notify (G_OBJECT (folder), "loading");
|
||
|
|
}
|
||
|
|
|
||
|
|
folder->files_update_timeout_source_id = 0;
|
||
|
|
|
||
|
|
return G_SOURCE_REMOVE;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
static void
|
||
|
|
thunar_folder_file_changed (ThunarFolder *folder,
|
||
|
|
ThunarFile *file)
|
||
|
|
{
|
||
|
|
_thunar_return_if_fail (THUNAR_IS_FILE (file));
|
||
|
|
_thunar_return_if_fail (THUNAR_IS_FOLDER (folder));
|
||
|
|
|
||
|
|
g_hash_table_add (folder->changed_files_map, g_object_ref (file));
|
||
|
|
|
||
|
|
if (folder->files_update_timeout_source_id == 0)
|
||
|
|
folder->files_update_timeout_source_id = g_timeout_add (THUNAR_FOLDER_UPDATE_TIMEOUT, (GSourceFunc) _thunar_folder_files_update_timeout, folder);
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
static void
|
||
|
|
thunar_folder_add_file (ThunarFolder *folder,
|
||
|
|
ThunarFile *file)
|
||
|
|
{
|
||
|
|
/* If it possibly was removed shortly before, just undo the "remove" */
|
||
|
|
if (g_hash_table_remove (folder->removed_files_map, file))
|
||
|
|
return;
|
||
|
|
|
||
|
|
/* Do nothing if we already have the file in our internal map */
|
||
|
|
if (g_hash_table_lookup (folder->files_map, file) != NULL)
|
||
|
|
return;
|
||
|
|
|
||
|
|
g_hash_table_add (folder->added_files_map, g_object_ref (file));
|
||
|
|
|
||
|
|
if (folder->files_update_timeout_source_id == 0)
|
||
|
|
folder->files_update_timeout_source_id = g_timeout_add (THUNAR_FOLDER_UPDATE_TIMEOUT, (GSourceFunc) _thunar_folder_files_update_timeout, folder);
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
static void
|
||
|
|
thunar_folder_remove_file (ThunarFolder *folder,
|
||
|
|
ThunarFile *file)
|
||
|
|
{
|
||
|
|
/* If it possibly was added shortly before, just undo the "add" */
|
||
|
|
if (g_hash_table_remove (folder->added_files_map, file))
|
||
|
|
return;
|
||
|
|
|
||
|
|
/* Do nothing if we dont have the file yet in our internal map */
|
||
|
|
if (g_hash_table_lookup (folder->files_map, file) == NULL)
|
||
|
|
return;
|
||
|
|
|
||
|
|
g_hash_table_add (folder->removed_files_map, g_object_ref (file));
|
||
|
|
|
||
|
|
if (folder->files_update_timeout_source_id == 0)
|
||
|
|
folder->files_update_timeout_source_id = g_timeout_add (THUNAR_FOLDER_UPDATE_TIMEOUT, (GSourceFunc) _thunar_folder_files_update_timeout, folder);
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
static void
|
||
|
|
thunar_folder_real_destroy (ThunarFolder *folder)
|
||
|
|
{
|
||
|
|
g_signal_handlers_destroy (G_OBJECT (folder));
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
static void
|
||
|
|
thunar_folder_error (ExoJob *job,
|
||
|
|
GError *error,
|
||
|
|
ThunarFolder *folder)
|
||
|
|
{
|
||
|
|
_thunar_return_if_fail (THUNAR_IS_FOLDER (folder));
|
||
|
|
_thunar_return_if_fail (THUNAR_IS_JOB (job));
|
||
|
|
|
||
|
|
/* tell the consumer about the problem */
|
||
|
|
g_signal_emit (G_OBJECT (folder), folder_signals[ERROR], 0, error);
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
static gboolean
|
||
|
|
thunar_folder_files_ready (ThunarJob *job,
|
||
|
|
GList *files,
|
||
|
|
ThunarFolder *folder)
|
||
|
|
{
|
||
|
|
GList *lp;
|
||
|
|
|
||
|
|
_thunar_return_val_if_fail (THUNAR_IS_FOLDER (folder), FALSE);
|
||
|
|
_thunar_return_val_if_fail (THUNAR_IS_JOB (job), FALSE);
|
||
|
|
|
||
|
|
/* merge the list with the existing list of new files */
|
||
|
|
for (lp = files; lp != NULL; lp = lp->next)
|
||
|
|
g_hash_table_add (folder->loaded_files_map, g_object_ref (lp->data));
|
||
|
|
|
||
|
|
thunar_g_list_free_full (files);
|
||
|
|
|
||
|
|
/* indicate that we took over ownership of the file list */
|
||
|
|
return TRUE;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
static void
|
||
|
|
_thunar_folder_load_content_types_finished (ThunarFolder *folder, ThunarJob *job)
|
||
|
|
{
|
||
|
|
if (folder->content_type_job != NULL)
|
||
|
|
{
|
||
|
|
g_object_unref (folder->content_type_job);
|
||
|
|
folder->content_type_job = NULL;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
* thunar_folder_load_content_types:
|
||
|
|
* @folder : a #ThunarFolder instance.
|
||
|
|
* @files : a #GList of #ThunarFile's for which the content type needs to be loaded.
|
||
|
|
*
|
||
|
|
* Starts a job to load the content type of all files inside the folder
|
||
|
|
**/
|
||
|
|
void
|
||
|
|
thunar_folder_load_content_types (ThunarFolder *folder,
|
||
|
|
GHashTable *files)
|
||
|
|
{
|
||
|
|
_thunar_return_if_fail (THUNAR_IS_FOLDER (folder));
|
||
|
|
|
||
|
|
/* check if we are currently connect to a job */
|
||
|
|
if (G_UNLIKELY (folder->content_type_job != NULL))
|
||
|
|
exo_job_cancel (EXO_JOB (folder->content_type_job));
|
||
|
|
|
||
|
|
/* start a new content_type_job */
|
||
|
|
folder->content_type_job = thunar_io_jobs_load_content_types (files);
|
||
|
|
g_signal_connect_object (folder->content_type_job, "finished", G_CALLBACK (_thunar_folder_load_content_types_finished), folder, G_CONNECT_SWAPPED);
|
||
|
|
|
||
|
|
exo_job_launch (EXO_JOB (folder->content_type_job));
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
static void
|
||
|
|
thunar_folder_finished (ExoJob *job,
|
||
|
|
ThunarFolder *folder)
|
||
|
|
{
|
||
|
|
GHashTableIter iter;
|
||
|
|
gpointer key;
|
||
|
|
gboolean file_list_changed = FALSE;
|
||
|
|
|
||
|
|
_thunar_return_if_fail (THUNAR_IS_FOLDER (folder));
|
||
|
|
_thunar_return_if_fail (THUNAR_IS_JOB (job));
|
||
|
|
_thunar_return_if_fail (THUNAR_IS_FILE (folder->corresponding_file));
|
||
|
|
|
||
|
|
/* determine all added files (files on new_files, but not on files) */
|
||
|
|
g_hash_table_iter_init (&iter, folder->loaded_files_map);
|
||
|
|
while (g_hash_table_iter_next (&iter, &key, NULL))
|
||
|
|
{
|
||
|
|
if (g_hash_table_contains (folder->files_map, key))
|
||
|
|
continue;
|
||
|
|
|
||
|
|
thunar_folder_add_file (folder, key);
|
||
|
|
file_list_changed = TRUE;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* this is to handle removed files after a folder reload */
|
||
|
|
/* determine all removed files (files on files, but not on new_files) */
|
||
|
|
g_hash_table_iter_init (&iter, folder->files_map);
|
||
|
|
while (g_hash_table_iter_next (&iter, &key, NULL))
|
||
|
|
{
|
||
|
|
if (key == NULL || g_hash_table_contains (folder->loaded_files_map, key))
|
||
|
|
continue;
|
||
|
|
|
||
|
|
/* will mark them to be removed on next timeout */
|
||
|
|
thunar_folder_remove_file (folder, THUNAR_FILE (key));
|
||
|
|
file_list_changed = TRUE;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* drop all mappings for new_files list too */
|
||
|
|
g_hash_table_remove_all (folder->loaded_files_map);
|
||
|
|
|
||
|
|
/* schedule a reload of the file information of all files if requested */
|
||
|
|
if (folder->reload_info)
|
||
|
|
{
|
||
|
|
folder->reload_info = FALSE;
|
||
|
|
g_hash_table_iter_init (&iter, folder->files_map);
|
||
|
|
while (g_hash_table_iter_next (&iter, &key, NULL))
|
||
|
|
{
|
||
|
|
if (key == NULL)
|
||
|
|
continue;
|
||
|
|
thunar_file_reload (THUNAR_FILE (key));
|
||
|
|
}
|
||
|
|
|
||
|
|
/* block 'file-changed' signals of the folder itself until reload is done, in order to prevent recursion */
|
||
|
|
g_signal_handlers_block_by_func (G_OBJECT (folder->corresponding_file), G_CALLBACK (thunar_folder_changed), folder);
|
||
|
|
|
||
|
|
/* reload folder itself */
|
||
|
|
thunar_file_reload (folder->corresponding_file);
|
||
|
|
|
||
|
|
/* listen to 'file-changed' signals of the folder itself again when reload is done */
|
||
|
|
g_signal_handlers_unblock_by_func (G_OBJECT (folder->corresponding_file), G_CALLBACK (thunar_folder_changed), folder);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (G_LIKELY (folder->job != NULL))
|
||
|
|
{
|
||
|
|
g_signal_handlers_disconnect_by_data (folder->job, folder);
|
||
|
|
g_object_unref (folder->job);
|
||
|
|
folder->job = NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* notify finished loading already here, if the filelist is already correct */
|
||
|
|
if (file_list_changed == FALSE)
|
||
|
|
{
|
||
|
|
folder->loaded = TRUE;
|
||
|
|
g_object_notify (G_OBJECT (folder), "loading");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/* The file representing the folder has changed */
|
||
|
|
static void
|
||
|
|
thunar_folder_changed (ThunarFile *file,
|
||
|
|
ThunarFolder *folder)
|
||
|
|
{
|
||
|
|
_thunar_return_if_fail (THUNAR_IS_FILE (file));
|
||
|
|
_thunar_return_if_fail (THUNAR_IS_FOLDER (folder));
|
||
|
|
|
||
|
|
/* reload the folder */
|
||
|
|
thunar_folder_reload (folder, FALSE);
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/* The file representing the folder is in destruction */
|
||
|
|
static void
|
||
|
|
thunar_folder_destroyed (ThunarFile *file,
|
||
|
|
ThunarFolder *folder)
|
||
|
|
{
|
||
|
|
_thunar_return_if_fail (THUNAR_IS_FILE (file));
|
||
|
|
_thunar_return_if_fail (THUNAR_IS_FOLDER (folder));
|
||
|
|
|
||
|
|
/* the folder is useless now */
|
||
|
|
if (!folder->in_destruction)
|
||
|
|
g_object_run_dispose (G_OBJECT (folder));
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
/* One of the files inside the folder is in destruction */
|
||
|
|
static void
|
||
|
|
thunar_folder_file_destroyed (ThunarFolder *folder,
|
||
|
|
ThunarFile *file)
|
||
|
|
{
|
||
|
|
_thunar_return_if_fail (THUNAR_IS_FILE (file));
|
||
|
|
_thunar_return_if_fail (THUNAR_IS_FOLDER (folder));
|
||
|
|
|
||
|
|
/* check if the corresponding file was destroyed */
|
||
|
|
if (G_UNLIKELY (folder->corresponding_file == file))
|
||
|
|
{
|
||
|
|
/* the folder is useless now */
|
||
|
|
if (!folder->in_destruction)
|
||
|
|
g_object_run_dispose (G_OBJECT (folder));
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
/* remove the file from our list */
|
||
|
|
thunar_folder_remove_file (folder, file);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
#if DEBUG_FILE_CHANGES
|
||
|
|
static void
|
||
|
|
thunar_file_infos_equal (ThunarFile *file,
|
||
|
|
GFile *event_file)
|
||
|
|
{
|
||
|
|
gchar **attrs;
|
||
|
|
GFileInfo *info1 = G_FILE_INFO (file->info);
|
||
|
|
GFileInfo *info2;
|
||
|
|
guint i;
|
||
|
|
gchar *attr1, *attr2;
|
||
|
|
gboolean printed = FALSE;
|
||
|
|
gchar *bname;
|
||
|
|
|
||
|
|
attrs = g_file_info_list_attributes (info1, NULL);
|
||
|
|
info2 = g_file_query_info (event_file, THUNARX_FILE_INFO_NAMESPACE,
|
||
|
|
G_FILE_QUERY_INFO_NONE, NULL, NULL);
|
||
|
|
|
||
|
|
if (info1 != NULL && info2 != NULL)
|
||
|
|
{
|
||
|
|
for (i = 0; attrs[i] != NULL; i++)
|
||
|
|
{
|
||
|
|
if (g_file_info_has_attribute (info2, attrs[i]))
|
||
|
|
{
|
||
|
|
attr1 = g_file_info_get_attribute_as_string (info1, attrs[i]);
|
||
|
|
attr2 = g_file_info_get_attribute_as_string (info2, attrs[i]);
|
||
|
|
|
||
|
|
if (g_strcmp0 (attr1, attr2) != 0)
|
||
|
|
{
|
||
|
|
if (!printed)
|
||
|
|
{
|
||
|
|
bname = g_file_get_basename (event_file);
|
||
|
|
g_print ("%s\n", bname);
|
||
|
|
g_free (bname);
|
||
|
|
|
||
|
|
printed = TRUE;
|
||
|
|
}
|
||
|
|
|
||
|
|
g_print (" %s: %s -> %s\n", attrs[i], attr1, attr2);
|
||
|
|
}
|
||
|
|
|
||
|
|
g_free (attr1);
|
||
|
|
g_free (attr2);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
g_object_unref (info2);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (printed)
|
||
|
|
g_print ("\n");
|
||
|
|
|
||
|
|
g_free (attrs);
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
static void
|
||
|
|
thunar_folder_monitor (GFileMonitor *monitor,
|
||
|
|
GFile *event_file,
|
||
|
|
GFile *other_file,
|
||
|
|
GFileMonitorEvent event_type,
|
||
|
|
gpointer user_data)
|
||
|
|
{
|
||
|
|
ThunarFolder *folder = THUNAR_FOLDER (user_data);
|
||
|
|
ThunarFile *renamed_file = NULL;
|
||
|
|
ThunarFile *event_file_thunar = NULL;
|
||
|
|
ThunarFile *other_file_thunar = NULL;
|
||
|
|
|
||
|
|
_thunar_return_if_fail (G_IS_FILE_MONITOR (monitor));
|
||
|
|
_thunar_return_if_fail (THUNAR_IS_FOLDER (folder));
|
||
|
|
_thunar_return_if_fail (folder->monitor == monitor);
|
||
|
|
_thunar_return_if_fail (THUNAR_IS_FILE (folder->corresponding_file));
|
||
|
|
_thunar_return_if_fail (G_IS_FILE (event_file));
|
||
|
|
|
||
|
|
if (g_file_equal (event_file, thunar_file_get_file (folder->corresponding_file)))
|
||
|
|
{
|
||
|
|
/* update/destroy the corresponding file */
|
||
|
|
if (event_type == G_FILE_MONITOR_EVENT_DELETED)
|
||
|
|
thunar_file_destroy (folder->corresponding_file);
|
||
|
|
else
|
||
|
|
thunar_file_reload (folder->corresponding_file);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* For rename/delete it is important to only do lookup here, no ThunarFile creation */
|
||
|
|
event_file_thunar = thunar_file_cache_lookup (event_file);
|
||
|
|
other_file_thunar = (other_file == NULL) ? NULL : thunar_file_cache_lookup (other_file);
|
||
|
|
|
||
|
|
switch (event_type)
|
||
|
|
{
|
||
|
|
case G_FILE_MONITOR_EVENT_MOVED_IN:
|
||
|
|
case G_FILE_MONITOR_EVENT_CREATED:
|
||
|
|
if (event_file_thunar == NULL)
|
||
|
|
{
|
||
|
|
event_file_thunar = thunar_file_get (event_file, NULL);
|
||
|
|
if (event_file_thunar == NULL)
|
||
|
|
{
|
||
|
|
g_info ("Failed to create ThunarFile for gfile");
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Add the file to our map via the timeout source */
|
||
|
|
thunar_folder_add_file (folder, event_file_thunar);
|
||
|
|
|
||
|
|
/* if we already ship the ThunarFile, reload it */
|
||
|
|
if (g_hash_table_lookup (folder->files_map, event_file_thunar) != NULL)
|
||
|
|
thunar_file_reload (event_file_thunar);
|
||
|
|
|
||
|
|
/* notify the thumbnail cache that we can now also move the thumbnail */
|
||
|
|
if (event_type == G_FILE_MONITOR_EVENT_MOVED_IN && other_file != NULL)
|
||
|
|
thunar_file_move_thumbnail_cache_file (other_file, event_file);
|
||
|
|
break;
|
||
|
|
|
||
|
|
case G_FILE_MONITOR_EVENT_MOVED_OUT:
|
||
|
|
case G_FILE_MONITOR_EVENT_DELETED:
|
||
|
|
if (event_type == G_FILE_MONITOR_EVENT_MOVED_OUT && other_file != NULL)
|
||
|
|
thunar_file_move_thumbnail_cache_file (event_file, other_file);
|
||
|
|
|
||
|
|
/* If the ThunarFile is not known to us, than we cannot remove it */
|
||
|
|
if (event_file_thunar == NULL)
|
||
|
|
break;
|
||
|
|
|
||
|
|
/* Drop it from the map, if we still have it */
|
||
|
|
thunar_folder_remove_file (folder, event_file_thunar);
|
||
|
|
|
||
|
|
/* destroy the old file */
|
||
|
|
thunar_file_destroy (event_file_thunar);
|
||
|
|
|
||
|
|
break;
|
||
|
|
|
||
|
|
case G_FILE_MONITOR_EVENT_RENAMED:
|
||
|
|
|
||
|
|
/* in case source and dest are already the same ThunarFile, just assume we have no source ThunarFile */
|
||
|
|
if (event_file_thunar != NULL && event_file_thunar == other_file_thunar)
|
||
|
|
{
|
||
|
|
g_object_unref (event_file_thunar);
|
||
|
|
event_file_thunar = NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* if we dont have any of the two files in the cache yet, try to create the renamed file */
|
||
|
|
if (event_file_thunar == NULL && other_file_thunar == NULL)
|
||
|
|
{
|
||
|
|
other_file_thunar = thunar_file_get (other_file, NULL);
|
||
|
|
if (other_file_thunar == NULL)
|
||
|
|
{
|
||
|
|
g_info ("Failed to create ThunarFile for gfile");
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/* if we already ship the new file as a ThunarFile, make use of it */
|
||
|
|
if (other_file_thunar != NULL)
|
||
|
|
renamed_file = other_file_thunar;
|
||
|
|
else
|
||
|
|
renamed_file = event_file_thunar;
|
||
|
|
|
||
|
|
/* remove both files from the internal maps */
|
||
|
|
if (event_file_thunar != NULL)
|
||
|
|
thunar_folder_remove_file (folder, event_file_thunar);
|
||
|
|
if (other_file_thunar != NULL)
|
||
|
|
thunar_folder_remove_file (folder, other_file_thunar);
|
||
|
|
|
||
|
|
/* replace the GFile in the ThunarFile for the renamed file with the new gfile */
|
||
|
|
thunar_file_replace_file (renamed_file, other_file);
|
||
|
|
|
||
|
|
/* re-add the renamed file into our internal map (this time we can use the timeout source) */
|
||
|
|
thunar_folder_add_file (folder, renamed_file);
|
||
|
|
|
||
|
|
/* destroy the old ThunarFile, if we have two of them */
|
||
|
|
if (event_file_thunar != NULL && other_file_thunar != NULL)
|
||
|
|
thunar_file_destroy (event_file_thunar);
|
||
|
|
|
||
|
|
/* reload the renamed file */
|
||
|
|
if (thunar_file_reload (renamed_file))
|
||
|
|
{
|
||
|
|
/* notify the thumbnail cache that we can now also move the thumbnail */
|
||
|
|
thunar_file_move_thumbnail_cache_file (event_file, other_file);
|
||
|
|
}
|
||
|
|
|
||
|
|
break;
|
||
|
|
|
||
|
|
case G_FILE_MONITOR_EVENT_CHANGED:
|
||
|
|
case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT:
|
||
|
|
case G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED:
|
||
|
|
if (event_file_thunar != NULL)
|
||
|
|
{
|
||
|
|
/* this will trigger a reload if the file and send a 'file-changed' signal */
|
||
|
|
thunar_folder_file_changed (folder, event_file_thunar);
|
||
|
|
|
||
|
|
#if DEBUG_FILE_CHANGES
|
||
|
|
thunar_file_infos_equal (event_file_thunar, event_file);
|
||
|
|
#endif
|
||
|
|
}
|
||
|
|
|
||
|
|
default:
|
||
|
|
/*
|
||
|
|
G_FILE_MONITOR_EVENT_PRE_UNMOUNT
|
||
|
|
G_FILE_MONITOR_EVENT_UNMOUNTED
|
||
|
|
G_FILE_MONITOR_EVENT_MOVED (deprecated/unused)
|
||
|
|
*/
|
||
|
|
break;
|
||
|
|
|
||
|
|
} /* end switch */
|
||
|
|
|
||
|
|
if (event_file_thunar != NULL)
|
||
|
|
g_object_unref (event_file_thunar);
|
||
|
|
if (other_file_thunar != NULL)
|
||
|
|
g_object_unref (other_file_thunar);
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
* thunar_folder_get_for_file:
|
||
|
|
* @file : a #ThunarFile.
|
||
|
|
*
|
||
|
|
* Opens the specified @file as #ThunarFolder and
|
||
|
|
* returns a reference to the folder.
|
||
|
|
*
|
||
|
|
* The caller is responsible to free the returned
|
||
|
|
* object using g_object_unref() when no longer
|
||
|
|
* needed.
|
||
|
|
*
|
||
|
|
* Return value: the #ThunarFolder which corresponds
|
||
|
|
* to @file.
|
||
|
|
**/
|
||
|
|
ThunarFolder *
|
||
|
|
thunar_folder_get_for_file (ThunarFile *file)
|
||
|
|
{
|
||
|
|
ThunarFolder *folder;
|
||
|
|
|
||
|
|
_thunar_return_val_if_fail (THUNAR_IS_FILE (file), NULL);
|
||
|
|
|
||
|
|
/* make sure the file is loaded */
|
||
|
|
if (!thunar_file_check_loaded (file))
|
||
|
|
return NULL;
|
||
|
|
|
||
|
|
_thunar_return_val_if_fail (thunar_file_is_directory (file), NULL);
|
||
|
|
|
||
|
|
/* load if the file is not a folder */
|
||
|
|
if (!thunar_file_is_directory (file))
|
||
|
|
return NULL;
|
||
|
|
|
||
|
|
/* determine the "thunar-folder" quark on-demand */
|
||
|
|
if (G_UNLIKELY (thunar_folder_quark == 0))
|
||
|
|
thunar_folder_quark = g_quark_from_static_string ("thunar-folder");
|
||
|
|
|
||
|
|
/* check if we already know that folder */
|
||
|
|
folder = g_object_get_qdata (G_OBJECT (file), thunar_folder_quark);
|
||
|
|
if (G_UNLIKELY (folder != NULL))
|
||
|
|
{
|
||
|
|
g_object_ref (G_OBJECT (folder));
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
/* allocate the new instance */
|
||
|
|
folder = g_object_new (THUNAR_TYPE_FOLDER, "corresponding-file", file, NULL);
|
||
|
|
|
||
|
|
/* connect the folder to the file */
|
||
|
|
g_object_set_qdata (G_OBJECT (file), thunar_folder_quark, folder);
|
||
|
|
|
||
|
|
/* schedule the loading of the folder */
|
||
|
|
thunar_folder_reload (folder, FALSE);
|
||
|
|
}
|
||
|
|
|
||
|
|
return folder;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
* thunar_folder_get_corresponding_file:
|
||
|
|
* @folder : a #ThunarFolder instance.
|
||
|
|
*
|
||
|
|
* Returns the #ThunarFile corresponding to this @folder.
|
||
|
|
*
|
||
|
|
* No reference is taken on the returned #ThunarFile for
|
||
|
|
* the caller, so if you need a persistent reference to
|
||
|
|
* the file, you'll have to call g_object_ref() yourself.
|
||
|
|
*
|
||
|
|
* Return value: the #ThunarFile corresponding to @folder.
|
||
|
|
**/
|
||
|
|
ThunarFile *
|
||
|
|
thunar_folder_get_corresponding_file (const ThunarFolder *folder)
|
||
|
|
{
|
||
|
|
_thunar_return_val_if_fail (THUNAR_IS_FOLDER (folder), NULL);
|
||
|
|
return folder->corresponding_file;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
* thunar_folder_get_files:
|
||
|
|
* @folder : a #ThunarFolder instance.
|
||
|
|
*
|
||
|
|
* Returns the hashtable of files currently known for @folder.
|
||
|
|
* The data is owned by the #ThunarFile instance and should not be modified or freed.
|
||
|
|
*
|
||
|
|
* Returns: (transfer none): A GHashTable containing all the #ThunarFiles inside the folder.
|
||
|
|
**/
|
||
|
|
GHashTable *
|
||
|
|
thunar_folder_get_files (const ThunarFolder *folder)
|
||
|
|
{
|
||
|
|
_thunar_return_val_if_fail (THUNAR_IS_FOLDER (folder), NULL);
|
||
|
|
return folder->files_map;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
* thunar_folder_get_loading:
|
||
|
|
* @folder : a #ThunarFolder instance.
|
||
|
|
*
|
||
|
|
* Tells whether the contents of the @folder are currently
|
||
|
|
* being loaded.
|
||
|
|
*
|
||
|
|
* Return value: %TRUE if @folder is loading, else %FALSE.
|
||
|
|
**/
|
||
|
|
gboolean
|
||
|
|
thunar_folder_get_loading (const ThunarFolder *folder)
|
||
|
|
{
|
||
|
|
_thunar_return_val_if_fail (THUNAR_IS_FOLDER (folder), FALSE);
|
||
|
|
return (!folder->loaded);
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
* thunar_folder_has_folder_monitor:
|
||
|
|
* @folder : a #ThunarFolder instance.
|
||
|
|
*
|
||
|
|
* Tells whether the @folder has a folder monitor running
|
||
|
|
*
|
||
|
|
* Return value: %TRUE if @folder has a folder monitor, else %FALSE.
|
||
|
|
**/
|
||
|
|
gboolean
|
||
|
|
thunar_folder_has_folder_monitor (const ThunarFolder *folder)
|
||
|
|
{
|
||
|
|
_thunar_return_val_if_fail (THUNAR_IS_FOLDER (folder), FALSE);
|
||
|
|
return (folder->monitor != NULL);
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
* thunar_folder_reload:
|
||
|
|
* @folder : a #ThunarFolder instance.
|
||
|
|
* @reload_info : reload all information for the files too
|
||
|
|
*
|
||
|
|
* Tells the @folder object to reread the directory
|
||
|
|
* contents from the underlying media.
|
||
|
|
**/
|
||
|
|
void
|
||
|
|
thunar_folder_reload (ThunarFolder *folder,
|
||
|
|
gboolean reload_info)
|
||
|
|
{
|
||
|
|
_thunar_return_if_fail (THUNAR_IS_FOLDER (folder));
|
||
|
|
|
||
|
|
/* reload file info too? */
|
||
|
|
folder->reload_info = reload_info;
|
||
|
|
|
||
|
|
/* stop content type loading */
|
||
|
|
if (G_UNLIKELY (folder->content_type_job != NULL))
|
||
|
|
exo_job_cancel (EXO_JOB (folder->content_type_job));
|
||
|
|
|
||
|
|
/* check if we are currently connect to a job */
|
||
|
|
if (G_UNLIKELY (folder->job != NULL))
|
||
|
|
{
|
||
|
|
/* disconnect from the job */
|
||
|
|
g_signal_handlers_disconnect_by_data (folder->job, folder);
|
||
|
|
g_object_unref (folder->job);
|
||
|
|
folder->job = NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* reset the loaded_files_map hash table */
|
||
|
|
g_hash_table_remove_all (folder->loaded_files_map);
|
||
|
|
|
||
|
|
/* start a new job */
|
||
|
|
folder->loaded = FALSE;
|
||
|
|
g_object_notify (G_OBJECT (folder), "loading");
|
||
|
|
|
||
|
|
folder->job = thunar_io_jobs_list_directory (thunar_file_get_file (folder->corresponding_file));
|
||
|
|
g_signal_connect (folder->job, "error", G_CALLBACK (thunar_folder_error), folder);
|
||
|
|
g_signal_connect (folder->job, "finished", G_CALLBACK (thunar_folder_finished), folder);
|
||
|
|
g_signal_connect (folder->job, "files-ready", G_CALLBACK (thunar_folder_files_ready), folder);
|
||
|
|
exo_job_launch (EXO_JOB (folder->job));
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
static gboolean
|
||
|
|
_thunar_folder_thumbnail_updated_timeout (gpointer data)
|
||
|
|
{
|
||
|
|
ThunarFolder *folder = THUNAR_FOLDER (data);
|
||
|
|
|
||
|
|
g_signal_emit (G_OBJECT (folder), folder_signals[THUMBNAILS_UPDATED], 0, folder->thumbnail_updated_files);
|
||
|
|
|
||
|
|
thunar_g_list_free_full (folder->thumbnail_updated_files);
|
||
|
|
folder->thumbnail_updated_files = NULL;
|
||
|
|
folder->thumbnail_updated_timeout_source_id = 0;
|
||
|
|
|
||
|
|
return G_SOURCE_REMOVE;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
static void
|
||
|
|
thunar_folder_thumbnail_updated (ThunarFolder *folder,
|
||
|
|
ThunarThumbnailSize size,
|
||
|
|
ThunarFile *file)
|
||
|
|
{
|
||
|
|
_thunar_return_if_fail (THUNAR_IS_FILE (file));
|
||
|
|
_thunar_return_if_fail (THUNAR_IS_FOLDER (folder));
|
||
|
|
|
||
|
|
folder->thumbnail_updated_files =
|
||
|
|
g_list_prepend (folder->thumbnail_updated_files, g_object_ref (file));
|
||
|
|
|
||
|
|
if (folder->thumbnail_updated_timeout_source_id == 0)
|
||
|
|
folder->thumbnail_updated_timeout_source_id = g_timeout_add (THUNAR_FOLDER_UPDATE_TIMEOUT, (GSourceFunc) _thunar_folder_thumbnail_updated_timeout, folder);
|
||
|
|
}
|