Index: krusader/VFS/default_vfs.h =================================================================== --- krusader/VFS/default_vfs.h +++ krusader/VFS/default_vfs.h @@ -76,6 +76,7 @@ protected: bool refreshInternal(const QUrl &origin, bool showHidden) Q_DECL_OVERRIDE; + bool startRefreshInternal(const QUrl &directory, bool showHidden) Q_DECL_OVERRIDE; protected slots: /// Handle result after dir listing job is finished Index: krusader/VFS/default_vfs.cpp =================================================================== --- krusader/VFS/default_vfs.cpp +++ krusader/VFS/default_vfs.cpp @@ -52,6 +52,9 @@ #include "../JobMan/jobman.h" #include "../JobMan/krjob.h" +//FIXME +#define NO_LOCAL_KIO 0 + default_vfs::default_vfs(): vfs(), _watcher() { _type = VFS_DEFAULT; @@ -208,12 +211,12 @@ } delete _watcher; // stop watching the old dir - +#if NO_LOCAL_KIO if (directory.isLocalFile()) { // we could read local directories with KIO but using Qt is a lot faster! return refreshLocal(directory); } - +#endif _currentDirectory = cleanUrl(directory); // start the listing job @@ -240,6 +243,48 @@ return !_listError; } +bool default_vfs::startRefreshInternal(const QUrl &directory, bool showHidden) +{ + if (!KProtocolManager::supportsListing(directory)) { + emit error(i18n("Protocol not supported by Krusader:\n%1", directory.url())); + return false; + } + + delete _watcher; // stop watching the old dir + +#if NO_LOCAL_KIO + if (directory.isLocalFile()) { + // we could read local directories with KIO but using Qt is a lot faster! + return refreshLocal(directory); + } +#endif + + _currentDirectory = cleanUrl(directory); + + // start the listing job + KIO::ListJob *job = KIO::listDir(_currentDirectory, KIO::HideProgressInfo, showHidden); + connect(job, &KIO::ListJob::entries, this, &default_vfs::slotAddFiles); + connect(job, &KIO::ListJob::redirection, this, &default_vfs::slotRedirection); + connect(job, &KIO::ListJob::permanentRedirection, this, &default_vfs::slotRedirection); + connect(job, &KIO::Job::result, this, [=] (KJob *job) { + if (job->error()) { + // we failed to refresh + emit error(job->errorString()); // display error message (in panel) + } + refreshResult(!job->error()); + }); + + // ensure connection credentials are asked only once + if(!parentWindow.isNull()) { + KIO::JobUiDelegate *ui = static_cast(job->uiDelegate()); + ui->setWindow(parentWindow); + } + + emit refreshJobStarted(job); + + return true; +} + // ==== protected slots ==== void default_vfs::slotListResult(KJob *job) @@ -269,12 +314,15 @@ const QUrl newUrl = preferLocalUrl(url); if (newUrl.scheme() != _currentDirectory.scheme()) { + abort(); //FIXME +#if 0 // abort and start over again, // some protocols (iso, zip, tar) do this on transition to local fs job->kill(); _isRefreshing = false; refresh(newUrl); return; +#endif } _currentDirectory = cleanUrl(newUrl); Index: krusader/VFS/vfs.h =================================================================== --- krusader/VFS/vfs.h +++ krusader/VFS/vfs.h @@ -155,6 +155,9 @@ // set the parent window to be used for dialogs void setParentWindow(QWidget *widget) { parentWindow = widget; } + bool startRefresh(const QUrl &directory = QUrl()); + void stopRefresh(); + public slots: /// Re-read the current directory files or change to another directory. Blocking. /// Returns true if directory was read. Returns false if failed or refresh job was killed. @@ -178,14 +181,18 @@ /// Emitted before a directory path is opened for reading. Used for automounting. void aboutToOpenDir(const QString &path); + void refreshFinished(bool success); + protected: /// Fill the vfs dictionary with vfiles, must be implemented for each VFS. virtual bool refreshInternal(const QUrl &origin, bool showHidden) = 0; + virtual bool startRefreshInternal(const QUrl &directory, bool showHidden) = 0; + /// Returns true if showing hidden files is set in config. bool showHiddenFiles(); /// Add a new vfile to the internal dictionary (while refreshing). - inline void addVfile(vfile *vf) { _vfiles.insert(vf->vfile_getName(), vf); } + inline void addVfile(vfile *vf) { _new_vfiles.insert(vf->vfile_getName(), vf); } /// Calculate the size of a file or directory (recursive). void calcSpace(const QUrl &url, KIO::filesize_t *totalSize, unsigned long *totalFiles, @@ -204,9 +211,10 @@ static vfile *createVFileFromKIO(const KIO::UDSEntry &_calcEntry, const QUrl &directory, bool virt = false); + void refreshResult(bool success); + VFS_TYPE _type; // the vfs type. QUrl _currentDirectory; // the path or file the VFS originates from. - bool _isRefreshing; // true if vfs is busy with refreshing QPointer parentWindow; protected slots: @@ -224,6 +232,10 @@ void clear(vfileDict &vfiles); vfileDict _vfiles; // The list of files in the current dictionary + vfileDict _new_vfiles; + + bool _isRefreshing = false; // true if vfs is busy with refreshing + bool _dirChange = false; // used in the calcSpace function bool *_calcKdsBusy; Index: krusader/VFS/vfs.cpp =================================================================== --- krusader/VFS/vfs.cpp +++ krusader/VFS/vfs.cpp @@ -46,6 +46,8 @@ #include #include +#include + #include "../defaults.h" #include "../krglobal.h" #include "krpermhandler.h" @@ -125,6 +127,81 @@ return adjustedUrl; } +bool vfs::startRefresh(const QUrl &directory) +{ + if (_isRefreshing) { + // NOTE: this does not happen (unless async)"; + return false; + } + + assert(_new_vfiles.isEmpty()); + + // workaround for krarc: find out if transition to local fs is wanted and adjust URL manually + QUrl url = directory; + if (_currentDirectory.scheme() == "krarc" && url.scheme() == "krarc" && + QDir(url.path()).exists()) { + url.setScheme("file"); + } + + _dirChange = !url.isEmpty() && cleanUrl(url) != _currentDirectory; + + const QUrl toRefresh = + _dirChange ? url.adjusted(QUrl::NormalizePathSegments) : _currentDirectory; + if (!toRefresh.isValid()) { + emit error(i18n("Malformed URL:\n%1", toRefresh.toDisplayString())); + _dirChange = false; + return false; + } + + _isRefreshing = true; + + if (_dirChange) { + vfileDict tmp = _vfiles; + _vfiles.clear(); + // show an empty directory while loading the new one and clear selection + emit cleared(); + clear(tmp); + } + + const bool started = startRefreshInternal(toRefresh, showHiddenFiles()); + + if (!started) { + // cleanup and abort + clear(_new_vfiles); + if (!_dirChange) { + vfileDict tmp = _vfiles; + _vfiles.clear(); + emit cleared(); + clear(tmp); + } + _isRefreshing = false; + _dirChange = false; + return false; + } + + return true; +} + +void vfs::refreshResult(bool success) +{ + vfileDict tmp = _vfiles; + _vfiles.clear(); + emit cleared(); + clear(tmp); + + if (success) + _vfiles = _new_vfiles; + else + clear(_new_vfiles); + + _new_vfiles.clear(); + _isRefreshing = false; + _dirChange = false; + + emit refreshDone(_dirChange); + emit refreshFinished(success); +} + bool vfs::refresh(const QUrl &directory) { @@ -133,6 +210,8 @@ return false; } + assert(_new_vfiles.isEmpty()); + // workaround for krarc: find out if transition to local fs is wanted and adjust URL manually QUrl url = directory; if (_currentDirectory.scheme() == "krarc" && url.scheme() == "krarc" && @@ -165,11 +244,15 @@ if (!dirChange) emit cleared(); clear(tempVfiles); + clear(_new_vfiles); + emit refreshDone(dirChange); return false; } + _vfiles = _new_vfiles; emit refreshDone(dirChange); + _new_vfiles.clear(); clear(tempVfiles); updateFilesystemInfo(); Index: krusader/VFS/virt_vfs.h =================================================================== --- krusader/VFS/virt_vfs.h +++ krusader/VFS/virt_vfs.h @@ -71,6 +71,7 @@ protected: bool refreshInternal(const QUrl &origin, bool showHidden) Q_DECL_OVERRIDE; + virtual bool startRefreshInternal(const QUrl &directory, bool showHidden) Q_DECL_OVERRIDE; private: /// Return current dir: "/" or pure directory name @@ -94,6 +95,8 @@ void slotStatResult(KJob *job); private: + class StatJob; + void showError(const QString &error); static QHash *> _virtVfsDict; // map virtual directories to containing files static QHash _metaInfoDict; // map virtual directories to meta infos Index: krusader/VFS/virt_vfs.cpp =================================================================== --- krusader/VFS/virt_vfs.cpp +++ krusader/VFS/virt_vfs.cpp @@ -23,6 +23,7 @@ #include #include #include +#include // QtWidgets #include @@ -36,6 +37,8 @@ #include #include +#include + #include "../defaults.h" #include "../krglobal.h" #include "../krservices.h" @@ -43,6 +46,66 @@ #define VIRT_VFS_DB "virt_vfs.db" +class virt_vfs::StatJob : public KIO::Job +{ +public: + StatJob(const QList &urls) : _it(urls) { + timer.setSingleShot(true); + connect(&timer, &QTimer::timeout, this, &StatJob::process); + timer.start(); + } + + const KIO::UDSEntryList &results() { return _results; } + +protected: + bool doKill() Q_DECL_OVERRIDE { + timer.stop(); + return Job::doKill(); + } + +private: + void process() { + if (!_it.hasNext()) + emitResult(); + QUrl url = _it.next(); + + if (url.scheme() == "virt") { // return a virtual directory in root +//FIXME + abort(); +// QString path = url.path().mid(1); +// if (path.isEmpty()) +// path = '/'; +// return new vfile(path, 0, "drwxr-xr-x", time(0), false, false, getuid(), getgid(), +// "inode/directory", "", 0, -1, url); + } + + const QUrl directory = url.adjusted(QUrl::RemoveFilename); + +//FIXME +// if (url.isLocalFile()) { +// QFileInfo file(url.path()); +// return file.exists() ? vfs::createLocalVFile(url.fileName(), directory.path(), true) : 0; +// } + + KIO::StatJob *statJob = KIO::stat(url, KIO::HideProgressInfo); + bool res = addSubjob(statJob); + assert(res); + + connect(statJob, &KIO::Job::result, this, [=] (KJob*) { + if (!statJob->error()) + _results << statJob->statResult(); + if (_it.hasNext()) + timer.start(); + else + emitResult(); + }); + } + + QListIterator _it; + KIO::UDSEntryList _results; + QTimer timer; +}; + QHash *> virt_vfs::_virtVfsDict; QHash virt_vfs::_metaInfoDict; @@ -258,6 +321,49 @@ return true; } +bool virt_vfs::startRefreshInternal(const QUrl &directory, bool /*showHidden*/) +{ + _currentDirectory = cleanUrl(directory); + _currentDirectory.setHost(""); + // remove invalid subdirectories + _currentDirectory.setPath("/" + _currentDirectory.path().remove('/')); + + if (!_virtVfsDict.contains(currentDir())) { +//FIXME + abort(); +#if 0 + // NOTE: silently creating non-existing directories here. The search and locate tools expect + // this. (And user can enter some directory and it will be created). + mkDirInternal(currentDir()); + save(); + // infinite loop possible + //emit filesystemChanged(currentDirectory()); + return true; +#endif + } + + QList *urlList = _virtVfsDict[currentDir()]; + _metaInfo = _metaInfoDict[currentDir()]; + + StatJob *statJob = new StatJob(*urlList); + connect(statJob, &KIO::Job::result, this, [=] (KJob*) { + if (!statJob->error()) { + for (const KIO::UDSEntry &entry : statJob->results()) { + vfile *vfile = vfs::createVFileFromKIO(entry, _currentDirectory); + if (vfile) + addVfile(vfile); + } + refreshResult(true); + save(); + } + else + refreshResult(false); + }); + emit refreshJobStarted(statJob); + + return true; +} + // ==== private ==== void virt_vfs::mkDirInternal(const QString &name)