You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
libfm-qt-packaging/src/core/dirlistjob.cpp

179 lines
7.0 KiB

#include "dirlistjob.h"
#include <gio/gio.h>
#include "fileinfo_p.h"
#include "gioptrs.h"
#include <QDebug>
namespace Fm {
DirListJob::DirListJob(const FilePath& path, Flags _flags, const std::shared_ptr<const HashSet>& cutFilesHashSet):
dir_path{path}, flags{_flags}, cutFilesHashSet_{cutFilesHashSet} {
}
void DirListJob::exec() {
GErrorPtr err;
GFileInfoPtr dir_inf;
GFilePtr dir_gfile = dir_path.gfile();
// FIXME: these are hacks for search:/// URI implemented by libfm which contains some bugs
bool isFileSearch = dir_path.hasUriScheme("search");
if(isFileSearch) {
// NOTE: The GFile instance changes its URI during file enumeration (bad design).
// So we create a copy here to avoid channging the gfile stored in dir_path.
// FIXME: later we should refactor file search and remove this dirty hack.
dir_gfile = GFilePtr{g_file_dup(dir_gfile.get())};
}
_retry:
err.reset();
dir_inf = GFileInfoPtr{
g_file_query_info(dir_gfile.get(), gfile_info_query_attribs,
G_FILE_QUERY_INFO_NONE, cancellable().get(), &err),
false
};
if(!dir_inf) {
ErrorAction act = emitError(err, ErrorSeverity::MODERATE);
if(act == ErrorAction::RETRY) {
err.reset();
goto _retry;
}
return;
}
if(g_file_info_get_file_type(dir_inf.get()) != G_FILE_TYPE_DIRECTORY) {
auto path_str = dir_path.toString();
err = GErrorPtr{
G_IO_ERROR,
G_IO_ERROR_NOT_DIRECTORY,
tr("The specified directory '%1' is not valid").arg(path_str.get())
};
emitError(err, ErrorSeverity::CRITICAL);
return;
}
else {
std::lock_guard<std::mutex> lock{mutex_};
dir_fi = std::make_shared<FileInfo>(dir_inf, dir_path.parent());
}
FileInfoList foundFiles;
/* check if FS is R/O and set attr. into inf */
// FIXME: _fm_file_info_job_update_fs_readonly(gf, inf, nullptr, nullptr);
err.reset();
GFileEnumeratorPtr enu = GFileEnumeratorPtr{
g_file_enumerate_children(dir_gfile.get(), gfile_info_query_attribs,
G_FILE_QUERY_INFO_NONE, cancellable().get(), &err),
false
};
if(enu) {
// qDebug() << "START LISTING:" << dir_path.toString().get();
while(!isCancelled()) {
err.reset();
GFileInfoPtr inf{g_file_enumerator_next_file(enu.get(), cancellable().get(), &err), false};
if(inf) {
#if 0
FmPath* dir, *sub;
GFile* child;
if(G_UNLIKELY(job->flags & FM_DIR_LIST_JOB_DIR_ONLY)) {
/* FIXME: handle symlinks */
if(g_file_info_get_file_type(inf) != G_FILE_TYPE_DIRECTORY) {
g_object_unref(inf);
continue;
}
}
#endif
// virtual folders may return children not within them
// For example: the search:/// URI implemented by libfm might return files from different folders during enumeration.
// So here we call g_file_enumerator_get_container() to get the real parent path rather than simply using dir_path.
// This is not the behaviour of gio, but the extensions by libfm might do this.
// FIXME: after we port these vfs implementation from libfm, we can redesign this.
FilePath realParentPath = FilePath{g_file_enumerator_get_container(enu.get()), true};
if(isFileSearch) { // this is a file sarch job (search:/// URI)
// FIXME: redesign file search and remove this dirty hack
// the libfm implementation of search:/// URI returns a customized GFile implementation that does not behave normally.
// let's get its actual URI and re-create a normal gio GFile instance from it.
realParentPath = FilePath::fromUri(realParentPath.uri().get());
}
#if 0
if(g_file_info_get_file_type(inf) == G_FILE_TYPE_DIRECTORY)
/* for dir: check if its FS is R/O and set attr. into inf */
{
_fm_file_info_job_update_fs_readonly(child, inf, nullptr, nullptr);
}
fi = fm_file_info_new_from_g_file_data(child, inf, sub);
#endif
auto fileInfo = std::make_shared<FileInfo>(inf, realParentPath);
if(emit_files_found) {
// Q_EMIT filesFound();
}
if(cutFilesHashSet_
&& cutFilesHashSet_->count(fileInfo->path().hash()) > 0) {
fileInfo->bindCutFiles(cutFilesHashSet_);
}
foundFiles.push_back(std::move(fileInfo));
}
else {
if(err) {
ErrorAction act = emitError(err, ErrorSeverity::MILD);
/* ErrorAction::RETRY is not supported. */
if(act == ErrorAction::ABORT) {
cancel();
}
}
/* otherwise it's EOL */
break;
}
}
err.reset();
g_file_enumerator_close(enu.get(), cancellable().get(), &err);
}
else {
emitError(err, ErrorSeverity::CRITICAL);
}
// qDebug() << "END LISTING:" << dir_path.toString().get();
if(!foundFiles.empty()) {
std::lock_guard<std::mutex> lock{mutex_};
files_.swap(foundFiles);
}
}
#if 0
//FIXME: incremental..
static gboolean emit_found_files(gpointer user_data) {
/* this callback is called from the main thread */
FmDirListJob* job = FM_DIR_LIST_JOB(user_data);
/* g_print("emit_found_files: %d\n", g_slist_length(job->files_to_add)); */
if(g_source_is_destroyed(g_main_current_source())) {
return FALSE;
}
g_signal_emit(job, signals[FILES_FOUND], 0, job->files_to_add);
g_slist_free_full(job->files_to_add, (GDestroyNotify)fm_file_info_unref);
job->files_to_add = nullptr;
job->delay_add_files_handler = 0;
return FALSE;
}
static gpointer queue_add_file(FmJob* fmjob, gpointer user_data) {
FmDirListJob* job = FM_DIR_LIST_JOB(fmjob);
FmFileInfo* file = FM_FILE_INFO(user_data);
/* this callback is called from the main thread */
/* g_print("queue_add_file: %s\n", fm_file_info_get_disp_name(file)); */
job->files_to_add = g_slist_prepend(job->files_to_add, fm_file_info_ref(file));
if(job->delay_add_files_handler == 0)
job->delay_add_files_handler = g_timeout_add_seconds_full(G_PRIORITY_LOW,
1, emit_found_files, g_object_ref(job), g_object_unref);
return nullptr;
}
void fm_dir_list_job_add_found_file(FmDirListJob* job, FmFileInfo* file) {
fm_file_info_list_push_tail(job->files, file);
if(G_UNLIKELY(job->emit_files_found)) {
fm_job_call_main_thread(FM_JOB(job), queue_add_file, file);
}
}
#endif
} // namespace Fm