MauiKit File Browsing

fm.cpp
1/*
2 * Copyright 2018 Camilo Higuita <milo.h@aol.com>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Library General Public License as
6 * published by the Free Software Foundation; either version 2, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details
13 *
14 * You should have received a copy of the GNU Library General Public
15 * License along with this program; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 */
19
20#include "fm.h"
21
22#ifdef COMPONENT_SYNCING
23#include "syncing.h"
24#endif
25
26#include <QDateTime>
27#include <QFileInfo>
28#include <QLocale>
29#include <QRegularExpression>
30#include <QUrl>
31#include <QDebug>
32
33#ifdef KIO_AVAILABLE
34#include <KCoreDirLister>
35#include <KFileItem>
36#include <KFilePlacesModel>
37#include <KIO/CopyJob>
38#include <KIO/DeleteJob>
39#include <KIO/MkdirJob>
40#include <KIO/SimpleJob>
41#include <QIcon>
42#else
43
44#include "fileloader.h"
45#include <QFileSystemWatcher>
46
47QDirLister::QDirLister(QObject *parent)
48 : QObject(parent)
49 , m_loader(new FMH::FileLoader)
50 , m_watcher(new QFileSystemWatcher(this))
51{
52 m_loader->setBatchCount(20);
53 m_loader->informer = &FMStatic::getFileInfoModel;
54 connect(m_loader, &FMH::FileLoader::itemsReady, [this](FMH::MODEL_LIST items, QList<QUrl> urls) {
55 Q_EMIT this->itemsReady(items, urls.first());
56 });
57
58 connect(m_loader, &FMH::FileLoader::itemReady, [this](FMH::MODEL item, QList<QUrl> urls) {
59 this->m_list << item;
60 this->m_watcher->addPath(QUrl(item[FMH::MODEL_KEY::URL]).toLocalFile());
61 Q_EMIT this->itemReady(item, urls.first());
62 });
63
65 Q_EMIT this->completed(urls.first());
66 });
67
68 connect(this->m_watcher, &QFileSystemWatcher::directoryChanged, [&](const QString &path) {
69 if (path == this->m_url.toLocalFile()) {
70 this->reviewChanges();
71 }
72 });
73
74 connect(this->m_watcher, &QFileSystemWatcher::fileChanged, [&](const QString &path) {
75 const auto fileUrl = QUrl::fromLocalFile(path);
76 if (this->includes(fileUrl)) {
77 if (FMH::fileExists(fileUrl)) {
78 Q_EMIT this->refreshItems({{this->m_list.at(this->indexOf(FMH::MODEL_KEY::URL, fileUrl.toString())), FMStatic::getFileInfoModel(fileUrl)}}, this->m_url);
79 }
80 }
81 });
82}
83
84void QDirLister::reviewChanges()
85{
86 if (this->m_checking)
87 return;
88
89 this->m_checking = true;
90 auto checkLoader = new FMH::FileLoader;
92
93 qDebug() << "Doign the check" << m_checking;
94
95 FMH::MODEL_LIST removedItems;
96 const auto mlist = this->m_list; // use a copy to not affect the list indexes on the iterations
97 for (const auto &item : qAsConst(mlist)) {
98 const auto fileUrl = QUrl(item[FMH::MODEL_KEY::URL]);
99
100 if (!FMH::fileExists(fileUrl)) {
101 const auto index = this->indexOf(FMH::MODEL_KEY::URL, fileUrl.toString());
102
103 if (index < this->m_list.count() && index >= 0) {
104 removedItems << item;
105 this->m_list.remove(index);
106 this->m_watcher->removePath(fileUrl.toLocalFile());
107 }
108 }
109 }
110
111 if (!removedItems.isEmpty())
112 Q_EMIT this->itemsDeleted(removedItems, this->m_url);
113
114 connect(checkLoader, &FMH::FileLoader::itemsReady, [=](FMH::MODEL_LIST items, QList<QUrl> urls) {
115 if (urls.first() == this->m_url) {
116 FMH::MODEL_LIST newItems;
117 for (const auto &item : qAsConst(items)) {
118 const auto fileUrl = QUrl(item[FMH::MODEL_KEY::URL]);
119 if (!this->includes(fileUrl)) {
120 newItems << item;
121
122 this->m_list << item;
123 this->m_watcher->addPath(fileUrl.toLocalFile());
124 }
125 }
126
127 if (!newItems.isEmpty())
128 Q_EMIT this->itemsAdded(newItems, this->m_url);
129 }
130
131 checkLoader->deleteLater();
132 this->m_checking = false;
133 });
134
136 checkLoader->deleteLater();
137 this->m_checking = false;
138 });
139
141
142 if (m_showDotFiles)
143 dirFilter = dirFilter | QDir::Hidden | QDir::System;
144
145 checkLoader->requestPath({this->m_url}, false, m_nameFilters.isEmpty() ? QStringList() : m_nameFilters.split(QStringLiteral(" ")), dirFilter);
146}
147
148bool QDirLister::includes(const QUrl &url)
149{
150 return this->indexOf(FMH::MODEL_KEY::URL, url.toString()) >= 0;
151}
152
153int QDirLister::indexOf(const FMH::MODEL_KEY &key, const QString &value) const
154{
155 const auto items = this->m_list;
156 const auto it = std::find_if(items.constBegin(), items.constEnd(), [&](const FMH::MODEL &item) -> bool {
157 return item[key] == value;
158 });
159
160 if (it != items.constEnd())
161 return std::distance(items.constBegin(), it);
162 else
163 return -1;
164}
165
166bool QDirLister::openUrl(const QUrl &url)
167{
168 // if(this->m_url == url)
169 // return false;
170
171 qDebug() << "open URL" << url;
172 this->m_url = url;
173 this->m_list.clear();
174 this->m_watcher->removePaths(QStringList() << this->m_watcher->directories() << this->m_watcher->files());
175
176 if (FMStatic::isDir(this->m_url)) {
177 this->m_watcher->addPath(this->m_url.toLocalFile());
178
180
181 if (m_showDotFiles)
182 {
183 dirFilter = dirFilter | QDir::Hidden;
184 }
185
186 m_loader->requestPath({this->m_url}, false, m_nameFilters.isEmpty() ? QStringList() : m_nameFilters.split(QStringLiteral(" ")), dirFilter);
187
188 } else
189 return false;
190
191 return true;
192}
193
194void QDirLister::setDirOnlyMode(bool value)
195{
196 m_dirOnly = value;
197}
198
199void QDirLister::setShowingDotFiles(bool value)
200{
201 m_showDotFiles = value;
202}
203
204void QDirLister::setNameFilter(QString filters)
205{
206 m_nameFilters = filters;
207}
208#endif
209
211 : QObject(parent)
212#ifdef COMPONENT_SYNCING
213 , sync(new Syncing(this))
214#endif
215#ifdef KIO_AVAILABLE
216 , dirLister(new KCoreDirLister(this))
217#else
218 , dirLister(new QDirLister(this))
219#endif
220{
221
222#ifdef KIO_AVAILABLE
223 this->dirLister->setDelayedMimeTypes(true);
224 this->dirLister->setAutoUpdate(true);
225
226 const static auto packItems = [](const KFileItemList &items) -> FMH::MODEL_LIST {
227 return std::accumulate(items.constBegin(), items.constEnd(), FMH::MODEL_LIST(), [](FMH::MODEL_LIST &res, const KFileItem &item) -> FMH::MODEL_LIST {
228 res << FMStatic::getFileInfo(item);
229 return res;
230 });
231 };
232
233 connect(dirLister, static_cast<void (KCoreDirLister::*)(const QUrl &)>(&KCoreDirLister::listingDirCompleted), this, [&](QUrl url) {
234 qDebug() << "PATH CONTENT READY" << url;
235 Q_EMIT this->pathContentReady(url);
236 });
237
238 connect(dirLister, static_cast<void (KCoreDirLister::*)(const QUrl &)>(&KCoreDirLister::listingDirCanceled), this, [&](QUrl url) {
239 qDebug() << "PATH CONTENT READY" << url;
240 Q_EMIT this->pathContentReady(url);
241 });
242
243 connect(dirLister, static_cast<void (KCoreDirLister::*)(const QUrl &, const KFileItemList &items)>(&KCoreDirLister::itemsAdded), this, [&](QUrl dirUrl, KFileItemList items) {
244 qDebug() << "MORE ITEMS WERE ADDED";
245 Q_EMIT this->pathContentItemsReady({dirUrl, packItems(items)});
246 });
247
248// connect(dirLister, static_cast<void (KCoreDirLister::*)(const KFileItemList &items)>(&KCoreDirLister::newItems), [&](KFileItemList items)
249// {
250// qDebug()<< "MORE NEW ITEMS WERE ADDED";
251// for(const auto &item : items)
252// qDebug()<< "MORE <<" << item.url();
253//
254// Q_EMIT this->pathContentChanged(dirLister->url());
255// });
256
257 connect(dirLister, static_cast<void (KCoreDirLister::*)(const KFileItemList &items)>(&KCoreDirLister::itemsDeleted), this, [&](KFileItemList items) {
258 qDebug() << "ITEMS WERE DELETED";
259 Q_EMIT this->pathContentItemsRemoved({dirLister->url(), packItems(items)});
260 });
261
262 connect(dirLister, static_cast<void (KCoreDirLister::*)(const QList<QPair<KFileItem, KFileItem>> &items)>(&KCoreDirLister::refreshItems), this, [&](QList<QPair<KFileItem, KFileItem>> items) {
263 qDebug() << "ITEMS WERE REFRESHED";
264
265 const auto res = std::accumulate(
266 items.constBegin(), items.constEnd(), QVector<QPair<FMH::MODEL, FMH::MODEL>>(), [](QVector<QPair<FMH::MODEL, FMH::MODEL>> &list, const QPair<KFileItem, KFileItem> &pair) -> QVector<QPair<FMH::MODEL, FMH::MODEL>> {
267 list << QPair<FMH::MODEL, FMH::MODEL> {FMStatic::getFileInfo(pair.first), FMStatic::getFileInfo(pair.second)};
268 return list;
269 });
270
272 });
273#else
274 connect(dirLister, &QDirLister::itemsReady, this, [&](FMH::MODEL_LIST items, QUrl url) {
275 Q_EMIT this->pathContentItemsReady({url, items});
276 });
277
278 connect(dirLister, &QDirLister::completed, this, [&](QUrl url) {
279 Q_EMIT this->pathContentReady(url);
280 });
281
282 connect(dirLister, &QDirLister::refreshItems, this, [&](QVector<QPair<FMH::MODEL, FMH::MODEL>> items, QUrl) {
283 qDebug() << "ITEMS WERE REFRESHED";
284 Q_EMIT this->pathContentItemsChanged(items);
285 });
286
287 connect(dirLister, &QDirLister::itemsAdded, this, [&](FMH::MODEL_LIST items, QUrl url) {
288 qDebug() << "MORE ITEMS WERE ADDED";
289 Q_EMIT this->pathContentItemsReady({url, items});
290 Q_EMIT this->pathContentReady(url); //Q_EMIT this to force to sort on fmlist
291 });
292
293 connect(dirLister, &QDirLister::itemsDeleted, this, [&](FMH::MODEL_LIST items, QUrl url) {
294 qDebug() << "ITEMS WERE DELETED";
295 Q_EMIT this->pathContentItemsRemoved({url, items});
296 });
297
298#endif
299
300#ifdef COMPONENT_SYNCING
301 connect(this->sync, &Syncing::listReady, [this](const FMH::MODEL_LIST &list, const QUrl &url) {
302 Q_EMIT this->cloudServerContentReady({url, list});
303 });
304
305 connect(this->sync, &Syncing::itemReady, [this](const FMH::MODEL &item, const QUrl &url, const Syncing::SIGNAL_TYPE &signalType) {
306 switch (signalType) {
307 case Syncing::SIGNAL_TYPE::OPEN:
308 FMStatic::openUrl(item[FMH::MODEL_KEY::PATH]);
309 break;
310
311 case Syncing::SIGNAL_TYPE::DOWNLOAD:
312 Q_EMIT this->cloudItemReady(item, url);
313 break;
314
315 case Syncing::SIGNAL_TYPE::COPY: {
316 QVariantMap data;
317 const auto keys = item.keys();
318 for (auto key : keys)
319 data.insert(FMH::MODEL_NAME[key], item[key]);
320
321 // this->copy(QVariantList {data}, this->sync->getCopyTo());
322 break;
323 }
324 default:
325 return;
326 }
327 });
328
329 connect(this->sync, &Syncing::error, [this](const QString &message) {
330 Q_EMIT this->warningMessage(message);
331 });
332
333 connect(this->sync, &Syncing::progress, [this](const int &percent) {
334 Q_EMIT this->loadProgress(percent);
335 });
336
337 connect(this->sync, &Syncing::dirCreated, [this](const FMH::MODEL &dir, const QUrl &url) {
338 Q_EMIT this->newItem(dir, url);
339 });
340
341 connect(this->sync, &Syncing::uploadReady, [this](const FMH::MODEL &item, const QUrl &url) {
342 Q_EMIT this->newItem(item, url);
343 });
344#endif
345}
346
347void FM::getPathContent(const QUrl &path, const bool &hidden, const bool &onlyDirs, const QStringList &filters, const QDirIterator::IteratorFlags &iteratorFlags)
348{
349 qDebug() << "Getting async path contents";
350 Q_UNUSED(iteratorFlags)
351
352#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
353 this->dirLister->setShowingDotFiles(hidden);
354#else
355 this->dirLister->setShowHiddenFiles(hidden);
356#endif
357 this->dirLister->setDirOnlyMode(onlyDirs);
358 this->dirLister->setNameFilter(filters.join(QStringLiteral(" ")));
359
360 if (this->dirLister->openUrl(path))
361 qDebug() << "GETTING PATH CONTENT" << path;
362}
363
364bool FM::getCloudServerContent(const QUrl &path, const QStringList &filters, const int &depth)
365{
366 Q_UNUSED(path)
367 Q_UNUSED(filters)
368 Q_UNUSED(depth)
369
370
371#ifdef COMPONENT_SYNCING
372 const auto __list = path.toString().replace("cloud:///", "/").split("/");
373
374 if (__list.isEmpty() || __list.size() < 2) {
375 qWarning() << "Could not parse username to get cloud server content";
376 return false;
377 }
378
379 auto user = __list[1];
380 // auto data = this->get(QString("select * from clouds where user = '%1'").arg(user));
381 QVariantList data;
382 if (data.isEmpty())
383 return false;
384
385 auto map = data.first().toMap();
386
387 user = map[FMH::MODEL_NAME[FMH::MODEL_KEY::USER]].toString();
388 auto server = map[FMH::MODEL_NAME[FMH::MODEL_KEY::SERVER]].toString();
389 auto password = map[FMH::MODEL_NAME[FMH::MODEL_KEY::PASSWORD]].toString();
390 this->sync->setCredentials(server, user, password);
391
392 this->sync->listContent(path, filters, depth);
393 return true;
394#else
395 return false;
396#endif
397}
398
399void FM::createCloudDir(const QString &path, const QString &name)
400{
401 Q_UNUSED(path)
402 Q_UNUSED(name)
403
404#ifdef COMPONENT_SYNCING
405 this->sync->createDir(path, name);
406#endif
407}
408
409void FM::openCloudItem(const QVariantMap &item)
410{
411 Q_UNUSED(item)
412
413#ifdef COMPONENT_SYNCING
414 this->sync->resolveFile(FMH::toModel(item), Syncing::SIGNAL_TYPE::OPEN);
415#endif
416}
417
418void FM::getCloudItem(const QVariantMap &item)
419{
420 Q_UNUSED(item)
421#ifdef COMPONENT_SYNCING
422 this->sync->resolveFile(FMH::toModel(item), Syncing::SIGNAL_TYPE::DOWNLOAD);
423#endif
424}
425
427{
428 Q_UNUSED(server)
429 return FMStatic::CloudCachePath + QStringLiteral("opendesktop/") + user;
430}
431
433{
434 Q_UNUSED(path)
435#ifdef COMPONENT_SYNCING
436 return QString(path).replace(FMStatic::PATHTYPE_URI[FMStatic::PATHTYPE_KEY::CLOUD_PATH] + this->sync->getUser(), "");
437#else
438 return QString();
439#endif
440}
441
442bool FM::cut(const QList<QUrl> &urls, const QUrl &where)
443{
444 return FMStatic::cut(urls, where);
445}
446
447bool FM::copy(const QList<QUrl> &urls, const QUrl &where)
448{
449 // QStringList cloudPaths;
450
451 return FMStatic::copy(urls, where);
452
453#ifdef COMPONENT_SYNCING
454 // if(!cloudPaths.isEmpty())
455 // {
456 // qDebug()<<"UPLOAD QUEUE" << cloudPaths;
457 // const auto firstPath = cloudPaths.takeLast();
458 // this->sync->setUploadQueue(cloudPaths);
459 //
460 // if(where.toString().split("/").last().contains("."))
461 // {
462 // QStringList whereList = where.toString().split("/");
463 // whereList.removeLast();
464 // auto whereDir = whereList.join("/");
465 // qDebug()<< "Trying ot copy to cloud" << where << whereDir;
466 //
467 // this->sync->upload(this->resolveLocalCloudPath(whereDir), firstPath);
468 // } else
469 // this->sync->upload(this->resolveLocalCloudPath(where.toString()), firstPath);
470 // }
471#endif
472}
The FileLoader class asynchronously loads batches of files from a given list of local directories or ...
Definition fileloader.h:81
static std::function< FMH::MODEL(const QUrl &url) informer)
A callback function which structures the retrieved file URLs, with the required information.
Definition fileloader.h:121
void finished(FMH::MODEL_LIST items, QList< QUrl > urls)
Emitted once the operation has completely finished retrieving all the existing files or reached the l...
void requestPath(const QList< QUrl > &urls, const bool &recursive, const QStringList &nameFilters={}, const QDir::Filters &filters=QDir::Files, const uint &limit=99999)
Sends the request to start iterating throughout all the given location URLs, and with the given param...
void itemReady(FMH::MODEL item, QList< QUrl > urls)
Emitted for every single item that becomes available.
void itemsReady(FMH::MODEL_LIST items, QList< QUrl > urls)
Emitted when the batch of file items is ready.
static bool cut(const QList< QUrl > &urls, const QUrl &where)
Perform a move/cut of a list of files to a destination.
Definition fmstatic.cpp:255
static const QHash< PATHTYPE_KEY, QString > PATHTYPE_URI
Similar to PATHTYPE_SCHEME, but mapped with the complete scheme.
Definition fmstatic.h:375
static void openUrl(const QUrl &url)
Given a URL it tries to open it using the default application associated to it.
Definition fmstatic.cpp:405
static const FMH::MODEL getFileInfoModel(const QUrl &path)
getFileInfoModel
Definition fmstatic.cpp:559
static bool copy(const QList< QUrl > &urls, const QUrl &destinationDir)
Perform a copy of the files to the given destination.
Definition fmstatic.cpp:216
static const QString CloudCachePath
Standard Cloud Cache location path
Definition fmstatic.h:404
static bool isDir(const QUrl &path)
Whether a local file URL is a directory.
Definition fmstatic.cpp:139
@ CLOUD_PATH
A remote cloud server path.
Definition fmstatic.h:307
bool copy(const QList< QUrl > &urls, const QUrl &where)
Copy a set of file URLs to a new destination.
Definition fm.cpp:447
void getPathContent(const QUrl &path, const bool &hidden=false, const bool &onlyDirs=false, const QStringList &filters=QStringList(), const QDirIterator::IteratorFlags &iteratorFlags=QDirIterator::NoIteratorFlags)
Given a path URL retrieve the contents information packaged as a model.
Definition fm.cpp:347
FM(QObject *parent=nullptr)
Creates the instance.
Definition fm.cpp:210
void pathContentItemsRemoved(FMStatic::PATH_CONTENT list)
Emitted when a set of entries in the current location have been removed.
bool cut(const QList< QUrl > &urls, const QUrl &where)
Cut a set of file URLs to a new destination.
Definition fm.cpp:442
void getCloudItem(const QVariantMap &item)
Download a remote server entry.
Definition fm.cpp:418
QString resolveLocalCloudPath(const QString &path)
Given a remote server address URL, resolve it to the local cache URL.
Definition fm.cpp:432
void pathContentReady(QUrl path)
Emitted once the contents of the current location are ready and the listing has finished.
void pathContentItemsChanged(QVector< QPair< FMH::MODEL, FMH::MODEL > > items)
Emitted when the current location entries have changed.
void pathContentItemsReady(FMStatic::PATH_CONTENT list)
Emitted when a set of entries for the current location are ready.
void openCloudItem(const QVariantMap &item)
Open a given remote item in an external application.
Definition fm.cpp:409
static QString resolveUserCloudCachePath(const QString &server, const QString &user)
Given the server address and the user name, resolve a local path for the cache of the files.
Definition fm.cpp:426
Q_INVOKABLE void createCloudDir(const QString &path, const QString &name)
Creates a directory in the server.
Definition fm.cpp:399
bool getCloudServerContent(const QUrl &server, const QStringList &filters=QStringList(), const int &depth=0)
Given a server URL address retrieve its contents.
Definition fm.cpp:364
void listingDirCompleted(const QUrl &dirUrl)
void refreshItems(const QList< QPair< KFileItem, KFileItem > > &items)
void itemsDeleted(const KFileItemList &items)
void listingDirCanceled(const QUrl &dirUrl)
void itemsAdded(const QUrl &directoryUrl, const KFileItemList &items)
The Syncing class.
Definition syncing.h:22
void uploadReady(FMH::MODEL item, QUrl url)
uploadReady
void progress(int percent)
progress
void error(QString message)
error
void listReady(FMH::MODEL_LIST data, QUrl url)
listReady
void itemReady(FMH::MODEL item, QUrl url, Syncing::SIGNAL_TYPE &signalType)
itemReady
void dirCreated(FMH::MODEL item, QUrl url)
dirCreated
bool insert(Part *part, qint64 *insertId=nullptr)
bool fileExists(const QUrl &path)
static const QHash< MODEL_KEY, QString > MODEL_NAME
const FMH::MODEL toModel(const QVariantMap &map)
KIOCORE_EXPORT QStringList list(const QString &fileClass)
typedef Filters
typedef IteratorFlags
bool addPath(const QString &path)
QStringList directories() const const
void directoryChanged(const QString &path)
void fileChanged(const QString &path)
bool removePath(const QString &path)
QStringList removePaths(const QStringList &paths)
QList< Key > keys() const const
void clear()
const_iterator constBegin() const const
const_iterator constEnd() const const
qsizetype count() const const
T & first()
bool isEmpty() const const
void remove(qsizetype i, qsizetype n)
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
bool isEmpty() const const
QString & replace(QChar before, QChar after, Qt::CaseSensitivity cs)
QStringList split(QChar sep, Qt::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
QString join(QChar separator) const const
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
QUrl fromLocalFile(const QString &localFile)
QString toLocalFile() const const
QString toString(FormattingOptions options) const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri May 17 2024 11:51:27 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.