📄 vfs-dir.c
字号:
/** C Implementation: vfs-dir** Description: Object used to present a directory*** Author: Hong Jen Yee (PCMan) <pcman.tw (AT) gmail.com>, (C) 2006** Copyright: See COPYING file that comes with this distribution**//*FIXME: The original multi-threading synchronization in this file is poor and needs re-check. Briefly speaking, it's totally in a mess. If this works without any problems, you are really lucky. If not, take it easy. That's quite normal. Finally, I found Thunar's thunar/thunar-thumbnail-generator.c, whose author is Benedikt Meurer, and use it as a reference. Now, the multi-threading handling is learned from Thunar and re-written.*/#include "vfs-dir.h"#include "vfs-file-info.h"#include "glib-mem.h"#include <string.h>static void vfs_dir_class_init( VFSDirClass* klass );static void vfs_dir_init( VFSDir* dir );static void vfs_dir_finalize( GObject *obj );static void vfs_dir_set_property ( GObject *obj, guint prop_id, const GValue *value, GParamSpec *pspec );static void vfs_dir_get_property ( GObject *obj, guint prop_id, GValue *value, GParamSpec *pspec );static void vfs_dir_load( VFSDir* dir, const char* path );static gpointer vfs_dir_load_thread( VFSDir* dir );static void vfs_dir_file_created( VFSDir* dir, const VFSFileInfo* file );static void vfs_dir_file_deleted( VFSDir* dir, const VFSFileInfo* file );static void vfs_dir_file_changed( VFSDir* dir, const VFSFileInfo* file );static void vfs_dir_monitor_callback( VFSFileMonitor* fm, VFSFileMonitorEvent event, const char* file_name, gpointer user_data );static gboolean vfs_dir_call_state_callback( VFSDir* dir );static gpointer load_thumbnail_thread( gpointer user_data );static gboolean on_thumbnail_idle( VFSDir* dir );static void on_mime_type_reload( gpointer user_data );enum { FILE_CREATED_SIGNAL = 0, FILE_DELETED_SIGNAL, FILE_CHANGED_SIGNAL, N_SIGNALS};static guint signals[ N_SIGNALS ] = { 0 };static GObjectClass *parent_class = NULL;static GHashTable* dir_hash = NULL;static int xdg_mime_cb_id = 0;static guint change_notify_timeout = 0;struct VFSDirStateCallbackEnt{ VFSDirStateCallback func; gpointer user_data; int stage;};enum{ LOAD_BIG_THUMBNAIL, LOAD_SMALL_THUMBNAIL, N_LOAD_TYPES};typedef struct _ThumbnailRequest{ int n_requests[ N_LOAD_TYPES ]; VFSFileInfo* file;}ThumbnailRequest;GType vfs_dir_get_type(){ static GType type = G_TYPE_INVALID; if ( G_UNLIKELY ( type == G_TYPE_INVALID ) ) { static const GTypeInfo info = { sizeof ( VFSDirClass ), NULL, NULL, ( GClassInitFunc ) vfs_dir_class_init, NULL, NULL, sizeof ( VFSDir ), 0, ( GInstanceInitFunc ) vfs_dir_init, NULL, }; type = g_type_register_static ( G_TYPE_OBJECT, "VFSDir", &info, 0 ); } return type;}void vfs_dir_class_init( VFSDirClass* klass ){ GObjectClass * object_class; object_class = ( GObjectClass * ) klass; parent_class = g_type_class_peek_parent ( klass ); object_class->set_property = vfs_dir_set_property; object_class->get_property = vfs_dir_get_property; object_class->finalize = vfs_dir_finalize; /* signals */ klass->file_created = vfs_dir_file_created; klass->file_deleted = vfs_dir_file_deleted; klass->file_changed = vfs_dir_file_changed; /* * file-created is emitted when there is a new file created in the dir. * The param is VFSFileInfo of the newly created file. */ signals[ FILE_CREATED_SIGNAL ] = g_signal_new ( "file-created", G_TYPE_FROM_CLASS ( klass ), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET ( VFSDirClass, file_created ), NULL, NULL, g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER ); /* * file-deleted is emitted when there is a file deleted in the dir. * The param is VFSFileInfo of the newly created file. */ signals[ FILE_DELETED_SIGNAL ] = g_signal_new ( "file-deleted", G_TYPE_FROM_CLASS ( klass ), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET ( VFSDirClass, file_deleted ), NULL, NULL, g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER ); /* * file-changed is emitted when there is a file changed in the dir. * The param is VFSFileInfo of the newly created file. */ signals[ FILE_CHANGED_SIGNAL ] = g_signal_new ( "file-changed", G_TYPE_FROM_CLASS ( klass ), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET ( VFSDirClass, file_changed ), NULL, NULL, g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER );}/* constructor */void vfs_dir_init( VFSDir* dir ){ dir->mutex = g_mutex_new();}/* destructor */static void vfs_dir_clear( VFSDir* dir ){ ThumbnailRequest * req; GList* l; if ( dir->monitor ) { vfs_file_monitor_remove( dir->monitor, vfs_dir_monitor_callback, dir ); } if ( dir->path ) { g_hash_table_remove( dir_hash, dir->path ); /* There is no VFSDir instance */ if ( 0 == g_hash_table_size( dir_hash ) ) { g_hash_table_destroy( dir_hash ); dir_hash = NULL; xdg_mime_remove_callback( xdg_mime_cb_id ); xdg_mime_cb_id = 0; if( change_notify_timeout ) { g_source_remove( change_notify_timeout ); change_notify_timeout = 0; } } g_free( dir->path ); g_free( dir->disp_path ); } if ( dir->thumbnail_mutex ) { g_mutex_lock( dir->thumbnail_mutex ); if ( dir->loaded_thumbnails ) { for ( l = dir->loaded_thumbnails; l ;l = l->next ) { req = l->data; vfs_file_info_unref( req->file ); g_slice_free( ThumbnailRequest, req ); } g_list_free( dir->loaded_thumbnails ); dir->loaded_thumbnails = NULL; } if ( dir->thumbnail_requests ) { while ( req = ( ThumbnailRequest* ) g_queue_pop_head( dir->thumbnail_requests ) ) { vfs_file_info_unref( req->file ); g_slice_free( ThumbnailRequest, req ); } g_mutex_unlock( dir->thumbnail_mutex ); if ( dir->thumbnail_thread ) { g_cond_wait( dir->thumbnail_cond, dir->thumbnail_mutex ); } g_cond_free( dir->thumbnail_cond ); dir->thumbnail_cond = NULL; } else { g_mutex_unlock( dir->thumbnail_mutex ); } if ( dir->thumbnail_idle ) { g_source_remove( dir->thumbnail_idle ); dir->thumbnail_idle = 0; } g_mutex_free( dir->thumbnail_mutex ); dir->thumbnail_mutex = NULL; } if ( dir->file_list ) { g_list_foreach( dir->file_list, ( GFunc ) vfs_file_info_unref, NULL ); g_list_free( dir->file_list ); dir->file_list = NULL; dir->n_files = 0; } if( dir->changed_files ) { g_slist_foreach( dir->changed_files, (GFunc)vfs_file_info_unref, NULL ); g_slist_free( dir->changed_files ); dir->changed_files = NULL; }}void vfs_dir_finalize( GObject *obj ){ VFSDir * dir = VFS_DIR( obj ); do{} while( g_source_remove_by_user_data( dir ) ); vfs_dir_clear( dir ); G_OBJECT_CLASS( parent_class ) ->finalize( obj );}void vfs_dir_get_property ( GObject *obj, guint prop_id, GValue *value, GParamSpec *pspec ){}void vfs_dir_set_property ( GObject *obj, guint prop_id, const GValue *value, GParamSpec *pspec ){}/* signal handlers */void vfs_dir_file_created( VFSDir* dir, const VFSFileInfo* file ){}void vfs_dir_file_deleted( VFSDir* dir, const VFSFileInfo* file ){}void vfs_dir_file_changed( VFSDir* dir, const VFSFileInfo* file ){}/* methods */VFSDir* vfs_dir_new(){ VFSDir * dir; dir = ( VFSDir* ) g_object_new( VFS_TYPE_DIR, NULL ); return dir;}void vfs_dir_load( VFSDir* dir, const char* path ){ GSList * l; struct VFSDirStateCallbackEnt* ent; if ( path ) { vfs_dir_clear( dir ); dir->path = g_strdup( path ); dir->disp_path = g_filename_display_name( path ); for ( l = dir->state_callback_list; l; l = l->next ) { ent = ( struct VFSDirStateCallbackEnt* ) l->data; ent->stage = 0; } dir->thread = g_thread_create( ( GThreadFunc ) vfs_dir_load_thread, dir, FALSE, NULL ); }}static gboolean is_dir_desktop( const char* path ){ static int len_home_dir = 0; static const char* home_dir; gboolean is_desktop = FALSE; if( G_UNLIKELY( 0 == len_home_dir ) ) { home_dir = g_get_home_dir(); len_home_dir = strlen( home_dir ); } if( 0 == strncmp( path, home_dir, len_home_dir) ) { if( path[len_home_dir] == '/' && G_UNLIKELY(0 == strcmp(path+len_home_dir+1, "Desktop"))) return TRUE; } return FALSE;}gpointer vfs_dir_load_thread( VFSDir* dir ){ const gchar * file_name; char* full_path; GDir* dir_content; VFSFileInfo* file; GList* l; gboolean is_desktop; dir->file_listed = 0; dir->load_complete = 0; if ( dir->path ) { /* Install file alteration monitor */ dir->monitor = vfs_file_monitor_add( dir->path, vfs_dir_monitor_callback, dir ); dir_content = g_dir_open( dir->path, 0, NULL ); if ( dir_content ) { is_desktop = is_dir_desktop( dir->path ); while ( !dir->cancel && ( file_name = g_dir_read_name( dir_content ) ) ) { full_path = g_build_filename( dir->path, file_name, NULL ); if ( !full_path ) continue; file = vfs_file_info_new(); if ( vfs_file_info_get( file, full_path, file_name ) ) { g_mutex_lock( dir->mutex ); /* Special processing for desktop folder */ if( G_UNLIKELY(is_desktop) ) vfs_file_info_load_special_info( file, full_path ); dir->file_list = g_list_prepend( dir->file_list, file ); g_mutex_unlock( dir->mutex ); ++dir->n_files; } else { vfs_file_info_unref( file ); } g_free( full_path ); } g_dir_close( dir_content ); } dir->file_listed = 1; // dir->load_notify = g_idle_add( // ( GSourceFunc ) vfs_dir_call_state_callback, dir ); /* FIXME: load thumbnails and do some time-consuming tasks here */ dir->load_complete = 1; dir->load_notify = g_idle_add( ( GSourceFunc ) vfs_dir_call_state_callback, dir ); } return NULL;}gboolean vfs_dir_is_loading( VFSDir* dir ){ return dir->thread ? TRUE : FALSE;}void vfs_cancel_load( VFSDir* dir ){ dir->cancel = TRUE; if ( dir->thread ) { g_thread_join( dir->thread ); dir->thread = NULL; }}GList* vfs_dir_find_file( VFSDir* dir, const char* file_name ){ GList * l; VFSFileInfo* file; for ( l = dir->file_list; l; l = l->next ) { file = ( VFSFileInfo* ) l->data; if ( file->name && 0 == strcmp( file->name, file_name ) ) { return l; } } return NULL;}static gboolean update_file_info( VFSDir* dir, VFSFileInfo* file ){ char* full_path; char* file_name; gboolean ret; gboolean is_desktop = is_dir_desktop(dir->path); /* FIXME: Dirty hack: steal the string to prevent memory allocation */ file_name = file->name; if( file->name == file->disp_name ) file->disp_name = NULL; file->name = NULL; full_path = g_build_filename( dir->path, file_name, NULL ); if ( G_LIKELY( full_path ) ) { if( G_LIKELY( vfs_file_info_get( file, full_path, file_name ) ) ) { ret = TRUE; if( G_UNLIKELY(is_desktop) ) vfs_file_info_load_special_info( file, full_path ); } else /* The file doesn't exist */ { GList* l; l = g_list_find( dir->file_list, file ); if( G_UNLIKELY(l) )
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -