/* * fm-dir-list-job.c * * Copyright 2009 PCMan * * 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., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. */ #ifdef HAVE_CONFIG_H #include #endif #include "fm-dir-list-job.h" #include #include #include #include #include "fm-mime-type.h" #include "fm-file-info-job.h" #include extern const char gfile_info_query_attribs[]; /* defined in fm-file-info-job.c */ static void fm_dir_list_job_finalize (GObject *object); G_DEFINE_TYPE(FmDirListJob, fm_dir_list_job, FM_TYPE_JOB); static gboolean fm_dir_list_job_run(FmDirListJob *job); static void fm_dir_list_job_class_init(FmDirListJobClass *klass) { GObjectClass *g_object_class; FmJobClass* job_class = FM_JOB_CLASS(klass); g_object_class = G_OBJECT_CLASS(klass); g_object_class->finalize = fm_dir_list_job_finalize; job_class->run = fm_dir_list_job_run; } static void fm_dir_list_job_init(FmDirListJob *self) { fm_job_init_cancellable(FM_JOB(self)); } FmJob* fm_dir_list_job_new(FmPath* path) { /* FIXME: should we cache this with hash table? Or, the cache * should be done at the level of FmFolder instead? */ FmDirListJob* job = (FmJob*)g_object_new(FM_TYPE_DIR_LIST_JOB, NULL); job->dir_path = fm_path_ref(path); job->files = fm_file_info_list_new(); return (FmJob*)job; } FmJob* fm_dir_list_job_new_for_gfile(GFile* gf) { /* FIXME: should we cache this with hash table? Or, the cache * should be done at the level of FmFolder instead? */ FmDirListJob* job = (FmJob*)g_object_new(FM_TYPE_DIR_LIST_JOB, NULL); job->dir_path = fm_path_new_for_gfile(gf); return (FmJob*)job; } static void fm_dir_list_job_finalize(GObject *object) { FmDirListJob *self; g_return_if_fail(object != NULL); g_return_if_fail(FM_IS_DIR_LIST_JOB(object)); self = FM_DIR_LIST_JOB(object); if(self->dir_path) fm_path_unref(self->dir_path); if(self->dir_fi) fm_file_info_unref(self->dir_fi); if(self->files) fm_list_unref(self->files); if (G_OBJECT_CLASS(fm_dir_list_job_parent_class)->finalize) (* G_OBJECT_CLASS(fm_dir_list_job_parent_class)->finalize)(object); } /* defined in fm-file-info.c */ FmFileInfo* _fm_file_info_new_from_menu_cache_item(FmPath* path, MenuCacheItem* item); static gpointer list_menu_items(FmJob* fmjob, gpointer user_data) { FmDirListJob* job = (FmDirListJob*)fmjob; FmFileInfo* fi; MenuCache* mc; MenuCacheDir* dir; GList* l; char* path_str, *p, ch; char* menu_name; const char* dir_path; guint32 de_flag; const char* de_name; /* example: menu://applications.menu/DesktopSettings */ path_str = fm_path_to_str(job->dir_path); p = path_str + 5; /* skip menu: */ while(*p == '/') ++p; menu_name = p; while(*p && *p != '/') ++p; ch = *p; *p = '\0'; menu_name = g_strconcat(menu_name, ".menu", NULL); mc = menu_cache_lookup_sync(menu_name); /* ensure that the menu cache is loaded */ if(!mc) /* if it's not loaded */ { /* try to set $XDG_MENU_PREFIX to "lxde-" for lxmenu-data */ const char* menu_prefix = g_getenv("XDG_MENU_PREFIX"); if(g_strcmp0(menu_prefix, "lxde-")) /* if current value is not lxde- */ { char* old_prefix = g_strdup(menu_prefix); g_setenv("XDG_MENU_PREFIX", "lxde-", TRUE); mc = menu_cache_lookup_sync(menu_name); /* restore original environment variable */ if(old_prefix) { g_setenv("XDG_MENU_PREFIX", old_prefix, TRUE); g_free(old_prefix); } else g_unsetenv("XDG_MENU_PREFIX"); if(!mc) { g_free(menu_name); return NULL; } } else { g_free(menu_name); return NULL; } } g_free(menu_name); *p = ch; dir_path = p; /* path of menu dir, such as: /Internet */ de_name = g_getenv("XDG_CURRENT_DESKTOP"); if(de_name) de_flag = menu_cache_get_desktop_env_flag(mc, de_name); else de_flag = (guint32)-1; /* the menu should be loaded now */ if(*dir_path && !(*dir_path == '/' && dir_path[1]=='\0') ) { char* tmp = g_strconcat("/", menu_cache_item_get_id(MENU_CACHE_ITEM(menu_cache_get_root_dir(mc))), dir_path, NULL); dir = menu_cache_get_dir_from_path(mc, tmp); g_free(tmp); } else dir = menu_cache_get_root_dir(mc); if(dir) { job->dir_fi = _fm_file_info_new_from_menu_cache_item(job->dir_path, dir); for(l=menu_cache_dir_get_children(dir);l;l=l->next) { MenuCacheItem* item = MENU_CACHE_ITEM(l->data); FmPath* item_path; GIcon* gicon; /* also hide menu items which should be hidden in current DE. */ if(!item || menu_cache_item_get_type(item) == MENU_CACHE_TYPE_SEP) continue; if(menu_cache_item_get_type(item) == MENU_CACHE_TYPE_APP && !menu_cache_app_get_is_visible(item, de_flag)) continue; item_path = fm_path_new_child(job->dir_path, menu_cache_item_get_id(item)); fi = _fm_file_info_new_from_menu_cache_item(item_path, item); fm_path_unref(item_path); fm_list_push_tail_noref(job->files, fi); } } menu_cache_unref(mc); g_free(path_str); return NULL; } gboolean fm_dir_list_job_list_xdg_menu(FmDirListJob* job) { /* Calling libmenu-cache is only allowed in main thread. */ fm_job_call_main_thread(FM_JOB(job), list_menu_items, NULL); return TRUE; } static gboolean fm_dir_list_job_run_posix(FmDirListJob* job) { FmFileInfo* fi; GError *err = NULL; char* dir_path; GDir* dir; dir_path = fm_path_to_str(job->dir_path); fi = fm_file_info_new(); fi->path = fm_path_ref(job->dir_path); if( _fm_file_info_job_get_info_for_native_file(FM_JOB(job), fi, dir_path, NULL) ) { job->dir_fi = fi; if(! fm_file_info_is_dir(fi)) { err = g_error_new(G_IO_ERROR, G_IO_ERROR_NOT_DIRECTORY, _("The specified directory is not valid")); fm_file_info_unref(fi); fm_job_emit_error(FM_JOB(job), err, FM_JOB_ERROR_CRITICAL); g_error_free(err); return FALSE; } } else { err = g_error_new(G_IO_ERROR, G_IO_ERROR_NOT_DIRECTORY, _("The specified directory is not valid")); fm_file_info_unref(fi); fm_job_emit_error(FM_JOB(job), err, FM_JOB_ERROR_CRITICAL); g_error_free(err); return FALSE; } dir = g_dir_open(dir_path, 0, &err); if( dir ) { char* name; GString* fpath = g_string_sized_new(4096); int dir_len = strlen(dir_path); g_string_append_len(fpath, dir_path, dir_len); if(fpath->str[dir_len-1] != '/') { g_string_append_c(fpath, '/'); ++dir_len; } while( ! fm_job_is_cancelled(FM_JOB(job)) && (name = g_dir_read_name(dir)) ) { g_string_truncate(fpath, dir_len); g_string_append(fpath, name); fi = fm_file_info_new(); fi->path = fm_path_new_child(job->dir_path, name); _retry: if( _fm_file_info_job_get_info_for_native_file(FM_JOB(job), fi, fpath->str, &err) ) fm_list_push_tail_noref(job->files, fi); else /* failed! */ { FmJobErrorAction act = fm_job_emit_error(job, err, FM_JOB_ERROR_MILD); g_error_free(err); err = NULL; if(act == FM_JOB_RETRY) goto _retry; fm_file_info_unref(fi); } } g_string_free(fpath, TRUE); g_dir_close(dir); } else { fm_job_emit_error(FM_JOB(job), err, FM_JOB_ERROR_CRITICAL); g_error_free(err); } g_free(dir_path); return TRUE; } static gboolean fm_dir_list_job_run_gio(FmDirListJob* job) { GFileEnumerator *enu; GFileInfo *inf; FmFileInfo* fi; GError *err = NULL; FmJob* fmjob = FM_JOB(job); GFile* gf; /* handle some built-in virtual dirs */ if( fm_path_is_xdg_menu(job->dir_path) ) /* xdg menu:// */ return fm_dir_list_job_list_xdg_menu(job); gf = fm_path_to_gfile(job->dir_path); _retry: inf = g_file_query_info(gf, gfile_info_query_attribs, 0, fm_job_get_cancellable(fmjob), &err); if(!inf ) { FmJobErrorAction act = fm_job_emit_error(fmjob, err, FM_JOB_ERROR_MODERATE); if( act == FM_JOB_RETRY ) { g_error_free(err); err = NULL; goto _retry; } else { g_object_unref(gf); g_error_free(err); return FALSE; } } if( g_file_info_get_file_type(inf) != G_FILE_TYPE_DIRECTORY) { err = g_error_new(G_IO_ERROR, G_IO_ERROR_NOT_DIRECTORY, _("The specified directory is not valid")); fm_job_emit_error(fmjob, err, FM_JOB_ERROR_CRITICAL); g_error_free(err); g_object_unref(gf); g_object_unref(inf); return FALSE; } /* FIXME: should we use fm_file_info_new + fm_file_info_set_from_gfileinfo? */ job->dir_fi = fm_file_info_new_from_gfileinfo(job->dir_path, inf); g_object_unref(inf); enu = g_file_enumerate_children (gf, gfile_info_query_attribs, 0, fm_job_get_cancellable(fmjob), &err); g_object_unref(gf); if(enu) { while( ! fm_job_is_cancelled(FM_JOB(job)) ) { inf = g_file_enumerator_next_file(enu, fm_job_get_cancellable(fmjob), &err); if(inf) { FmPath* sub = fm_path_new_child(job->dir_path, g_file_info_get_name(inf)); fi = fm_file_info_new_from_gfileinfo(sub, inf); fm_path_unref(sub); fm_list_push_tail_noref(job->files, fi); } else { if(err) { FmJobErrorAction act = fm_job_emit_error(fmjob, err, FM_JOB_ERROR_MILD); g_error_free(err); /* FM_JOB_RETRY is not supported. */ if(act == FM_JOB_ABORT) fm_job_cancel(job); } break; /* FIXME: error handling */ } g_object_unref(inf); } g_file_enumerator_close(enu, NULL, &err); g_object_unref(enu); } else { fm_job_emit_error(fmjob, err, FM_JOB_ERROR_CRITICAL); g_error_free(err); return FALSE; } return TRUE; } gboolean fm_dir_list_job_run(FmDirListJob* job) { gboolean ret; if(fm_path_is_native(job->dir_path)) /* if this is a native file on real file system */ ret = fm_dir_list_job_run_posix(job); else /* this is a virtual path or remote file system path */ ret = fm_dir_list_job_run_gio(job); return ret; } FmFileInfoList* fm_dir_dist_job_get_files(FmDirListJob* job) { return job->files; }