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.
179 lines
7.0 KiB
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
|