diff --git a/CMakeLists.txt b/CMakeLists.txt index 0c40dd3..c0e2182 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.14) -project(cutefish-filemanager LANGUAGES CXX) +project(filemanager LANGUAGES CXX) set(CMAKE_INCLUDE_CURRENT_DIR ON) @@ -8,62 +8,58 @@ set(CMAKE_AUTOUIC ON) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTORCC ON) -set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) -find_package(Qt5 COMPONENTS Core Quick Concurrent DBus LinguistTools REQUIRED) -find_package(KF5KIO) +find_package(Qt5 COMPONENTS Core DBus Quick LinguistTools REQUIRED) find_package(MeuiKit REQUIRED) +find_package(KF5KIO) +find_package(KF5Solid) + add_executable(cutefish-filemanager - src/main.cpp - src/fm.cpp - src/fmh.cpp - src/fmstatic.cpp - src/fmlist.cpp - src/handy.cpp - src/placeslist.cpp - src/pathlist.cpp - src/baselist.cpp - src/basemodel.cpp - src/rubberband.cpp - src/iconthemeprovider.cpp + main.cpp + model/foldermodel.cpp + model/placesmodel.cpp + model/placesitem.cpp + model/pathbarmodel.cpp + model/dirlister.cpp + model/positioner.cpp - src/lib/foldermodel.cpp - src/lib/positioner.cpp - src/lib/itemviewadapter.cpp - src/lib/fileitemactions.cpp - src/lib/placesmodel.cpp - src/lib/placesitem.cpp + dialogs/propertiesdialog.cpp + widgets/rubberband.cpp + widgets/itemviewadapter.cpp - src/dialogs/propertiesdialog.cpp + desktop/desktopview.cpp + desktop/desktopsettings.cpp - src/desktop/desktopview.cpp - src/desktop/desktopsettings.cpp + helper/thumbnailer.cpp + helper/pathhistory.cpp - qml.qrc + desktopiconprovider.cpp + + qml.qrc +) + +target_link_libraries(cutefish-filemanager + PRIVATE + Qt5::Core + Qt5::DBus + Qt5::Quick + + KF5::KIOCore + KF5::KIOFileWidgets + KF5::KIOWidgets + KF5::Solid + + MeuiKit ) file(GLOB TS_FILES translations/*.ts) qt5_create_translation(QM_FILES ${TS_FILES}) add_custom_target(translations DEPENDS ${QM_FILES} SOURCES ${TS_FILES}) -add_dependencies(${PROJECT_NAME} translations) +add_dependencies(cutefish-filemanager translations) -target_link_libraries(cutefish-filemanager - PRIVATE - Qt5::Core - Qt5::GuiPrivate - Qt5::Quick - Qt5::Concurrent - Qt5::DBus - - KF5::KIOCore - KF5::KIOFileWidgets - KF5::KIOWidgets - - MeuiKit -) - -install(TARGETS ${PROJECT_NAME} RUNTIME DESTINATION /usr/bin) +install(TARGETS cutefish-filemanager RUNTIME DESTINATION /usr/bin) install(FILES cutefish-filemanager.desktop DESTINATION "/usr/share/applications") install(FILES ${QM_FILES} DESTINATION /usr/share/cutefish-filemanager/translations) diff --git a/README.md b/README.md index e967892..f94fa4b 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,14 @@ Cutefish File Manager ## Dependencies +### Ubuntu + +``` +sudo apt install libkf5solid-dev +``` + +### ArchLinux + ```shell sudo pacman -S extra-cmake-modules qt5-base qt5-quickcontrols2 taglib kio ``` @@ -25,4 +33,4 @@ sudo make install ## License -This project has been licensed by GPLv3. \ No newline at end of file +This project has been licensed by GPLv3. diff --git a/cutefish-filemanager.desktop b/cutefish-filemanager.desktop index cfaa980..814e7ea 100644 --- a/cutefish-filemanager.desktop +++ b/cutefish-filemanager.desktop @@ -1,11 +1,11 @@ [Desktop Entry] Type=Application Name=File Manager -Name[zh_CN]=文件管理器 +Name[zh_CN]=文件管理 GenericName=File Manager Comment=Cutefish File Manager Exec=cutefish-filemanager %U MimeType=inode/directory; Icon=file-system-manager Categories=FileManager;Utility;Core;Qt; -StartupNotify=true +StartupNotify=true \ No newline at end of file diff --git a/debian/control b/debian/control index d24b1b8..28f0b33 100644 --- a/debian/control +++ b/debian/control @@ -6,6 +6,7 @@ Build-Depends: cmake, debhelper (>= 9), extra-cmake-modules, libkf5kio-dev, + libkf5solid-dev, qtbase5-dev, qtbase5-private-dev, qtdeclarative5-dev, diff --git a/src/desktop/desktopsettings.cpp b/desktop/desktopsettings.cpp similarity index 72% rename from src/desktop/desktopsettings.cpp rename to desktop/desktopsettings.cpp index 293eb45..3298c39 100644 --- a/src/desktop/desktopsettings.cpp +++ b/desktop/desktopsettings.cpp @@ -1,3 +1,22 @@ +/* + * Copyright (C) 2021 CutefishOS Team. + * + * Author: revenmartin + * + * 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 3 of the License, or + * 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, see . + */ + #include "desktopsettings.h" #include diff --git a/src/desktop/desktopsettings.h b/desktop/desktopsettings.h similarity index 58% rename from src/desktop/desktopsettings.h rename to desktop/desktopsettings.h index 259d750..e7f3d10 100644 --- a/src/desktop/desktopsettings.h +++ b/desktop/desktopsettings.h @@ -1,3 +1,22 @@ +/* + * Copyright (C) 2021 CutefishOS Team. + * + * Author: revenmartin + * + * 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 3 of the License, or + * 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, see . + */ + #ifndef SETTINGS_H #define SETTINGS_H diff --git a/src/desktop/desktopview.cpp b/desktop/desktopview.cpp similarity index 65% rename from src/desktop/desktopview.cpp rename to desktop/desktopview.cpp index 79221a4..9e1240a 100644 --- a/src/desktop/desktopview.cpp +++ b/desktop/desktopview.cpp @@ -1,3 +1,22 @@ +/* + * Copyright (C) 2021 CutefishOS Team. + * + * Author: revenmartin + * + * 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 3 of the License, or + * 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, see . + */ + #include "desktopview.h" #include @@ -15,7 +34,6 @@ DesktopView::DesktopView(QQuickView *parent) m_screenRect = qApp->primaryScreen()->geometry(); m_screenAvailableRect = qApp->primaryScreen()->availableGeometry(); - // setFlags(Qt::Window | Qt::FramelessWindowHint); setTitle(tr("Desktop")); KWindowSystem::setType(winId(), NET::Desktop); @@ -25,7 +43,7 @@ DesktopView::DesktopView(QQuickView *parent) setScreen(qApp->primaryScreen()); setResizeMode(QQuickView::SizeRootObjectToView); - setSource(QStringLiteral("qrc:/qml/Desktop/Desktop.qml")); + setSource(QStringLiteral("qrc:/qml/Desktop/main.qml")); onGeometryChanged(); diff --git a/desktop/desktopview.h b/desktop/desktopview.h new file mode 100644 index 0000000..ee6ba55 --- /dev/null +++ b/desktop/desktopview.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2021 CutefishOS Team. + * + * Author: revenmartin + * + * 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 3 of the License, or + * 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, see . + */ + +#ifndef DESKTOPVIEW_H +#define DESKTOPVIEW_H + +#include + +class DesktopView : public QQuickView +{ + Q_OBJECT + Q_PROPERTY(QRect screenRect READ screenRect NOTIFY screenRectChanged) + Q_PROPERTY(QRect screenAvailableRect READ screenAvailableRect NOTIFY screenAvailableGeometryChanged) + +public: + explicit DesktopView(QQuickView *parent = nullptr); + + QRect screenRect(); + QRect screenAvailableRect(); + +signals: + void screenRectChanged(); + void screenAvailableGeometryChanged(); + +private slots: + void onGeometryChanged(); + void onAvailableGeometryChanged(const QRect &geometry); + +private: + QRect m_screenRect; + QRect m_screenAvailableRect; +}; + +#endif // DESKTOPVIEW_H diff --git a/desktopiconprovider.cpp b/desktopiconprovider.cpp new file mode 100644 index 0000000..12fced0 --- /dev/null +++ b/desktopiconprovider.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2021 CutefishOS Team. + * + * Author: revenmartin + * + * 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 3 of the License, or + * 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, see . + */ + +#include "desktopiconprovider.h" +#include + +DesktopIconProvider::DesktopIconProvider() + : QQuickImageProvider(QQuickImageProvider::Pixmap) +{ + +} + +QPixmap DesktopIconProvider::requestPixmap(const QString &id, QSize *realSize, + const QSize &requestedSize) +{ + // Sanitize requested size + QSize size(requestedSize); + if (size.width() < 1) + size.setWidth(1); + if (size.height() < 1) + size.setHeight(1); + + // Return real size + if (realSize) + *realSize = size; + + // Is it a path? + if (id.startsWith(QLatin1Char('/'))) + return QPixmap(id).scaled(size); + + // Return icon from theme or fallback to a generic icon + QIcon icon = QIcon::fromTheme(id); + if (icon.isNull()) + icon = QIcon::fromTheme(QLatin1String("application-x-desktop")); + + return icon.pixmap(size); +} diff --git a/desktopiconprovider.h b/desktopiconprovider.h new file mode 100644 index 0000000..c3c042b --- /dev/null +++ b/desktopiconprovider.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2021 CutefishOS Team. + * + * Author: revenmartin + * + * 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 3 of the License, or + * 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, see . + */ + +#ifndef DESKTOPICONPROVIDER_H +#define DESKTOPICONPROVIDER_H + +#include + +class DesktopIconProvider : public QQuickImageProvider +{ +public: + DesktopIconProvider(); + + QPixmap requestPixmap(const QString &id, QSize *realSize, const QSize &requestedSize); +}; + +#endif // DESKTOPICONPROVIDER_H diff --git a/src/dialogs/propertiesdialog.cpp b/dialogs/propertiesdialog.cpp similarity index 57% rename from src/dialogs/propertiesdialog.cpp rename to dialogs/propertiesdialog.cpp index 2583932..1a8b656 100644 --- a/src/dialogs/propertiesdialog.cpp +++ b/dialogs/propertiesdialog.cpp @@ -1,5 +1,27 @@ +/* + * Copyright (C) 2021 CutefishOS Team. + * + * Author: revenmartin + * + * 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 3 of the License, or + * 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, see . + */ + #include "propertiesdialog.h" -#include "../iconthemeprovider.h" +#include "../desktopiconprovider.h" + +#include +#include #include #include @@ -9,6 +31,19 @@ #include #include +inline QString concatPaths(const QString &path1, const QString &path2) +{ + Q_ASSERT(!path2.startsWith(QLatin1Char('/'))); + + if (path1.isEmpty()) { + return path2; + } else if (!path1.endsWith(QLatin1Char('/'))) { + return path1 + QLatin1Char('/') + path2; + } else { + return path1 + path2; + } +} + PropertiesDialog::PropertiesDialog(const KFileItem &item, QObject *parent) : QObject(parent) { @@ -40,16 +75,16 @@ void PropertiesDialog::showDialog(const KFileItem &item) { PropertiesDialog *dlg = new PropertiesDialog(item); QQmlApplicationEngine *engine = new QQmlApplicationEngine; - engine->addImageProvider(QStringLiteral("icontheme"), new IconThemeProvider()); + engine->addImageProvider(QStringLiteral("icontheme"), new DesktopIconProvider()); engine->rootContext()->setContextProperty("main", dlg); engine->load(QUrl("qrc:/qml/Dialogs/PropertiesDialog.qml")); } void PropertiesDialog::showDialog(const KFileItemList &items) -{ +{ PropertiesDialog *dlg = new PropertiesDialog(items); QQmlApplicationEngine *engine = new QQmlApplicationEngine; - engine->addImageProvider(QStringLiteral("icontheme"), new IconThemeProvider()); + engine->addImageProvider(QStringLiteral("icontheme"), new DesktopIconProvider()); engine->rootContext()->setContextProperty("main", dlg); engine->load(QUrl("qrc:/qml/Dialogs/PropertiesDialog.qml")); } @@ -59,6 +94,11 @@ bool PropertiesDialog::multiple() const return m_multiple; } +bool PropertiesDialog::isWritable() const +{ + return m_items.first().isWritable(); +} + QString PropertiesDialog::location() const { return m_location; @@ -99,6 +139,46 @@ QString PropertiesDialog::accessedTime() const return m_accessedTime; } +KFileItemList PropertiesDialog::items() const +{ + return m_items; +} + +void PropertiesDialog::accept(const QString &text) +{ + KFileItemList list = items(); + + if (list.size() == 1) { + KFileItem item = list.first(); + + QString n = text; + while (!n.isEmpty() && n[n.length() - 1].isSpace()) + n.chop(1); + + if (n.isEmpty()) + return; + + QString newFileName = KIO::encodeFileName(n); + + if (fileName() != newFileName) { + QUrl newUrl; + + if (!location().isEmpty()) { + newUrl = location(); + newUrl.setPath(concatPaths(newUrl.path(), newFileName)); + newUrl.setScheme(item.url().scheme()); + + auto job = KIO::move(item.url(), newUrl, KIO::HideProgressInfo); + job->start(); + } + } + } +} + +void PropertiesDialog::reject() +{ +} + void PropertiesDialog::init() { m_multiple = m_items.count() > 1; @@ -122,13 +202,11 @@ void PropertiesDialog::init() m_size = KIO::convertSize(m_items.first().size()); m_location = QFileInfo(m_items.first().localPath()).dir().path(); - qDebug() << m_items.first().mimetype() << " ???"; - m_creationTime = item.time(KFileItem::CreationTime).toString(); m_modifiedTime = item.time(KFileItem::ModificationTime).toString(); m_accessedTime = item.time(KFileItem::AccessTime).toString(); } else { - m_fileName = QString("%1 files").arg(m_items.count()); + m_fileName = tr("%1 files").arg(m_items.count()); m_location = QFileInfo(m_items.first().localPath()).dir().path(); } } diff --git a/src/dialogs/propertiesdialog.h b/dialogs/propertiesdialog.h similarity index 67% rename from src/dialogs/propertiesdialog.h rename to dialogs/propertiesdialog.h index 95d0288..9cdc65c 100644 --- a/src/dialogs/propertiesdialog.h +++ b/dialogs/propertiesdialog.h @@ -1,3 +1,22 @@ +/* + * Copyright (C) 2021 CutefishOS Team. + * + * Author: revenmartin + * + * 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 3 of the License, or + * 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, see . + */ + #ifndef PROPERTIESDIALOG_H #define PROPERTIESDIALOG_H @@ -19,6 +38,7 @@ class PropertiesDialog : public QObject Q_PROPERTY(QString modifiedTime READ modifiedTime CONSTANT) Q_PROPERTY(QString accessedTime READ accessedTime CONSTANT) Q_PROPERTY(bool multiple READ multiple CONSTANT) + Q_PROPERTY(bool isWritable READ isWritable CONSTANT) public: explicit PropertiesDialog(const KFileItem &item, QObject *parent = nullptr); @@ -30,6 +50,7 @@ public: static void showDialog(const KFileItemList &items); bool multiple() const; + bool isWritable() const; QString location() const; QString fileName() const; @@ -41,6 +62,11 @@ public: QString modifiedTime() const; QString accessedTime() const; + KFileItemList items() const; + + Q_INVOKABLE void accept(const QString &text); + Q_INVOKABLE void reject(); + signals: void fileNameChanged(); void iconNameChanged(); diff --git a/helper/pathhistory.cpp b/helper/pathhistory.cpp new file mode 100644 index 0000000..377611b --- /dev/null +++ b/helper/pathhistory.cpp @@ -0,0 +1,34 @@ +#include "pathhistory.h" +#include + +PathHistory::PathHistory(QObject *parent) + : QObject(parent) +{ + +} + +void PathHistory::append(const QUrl &path) +{ + m_prevHistory.append(path); +} + +QUrl PathHistory::posteriorPath() +{ + if (m_postHistory.isEmpty()) + return QUrl(); + + return m_postHistory.takeLast(); +} + +QUrl PathHistory::previousPath() +{ + if (m_prevHistory.isEmpty()) + return QUrl(); + + if (m_prevHistory.length() < 2) + return m_prevHistory.at(0); + + m_postHistory.append(m_prevHistory.takeLast()); + return m_prevHistory.takeLast(); +} + diff --git a/helper/pathhistory.h b/helper/pathhistory.h new file mode 100644 index 0000000..c6297d4 --- /dev/null +++ b/helper/pathhistory.h @@ -0,0 +1,23 @@ +#ifndef PATHHISTORY_H +#define PATHHISTORY_H + +#include + +class PathHistory : public QObject +{ + Q_OBJECT + +public: + explicit PathHistory(QObject *parent = nullptr); + + void append(const QUrl &path); + + QUrl posteriorPath(); + QUrl previousPath(); + +private: + QVector m_prevHistory; + QVector m_postHistory; +}; + +#endif // PATHHISTORY_H diff --git a/helper/thumbnailer.cpp b/helper/thumbnailer.cpp new file mode 100644 index 0000000..1d18adf --- /dev/null +++ b/helper/thumbnailer.cpp @@ -0,0 +1,37 @@ +#include "thumbnailer.h" + +#include + +#include +#include + +QQuickImageResponse *Thumbnailer::requestImageResponse(const QString &id, const QSize &requestedSize) +{ + AsyncImageResponse *response = new AsyncImageResponse(id, requestedSize); + return response; +} + +AsyncImageResponse::AsyncImageResponse(const QString &id, const QSize &requestedSize) + : m_id(id) + , m_requestedSize(requestedSize) +{ + QStringList plugins = KIO::PreviewJob::defaultPlugins(); + auto job = new KIO::PreviewJob(KFileItemList() << KFileItem(QUrl::fromUserInput(id)), requestedSize, &plugins); + + connect(job, &KIO::PreviewJob::gotPreview, [this](KFileItem, QPixmap pixmap) { + m_image = pixmap.toImage(); + emit this->finished(); + }); + + connect(job, &KIO::PreviewJob::failed, [this](KFileItem) { + emit this->cancel(); + emit this->finished(); + }); + + job->start(); +} + +QQuickTextureFactory *AsyncImageResponse::textureFactory() const +{ + return QQuickTextureFactory::textureFactoryForImage(m_image); +} diff --git a/helper/thumbnailer.h b/helper/thumbnailer.h new file mode 100644 index 0000000..5ce5a63 --- /dev/null +++ b/helper/thumbnailer.h @@ -0,0 +1,25 @@ +#ifndef THUMBNAILER_H +#define THUMBNAILER_H + +#include +#include + +class AsyncImageResponse : public QQuickImageResponse +{ +public: + AsyncImageResponse(const QString &id, const QSize &requestedSize); + QQuickTextureFactory *textureFactory() const override; + +private: + QString m_id; + QSize m_requestedSize; + QImage m_image; +}; + +class Thumbnailer : public QQuickAsyncImageProvider +{ +public: + QQuickImageResponse *requestImageResponse(const QString &id, const QSize &requestedSize) override; +}; + +#endif // THUMBNAILER_H diff --git a/images/folder-document.svg b/images/folder-document.svg index 3e8c5af..fbdae6b 100755 --- a/images/folder-document.svg +++ b/images/folder-document.svg @@ -1,6 +1,120 @@ - - - - - + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main.cpp b/main.cpp similarity index 70% rename from src/main.cpp rename to main.cpp index 6f2a671..3b6b587 100644 --- a/src/main.cpp +++ b/main.cpp @@ -1,7 +1,7 @@ /* * Copyright (C) 2021 CutefishOS Team. * - * Author: rekols + * Author: revenmartin * * 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 @@ -18,33 +18,23 @@ */ #include +#include #include + #include #include -#include -#include - -#include "fmlist.h" -#include "fm.h" -#include "basemodel.h" -#include "baselist.h" -#include "handy.h" -#include "placeslist.h" -#include "pathlist.h" +#include "model/placesmodel.h" +#include "model/foldermodel.h" +#include "model/pathbarmodel.h" +#include "widgets/rubberband.h" +#include "widgets/itemviewadapter.h" #include "desktop/desktopsettings.h" #include "desktop/desktopview.h" -#include "rubberband.h" - -#include "lib/foldermodel.h" -#include "lib/placesmodel.h" -#include "lib/itemviewadapter.h" -#include "lib/positioner.h" +#include "helper/thumbnailer.h" int main(int argc, char *argv[]) { - const char *uri = "Cutefish.FileManager"; - QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QApplication app(argc, argv); @@ -62,6 +52,16 @@ int main(int argc, char *argv[]) } } + // Register QML Type. + const char *uri = "Cutefish.FileManager"; + qmlRegisterType(uri, 1, 0, "PlacesModel"); + qmlRegisterType(uri, 1, 0, "FolderModel"); + qmlRegisterType(uri, 1, 0, "PathBarModel"); + qmlRegisterType(uri, 1, 0, "RubberBand"); + qmlRegisterType(uri, 1, 0, "ItemViewAdapter"); + qmlRegisterType(uri, 1, 0, "DesktopSettings"); + qmlRegisterAnonymousType(uri, 1); + QCommandLineParser parser; parser.setApplicationDescription(QStringLiteral("File Manager")); parser.addHelpOption(); @@ -70,16 +70,6 @@ int main(int argc, char *argv[]) parser.addOption(desktopOption); parser.process(app); - qmlRegisterAnonymousType(uri, 1); - qmlRegisterType(uri, 1, 0, "DesktopSettings"); - qmlRegisterType(uri, 1, 0, "RubberBand"); - - qmlRegisterType(uri, 1, 0, "FolderModel"); - qmlRegisterType(uri, 1, 0, "ItemViewAdapter"); - qmlRegisterType(uri, 1, 0, "Positioner"); - - qmlRegisterType(uri, 1, 0, "PlacesModel"); - if (parser.isSet(desktopOption)) { DesktopView view; view.show(); @@ -87,24 +77,6 @@ int main(int argc, char *argv[]) return app.exec(); } - qmlRegisterAnonymousType(uri, 1); // ABSTRACT BASE LIST - qmlRegisterType(uri, 1, 0, "BaseModel"); // BASE MODEL - qmlRegisterType(uri, 1, 0, "PlacesList"); - qmlRegisterType(uri, 1, 0, "PathList"); - - qmlRegisterType(uri, 1, 0, "FMList"); - qmlRegisterSingletonType(uri, 1, 0, "FM", [](QQmlEngine *engine, QJSEngine *scriptEngine) -> QObject * { - Q_UNUSED(engine) - Q_UNUSED(scriptEngine) - return new FMStatic; - }); - - qmlRegisterSingletonType(uri, 1, 0, "Handy", [](QQmlEngine *engine, QJSEngine *scriptEngine) -> QObject * { - Q_UNUSED(engine) - Q_UNUSED(scriptEngine) - return new Handy; - }); - QQmlApplicationEngine engine; const QUrl url(QStringLiteral("qrc:/qml/main.qml")); QObject::connect(&engine, &QQmlApplicationEngine::objectCreated, @@ -113,6 +85,7 @@ int main(int argc, char *argv[]) QCoreApplication::exit(-1); }, Qt::QueuedConnection); engine.load(url); + engine.addImageProvider("thumbnailer", new Thumbnailer()); return app.exec(); } diff --git a/model/dirlister.cpp b/model/dirlister.cpp new file mode 100644 index 0000000..d8ac4a7 --- /dev/null +++ b/model/dirlister.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2021 CutefishOS Team. + * + * Author: revenmartin + * + * 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 3 of the License, or + * 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, see . + */ + +#include "dirlister.h" + +DirLister::DirLister(QObject *parent) + : KDirLister(parent) +{ +} + +DirLister::~DirLister() +{ +} + +void DirLister::handleError(KIO::Job *job) +{ + if (!autoErrorHandlingEnabled()) { + emit error(job->errorString()); + return; + } + + KDirLister::handleError(job); +} diff --git a/model/dirlister.h b/model/dirlister.h new file mode 100644 index 0000000..3b711ca --- /dev/null +++ b/model/dirlister.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2021 CutefishOS Team. + * + * Author: revenmartin + * + * 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 3 of the License, or + * 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, see . + */ + +#ifndef DIRLISTER_H +#define DIRLISTER_H + +#include +#include + +class DirLister : public KDirLister +{ + Q_OBJECT + +public: + explicit DirLister(QObject *parent = nullptr); + ~DirLister() override; + +Q_SIGNALS: + void error(const QString &string); + +protected: + void handleError(KIO::Job *job) override; +}; + +#endif // DIRLISTER_H diff --git a/model/foldermodel.cpp b/model/foldermodel.cpp new file mode 100644 index 0000000..6849622 --- /dev/null +++ b/model/foldermodel.cpp @@ -0,0 +1,1126 @@ +#include "foldermodel.h" +#include "dirlister.h" + +#include "../dialogs/propertiesdialog.h" + +// Qt +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Qt Quick +#include + +// KIO +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +FolderModel::FolderModel(QObject *parent) + : QSortFilterProxyModel(parent) + , m_dirWatch(nullptr) + , m_sortMode(0) + , m_sortDesc(false) + , m_sortDirsFirst(true) + , m_complete(false) + , m_actionCollection(this) + , m_dragInProgress(false) + , m_viewAdapter(nullptr) +{ + DirLister *dirLister = new DirLister(this); + dirLister->setDelayedMimeTypes(true); + dirLister->setAutoErrorHandlingEnabled(false, nullptr); + + m_dirModel = new KDirModel(this); + m_dirModel->setDirLister(dirLister); + m_dirModel->setDropsAllowed(KDirModel::DropOnDirectory | KDirModel::DropOnLocalExecutable); + + m_selectionModel = new QItemSelectionModel(this, this); + connect(m_selectionModel, &QItemSelectionModel::selectionChanged, this, &FolderModel::selectionChanged); + + setSourceModel(m_dirModel); + setSortLocaleAware(true); + setFilterCaseSensitivity(Qt::CaseInsensitive); + setDynamicSortFilter(true); + + sort(m_sortMode, m_sortDesc ? Qt::DescendingOrder : Qt::AscendingOrder); + createActions(); + + connect(this, SIGNAL(rowsInserted(QModelIndex,int,int)), SIGNAL(statusTextChanged())); + connect(this, SIGNAL(rowsRemoved(QModelIndex,int,int)), SIGNAL(statusTextChanged())); + connect(this, SIGNAL(modelReset()), SIGNAL(statusTextChanged())); +} + +FolderModel::~FolderModel() +{ + +} + +void FolderModel::classBegin() +{ + +} + +void FolderModel::componentComplete() +{ + m_complete = true; + invalidate(); +} + +QHash FolderModel::roleNames() const +{ + return staticRoleNames(); +} + +QHash FolderModel::staticRoleNames() +{ + QHash roleNames; + roleNames[Qt::DisplayRole] = "display"; + roleNames[Qt::DecorationRole] = "decoration"; + roleNames[BlankRole] = "blank"; + roleNames[SelectedRole] = "selected"; + roleNames[IsDirRole] = "isDir"; + roleNames[UrlRole] = "url"; + roleNames[FileNameRole] = "fileName"; + roleNames[IconNameRole] = "iconName"; + roleNames[ThumbnailRole] = "thumbnail"; + return roleNames; +} + +QVariant FolderModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + + KFileItem item = itemForIndex(index); + + switch (role) { + case BlankRole: + return m_dragIndexes.contains(index); + case SelectedRole: + return m_selectionModel->isSelected(index); + case UrlRole: + return item.url(); + case FileNameRole: + return item.url().fileName(); + case IconNameRole: + return item.iconName(); + case ThumbnailRole: { + // Svg Image + if (item.mimetype() == "image/svg" || + item.mimetype() == "image/svg+xml") { + return item.url(); + } + + // Support + if (isSupportThumbnails(item.mimetype())) { + return "image://thumbnailer/" + item.url().toString(); + } + + return QVariant(); + } + default: + break; + } + + return QSortFilterProxyModel::data(index, role); +} + +KFileItem FolderModel::itemForIndex(const QModelIndex &index) const +{ + return m_dirModel->itemForIndex(mapToSource(index)); +} + +QList FolderModel::selectedUrls() const +{ + const auto indexes = m_selectionModel->selectedIndexes(); + + QList urls; + urls.reserve(indexes.count()); + + for (const QModelIndex &index : indexes) { + urls.append(itemForIndex(index).url()); + } + + return urls; +} + +QString FolderModel::url() const +{ + return m_url; +} + +void FolderModel::setUrl(const QString &url) +{ + const QUrl &resolvedNewUrl = resolve(url); + + // Refresh this directory. + if (url == m_url) { + m_dirModel->dirLister()->updateDirectory(resolvedNewUrl); + return; + } + + m_pathHistory.append(resolvedNewUrl); + + beginResetModel(); + m_url = url; + m_dirModel->dirLister()->openUrl(resolvedNewUrl); + clearDragImages(); + m_dragIndexes.clear(); + endResetModel(); + + emit urlChanged(); + emit resolvedUrlChanged(); + + if (m_dirWatch) { + delete m_dirWatch; + m_dirWatch = nullptr; + } + + if (resolvedNewUrl.isValid()) { + m_dirWatch = new KDirWatch(this); + m_dirWatch->addFile(resolvedNewUrl.toLocalFile() + QLatin1String("/.directory")); + } +} + +QUrl FolderModel::resolvedUrl() const +{ + return m_dirModel->dirLister()->url(); +} + +QUrl FolderModel::resolve(const QString &url) +{ + QUrl resolvedUrl; + + if (url.startsWith(QLatin1Char('~'))) { + resolvedUrl = QUrl::fromLocalFile(QDir::homePath()); + } else { + resolvedUrl = QUrl::fromUserInput(url); + } + + return resolvedUrl; +} + +FolderModel::Status FolderModel::status() const +{ + return m_status; +} + +void FolderModel::setStatus(FolderModel::Status status) +{ + if (m_status != status) { + m_status = status; + emit statusChanged(); + } +} + +int FolderModel::sortMode() const +{ + return m_sortMode; +} + +void FolderModel::setSortMode(int mode) +{ + if (m_sortMode != mode) { + m_sortMode = mode; + + if (mode == -1 /* Unsorted */) { + setDynamicSortFilter(false); + } else { + invalidateIfComplete(); + sort(m_sortMode, m_sortDesc ? Qt::DescendingOrder : Qt::AscendingOrder); + setDynamicSortFilter(true); + } + + emit sortModeChanged(); + } +} + +bool FolderModel::sortDirsFirst() const +{ + return m_sortDirsFirst; +} + +void FolderModel::setSortDirsFirst(bool enable) +{ + if (m_sortDirsFirst != enable) { + m_sortDirsFirst = enable; + + if (m_sortMode != -1 /* Unsorted */) { + invalidateIfComplete(); + sort(m_sortMode, m_sortDesc ? Qt::DescendingOrder : Qt::AscendingOrder); + } + + emit sortDirsFirstChanged(); + } +} + +QObject *FolderModel::viewAdapter() const +{ + return m_viewAdapter; +} + +void FolderModel::setViewAdapter(QObject *adapter) +{ + if (m_viewAdapter != adapter) { + ItemViewAdapter *viewAdapter = dynamic_cast(adapter); + m_viewAdapter = viewAdapter; + emit viewAdapterChanged(); + } +} + +bool FolderModel::dragging() const +{ + return m_dragInProgress; +} + +bool FolderModel::isDir(const QModelIndex &index, const KDirModel *dirModel) const +{ + KFileItem item = dirModel->itemForIndex(index); + if (item.isDir()) { + return true; + } + + return false; +} + +bool FolderModel::lessThan(const QModelIndex &left, const QModelIndex &right) const +{ + const KDirModel *dirModel = static_cast(sourceModel()); + + if (m_sortDirsFirst || left.column() == KDirModel::Size) { + bool leftIsDir = isDir(left, dirModel); + bool rightIsDir = isDir(right, dirModel); + + if (leftIsDir && !rightIsDir) { + return (sortOrder() == Qt::AscendingOrder); + } + + if (!leftIsDir && rightIsDir) { + return (sortOrder() == Qt::DescendingOrder); + } + } + + const KFileItem leftItem = dirModel->data(left, KDirModel::FileItemRole).value(); + const KFileItem rightItem = dirModel->data(right, KDirModel::FileItemRole).value(); + const int column = left.column(); + int result = 0; + + switch (column) { + case KDirModel::Size: { + if (isDir(left, dirModel) && isDir(right, dirModel)) { + const int leftChildCount = dirModel->data(left, KDirModel::ChildCountRole).toInt(); + const int rightChildCount = dirModel->data(right, KDirModel::ChildCountRole).toInt(); + if (leftChildCount < rightChildCount) + result = -1; + else if (leftChildCount > rightChildCount) + result = +1; + } else { + const KIO::filesize_t leftSize = leftItem.size(); + const KIO::filesize_t rightSize = rightItem.size(); + if (leftSize < rightSize) + result = -1; + else if (leftSize > rightSize) + result = +1; + } + + break; + } + case KDirModel::ModifiedTime: { + const long long leftTime = leftItem.entry().numberValue(KIO::UDSEntry::UDS_MODIFICATION_TIME, -1); + const long long rightTime = rightItem.entry().numberValue(KIO::UDSEntry::UDS_MODIFICATION_TIME, -1); + if (leftTime < rightTime) + result = -1; + else if (leftTime > rightTime) + result = +1; + + break; + } + case KDirModel::Type: + result = QString::compare(dirModel->data(left, Qt::DisplayRole).toString(), dirModel->data(right, Qt::DisplayRole).toString()); + break; + + default: + break; + } + + if (result != 0) + return result < 0; + + QCollator collator; + + result = collator.compare(leftItem.text(), rightItem.text()); + + if (result != 0) + return result < 0; + + result = collator.compare(leftItem.name(), rightItem.name()); + + if (result != 0) + return result < 0; + + return QString::compare(leftItem.url().url(), rightItem.url().url(), Qt::CaseSensitive); +} + +Qt::DropActions FolderModel::supportedDragActions() const +{ + return Qt::CopyAction | Qt::MoveAction | Qt::LinkAction; +} + +Qt::DropActions FolderModel::supportedDropActions() const +{ + return Qt::CopyAction | Qt::MoveAction | Qt::LinkAction; +} + +KFileItem FolderModel::rootItem() const +{ + return m_dirModel->dirLister()->rootItem(); +} + +QString FolderModel::statusText() +{ + if (m_selectionModel && m_selectionModel->hasSelection()) { + if (m_selectionModel->selectedIndexes().size() == 1) { + KFileItem item = itemForIndex(m_selectionModel->selectedIndexes().first()); + return item.name(); + } + + return tr("%1 selected").arg(m_selectionModel->selectedIndexes().size()); + } + + if (m_dirModel->rowCount() == 1) { + return tr("%1 item").arg(m_dirModel->rowCount()); + } else if (m_dirModel->rowCount() <= 0) + return ""; + + return tr("%1 items").arg(m_dirModel->rowCount()); +} + +QString FolderModel::homePath() const +{ + return QStandardPaths::writableLocation(QStandardPaths::HomeLocation); +} + +QString FolderModel::desktopPath() const +{ + return QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); +} + +QAction *FolderModel::action(const QString &name) const +{ + return m_actionCollection.action(name); +} + +void FolderModel::up() +{ + const QUrl &url = KIO::upUrl(resolvedUrl()); + + if (url.isValid()) { + setUrl(url.toString()); + } +} + +void FolderModel::goBack() +{ + QUrl url = m_pathHistory.previousPath(); + + if (url.isEmpty()) + url = resolvedUrl(); + + setUrl(url.toString()); +} + +void FolderModel::goForward() +{ + QUrl url = m_pathHistory.posteriorPath(); + + if (url.isEmpty()) + url = resolvedUrl(); + + setUrl(url.toString()); +} + +bool FolderModel::supportSetAsWallpaper(const QString &mimeType) +{ + if (mimeType == "image/jpeg" || mimeType == "image/png") + return true; + + return false; +} + +int FolderModel::fileExtensionBoundary(int row) +{ + const QModelIndex idx = index(row, 0); + const QString &name = data(idx, Qt::DisplayRole).toString(); + + int boundary = name.length(); + + if (data(idx, IsDirRole).toBool()) { + return boundary; + } + + QMimeDatabase db; + const QString &ext = db.suffixForFileName(name); + + if (ext.isEmpty()) { + boundary = name.lastIndexOf(QLatin1Char('.')); + + if (boundary < 1) { + boundary = name.length(); + } + } else { + boundary -= ext.length() + 1; + } + + return boundary; +} + +bool FolderModel::hasSelection() const +{ + return m_selectionModel->hasSelection(); +} + +bool FolderModel::isSelected(int row) const +{ + if (row < 0) + return false; + + return m_selectionModel->isSelected(index(row, 0)); +} + +bool FolderModel::isBlank(int row) const +{ + if (row < 0) { + return true; + } + + return data(index(row, 0), BlankRole).toBool(); +} + +void FolderModel::setSelected(int row) +{ + if (row < 0) + return; + + m_selectionModel->select(index(row, 0), QItemSelectionModel::Select); +} + +void FolderModel::selectAll() +{ + setRangeSelected(0, rowCount() - 1); +} + +void FolderModel::toggleSelected(int row) +{ + if (row < 0) + return; + + m_selectionModel->select(index(row, 0), QItemSelectionModel::Toggle); +} + +void FolderModel::setRangeSelected(int anchor, int to) +{ + if (anchor < 0 || to < 0) { + return; + } + + QItemSelection selection(index(anchor, 0), index(to, 0)); + m_selectionModel->select(selection, QItemSelectionModel::ClearAndSelect); +} + +void FolderModel::updateSelection(const QVariantList &rows, bool toggle) +{ + QItemSelection newSelection; + + int iRow = -1; + + foreach (const QVariant &row, rows) { + iRow = row.toInt(); + + if (iRow < 0) { + return; + } + + const QModelIndex &idx = index(iRow, 0); + newSelection.select(idx, idx); + } + + if (toggle) { + QItemSelection pinnedSelection = m_pinnedSelection; + pinnedSelection.merge(newSelection, QItemSelectionModel::Toggle); + m_selectionModel->select(pinnedSelection, QItemSelectionModel::ClearAndSelect); + } else { + m_selectionModel->select(newSelection, QItemSelectionModel::ClearAndSelect); + } +} + +void FolderModel::clearSelection() +{ + if (m_selectionModel->hasSelection()) + m_selectionModel->clear(); +} + +void FolderModel::pinSelection() +{ + m_pinnedSelection = m_selectionModel->selection(); +} + +void FolderModel::unpinSelection() +{ + m_pinnedSelection = QItemSelection(); +} + +void FolderModel::rename(int row, const QString &name) +{ + if (row < 0) + return; + + QModelIndex idx = index(row, 0); + m_dirModel->setData(mapToSource(idx), name, Qt::EditRole); +} + +void FolderModel::copy() +{ + if (!m_selectionModel->hasSelection()) + return; + + if (QAction *action = m_actionCollection.action("copy")) + if (!action->isEnabled()) + return; + + QMimeData *mimeData = QSortFilterProxyModel::mimeData(m_selectionModel->selectedIndexes()); + QApplication::clipboard()->setMimeData(mimeData); +} + +void FolderModel::paste() +{ + if (QAction *action = m_actionCollection.action("paste")) + if (!action->isEnabled()) + return; + + KIO::paste(QApplication::clipboard()->mimeData(), m_dirModel->dirLister()->url()); +} + +void FolderModel::cut() +{ + if (!m_selectionModel->hasSelection()) + return; + + if (QAction *action = m_actionCollection.action("cut")) + if (!action->isEnabled()) + return; + + QMimeData *mimeData = QSortFilterProxyModel::mimeData(m_selectionModel->selectedIndexes()); + KIO::setClipboardDataCut(mimeData, true); + QApplication::clipboard()->setMimeData(mimeData); +} + +void FolderModel::openSelected() +{ + if (!m_selectionModel->hasSelection()) + return; + + const QList urls = selectedUrls(); + + if (urls.size() == 1 && KFileItem(urls.first()).isDir()) { + setUrl(urls.first().toLocalFile()); + return; + } + + for (const QUrl &url : urls) { + (void)new KRun(url, nullptr); + } +} + +void FolderModel::deleteSelected() +{ + if (!m_selectionModel->hasSelection()) { + return; + } + + if (QAction *action = m_actionCollection.action(QStringLiteral("del"))) { + if (!action->isEnabled()) { + return; + } + } + + const QList urls = selectedUrls(); + KIO::JobUiDelegate uiDelegate; + + if (uiDelegate.askDeleteConfirmation(urls, KIO::JobUiDelegate::Delete, KIO::JobUiDelegate::DefaultConfirmation)) { + KIO::Job *job = KIO::del(urls); + job->uiDelegate()->setAutoErrorHandlingEnabled(true); + } +} + +void FolderModel::moveSelectedToTrash() +{ + if (!m_selectionModel->hasSelection()) { + return; + } + + if (QAction *action = m_actionCollection.action(QStringLiteral("trash"))) { + if (!action->isEnabled()) { + return; + } + } + + const QList urls = selectedUrls(); + KIO::JobUiDelegate uiDelegate; + + if (uiDelegate.askDeleteConfirmation(urls, KIO::JobUiDelegate::Trash, KIO::JobUiDelegate::DefaultConfirmation)) { + KIO::Job *job = KIO::trash(urls); + job->uiDelegate()->setAutoErrorHandlingEnabled(true); + KIO::FileUndoManager::self()->recordJob(KIO::FileUndoManager::Trash, urls, QUrl(QStringLiteral("trash:/")), job); + } +} + +void FolderModel::emptyTrash() +{ + KIO::JobUiDelegate uiDelegate; + uiDelegate.setWindow(QApplication::desktop()); + + if (uiDelegate.askDeleteConfirmation(QList(), KIO::JobUiDelegate::EmptyTrash, KIO::JobUiDelegate::DefaultConfirmation)) { + KIO::Job *job = KIO::emptyTrash(); + job->uiDelegate()->setAutoErrorHandlingEnabled(true); + } +} + +void FolderModel::setDragHotSpotScrollOffset(int x, int y) +{ + m_dragHotSpotScrollOffset.setX(x); + m_dragHotSpotScrollOffset.setY(y); +} + +void FolderModel::addItemDragImage(int row, int x, int y, int width, int height, const QVariant &image) +{ + if (row < 0) + return; + + delete m_dragImages.take(row); + + DragImage *dragImage = new DragImage(); + dragImage->row = row; + dragImage->rect = QRect(x, y, width, height); + dragImage->image = image.value(); + dragImage->blank = false; + + m_dragImages.insert(row, dragImage); +} + +void FolderModel::clearDragImages() +{ + qDeleteAll(m_dragImages); + m_dragImages.clear(); +} + +void FolderModel::dragSelected(int x, int y) +{ + if (m_dragInProgress) + return; + + m_dragInProgress = true; + emit draggingChanged(); + + QMetaObject::invokeMethod(this, "dragSelectedInternal", Qt::QueuedConnection, Q_ARG(int, x), Q_ARG(int, y)); +} + +void FolderModel::setWallpaperSelected() +{ + if (!m_selectionModel) + return; + + QUrl url = selectedUrls().first(); + + if (!url.isLocalFile()) + return; + + QDBusInterface iface("org.cutefish.Settings", "/Theme", + "org.cutefish.Theme", + QDBusConnection::sessionBus(), nullptr); + if (iface.isValid()) + iface.call("setWallpaper", url.toLocalFile()); +} + +void FolderModel::openContextMenu(QQuickItem *visualParent, Qt::KeyboardModifiers modifiers) +{ + Q_UNUSED(modifiers); + + updateActions(); + + const QModelIndexList indexes = m_selectionModel->selectedIndexes(); + QMenu *menu = new QMenu; + + if (indexes.isEmpty()) { + // Open folder menu. + QAction *selectAll = new QAction(tr("Select All"), this); + connect(selectAll, &QAction::triggered, this, &FolderModel::selectAll); + + menu->addAction(m_actionCollection.action("newFolder")); + menu->addSeparator(); + menu->addAction(m_actionCollection.action("paste")); + menu->addAction(selectAll); + if (m_actionCollection.action("terminal")->isVisible()) { + menu->addSeparator(); + menu->addAction(m_actionCollection.action("terminal")); + } + menu->addSeparator(); + menu->addAction(m_actionCollection.action("emptyTrash")); + menu->addAction(m_actionCollection.action("properties")); + } else { + // Open the items menu. + menu->addAction(m_actionCollection.action("open")); + menu->addAction(m_actionCollection.action("cut")); + menu->addAction(m_actionCollection.action("copy")); + menu->addAction(m_actionCollection.action("trash")); + menu->addAction(m_actionCollection.action("del")); + menu->addAction(m_actionCollection.action("rename")); + + if (m_actionCollection.action("terminal")->isVisible()) { + menu->addSeparator(); + menu->addAction(m_actionCollection.action("terminal")); + } + + menu->addAction(m_actionCollection.action("wallpaper")); + menu->addSeparator(); + menu->addAction(m_actionCollection.action("properties")); + } + + QPoint position; + if (visualParent) { + position = visualParent->mapToGlobal(QPointF(0, visualParent->height())).toPoint(); + } else { + position = QCursor::pos(); + } + + menu->installEventFilter(this); + menu->setAttribute(Qt::WA_TranslucentBackground); + menu->winId(); + menu->popup(position); + connect(menu, &QMenu::aboutToHide, [menu]() { + menu->deleteLater(); + }); +} + +void FolderModel::openPropertiesDialog() +{ + const QModelIndexList indexes = m_selectionModel->selectedIndexes(); + + if (indexes.isEmpty()) { + PropertiesDialog::showDialog(QUrl::fromLocalFile(url())); + return; + } + + KFileItemList items; + items.reserve(indexes.count()); + for (const QModelIndex &index : indexes) { + KFileItem item = itemForIndex(index); + if (!item.isNull()) { + items.append(item); + } + } + + PropertiesDialog::showDialog(items); +} + +void FolderModel::openInTerminal() +{ + qDebug() << "TODO"; +} + +void FolderModel::selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) +{ + QModelIndexList indices = selected.indexes(); + indices.append(deselected.indexes()); + + QVector roles; + roles.append(SelectedRole); + + foreach (const QModelIndex &index, indices) { + emit dataChanged(index, index, roles); + } + + if (!m_selectionModel->hasSelection()) { + clearDragImages(); + } else { + foreach (const QModelIndex &idx, deselected.indexes()) { + delete m_dragImages.take(idx.row()); + } + } + + updateActions(); + + // 选中状态信息 + emit statusTextChanged(); +} + +void FolderModel::dragSelectedInternal(int x, int y) +{ + if (!m_viewAdapter || !m_selectionModel->hasSelection()) { + m_dragInProgress = false; + emit draggingChanged(); + return; + } + + QQuickItem *item = qobject_cast(m_viewAdapter->adapterView()); + QDrag *drag = new QDrag(item); + addDragImage(drag, x, y); + + m_dragIndexes = m_selectionModel->selectedIndexes(); + std::sort(m_dragIndexes.begin(), m_dragIndexes.end()); + + // TODO: Optimize to emit contiguous groups. + emit dataChanged(m_dragIndexes.first(), m_dragIndexes.last(), QVector() << BlankRole); + + QModelIndexList sourceDragIndexes; + sourceDragIndexes.reserve(m_dragIndexes.count()); + foreach (const QModelIndex &index, m_dragIndexes) { + sourceDragIndexes.append(mapToSource(index)); + } + + drag->setMimeData(m_dirModel->mimeData(sourceDragIndexes)); + + // Due to spring-loading (aka auto-expand), the URL might change + // while the drag is in-flight - in that case we don't want to + // unnecessarily emit dataChanged() for (possibly invalid) indices + // after it ends. + const QUrl currentUrl(m_dirModel->dirLister()->url()); + + item->grabMouse(); + drag->exec(supportedDragActions()); + item->ungrabMouse(); + + m_dragInProgress = false; + emit draggingChanged(); + + if (m_dirModel->dirLister()->url() == currentUrl) { + const QModelIndex first(m_dragIndexes.first()); + const QModelIndex last(m_dragIndexes.last()); + m_dragIndexes.clear(); + // TODO: Optimize to emit contiguous groups. + emit dataChanged(first, last, QVector() << BlankRole); + } +} + +bool FolderModel::isSupportThumbnails(const QString &mimeType) const +{ + const QStringList supportsMimetypes = {"image/bmp", "image/png", "image/gif", "image/jpeg", "image/web", + "application/pdf", "application/rtf", "application/doc", "application/odf"}; + + if (supportsMimetypes.contains(mimeType)) + return true; + + return false; +} + +void FolderModel::invalidateIfComplete() +{ + if (!m_complete) + return; + + invalidate(); +} + +void FolderModel::invalidateFilterIfComplete() +{ + if (!m_complete) + return; + + invalidateFilter(); +} + +void FolderModel::createActions() +{ + QAction *open = new QAction(tr("Open"), this); + connect(open, &QAction::triggered, this, &FolderModel::openSelected); + + QAction *cut = new QAction(tr("Cut"), this); + connect(cut, &QAction::triggered, this, &FolderModel::cut); + + QAction *copy = new QAction(tr("Copy"), this); + connect(copy, &QAction::triggered, this, &FolderModel::copy); + + QAction *paste = new QAction(tr("Paste"), this); + connect(paste, &QAction::triggered, this, &FolderModel::paste); + + QAction *newFolder = new QAction(tr("New Folder"), this); + + QAction *trash = new QAction(tr("Move To Trash"), this); + connect(trash, &QAction::triggered, this, &FolderModel::moveSelectedToTrash); + + QAction *emptyTrash = new QAction(tr("Empty Trash"), this); + connect(emptyTrash, &QAction::triggered, this, &FolderModel::emptyTrash); + + QAction *del = new QAction(tr("Delete"), this); + connect(del, &QAction::triggered, this, &FolderModel::deleteSelected); + + QAction *rename = new QAction(tr("Rename"), this); + connect(rename, &QAction::triggered, this, &FolderModel::requestRename); + + QAction *terminal = new QAction(tr("Open in Terminal"), this); + connect(terminal, &QAction::triggered, this, &FolderModel::openInTerminal); + + QAction *wallpaper = new QAction(tr("Set as Wallpaper"), this); + connect(wallpaper, &QAction::triggered, this, &FolderModel::setWallpaperSelected); + + QAction *properties = new QAction(tr("Properties"), this); + QObject::connect(properties, &QAction::triggered, this, &FolderModel::openPropertiesDialog); + + m_actionCollection.addAction(QStringLiteral("open"), open); + m_actionCollection.addAction(QStringLiteral("cut"), cut); + m_actionCollection.addAction(QStringLiteral("copy"), copy); + m_actionCollection.addAction(QStringLiteral("paste"), paste); + m_actionCollection.addAction(QStringLiteral("newFolder"), newFolder); + m_actionCollection.addAction(QStringLiteral("trash"), trash); + m_actionCollection.addAction(QStringLiteral("emptyTrash"), emptyTrash); + m_actionCollection.addAction(QStringLiteral("del"), del); + m_actionCollection.addAction(QStringLiteral("rename"), rename); + m_actionCollection.addAction(QStringLiteral("terminal"), terminal); + m_actionCollection.addAction(QStringLiteral("wallpaper"), wallpaper); + m_actionCollection.addAction(QStringLiteral("properties"), properties); +} + +void FolderModel::updateActions() +{ + const QModelIndexList indexes = m_selectionModel->selectedIndexes(); + + KFileItemList items; + QList urls; + bool hasRemoteFiles = false; + bool isTrashLink = false; + const bool isTrash = (resolvedUrl().scheme() == QLatin1String("trash")); + + if (indexes.isEmpty()) { + items << rootItem(); + } else { + items.reserve(indexes.count()); + urls.reserve(indexes.count()); + for (const QModelIndex &index : indexes) { + KFileItem item = itemForIndex(index); + if (!item.isNull()) { + hasRemoteFiles |= item.localPath().isEmpty(); + items.append(item); + urls.append(item.url()); + } + } + } + + KFileItemListProperties itemProperties(items); + // Check if we're showing the menu for the trash link + if (items.count() == 1 && items.at(0).isDesktopFile()) { + KDesktopFile file(items.at(0).localPath()); + if (file.hasLinkType() && file.readUrl() == QLatin1String("trash:/")) { + isTrashLink = true; + } + } + + if (QAction *newFolder = m_actionCollection.action(QStringLiteral("newFolder"))) { + newFolder->setVisible(!isTrash); + newFolder->setEnabled(rootItem().isWritable()); + } + + if (QAction *paste = m_actionCollection.action(QStringLiteral("paste"))) { + bool enable = false; + + QList urls = KUrlMimeData::urlsFromMimeData(QApplication::clipboard()->mimeData()); + + if (!urls.isEmpty() && rootItem().isWritable()) { + enable = rootItem().isWritable(); + } + + paste->setEnabled(enable); + } + + if (QAction *rename = m_actionCollection.action(QStringLiteral("rename"))) { + rename->setEnabled(itemProperties.supportsMoving()); + rename->setVisible(!isTrash); + } + + if (QAction *trash = m_actionCollection.action("trash")) { + trash->setEnabled(itemProperties.supportsMoving()); + trash->setVisible(!isTrash); + } + + if (QAction *emptyTrash = m_actionCollection.action("emptyTrash")) { + emptyTrash->setVisible(isTrash); + } + + if (QAction *del = m_actionCollection.action(QStringLiteral("del"))) { + del->setVisible(isTrash && itemProperties.supportsDeleting()); + } + + if (QAction *terminal = m_actionCollection.action("terminal")) { + terminal->setVisible(items.size() == 1 && items.first().isDir()); + } + + if (QAction *terminal = m_actionCollection.action("wallpaper")) { + terminal->setVisible(items.size() == 1 && supportSetAsWallpaper(items.first().mimetype())); + } + + if (QAction *properties = m_actionCollection.action("properties")) { + properties->setVisible(!isTrash); + } +} + +void FolderModel::addDragImage(QDrag *drag, int x, int y) +{ + if (!drag || m_dragImages.isEmpty()) + return; + + QRegion region; + + foreach (DragImage *image, m_dragImages) { + image->blank = isBlank(image->row); + image->rect.translate(-m_dragHotSpotScrollOffset.x(), -m_dragHotSpotScrollOffset.y()); + if (!image->blank && !image->image.isNull()) { + region = region.united(image->rect); + } + } + + QRect rect = region.boundingRect(); + QPoint offset = rect.topLeft(); + rect.translate(-offset.x(), -offset.y()); + + QImage dragImage(rect.size(), QImage::Format_RGBA8888); + dragImage.fill(Qt::transparent); + + QPainter painter(&dragImage); + QPoint pos; + + foreach (DragImage *image, m_dragImages) { + if (!image->blank && !image->image.isNull()) { + pos = image->rect.translated(-offset.x(), -offset.y()).topLeft(); + image->cursorOffset.setX(pos.x() - (x - offset.x())); + image->cursorOffset.setY(pos.y() - (y - offset.y())); + + painter.drawImage(pos, image->image); + } + + // FIXME HACK: Operate on copy. + image->rect.translate(m_dragHotSpotScrollOffset.x(), m_dragHotSpotScrollOffset.y()); + } + + drag->setPixmap(QPixmap::fromImage(dragImage)); + drag->setHotSpot(QPoint(x - offset.x(), y - offset.y())); +} diff --git a/model/foldermodel.h b/model/foldermodel.h new file mode 100644 index 0000000..5ed17db --- /dev/null +++ b/model/foldermodel.h @@ -0,0 +1,202 @@ +#ifndef FOLDERMODEL_H +#define FOLDERMODEL_H + +#include "../widgets/itemviewadapter.h" +#include "../helper/pathhistory.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +class QDrag; +class FolderModel : public QSortFilterProxyModel, public QQmlParserStatus +{ + Q_OBJECT + Q_INTERFACES(QQmlParserStatus) + Q_PROPERTY(QString url READ url WRITE setUrl NOTIFY urlChanged) + Q_PROPERTY(QUrl resolvedUrl READ resolvedUrl NOTIFY resolvedUrlChanged) + Q_PROPERTY(int sortMode READ sortMode WRITE setSortMode NOTIFY sortModeChanged) + Q_PROPERTY(bool sortDirsFirst READ sortDirsFirst WRITE setSortDirsFirst NOTIFY sortDirsFirstChanged) + Q_PROPERTY(bool dragging READ dragging NOTIFY draggingChanged) + Q_PROPERTY(QObject *viewAdapter READ viewAdapter WRITE setViewAdapter NOTIFY viewAdapterChanged) + Q_PROPERTY(QString statusText READ statusText NOTIFY statusTextChanged) + +public: + enum DataRole { + BlankRole = Qt::UserRole + 1, + SelectedRole, + IsDirRole, + UrlRole, + FileNameRole, + IconNameRole, + ThumbnailRole + }; + + enum FilterMode { + NoFilter = 0, + FilterShowMatches, + FilterHideMatches, + }; + + enum Status { + None, + Ready, + Listing, + Canceled, + }; + Q_ENUM(Status) + + struct DragImage { + int row; + QRect rect; + QPoint cursorOffset; + QImage image; + bool blank; + }; + + explicit FolderModel(QObject *parent = nullptr); + ~FolderModel() override; + + void classBegin() override; + void componentComplete() override; + + QHash roleNames() const override; + static QHash staticRoleNames(); + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + + KFileItem itemForIndex(const QModelIndex &index) const; + + QList selectedUrls() const; + + QString url() const; + void setUrl(const QString &url); + + QUrl resolvedUrl() const; + Q_INVOKABLE QUrl resolve(const QString &url); + + Status status() const; + void setStatus(Status status); + + int sortMode() const; + void setSortMode(int mode); + + bool sortDirsFirst() const; + void setSortDirsFirst(bool enable); + + QObject *viewAdapter() const; + void setViewAdapter(QObject *adapter); + + bool dragging() const; + + bool isDir(const QModelIndex &index, const KDirModel *dirModel) const; + bool lessThan(const QModelIndex &left, const QModelIndex &right) const override; + + Qt::DropActions supportedDragActions() const override; + Qt::DropActions supportedDropActions() const override; + + KFileItem rootItem() const; + + QString statusText(); + + Q_INVOKABLE QString homePath() const; + Q_INVOKABLE QString desktopPath() const; + Q_INVOKABLE QAction *action(const QString &name) const; + + Q_INVOKABLE void up(); + Q_INVOKABLE void goBack(); + Q_INVOKABLE void goForward(); + + Q_INVOKABLE bool supportSetAsWallpaper(const QString &mimeType); + Q_INVOKABLE int fileExtensionBoundary(int row); + + Q_INVOKABLE bool hasSelection() const; + Q_INVOKABLE bool isSelected(int row) const; + Q_INVOKABLE bool isBlank(int row) const; + Q_INVOKABLE void setSelected(int row); + Q_INVOKABLE void selectAll(); + Q_INVOKABLE void toggleSelected(int row); + Q_INVOKABLE void setRangeSelected(int anchor, int to); + Q_INVOKABLE void updateSelection(const QVariantList &rows, bool toggle); + Q_INVOKABLE void clearSelection(); + Q_INVOKABLE void pinSelection(); + Q_INVOKABLE void unpinSelection(); + + Q_INVOKABLE void rename(int row, const QString &name); + Q_INVOKABLE void copy(); + Q_INVOKABLE void paste(); + Q_INVOKABLE void cut(); + Q_INVOKABLE void openSelected(); + Q_INVOKABLE void deleteSelected(); + Q_INVOKABLE void moveSelectedToTrash(); + Q_INVOKABLE void emptyTrash(); + + Q_INVOKABLE void setDragHotSpotScrollOffset(int x, int y); + Q_INVOKABLE void addItemDragImage(int row, int x, int y, int width, int height, const QVariant &image); + Q_INVOKABLE void clearDragImages(); + Q_INVOKABLE void dragSelected(int x, int y); + + Q_INVOKABLE void setWallpaperSelected(); + + Q_INVOKABLE void openContextMenu(QQuickItem *visualParent = nullptr, Qt::KeyboardModifiers modifiers = Qt::NoModifier); + Q_INVOKABLE void openPropertiesDialog(); + Q_INVOKABLE void openInTerminal(); + +signals: + void urlChanged(); + void resolvedUrlChanged(); + void statusChanged(); + void sortModeChanged(); + void sortDescChanged(); + void sortDirsFirstChanged(); + void requestRename(); + void draggingChanged(); + void viewAdapterChanged(); + void statusTextChanged(); + +private slots: + void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected); + void dragSelectedInternal(int x, int y); + +private: + void invalidateIfComplete(); + void invalidateFilterIfComplete(); + void createActions(); + void updateActions(); + void addDragImage(QDrag *drag, int x, int y); + + bool isSupportThumbnails(const QString &mimeType) const; + +private: + KDirModel *m_dirModel; + KDirWatch *m_dirWatch; + + QItemSelectionModel *m_selectionModel; + QItemSelection m_pinnedSelection; + QString m_url; + + Status m_status; + int m_sortMode; + bool m_sortDesc; + bool m_sortDirsFirst; + + bool m_complete; + + KActionCollection m_actionCollection; + QHash m_dragImages; + QModelIndexList m_dragIndexes; + QPoint m_dragHotSpotScrollOffset; + bool m_dragInProgress; + + QPointer m_viewAdapter; + + // Save path history + PathHistory m_pathHistory; +}; + +#endif // FOLDERMODEL_H diff --git a/model/pathbarmodel.cpp b/model/pathbarmodel.cpp new file mode 100644 index 0000000..d76b2b4 --- /dev/null +++ b/model/pathbarmodel.cpp @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2021 CutefishOS Team. + * + * Author: revenmartin + * + * 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 3 of the License, or + * 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, see . + */ + +#include "pathbarmodel.h" + +PathBarModel::PathBarModel(QObject *parent) + : QAbstractItemModel(parent) +{ + +} + +PathBarModel::~PathBarModel() +{ +} + +QString PathBarModel::url() const +{ + return m_url; +} +#include +void PathBarModel::setUrl(const QString &url) +{ + if (m_url != url) { + beginResetModel(); + m_url = url; + + qDeleteAll(m_pathList); + m_pathList.clear(); + + QString _url = url; + while (_url.endsWith("/")) + _url.chop(1); + _url += '/'; + + int count = _url.count("/"); + + for (int i = 0; i < count; ++i) { + _url = QString(_url).left(_url.lastIndexOf("/")); + QString label = QString(_url).right(_url.length() - _url.lastIndexOf("/") - 1); + + if (label.isEmpty()) + continue; + + PathBarItem *item = new PathBarItem; + + if (label.contains(":") && i == count - 1) { + item->name = "/"; + item->url = QUrl(_url + "///"); + } else { + item->name = label; + item->url = QUrl(_url); + } + + m_pathList.append(item); + } + + std::reverse(m_pathList.begin(), m_pathList.end()); + + endResetModel(); + emit urlChanged(); + } +} + +QHash PathBarModel::roleNames() const +{ + QHash roleNames; + roleNames[PathBarModel::NameRole] = "name"; + roleNames[PathBarModel::UrlRole] = "url"; + roleNames[PathBarModel::PathRole] = "path"; + return roleNames; +} + +int PathBarModel::rowCount(const QModelIndex &parent) const +{ + if (parent.isValid()) + return 0; + + return m_pathList.size(); +} + +int PathBarModel::columnCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + return 1; +} + +QVariant PathBarModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + + PathBarItem *item = m_pathList.at(index.row()); + + switch (role) { + case PathBarModel::NameRole: + return item->name; + case PathBarModel::UrlRole: + return item->url; + case PathBarModel::PathRole: + return item->url.toString(QUrl::PreferLocalFile); + } + + return QVariant(); +} + +QModelIndex PathBarModel::index(int row, int column, const QModelIndex &parent) const +{ + if (row < 0 || column != 0 || row >= m_pathList.size()) { + return QModelIndex(); + } + + if (parent.isValid()) { + return QModelIndex(); + } + + return createIndex(row, column, m_pathList.at(row)); +} + +QModelIndex PathBarModel::parent(const QModelIndex &child) const +{ + Q_UNUSED(child); + return QModelIndex(); +} diff --git a/model/pathbarmodel.h b/model/pathbarmodel.h new file mode 100644 index 0000000..597fafa --- /dev/null +++ b/model/pathbarmodel.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2021 CutefishOS Team. + * + * Author: revenmartin + * + * 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 3 of the License, or + * 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, see . + */ + +#ifndef PATHBARMODEL_H +#define PATHBARMODEL_H + +#include +#include + +struct PathBarItem { + QString name; + QUrl url; +}; + +class PathBarModel : public QAbstractItemModel +{ + Q_OBJECT + Q_PROPERTY(QString url READ url WRITE setUrl NOTIFY urlChanged) + +public: + enum DataRole { + NameRole = Qt::UserRole + 1, + UrlRole, + PathRole + }; + Q_ENUMS(DataRole); + + explicit PathBarModel(QObject *parent = nullptr); + ~PathBarModel(); + + QString url() const; + void setUrl(const QString &url); + + QHash roleNames() const override; + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + int columnCount(const QModelIndex &parent = QModelIndex()) const override; + + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + + QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; + QModelIndex parent(const QModelIndex &child) const override; + +signals: + void urlChanged(); + +private: + QString m_url; + QList m_pathList; +}; + +#endif // PATHBARMODEL_H diff --git a/src/lib/placesitem.cpp b/model/placesitem.cpp similarity index 51% rename from src/lib/placesitem.cpp rename to model/placesitem.cpp index 71549b0..04d4082 100644 --- a/src/lib/placesitem.cpp +++ b/model/placesitem.cpp @@ -1,13 +1,30 @@ +/* + * Copyright (C) 2021 CutefishOS Team. + * + * Author: revenmartin + * + * 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 3 of the License, or + * 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, see . + */ + #include "placesitem.h" #include PlacesItem::PlacesItem(const QString &displayName, - const QString &iconName, QUrl url, QObject *parent) : QObject(parent) , m_displayName(displayName) - , m_iconName(iconName) , m_url(url) { } @@ -54,5 +71,5 @@ void PlacesItem::setUrl(const QUrl &url) QString PlacesItem::path() const { - return m_url.toString(); + return m_url.toString(QUrl::PreferLocalFile); } diff --git a/src/lib/placesitem.h b/model/placesitem.h similarity index 50% rename from src/lib/placesitem.h rename to model/placesitem.h index 3ea728f..9789d54 100644 --- a/src/lib/placesitem.h +++ b/model/placesitem.h @@ -1,3 +1,22 @@ +/* + * Copyright (C) 2021 CutefishOS Team. + * + * Author: revenmartin + * + * 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 3 of the License, or + * 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, see . + */ + #ifndef PLACESITEM_H #define PLACESITEM_H @@ -10,7 +29,6 @@ class PlacesItem : public QObject public: explicit PlacesItem(const QString &displayName = QString(), - const QString &iconName = QString(), QUrl url = QUrl(), QObject *parent = nullptr); diff --git a/src/lib/placesmodel.cpp b/model/placesmodel.cpp similarity index 60% rename from src/lib/placesmodel.cpp rename to model/placesmodel.cpp index f893bd7..3c1eaf2 100644 --- a/src/lib/placesmodel.cpp +++ b/model/placesmodel.cpp @@ -1,63 +1,111 @@ +/* + * Copyright (C) 2021 CutefishOS Team. + * + * Author: revenmartin + * + * 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 3 of the License, or + * 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, see . + */ + #include "placesmodel.h" #include #include +#include + +#include +#include +#include +#include +#include PlacesModel::PlacesModel(QObject *parent) : QAbstractItemModel(parent) { const QString homePath = QStandardPaths::writableLocation(QStandardPaths::HomeLocation); + if (QDir(homePath).exists()) { - PlacesItem *item = new PlacesItem(tr("Home"), "", QUrl::fromLocalFile(homePath)); + PlacesItem *item = new PlacesItem(tr("Home"), QUrl::fromLocalFile(homePath)); item->setIconPath("qrc:/images/folder-home.svg"); m_items.append(item); } const QString desktopPath = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); if (QDir(desktopPath).exists()) { - PlacesItem *item = new PlacesItem(tr("Desktop"), "", QUrl::fromLocalFile(desktopPath)); + PlacesItem *item = new PlacesItem(tr("Desktop"), QUrl::fromLocalFile(desktopPath)); item->setIconPath("qrc:/images/folder-desktop.svg"); m_items.append(item); } const QString documentsPath = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); if (QDir(documentsPath).exists()) { - PlacesItem *item = new PlacesItem(tr("Documents"), "folder-documents", QUrl::fromLocalFile(documentsPath)); + PlacesItem *item = new PlacesItem(tr("Documents"), QUrl::fromLocalFile(documentsPath)); item->setIconPath("qrc:/images/folder-document.svg"); m_items.append(item); } const QString downloadPath = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation); if (QDir(downloadPath).exists()) { - PlacesItem *item = new PlacesItem(tr("Downloads"), "folder-downloads", QUrl::fromLocalFile(downloadPath)); + PlacesItem *item = new PlacesItem(tr("Downloads"), QUrl::fromLocalFile(downloadPath)); item->setIconPath("qrc:/images/folder-download.svg"); m_items.append(item); } const QString musicPath = QStandardPaths::writableLocation(QStandardPaths::MusicLocation); if (QDir(musicPath).exists()) { - PlacesItem *item = new PlacesItem(tr("Music"), "folder-music", QUrl::fromLocalFile(musicPath)); + PlacesItem *item = new PlacesItem(tr("Music"), QUrl::fromLocalFile(musicPath)); item->setIconPath("qrc:/images/folder-music.svg"); m_items.append(item); } const QString picturePath = QStandardPaths::writableLocation(QStandardPaths::PicturesLocation); if (QDir(picturePath).exists()) { - PlacesItem *item = new PlacesItem(tr("Pictures"), "folder-pictures", QUrl::fromLocalFile(picturePath)); + PlacesItem *item = new PlacesItem(tr("Pictures"), QUrl::fromLocalFile(picturePath)); item->setIconPath("qrc:/images/folder-picture.svg"); m_items.append(item); } const QString videoPath = QStandardPaths::writableLocation(QStandardPaths::MoviesLocation); if (QDir(videoPath).exists()) { - PlacesItem *item = new PlacesItem(tr("Videos"), "folder-videos", QUrl::fromLocalFile(videoPath)); + PlacesItem *item = new PlacesItem(tr("Videos"), QUrl::fromLocalFile(videoPath)); item->setIconPath("qrc:/images/folder-video.svg"); m_items.append(item); } - PlacesItem *trashItem = new PlacesItem(tr("Trash"), "", QUrl(QStringLiteral("trash:/"))); + PlacesItem *trashItem = new PlacesItem(tr("Trash"), QUrl(QStringLiteral("trash:/"))); trashItem->setIconPath("qrc:/images/user-trash.svg"); m_items.append(trashItem); + + Solid::Predicate predicate; + QString predicateStr( + QString::fromLatin1("[[[[ StorageVolume.ignored == false AND [ StorageVolume.usage == 'FileSystem' OR StorageVolume.usage == 'Encrypted' ]]" + " OR " + "[ IS StorageAccess AND StorageDrive.driveType == 'Floppy' ]]" + " OR " + "OpticalDisc.availableContent & 'Audio' ]" + " OR " + "StorageAccess.ignored == false ]")); + predicate = Solid::Predicate::fromString(predicateStr); + + Solid::DeviceNotifier *notifier = Solid::DeviceNotifier::instance(); + const QList &deviceList = Solid::Device::listFromQuery(predicate); + + for (const Solid::Device &device : deviceList) { + qDebug() << device.udi(); + + PlacesItem *deviceItem = new PlacesItem(device.udi()); + m_items.append(deviceItem); + } } PlacesModel::~PlacesModel() diff --git a/src/lib/placesmodel.h b/model/placesmodel.h similarity index 57% rename from src/lib/placesmodel.h rename to model/placesmodel.h index 8361e4a..a830466 100644 --- a/src/lib/placesmodel.h +++ b/model/placesmodel.h @@ -1,3 +1,22 @@ +/* + * Copyright (C) 2021 CutefishOS Team. + * + * Author: revenmartin + * + * 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 3 of the License, or + * 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, see . + */ + #ifndef PLACESMODEL_H #define PLACESMODEL_H diff --git a/src/lib/positioner.cpp b/model/positioner.cpp similarity index 100% rename from src/lib/positioner.cpp rename to model/positioner.cpp diff --git a/src/lib/positioner.h b/model/positioner.h similarity index 95% rename from src/lib/positioner.h rename to model/positioner.h index 6f3e73a..04d6924 100644 --- a/src/lib/positioner.h +++ b/model/positioner.h @@ -23,13 +23,10 @@ #include class FolderModel; - class QTimer; - class Positioner : public QAbstractItemModel { Q_OBJECT - Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged) Q_PROPERTY(FolderModel *folderModel READ folderModel WRITE setFolderModel NOTIFY folderModelChanged) Q_PROPERTY(int perStripe READ perStripe WRITE setPerStripe NOTIFY perStripeChanged) @@ -74,24 +71,13 @@ public: int rowCount(const QModelIndex &parent = QModelIndex()) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; -#ifdef BUILD_TESTING - QHash proxyToSourceMapping() const - { - return m_proxyToSource; - } - QHash sourceToProxyMapping() const - { - return m_sourceToProxy; - } -#endif - -Q_SIGNALS: +signals: void enabledChanged() const; void folderModelChanged() const; void perStripeChanged() const; void positionsChanged() const; -private Q_SLOTS: +private slots: void updatePositions(); void sourceStatusChanged(); void sourceDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector &roles); @@ -137,4 +123,4 @@ private: bool m_beginInsertRowsCalled = false; // used to sync the amount of begin/endInsertRows calls }; -#endif +#endif // POSITIONER_H diff --git a/qml.qrc b/qml.qrc index 4b7e4ee..12227de 100644 --- a/qml.qrc +++ b/qml.qrc @@ -1,11 +1,12 @@ qml/main.qml + qml/FolderPage.qml qml/SideBar.qml - qml/SidebarItem.qml + qml/FolderListItem.qml + qml/Dialogs/PropertiesDialog.qml qml/FolderListView.qml - qml/PathBar.qml - qml/FolderListDelegate.qml + qml/Controls/IconButton.qml images/dark/go-next.svg images/dark/go-previous.svg images/dark/grid.svg @@ -14,27 +15,19 @@ images/light/go-previous.svg images/light/grid.svg images/light/list.svg - qml/IconButton.qml + images/folder-desktop.svg + images/folder-document.svg images/folder-download.svg images/folder-home.svg images/folder-music.svg images/folder-picture.svg images/folder-video.svg - images/folder-document.svg - qml/GlobalSettings.qml - qml/FolderIconView.qml - qml/FolderIconDelegate.qml - qml/BrowserView.qml - qml/ItemMenu.qml - qml/BrowserMenu.qml - qml/Desktop/DesktopFolderView.qml - qml/Desktop/FolderViewDropArea.qml - qml/Desktop/FolderItemDelegate.qml - qml/Desktop/Desktop.qml - qml/Desktop/FolderTools.js - qml/Dialogs/PropertiesDialog.qml - qml/IconDelegate.qml - images/folder-desktop.svg images/user-trash.svg + qml/PathBar.qml + qml/Desktop/main.qml + qml/FolderGridView.qml + qml/GlobalSettings.qml + qml/FolderGridItem.qml + qml/Dialogs/CreateFolderDialog.qml diff --git a/qml/BrowserMenu.qml b/qml/BrowserMenu.qml deleted file mode 100644 index e341624..0000000 --- a/qml/BrowserMenu.qml +++ /dev/null @@ -1,61 +0,0 @@ -import QtQuick 2.12 -import QtQuick.Controls 2.12 -import Cutefish.FileManager 1.0 -import MeuiKit 1.0 as Meui - -Menu { - id: control - - property FMList currentList - - signal emptyTrashClicked() - signal propertiesClicked() - signal selectAllClicked() - - MenuItem { - id: newFolderItem - text: qsTr("New Folder") - enabled: currentList.pathType !== FMList.TRASH_PATH - } - - MenuSeparator { - visible: newFolderItem.visible && pasteItem.visible - } - - MenuItem { - id: pasteItem - text: qsTr("Paste") - onTriggered: paste() - enabled: currentList.pathType !== FMList.TRASH_PATH - } - - MenuItem { - text: qsTr("Select All") - onTriggered: control.selectAllClicked() - } - - MenuItem { - id: terminal - text: qsTr("Open in Terminal") - } - - MenuItem { - id: properties - text: qsTr("Properties") - onTriggered: { - propertiesClicked() - close() - } - } - - MenuItem { - id: emptyItem - text: qsTr("Empty Trash") - visible: currentList.pathType === FMList.TRASH_PATH - onTriggered: control.emptyTrashClicked() - } - - function show(parent = control, x, y) { - popup(parent, x, y) - } -} diff --git a/qml/BrowserView.qml b/qml/BrowserView.qml deleted file mode 100644 index be74dd7..0000000 --- a/qml/BrowserView.qml +++ /dev/null @@ -1,107 +0,0 @@ -import QtQuick 2.12 -import QtQuick.Controls 2.12 -import QtQuick.Layouts 1.12 -import QtGraphicalEffects 1.0 - -import MeuiKit 1.0 as Meui -import Cutefish.FileManager 1.0 as FM - -Item { - id: control - - property alias model: dirModel - property alias url: dirModel.url - property alias currentView: viewLoader.item - - signal openPathBar - - FM.FolderModel { - id: dirModel - sortDirsFirst: true - parseDesktopFiles: true - url: dirModel.homePath() - previews: true - previewPlugins: [] - } - - FM.Positioner { - id: positioner - folderModel: dirModel - enabled: true - } - - Rectangle { - anchors.fill: parent - anchors.topMargin: 0 - anchors.leftMargin: Meui.Theme.smallRadius / 2 - anchors.rightMargin: Meui.Theme.smallRadius - anchors.bottomMargin: Meui.Theme.smallRadius - radius: Meui.Theme.smallRadius - color: Meui.Theme.backgroundColor - - Label { - anchors.centerIn: parent - text: qsTr("No Files") - font.pointSize: 20 - visible: dirModel.status === FM.FolderModel.Ready && currentView.count === 0 - } - } - - Loader { - id: viewLoader - anchors.fill: parent - anchors.bottomMargin: Meui.Units.largeSpacing - sourceComponent: switch (settings.viewMethod) { - case 0: return listViewBrowser - case 1: return gridViewBrowser - } - } - - Component { - id: listViewBrowser - - FolderListView { - id: _listViewBrowser - anchors.fill: parent - model: dirModel - - leftMargin: Meui.Units.largeSpacing + Meui.Units.smallSpacing - rightMargin: Meui.Units.largeSpacing + Meui.Units.smallSpacing - topMargin: Meui.Units.smallSpacing - bottomMargin: Meui.Units.largeSpacing * 2 - - delegate: FolderListDelegate { - id: listDelegate - width: ListView.view.width - ListView.view.leftMargin - ListView.view.rightMargin - height: 48 - } - } - } - - Component { - id: gridViewBrowser - - FolderIconView { - id: _gridViewBrowser - anchors.fill: parent - model: positioner - - leftMargin: Meui.Units.largeSpacing - rightMargin: Meui.Units.largeSpacing - - delegate: FolderIconDelegate { - id: iconDelegate - height: _gridViewBrowser.cellHeight - width: _gridViewBrowser.cellWidth - } - } - } - - Component.onCompleted: { - control.currentView.forceActiveFocus() - } - - function openFolder(url) { - dirModel.url = url - } -} diff --git a/qml/IconButton.qml b/qml/Controls/IconButton.qml similarity index 100% rename from qml/IconButton.qml rename to qml/Controls/IconButton.qml diff --git a/qml/Desktop/Desktop.qml b/qml/Desktop/Desktop.qml deleted file mode 100644 index a79d028..0000000 --- a/qml/Desktop/Desktop.qml +++ /dev/null @@ -1,114 +0,0 @@ -import QtQuick 2.4 -import QtQuick.Controls 2.4 -import QtQuick.Layouts 1.3 -import QtGraphicalEffects 1.0 - -import Qt.labs.platform 1.0 -import Cutefish.FileManager 1.0 -import MeuiKit 1.0 as Meui - -FolderViewDropArea { - id: root - visible: true - - preventStealing: true - - property bool containsDrag: false - - folderView: folderViewLayer.item - - function isDrag(fromX, fromY, toX, toY) { - var length = Math.abs(fromX - toX) + Math.abs(fromY - toY); - return length >= Qt.styleHints.startDragDistance; - } - - function isFileDrag(event) { - var taskUrl = event.mimeData.formats.indexOf("text/x-orgkdeplasmataskmanager_taskurl") !== -1; - var arkService = event.mimeData.formats.indexOf("application/x-kde-ark-dndextract-service") !== -1; - var arkPath = event.mimeData.formats.indexOf("application/x-kde-ark-dndextract-path") !== -1; - return (event.mimeData.hasUrls || taskUrl || (arkService && arkPath)); - } - - onDragEnter: { - if (!isFileDrag(event)) - event.ignore(); - - // Firefox tabs are regular drags. Since all of our drop handling is asynchronous - // we would accept this drop and have Firefox not spawn a new window. (Bug 337711) - if (event.mimeData.formats.indexOf("application/x-moz-tabbrowser-tab") > -1) { - event.ignore(); - } - } - - onDragMove: { - - } - - onDragLeave: { - - } - - onDrop: { - - } - - DesktopSettings { - id: settings - } - - Loader { - id: backgroundLoader - anchors.fill: parent - sourceComponent: settings.backgroundType === 0 ? wallpaper : background - } - - Component { - id: wallpaper - - Image { - source: "file://" + settings.wallpaper - sourceSize: Qt.size(width, height) - fillMode: Image.PreserveAspectCrop - clip: true - cache: false - - ColorOverlay { - id: dimsWallpaper - anchors.fill: parent - source: parent - color: "#000000" - opacity: Meui.Theme.darkMode && settings.dimsWallpaper ? 0.4 : 0.0 - - Behavior on opacity { - NumberAnimation { - duration: 200 - } - } - - } - } - } - - Component { - id: background - - Rectangle { - anchors.fill: parent - color: settings.backgroundColor - } - } - - Loader { - id: folderViewLayer - anchors.fill: parent - - property bool ready: status == Loader.Ready - property Item view: item ? item : null - property QtObject model: item ? item.model : null - - focus: true - active: true - asynchronous: false - source: "DesktopFolderView.qml" - } -} diff --git a/qml/Desktop/DesktopFolderView.qml b/qml/Desktop/DesktopFolderView.qml deleted file mode 100644 index c1ea895..0000000 --- a/qml/Desktop/DesktopFolderView.qml +++ /dev/null @@ -1,1087 +0,0 @@ -import QtQuick 2.12 -import QtQml 2.12 -import QtQuick.Controls 2.12 -import QtQuick.Layouts 1.12 -import Cutefish.FileManager 1.0 as FM -import MeuiKit 1.0 as Meui - -import "../" -import "FolderTools.js" as FolderTools - -FocusScope { - id: control - - property QtObject model: dir - property Item rubberBand: null - - property alias positions: positioner.positions - property alias scrollLeft: gridView.scrollLeft - property alias scrollRight: gridView.scrollRight - property alias scrollUp: gridView.scrollUp - property alias scrollDown: gridView.scrollDown - property alias hoveredItem: gridView.hoveredItem - property int previouslySelectedItemIndex: -1 - - property alias cellWidth: gridView.cellWidth - property alias cellHeight: gridView.cellHeight - - function positionViewAtBeginning() { - gridView.positionViewAtBeginning(); - } - - function rename() { - if (gridView.currentIndex != -1) { - var renameAction = folderView.model.action("rename"); - if (renameAction && !renameAction.enabled) { - return; - } - - if (!editor) { - editor = editorComponent.createObject(listener); - } - - editor.targetItem = gridView.currentItem; - } - } - - function cancelRename() { - if (editor) { - editor.targetItem = null; - } - } - - function handleDragMove(x, y) { - var child = childAt(x, y); - - if (child !== null) { - hoveredItem = null; - } else { - var pos = mapToItem(gridView.contentItem, x, y); - var item = gridView.itemAt(pos.x, pos.y); - - if (item && item.isDir) { - hoveredItem = item; - } else { - hoveredItem = null; - } - } - } - - function endDragMove() { - if (hoveredItem) { - hoveredItem = null; - } - } - - function dropItemAt(pos) { - var item = gridView.itemAt(pos.x, pos.y); - - if (item) { - if (item.blank) { - return -1; - } - - var hOffset = Math.abs(Math.min(gridView.contentX, gridView.originX)); - var hPos = mapToItem(item.frame, pos.x + hOffset, pos.y); - - if ((hPos.x < 0 || hPos.y < 0 || hPos.x > item.frame.width || hPos.y > item.frame.height)) { - return -1; - } else { - return positioner.map(item.index); - } - } - - return -1; - } - - function drop(target, event, pos) { - var dropPos = mapToItem(gridView.contentItem, pos.x, pos.y); - var dropIndex = gridView.indexAt(dropPos.x, dropPos.y); - var dragPos = mapToItem(gridView.contentItem, listener.dragX, listener.dragY); - var dragIndex = gridView.indexAt(dragPos.x, dragPos.y); - - if (listener.dragX === -1 || dragIndex !== dropIndex) { - dir.drop(target, event, dropItemAt(dropPos), root.isContainment && !plasmoid.immutable); - } - } - - GlobalSettings { - id: settings - } - - MouseArea { - id: listener - anchors.fill: parent - z: 999 - - property alias hoveredItem: gridView.hoveredItem - - property Item pressedItem: null - property int pressX: -1 - property int pressY: -1 - property int dragX: -1 - property int dragY: -1 - property variant cPress: null - property bool doubleClickInProgress: false - - Keys.forwardTo: gridView - - hoverEnabled: true - - onPressXChanged: { - cPress = mapToItem(gridView.contentItem, pressX, pressY); - } - - onPressYChanged: { - cPress = mapToItem(gridView.contentItem, pressX, pressY); - } - - acceptedButtons: Qt.LeftButton | Qt.RightButton - - onWheel: { - if (wheel.modifiers & Qt.ControlModifier) { - if (wheel.angleDelta.y > 0) - gridView.increaseIconSize() - else - gridView.decreaseIconSize() - } - } - - onPressed: { - gridView.focus = true - gridView.forceActiveFocus() - - if (mouse.source === Qt.MouseEventSynthesizedByQt) { - var index = gridView.indexAt(mouse.x, mouse.y); - var indexItem = gridView.itemAtIndex(index); - if (indexItem && indexItem.iconArea) { - gridView.currentIndex = index; - hoveredItem = indexItem; - } else { - hoveredItem = null; - } - } - - pressX = mouse.x; - pressY = mouse.y; - - if (!hoveredItem || hoveredItem.blank) { - if (!gridView.ctrlPressed) { - gridView.currentIndex = -1; - previouslySelectedItemIndex = -1; - dir.clearSelection(); - } - - if (mouse.buttons & Qt.RightButton) { - clearPressState(); - dir.openContextMenu(null, mouse.modifiers); - mouse.accepted = true; - } - } else { - pressedItem = hoveredItem; - - if (gridView.shiftPressed && gridView.currentIndex != -1) { - positioner.setRangeSelected(gridView.anchorIndex, hoveredItem.index); - } else { - if (!gridView.ctrlPressed && !dir.isSelected(positioner.map(hoveredItem.index))) { - previouslySelectedItemIndex = -1; - dir.clearSelection(); - } - - if (gridView.ctrlPressed) { - dir.toggleSelected(positioner.map(hoveredItem.index)); - } else { - dir.setSelected(positioner.map(hoveredItem.index)); - } - - gridView.currentIndex = hoveredItem.index; - - if (mouse.buttons & Qt.RightButton) { - clearPressState(); - - dir.openContextMenu(null, mouse.modifiers); - mouse.accepted = true; - } - } - } - - // control.pressed(); - } - - onPositionChanged: { - gridView.ctrlPressed = (mouse.modifiers & Qt.ControlModifier); - gridView.shiftPressed = (mouse.modifiers & Qt.ShiftModifier); - - var cPos = mapToItem(gridView.contentItem, mouse.x, mouse.y); - var item = gridView.itemAt(cPos.x, cPos.y); - var leftEdge = Math.min(gridView.contentX, gridView.originX); - - if (!item || item.blank) { - if (gridView.hoveredItem && !root.containsDrag) { - gridView.hoveredItem = null; - } - } else { - var fPos = mapToItem(item.frame, mouse.x, mouse.y); - - if (fPos.x < 0 || fPos.y < 0 || fPos.x > item.frame.width || fPos.y > item.frame.height) { - gridView.hoveredItem = null; - } else { - gridView.hoveredItem = item - } - } - - // Trigger autoscroll. - if (pressX != -1) { - gridView.scrollLeft = (mouse.x <= 0 && gridView.contentX > leftEdge); - gridView.scrollRight = (mouse.x >= gridView.width - && gridView.contentX < gridView.contentItem.width - gridView.width); - gridView.scrollUp = (mouse.y <= 0 && gridView.contentY > 0); - gridView.scrollDown = (mouse.y >= gridView.height - && gridView.contentY < gridView.contentItem.height - gridView.height); - } - - // Update rubberband geometry. - if (control.rubberBand) { - var rB = control.rubberBand; - - if (cPos.x < cPress.x) { - rB.x = Math.max(leftEdge, cPos.x); - rB.width = Math.abs(rB.x - cPress.x); - } else { - rB.x = cPress.x; - var ceil = Math.max(gridView.width, gridView.contentItem.width) + leftEdge; - rB.width = Math.min(ceil - rB.x, Math.abs(rB.x - cPos.x)); - } - - if (cPos.y < cPress.y) { - rB.y = Math.max(0, cPos.y); - rB.height = Math.abs(rB.y - cPress.y); - } else { - rB.y = cPress.y; - var ceil = Math.max(gridView.height, gridView.contentItem.height); - rB.height = Math.min(ceil - rB.y, Math.abs(rB.y - cPos.y)); - } - - // Ensure rubberband is at least 1px in size or else it will become - // invisible and not match any items. - rB.width = Math.max(1, rB.width); - rB.height = Math.max(1, rB.height); - - gridView.rectangleSelect(rB.x, rB.y, rB.width, rB.height); - - return; - } - - // Drag initiation. - if (pressX != -1 && root.isDrag(pressX, pressY, mouse.x, mouse.y)) { - if (pressedItem != null && dir.isSelected(positioner.map(pressedItem.index))) { - dragX = mouse.x; - dragY = mouse.y; - gridView.verticalDropHitscanOffset = pressedItem.iconArea.y + (pressedItem.iconArea.height / 2) - dir.dragSelected(mouse.x, mouse.y); - dragX = -1; - dragY = -1; - clearPressState(); - } else { - dir.pinSelection(); - control.rubberBand = rubberBandObject.createObject(gridView.contentItem, {x: cPress.x, y: cPress.y}) - gridView.interactive = false; - } - } - } - - onCanceled: pressCanceled() - onReleased: pressCanceled() - - onDoubleClicked: { - clearPressState() - - if (!hoveredItem || hoveredItem.blank || gridView.currentIndex == -1 || gridView.ctrlPressed || gridView.shiftPressed) { - return; - } - - if (mouse.button === Qt.LeftButton) - dir.runSelected() - } - - onClicked: { - clearPressState(); - - if (!hoveredItem || hoveredItem.blank || gridView.currentIndex == -1 || gridView.ctrlPressed || gridView.shiftPressed) { - return; - } - - var pos = mapToItem(hoveredItem, mouse.x, mouse.y); - - // Moving from an item to its preview popup dialog doesn't unset hoveredItem - // even though the cursor has left it, so we need to check whether the click - // actually occurred inside the item we expect it in before going ahead. If it - // didn't, clean up (e.g. dismissing the dialog as a side-effect of unsetting - // hoveredItem) and abort. - if (pos.x < 0 || pos.x > hoveredItem.width || pos.y < 0 || pos.y > hoveredItem.height) { - hoveredItem = null; - previouslySelectedItemIndex = -1; - dir.clearSelection(); - - return; - // If the hoveredItem is clicked while having a preview popup dialog open, - // only dismiss the dialog and abort. - } - } - - onContainsMouseChanged: { - if (!containsMouse && !control.rubberBand) { - clearPressState(); - - if (gridView.hoveredItem) { - gridView.hoveredItem = null; - } - } - } - - function pressCanceled() { - if (control.rubberBand) { - control.rubberBand.close() - control.rubberBand = null - - gridView.interactive = true; - gridView.cachedRectangleSelection = null; - dir.unpinSelection(); - } - - clearPressState(); - gridView.cancelAutoscroll(); - } - - function clearPressState() { - pressedItem = null; - pressX = -1; - pressY = -1; - } - } - - GridView { - id: gridView - anchors.fill: parent - - property int verticalDropHitscanOffset: 0 - - property Item hoveredItem: null - - property int anchorIndex: 0 - property bool ctrlPressed: false - property bool shiftPressed: false - - property bool overflowing: (visibleArea.heightRatio < 1.0 || visibleArea.widthRatio < 1.0) - - property bool scrollLeft: false - property bool scrollRight: false - property bool scrollUp: false - property bool scrollDown: false - - property variant cachedRectangleSelection: null - - flow: GridView.FlowTopToBottom - - currentIndex: -1 - - keyNavigationWraps: false - boundsBehavior: Flickable.StopAtBounds - - focus: true - - onActiveFocusChanged: { - if (!activeFocus) { - dir.clearSelection() - } - } - - property var iconSize: settings.desktopIconSize + Meui.Units.largeSpacing - - cellWidth: { - var extraWidth = calcExtraSpacing(iconSize, gridView.width - leftMargin - rightMargin); - return iconSize + extraWidth; - } - - cellHeight: { - var extraHeight = calcExtraSpacing(iconSize, gridView.height - topMargin - bottomMargin); - return iconSize + extraHeight; - } - - function increaseIconSize() { - if (iconSize >= settings.maximumIconSize) { - iconSize = settings.maximumIconSize - return - } - - iconSize += (iconSize * 0.1) - settings.desktopIconSize = iconSize - } - - function decreaseIconSize() { - if (iconSize <= settings.minimumIconSize) { - iconSize = settings.minimumIconSize - return - } - - iconSize -= (iconSize * 0.1) - settings.desktopIconSize = iconSize - } - - function calcExtraSpacing(cellSize, containerSize) { - var availableColumns = Math.floor(containerSize / cellSize); - var extraSpacing = 0; - if (availableColumns > 0) { - var allColumnSize = availableColumns * cellSize; - var extraSpace = Math.max(containerSize - allColumnSize, 0); - extraSpacing = extraSpace / availableColumns; - } - return Math.floor(extraSpacing); - } - - function updateSelection(modifier) { - if (modifier & Qt.ShiftModifier) { - positioner.setRangeSelected(anchorIndex, currentIndex); - } else { - dir.clearSelection(); - dir.setSelected(positioner.map(currentIndex)); - if (currentIndex == -1) { - previouslySelectedItemIndex = -1; - } - previouslySelectedItemIndex = currentIndex; - } - } - - leftMargin: desktopView.screenAvailableRect ? desktopView.screenAvailableRect.x : 0 - topMargin: desktopView.screenAvailableRect ? desktopView.screenAvailableRect.y : 0 - rightMargin: desktopView.screenRect.width - (desktopView.screenAvailableRect.x + desktopView.screenAvailableRect.width) - bottomMargin: desktopView.screenRect.height - (desktopView.screenAvailableRect.y + desktopView.screenAvailableRect.height) - - delegate: FolderItemDelegate { - width: gridView.cellWidth - height: gridView.cellHeight - } - - onContentXChanged: { - // cancelRename() - - dir.setDragHotSpotScrollOffset(contentX, contentY); - - if (contentX == 0) { - scrollLeft = false; - } - - if (contentX == contentItem.width - width) { - scrollRight = false; - } - - // Update rubberband geometry. - if (control.rubberBand) { - var rB = control.rubberBand; - - if (scrollLeft) { - rB.x = Math.min(gridView.contentX, gridView.originX); - rB.width = listener.cPress.x; - } - - if (scrollRight) { - var lastCol = gridView.contentX + gridView.width; - rB.width = lastCol - rB.x; - } - - gridView.rectangleSelect(rB.x, rB.y, rB.width, rB.height); - } - } - - onContentYChanged: { - // cancelRename(); - - dir.setDragHotSpotScrollOffset(contentX, contentY); - - if (contentY == 0) { - scrollUp = false; - } - - if (contentY == contentItem.height - height) { - scrollDown = false; - } - - // Update rubberband geometry. - if (control.rubberBand) { - var rB = control.rubberBand; - - if (scrollUp) { - rB.y = 0; - rB.height = listener.cPress.y; - } - - if (scrollDown) { - var lastRow = gridView.contentY + gridView.height; - rB.height = lastRow - rB.y; - } - - gridView.rectangleSelect(rB.x, rB.y, rB.width, rB.height); - } - } - - onFlowChanged: { - // FIXME TODO: Preserve positions. - if (positioner.enabled) { - positioner.reset(); - } - } - - onLayoutDirectionChanged: { - // FIXME TODO: Preserve positions. - if (positioner.enabled) { - positioner.reset(); - } - } - - onCurrentIndexChanged: { - positionViewAtIndex(currentIndex, GridView.Contain); - } - - onCachedRectangleSelectionChanged: { - if (cachedRectangleSelection == null) { - return; - } - - if (cachedRectangleSelection.length) { - // Set current index to start of selection. - // cachedRectangleSelection is pre-sorted. - currentIndex = cachedRectangleSelection[0]; - } - - dir.updateSelection(cachedRectangleSelection.map(positioner.map), gridView.ctrlPressed); - } - - Behavior on contentX { id: smoothX; enabled: false; SmoothedAnimation { velocity: 700 } } - Behavior on contentY { id: smoothY; enabled: false; SmoothedAnimation { velocity: 700 } } - - Keys.onPressed: { - event.accepted = true - - if (event.matches(StandardKey.Delete)) { - if (dir.hasSelection()) { - dir.action("trash").trigger(); - } - } else if (event.key === Qt.Key_Control) { - ctrlPressed = true; - } else if (event.key === Qt.Key_Shift) { - shiftPressed = true; - - if (currentIndex != -1) { - anchorIndex = currentIndex; - } - } else if (event.key === Qt.Key_Home) { - currentIndex = 0; - updateSelection(event.modifiers); - } else if (event.key === Qt.Key_End) { - currentIndex = count - 1; - updateSelection(event.modifiers); - } else if (event.matches(StandardKey.Copy)) { - dir.copy(); - } else if (event.matches(StandardKey.Paste)) { - dir.paste(); - } else if (event.matches(StandardKey.Cut)) { - dir.cut(); - } else if (event.matches(StandardKey.Undo)) { - dir.undo(); - } else if (event.matches(StandardKey.Refresh)) { - dir.refresh(); - } else if (event.matches(StandardKey.SelectAll)) { - positioner.setRangeSelected(0, count - 1); - } else { - event.accepted = false; - } - } - - Keys.onReturnPressed: { - if (event.modifiers === Qt.AltModifier) { - dir.openPropertiesDialog(); - } else { - dir.runSelected() - } - } - - Keys.onEnterPressed: Keys.returnPressed(event) - - Keys.onReleased: { - if (event.key === Qt.Key_Control) { - ctrlPressed = false; - } else if (event.key === Qt.Key_Shift) { - shiftPressed = false; - anchorIndex = 0; - } - } - - Keys.onLeftPressed: { - if (positioner.enabled) { - var newIndex = positioner.nearestItem(currentIndex, - FolderTools.effectiveNavDirection(gridView.flow, gridView.effectiveLayoutDirection, Qt.LeftArrow)); - - if (newIndex !== -1) { - currentIndex = newIndex; - updateSelection(event.modifiers); - } - } else { - var oldIndex = currentIndex; - - moveCurrentIndexLeft(); - - if (oldIndex === currentIndex) { - return; - } - - updateSelection(event.modifiers); - } - } - - Keys.onRightPressed: { - if (positioner.enabled) { - var newIndex = positioner.nearestItem(currentIndex, - FolderTools.effectiveNavDirection(gridView.flow, gridView.effectiveLayoutDirection, Qt.RightArrow)); - - if (newIndex !== -1) { - currentIndex = newIndex; - updateSelection(event.modifiers); - } - } else { - var oldIndex = currentIndex; - - moveCurrentIndexRight(); - - if (oldIndex === currentIndex) { - return; - } - - updateSelection(event.modifiers); - } - } - - Keys.onUpPressed: { - if (positioner.enabled) { - var newIndex = positioner.nearestItem(currentIndex, - FolderTools.effectiveNavDirection(gridView.flow, gridView.effectiveLayoutDirection, Qt.UpArrow)); - - if (newIndex !== -1) { - currentIndex = newIndex; - updateSelection(event.modifiers); - } - } else { - var oldIndex = currentIndex; - - moveCurrentIndexUp(); - - if (oldIndex === currentIndex) { - return; - } - - updateSelection(event.modifiers); - } - } - - Keys.onDownPressed: { - if (positioner.enabled) { - var newIndex = positioner.nearestItem(currentIndex, - FolderTools.effectiveNavDirection(gridView.flow, gridView.effectiveLayoutDirection, Qt.DownArrow)); - - if (newIndex !== -1) { - currentIndex = newIndex; - updateSelection(event.modifiers); - } - } else { - var oldIndex = currentIndex; - - moveCurrentIndexDown(); - - if (oldIndex === currentIndex) { - return; - } - - updateSelection(event.modifiers); - } - } - - function cancelAutoscroll() { - scrollLeft = false; - scrollRight = false; - scrollUp = false; - scrollDown = false; - } - - function rectangleSelect(x, y, width, height) { - var rows = (gridView.flow == GridView.FlowLeftToRight); - var axis = rows ? gridView.width : gridView.height; - var step = rows ? cellWidth : cellHeight; - var perStripe = Math.floor(axis / step); - var stripes = Math.ceil(gridView.count / perStripe); - var cWidth = gridView.cellWidth - (2 * Meui.Units.smallSpacing); - var cHeight = gridView.cellHeight - (2 * Meui.Units.smallSpacing); - var midWidth = gridView.cellWidth / 2; - var midHeight = gridView.cellHeight / 2; - var indices = []; - - for (var s = 0; s < stripes; s++) { - for (var i = 0; i < perStripe; i++) { - var index = (s * perStripe) + i; - - if (index >= gridView.count) { - break; - } - - if (positioner.isBlank(index)) { - continue; - } - - var itemX = ((rows ? i : s) * gridView.cellWidth); - var itemY = ((rows ? s : i) * gridView.cellHeight); - - if (gridView.effectiveLayoutDirection == Qt.RightToLeft) { - itemX -= (rows ? gridView.contentX : gridView.originX); - itemX += cWidth; - itemX = (rows ? gridView.width : gridView.contentItem.width) - itemX; - } - - // Check if the rubberband intersects this cell first to avoid doing more - // expensive work. - if (control.rubberBand.intersects(Qt.rect(itemX + Meui.Units.smallSpacing, itemY + Meui.Units.smallSpacing, - cWidth, cHeight))) { - var item = gridView.contentItem.childAt(itemX + midWidth, itemY + midHeight); - - // If this is a visible item, check for intersection with the actual - // icon or label rects for better feel. - if (item && item.iconArea) { - var iconRect = Qt.rect(itemX + item.iconArea.x, itemY + item.iconArea.y, - item.iconArea.width, item.iconArea.height); - - if (control.rubberBand.intersects(iconRect)) { - indices.push(index); - continue; - } - - var labelRect = Qt.rect(itemX + item.labelArea.x, itemY + item.labelArea.y, - item.labelArea.width, item.labelArea.height); - - if (control.rubberBand.intersects(labelRect)) { - indices.push(index); - continue; - } - } else { - // Otherwise be content with the cell intersection. - indices.push(index); - } - } - } - } - - gridView.cachedRectangleSelection = indices; - } - } - - FM.FolderModel { - id: dir - - sortDirsFirst: true - parseDesktopFiles: true - viewAdapter: viewAdapter - url: dir.desktopPath() - previews: true - previewPlugins: [] - - onListingCompleted: { - if (!gridView.model) { - gridView.model = positioner; - gridView.currentIndex = -1 - } - } - - onMove: { - var rows = (gridView.flow == GridView.FlowLeftToRight); - var axis = rows ? gridView.width : gridView.height; - var step = rows ? cellWidth : cellHeight; - var perStripe = Math.floor(axis / step); - var dropPos = mapToItem(gridView.contentItem, x, y); - var leftEdge = Math.min(gridView.contentX, gridView.originX); - - var moves = [] - var itemX = -1; - var itemY = -1; - var col = -1; - var row = -1; - var from = -1; - var to = -1; - - for (var i = 0; i < urls.length; i++) { - from = positioner.indexForUrl(urls[i]); - to = -1; - - if (from === -1) { - continue; - } - - var offset = dir.dragCursorOffset(positioner.map(from)); - - if (offset.x === -1) { - continue; - } - - itemX = dropPos.x + offset.x + (listener.dragX % cellWidth) + (cellWidth / 2); - itemY = dropPos.y + offset.y + (listener.dragY % cellHeight) + gridView.verticalDropHitscanOffset; - - if (gridView.effectiveLayoutDirection == Qt.RightToLeft) { - itemX -= (rows ? gridView.contentX : gridView.originX); - itemX = (rows ? gridView.width : gridView.contentItem.width) - itemX; - } - - col = Math.floor(itemX / gridView.cellWidth); - row = Math.floor(itemY / gridView.cellHeight); - - if ((rows ? col : row) < perStripe) { - to = ((rows ? row : col) * perStripe) + (rows ? col : row); - - if (to < 0) { - return; - } - } - - if (from !== to) { - moves.push(from); - moves.push(to); - } - } - - if (moves.length) { - positioner.move(moves); - gridView.forceLayout(); - } - - previouslySelectedItemIndex = -1; - dir.clearSelection(); - } - } - - FM.Positioner { - id: positioner - folderModel: dir - enabled: true - perStripe: Math.floor(((gridView.flow == GridView.FlowLeftToRight) - ? gridView.width : gridView.height) / ((gridView.flow == GridView.FlowLeftToRight) - ? gridView.cellWidth : gridView.cellHeight)); - } - - FM.ItemViewAdapter { - id: viewAdapter - - adapterView: gridView - adapterModel: positioner - adapterIconSize: gridView.iconSize * 2 - adapterVisibleArea: Qt.rect(gridView.contentX, gridView.contentY, gridView.contentWidth, gridView.contentHeight) - - Component.onCompleted: { - gridView.movementStarted.connect(viewAdapter.viewScrolled); - dir.viewAdapter = viewAdapter; - } - } - - Component { - id: rubberBandObject - - FM.RubberBand { - id: rubberBand - - width: 0 - height: 0 - z: 99999 - color: Meui.Theme.highlightColor - - function close() { - opacityAnimation.restart() - } - - OpacityAnimator { - id: opacityAnimation - target: rubberBand - to: 0 - from: 1 - duration: 150 - - // This easing curve has an elognated start, which works - // better than a standard easing curve for the rubberband - // animation, which fades out fast and is generally of a - // small area. - easing { - bezierCurve: [0.4, 0.0, 1, 1] - type: Easing.Bezier - } - - onFinished: { - rubberBand.visible = false - rubberBand.enabled = false - rubberBand.destroy() - } - } - } - } - - Component { - id: editorComponent - - TextArea { - id: editor - - visible: false - - wrapMode: TextEdit.Wrap - - textMargin: 0 - - horizontalAlignment: TextEdit.AlignHCenter - - property Item targetItem: null - - onTargetItemChanged: { - if (targetItem != null) { - var xy = getXY(); - x = xy[0]; - y = xy[1]; - width = getWidth(); - height = getInitHeight(); - text = targetItem.label.text; - adjustSize(); - editor.select(0, dir.fileExtensionBoundary(positioner.map(targetItem.index))); - if(isPopup) { - flickableItem.contentX = Math.max(flickableItem.contentWidth - contentItem.width, 0); - } else { - flickableItem.contentY = Math.max(flickableItem.contentHeight - contentItem.height, 0); - } - visible = true; - } else { - x: 0 - y: 0 - visible = false; - } - } - - onVisibleChanged: { - if (visible) { - focus = true; - } else { - scrollArea.focus = true; - } - } - - Keys.onPressed: { - switch(event.key) { - case Qt.Key_Return: - case Qt.Key_Enter: - commit(); - break; - case Qt.Key_Escape: - if (targetItem) { - targetItem = null; - event.accepted = true; - } - break; - case Qt.Key_Home: - if (event.modifiers & Qt.ShiftModifier) { - editor.select(0, cursorPosition); - } else { - editor.select(0, 0); - } - event.accepted = true; - break; - case Qt.Key_End: - if (event.modifiers & Qt.ShiftModifier) { - editor.select(cursorPosition, text.length); - } else { - editor.select(text.length, text.length); - } - event.accepted = true; - break; - default: - adjustSize(); - break; - } - } - - Keys.onReleased: { - adjustSize(); - } - - function getXY() { - var pos = control.mapFromItem(targetItem, targetItem.labelArea.x, targetItem.labelArea.y); - var _x, _y; - - _x = targetItem.x + Math.abs(Math.min(gridView.contentX, gridView.originX)); - _x += __style.padding.left; - _x += scrollArea.viewport.x; - if (verticalScrollBarPolicy == Qt.ScrollBarAlwaysOn - && gridView.effectiveLayoutDirection == Qt.RightToLeft) { - _x -= __verticalScrollBar.parent.verticalScrollbarOffset; - } - _y = pos.y + PlasmaCore.Units.smallSpacing - __style.padding.top; - - return([ _x, _y ]); - } - - function getWidth(addWidthVerticalScroller) { - return targetItem.label.parent.width - PlasmaCore.Units.smallSpacing; - } - - function getHeight(addWidthHoriozontalScroller, init) { - var _height; - if (init) { - _height = targetItem.labelArea.height + __style.padding.top + __style.padding.bottom; - } else { - var realHeight = contentHeight + __style.padding.top + __style.padding.bottom; - _height = realHeight; - } - return(_height + (addWidthHoriozontalScroller ? __horizontalScrollBar.parent.horizontalScrollbarOffset : 0)); - } - - function getInitHeight() { - return(getHeight(false, true)); - } - - function adjustSize() { - if (isPopup) { - if (contentWidth + __style.padding.left + __style.padding.right > width) { - visible = true; - horizontalScrollBarPolicy = Qt.ScrollBarAlwaysOn; - height = getHeight(true); - } else { - horizontalScrollBarPolicy = Qt.ScrollBarAlwaysOff; - height = getHeight(); - } - } else { - height = getHeight(); - if(contentHeight + __style.padding.top + __style.padding.bottom > height) { - visible = true; - verticalScrollBarPolicy = Qt.ScrollBarAlwaysOn; - width = getWidth(true); - } else { - verticalScrollBarPolicy = Qt.ScrollBarAlwaysOff; - width = getWidth(); - } - } - - var xy = getXY(); - x = xy[0]; - y = xy[1]; - } - - function commit() { - if (targetItem) { - dir.rename(positioner.map(targetItem.index), text); - targetItem = null; - } - } - } - -// Component.onCompleted: { -// dir.requestRename.connect(rename); -// } - } -} diff --git a/qml/Desktop/FolderItemDelegate.qml b/qml/Desktop/FolderItemDelegate.qml deleted file mode 100644 index be3d58b..0000000 --- a/qml/Desktop/FolderItemDelegate.qml +++ /dev/null @@ -1,116 +0,0 @@ -import QtQuick 2.12 -import QtQuick.Controls 2.12 -import QtQuick.Layouts 1.12 -import QtGraphicalEffects 1.0 - -import MeuiKit 1.0 as Meui - -import org.kde.plasma.core 2.0 as PlasmaCore - -Item { - id: main - - property int index: model.index - property string name: model.blank ? "" : model.display - property bool blank: model.blank - property bool isDir: model.blank ? false : model.isDir - property bool selected: model.blank ? false : model.selected - property Item frame: contentItem - property Item iconArea: icon - property Item labelArea: label - - property bool hovered: (main.GridView.view.hoveredItem === main) - - property color hoveredColor: Qt.rgba(Meui.Theme.highlightColor.r, - Meui.Theme.highlightColor.g, - Meui.Theme.highlightColor.b, 0.1) - property color selectedColor: Qt.rgba(Meui.Theme.highlightColor.r, - Meui.Theme.highlightColor.g, - Meui.Theme.highlightColor.b, 0.9) - - Accessible.name: name - Accessible.role: Accessible.Canvas - - onSelectedChanged: { - if (selected && !blank) { - contentItem.grabToImage(function(result) { - dir.addItemDragImage(positioner.map(index), main.x + contentItem.x, main.y + contentItem.y, contentItem.width, contentItem.height, result.image); - }); - } - } - - Rectangle { - anchors.fill: parent - anchors.margins: Meui.Units.largeSpacing - radius: Meui.Theme.bigRadius - color: selected ? selectedColor : main.hovered ? hoveredColor : "transparent" - - border.color: Qt.rgba(Meui.Theme.highlightColor.r, - Meui.Theme.highlightColor.g, - Meui.Theme.highlightColor.b, 0.3) - border.width: main.hovered || selected ? 1 : 0 - } - - Item { - id: contentItem - anchors.fill: parent - anchors.margins: Meui.Units.largeSpacing - - PlasmaCore.IconItem { - id: icon - z: 2 - - anchors.top: parent.top - anchors.topMargin: Meui.Units.smallSpacing - anchors.horizontalCenter: parent.horizontalCenter - - height: main.height * 0.55 - width: height - - animated: false - usesPlasmaTheme: false - smooth: true - source: model.blank ? "" : model.decoration - overlays: model.blank ? "" : model.overlays - } - - Label { - id: label - z: 2 - - anchors.top: icon.bottom - anchors.topMargin: Meui.Units.smallSpacing - anchors.left: parent.left - anchors.right: parent.right - anchors.bottom: parent.bottom - - width: parent.width - - textFormat: Text.PlainText - - horizontalAlignment: Qt.AlignHCenter - verticalAlignment: Qt.AlignTop - - wrapMode: Text.Wrap - elide: Text.ElideRight - color: "#FFFFFF" - opacity: model.isHidden ? 0.6 : 1 - text: model.blank ? "" : model.display - font.italic: model.isLink - } - - DropShadow { - anchors.fill: label - z: 1 - horizontalOffset: 1 - verticalOffset: 1 - radius: Math.round(4 * Meui.Units.devicePixelRatio) - samples: radius * 2 + 1 - spread: 0.35 - color: "black" - opacity: model.isHidden ? 0.4 : 0.5 - source: label - visible: !selected - } - } -} diff --git a/qml/Desktop/FolderTools.js b/qml/Desktop/FolderTools.js deleted file mode 100644 index 2e8569d..0000000 --- a/qml/Desktop/FolderTools.js +++ /dev/null @@ -1,43 +0,0 @@ -function effectiveNavDirection(flow, layoutDirection, direction) { - if (direction === Qt.LeftArrow) { - if (flow === GridView.FlowLeftToRight) { - if (layoutDirection === Qt.LeftToRight) { - return Qt.LeftArrow; - } else { - return Qt.RightArrow; - } - } else { - if (layoutDirection === Qt.LeftToRight) { - return Qt.UpArrow; - } else { - return Qt.DownArrow; - } - } - } else if (direction === Qt.RightArrow) { - if (flow === GridView.FlowLeftToRight) { - if (layoutDirection === Qt.LeftToRight) { - return Qt.RightArrow; - } else { - return Qt.LeftArrow; - } - } else { - if (layoutDirection === Qt.LeftToRight) { - return Qt.DownArrow; - } else { - return Qt.UpArrow; - } - } - } else if (direction === Qt.UpArrow) { - if (flow === GridView.FlowLeftToRight) { - return Qt.UpArrow; - } else { - return Qt.LeftArrow; - } - } else if (direction === Qt.DownArrow) { - if (flow === GridView.FlowLeftToRight) { - return Qt.DownArrow; - } else { - return Qt.RightArrow - } - } -} diff --git a/qml/Desktop/FolderViewDropArea.qml b/qml/Desktop/FolderViewDropArea.qml deleted file mode 100644 index c82118e..0000000 --- a/qml/Desktop/FolderViewDropArea.qml +++ /dev/null @@ -1,53 +0,0 @@ -import QtQuick 2.12 -import MeuiKit 1.0 as Meui -import org.kde.draganddrop 2.0 as DragDrop - -DragDrop.DropArea { - id: dropArea - - property Item folderView: null - - function handleDragMove(folderView, pos) { - // Trigger autoscroll. - folderView.scrollLeft = (pos.x < (Meui.Units.largeSpacing * 3)); - folderView.scrollRight = (pos.x > width - (Meui.Units.largeSpacing * 3)); - folderView.scrollUp = (pos.y < (Meui.Units.largeSpacing * 3)); - folderView.scrollDown = (pos.y > height - (Meui.Units.largeSpacing * 3)); - - folderView.handleDragMove(pos.x, pos.y); - } - - function handleDragEnd(folderView) { - // Cancel autoscroll. - folderView.scrollLeft = false; - folderView.scrollRight = false; - folderView.scrollUp = false; - folderView.scrollDown = false; - - folderView.endDragMove(); - } - - onDragMove: { - // TODO: We should reject drag moves onto file items that don't accept drops - // (cf. QAbstractItemModel::flags() here, but DeclarativeDropArea currently - // is currently incapable of rejecting drag events. - - if (folderView) { - handleDragMove(folderView, mapToItem(folderView, event.x, event.y)); - } - } - - onDragLeave: { - if (folderView) { - handleDragEnd(folderView); - } - } - - onDrop: { - if (folderView) { - handleDragEnd(folderView); - - folderView.drop(folderView, event, mapToItem(folderView, event.x, event.y)); - } - } -} diff --git a/qml/Desktop/main.qml b/qml/Desktop/main.qml new file mode 100644 index 0000000..822951c --- /dev/null +++ b/qml/Desktop/main.qml @@ -0,0 +1,111 @@ +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Layouts 1.12 +import QtGraphicalEffects 1.0 + +import Cutefish.FileManager 1.0 +import MeuiKit 1.0 as Meui +import "../" + +Item { + id: rootItem + + DesktopSettings { + id: settings + } + + Loader { + id: backgroundLoader + anchors.fill: parent + sourceComponent: settings.backgroundType === 0 ? wallpaper : background + } + + Component { + id: background + + Rectangle { + anchors.fill: parent + color: settings.backgroundColor + } + } + + Component { + id: wallpaper + + Image { + source: "file://" + settings.wallpaper + sourceSize: Qt.size(width, height) + fillMode: Image.PreserveAspectCrop + clip: true + cache: false + + ColorOverlay { + id: dimsWallpaper + anchors.fill: parent + source: parent + color: "#000000" + opacity: Meui.Theme.darkMode && settings.dimsWallpaper ? 0.4 : 0.0 + + Behavior on opacity { + NumberAnimation { + duration: 200 + } + } + + } + } + } + + FolderGridView { + anchors.fill: parent + cellHeight: 128 + cellWidth: 128 + model: FolderModel { + id: folderModel + url: desktopPath() + } + + leftMargin: desktopView.screenAvailableRect ? desktopView.screenAvailableRect.x : 0 + topMargin: desktopView.screenAvailableRect ? desktopView.screenAvailableRect.y : 0 + rightMargin: desktopView.screenRect.width - (desktopView.screenAvailableRect.x + desktopView.screenAvailableRect.width) + bottomMargin: desktopView.screenRect.height - (desktopView.screenAvailableRect.y + desktopView.screenAvailableRect.height) + + delegate: FolderGridItem {} + } + + Component { + id: rubberBandObject + + RubberBand { + id: rubberBand + + width: 0 + height: 0 + z: 99999 + color: Meui.Theme.highlightColor + + function close() { + opacityAnimation.restart() + } + + OpacityAnimator { + id: opacityAnimation + target: rubberBand + to: 0 + from: 1 + duration: 150 + + easing { + bezierCurve: [0.4, 0.0, 1, 1] + type: Easing.Bezier + } + + onFinished: { + rubberBand.visible = false + rubberBand.enabled = false + rubberBand.destroy() + } + } + } + } +} diff --git a/qml/Dialogs/CreateFolderDialog.qml b/qml/Dialogs/CreateFolderDialog.qml new file mode 100644 index 0000000..ad7209f --- /dev/null +++ b/qml/Dialogs/CreateFolderDialog.qml @@ -0,0 +1,12 @@ +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Layouts 1.12 +import MeuiKit 1.0 as Meui + +Dialog { + id: control + modal: true + + x: (parent.width - control.width) / 2 + y: (parent.height - control.height) / 2 +} diff --git a/qml/Dialogs/PropertiesDialog.qml b/qml/Dialogs/PropertiesDialog.qml index 9e4219c..7ff9f38 100644 --- a/qml/Dialogs/PropertiesDialog.qml +++ b/qml/Dialogs/PropertiesDialog.qml @@ -7,7 +7,7 @@ import MeuiKit 1.0 as Meui Window { id: control title: qsTr("Properties") - flags: Qt.Dialog | Qt.WindowStaysOnTopHint + flags: Qt.Dialog visible: true @@ -67,7 +67,7 @@ Window { focus: true Layout.fillWidth: true Keys.onEscapePressed: control.close() - enabled: !main.multiple + enabled: !main.multiple && main.isWritable } } @@ -83,6 +83,7 @@ Window { Label { text: qsTr("Type:") Layout.alignment: Qt.AlignRight + color: Meui.Theme.disabledTextColor visible: mimeType.visible } @@ -95,6 +96,7 @@ Window { Label { text: qsTr("Location:") Layout.alignment: Qt.AlignRight + color: Meui.Theme.disabledTextColor } Label { @@ -105,18 +107,20 @@ Window { Label { text: qsTr("Size:") Layout.alignment: Qt.AlignRight + color: Meui.Theme.disabledTextColor // visible: size.visible } Label { id: size - text: main.size + text: main.size ? main.size : qsTr("Calculating...") // visible: text } Label { text: qsTr("Created:") Layout.alignment: Qt.AlignRight + color: Meui.Theme.disabledTextColor visible: creationTime.visible } @@ -129,6 +133,7 @@ Window { Label { text: qsTr("Modified:") Layout.alignment: Qt.AlignRight + color: Meui.Theme.disabledTextColor visible: modifiedTime.visible } @@ -141,6 +146,7 @@ Window { Label { text: qsTr("Accessed:") Layout.alignment: Qt.AlignRight + color: Meui.Theme.disabledTextColor visible: accessTime.visible } @@ -162,13 +168,19 @@ Window { Button { text: qsTr("Cancel") Layout.fillWidth: true - onClicked: control.close() + onClicked: { + control.close() + main.reject() + } } Button { text: qsTr("OK") Layout.fillWidth: true - onClicked: control.close() + onClicked: { + main.accept(_textField.text) + control.close() + } flat: true } } diff --git a/qml/FolderGridItem.qml b/qml/FolderGridItem.qml new file mode 100644 index 0000000..cf77e93 --- /dev/null +++ b/qml/FolderGridItem.qml @@ -0,0 +1,102 @@ +import QtQuick 2.12 +import QtQuick.Layouts 1.12 +import QtQuick.Controls 2.12 +import QtGraphicalEffects 1.0 + +import Cutefish.FileManager 1.0 +import MeuiKit 1.0 as Meui + +Item { + id: control + + width: GridView.view.cellWidth + height: GridView.view.cellHeight + + property Item iconArea: _image.visible ? _image : _icon + property Item textArea: _label + + property int index: model.index + property bool hovered: GridView.view.hoveredItem === control + property bool selected: model.selected + property bool blank: model.blank + + ColumnLayout { + anchors.fill: parent + anchors.margins: Meui.Units.largeSpacing + spacing: Meui.Units.smallSpacing + + Item { + id: _iconItem + Layout.preferredHeight: parent.height * 0.7 + Layout.fillWidth: true + + Image { + id: _icon + anchors.centerIn: parent + width: parent.height + height: width + sourceSize: Qt.size(width, height) + source: "image://icontheme/" + model.iconName + visible: !_image.visible + } + + Image { + id: _image + anchors.fill: parent + anchors.leftMargin: Meui.Units.smallSpacing + anchors.rightMargin: Meui.Units.smallSpacing + + fillMode: Image.PreserveAspectCrop + visible: status === Image.Ready + horizontalAlignment: Qt.AlignHCenter + verticalAlignment: Qt.AlignVCenter + sourceSize.width: width + sourceSize.height: height + source: model.thumbnail ? model.thumbnail : "" + asynchronous: true + cache: true + + layer.enabled: true + layer.effect: OpacityMask { + maskSource: Item { + width: _image.width + height: _image.height + + Rectangle { + anchors.centerIn: parent + width: Math.min(parent.width, _image.paintedWidth) + height: Math.min(parent.height, _image.paintedHeight) + radius: Meui.Theme.smallRadius + } + } + } + } + } + + Item { + Layout.fillHeight: true + Layout.fillWidth: true + + Layout.preferredHeight: Math.min(_label.implicitHeight, height) + + Rectangle { + anchors.centerIn: parent + width: Math.min(_label.implicitWidth + Meui.Units.largeSpacing, parent.width) + height: Math.min(_label.implicitHeight + Meui.Units.largeSpacing, parent.height) + color: selected ? Meui.Theme.highlightColor : "transparent" + radius: Meui.Theme.smallRadius + } + + Label { + id: _label + anchors.fill: parent + horizontalAlignment: Qt.AlignHCenter + verticalAlignment: Qt.AlignVCenter + elide: Qt.ElideRight + wrapMode: Text.Wrap + color: selected ? Meui.Theme.highlightedTextColor : Meui.Theme.textColor + text: model.fileName + } + } + } +} diff --git a/qml/FolderGridView.qml b/qml/FolderGridView.qml new file mode 100644 index 0000000..0e65587 --- /dev/null +++ b/qml/FolderGridView.qml @@ -0,0 +1,380 @@ +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Layouts 1.12 + +import Cutefish.FileManager 1.0 +import MeuiKit 1.0 as Meui + +GridView { + id: control + + property Item rubberBand: null + property Item hoveredItem: null + property Item pressedItem: null + + property int verticalDropHitscanOffset: 0 + + property int pressX: -1 + property int pressY: -1 + + property int dragX: -1 + property int dragY: -1 + + property bool ctrlPressed: false + property bool shiftPressed: false + + property int previouslySelectedItemIndex: -1 + property variant cPress: null + property Item editor: null + property int anchorIndex: 0 + + property var itemSize: 96 + Meui.Units.fontMetrics.height + + property var itemWidth: itemSize + Meui.Units.smallSpacing + property var itemHeight: itemSize + Meui.Units.largeSpacing + + property variant cachedRectangleSelection: null + + property bool scrollLeft: false + property bool scrollRight: false + property bool scrollUp: false + property bool scrollDown: false + + signal keyPress(var event) + + leftMargin: Meui.Units.smallSpacing + rightMargin: Meui.Units.smallSpacing + + highlightMoveDuration: 0 + keyNavigationEnabled : true + keyNavigationWraps : true + Keys.onPressed: { + if (event.key === Qt.Key_Control) { + ctrlPressed = true + } else if (event.key === Qt.Key_Shift) { + shiftPressed = true + + if (currentIndex != -1) + anchorIndex = currentIndex + } + + control.keyPress(event) + } + Keys.onReleased: { + if (event.key === Qt.Key_Control) { + ctrlPressed = false + } else if (event.key === Qt.Key_Shift) { + shiftPressed = false + anchorIndex = 0 + } + } + + cellHeight: { + // var extraHeight = calcExtraSpacing(itemHeight, control.height - topMargin - bottomMargin) + return itemHeight // + extraHeight + } + + cellWidth: { + var extraWidth = calcExtraSpacing(itemWidth, control.width - leftMargin - rightMargin) + return itemWidth + extraWidth + } + + clip: true + currentIndex: -1 + ScrollBar.vertical: ScrollBar { + bottomPadding: Meui.Theme.mediumRadius + } + + onPressXChanged: { + cPress = mapToItem(control.contentItem, pressX, pressY) + } + + onPressYChanged: { + cPress = mapToItem(control.contentItem, pressX, pressY) + } + + onCachedRectangleSelectionChanged: { + if (cachedRectangleSelection === null) + return + + if (cachedRectangleSelection.length) + control.currentIndex[0] + + folderModel.updateSelection(cachedRectangleSelection, control.ctrlPressed) + } + + MouseArea { + id: _mouseArea + anchors.fill: parent + propagateComposedEvents: true + preventStealing: true + acceptedButtons: Qt.RightButton | Qt.LeftButton + hoverEnabled: true + enabled: true + z: -1 + + onDoubleClicked: { + if (mouse.button === Qt.LeftButton && control.pressedItem) + folderModel.openSelected() + } + + onPressed: { + control.forceActiveFocus() + +// if (mouse.source === Qt.MouseEventSynthesizedByQt) { +// var index = control.indexAt(mouse.x, mouse.y) +// var indexItem = control.itemAtIndex(index) +// if (indexItem && indexItem.iconArea) { +// control.currentIndex = index +// hoveredItem = indexItem +// } else { +// hoveredItem = null +// } +// } + + pressX = mouse.x + pressY = mouse.y + + if (!hoveredItem || hoveredItem.blank) { + if (!control.ctrlPressed) { + control.currentIndex = -1 + previouslySelectedItemIndex = -1 + folderModel.clearSelection() + } + + if (mouse.buttons & Qt.RightButton) { + clearPressState() + folderModel.openContextMenu(null, mouse.modifiers) + mouse.accepted = true + } + } else { + pressedItem = hoveredItem + + var pos = mapToItem(hoveredItem, mouse.x, mouse.y) + + if (control.shiftPressed && control.currentIndex !== -1) { + folderModel.setRangeSelected(control.anchorIndex, hoveredItem.index) + } else { + if (!control.ctrlPressed && !folderModel.isSelected(hoveredItem.index)) { + previouslySelectedItemIndex = -1 + folderModel.clearSelection() + } + + if (control.ctrlPressed) { + folderModel.toggleSelected(hoveredItem.index) + } else { + folderModel.setSelected(hoveredItem.index) + } + } + + control.currentIndex = hoveredItem.index + + if (mouse.buttons & Qt.RightButton) { + clearPressState() + folderModel.openContextMenu(null, mouse.modifiers) + mouse.accepted = true + } + } + } + + onPositionChanged: { + control.ctrlPressed = (mouse.modifiers & Qt.ControlModifier) + control.shiftPressed = (mouse.modifiers & Qt.ShiftModifier) + + var cPos = mapToItem(control.contentItem, mouse.x, mouse.y) + var item = control.itemAt(cPos.x, cPos.y) + var leftEdge = Math.min(control.contentX, control.originX) + + if (!item || item.blank) { + if (control.hoveredItem/* && !root.containsDrag*/) { + control.hoveredItem = null + } + } else { + var fPos = mapToItem(item.iconArea, mouse.x, mouse.y) + + if (fPos.x < 0 || fPos.y < 0 || fPos.x > item.iconArea.width || fPos.y > item.iconArea.height) { + control.hoveredItem = null + } else { + control.hoveredItem = item + } + } + + // Trigger autoscroll. + if (pressX != -1) { + control.scrollLeft = (mouse.x <= 0 && control.contentX > leftEdge); + control.scrollRight = (mouse.x >= control.width + && control.contentX < control.contentItem.width - control.width); + control.scrollUp = (mouse.y <= 0 && control.contentY > 0); + control.scrollDown = (mouse.y >= control.height + && control.contentY < control.contentItem.height - control.height); + } + + // Update rubberband geometry. + if (control.rubberBand) { + var rB = control.rubberBand + + if (cPos.x < cPress.x) { + rB.x = Math.max(leftEdge, cPos.x) + rB.width = Math.abs(rB.x - cPress.x) + } else { + rB.x = cPress.x + var ceil = Math.max(control.width, control.contentItem.width) + leftEdge + rB.width = Math.min(ceil - rB.x, Math.abs(rB.x - cPos.x)) + } + + if (cPos.y < cPress.y) { + rB.y = Math.max(0, cPos.y) + rB.height = Math.abs(rB.y - cPress.y) + } else { + rB.y = cPress.y + var ceil = Math.max(control.height, control.contentItem.height) + rB.height = Math.min(ceil - rB.y, Math.abs(rB.y - cPos.y)) + } + + // Ensure rubberband is at least 1px in size or else it will become + // invisible and not match any items. + rB.width = Math.max(1, rB.width) + rB.height = Math.max(1, rB.height) + + control.rectangleSelect(rB.x, rB.y, rB.width, rB.height) + + return + } + + if (pressX != -1) { + if (pressedItem != null && folderModel.isSelected(pressedItem.index)) { + control.dragX = mouse.x + control.dragY = mouse.y + control.verticalDropHitscanOffset = pressedItem.y + (pressedItem.height / 2) + folderModel.dragSelected(mouse.x, mouse.y) + control.dragX = -1 + control.dragY = -1 + clearPressState() + } else { + folderModel.pinSelection() + control.rubberBand = rubberBandObject.createObject(control.contentItem, {x: cPress.x, y: cPress.y}) + control.interactive = false + } + } + } + + onClicked: { + clearPressState() + + if (mouse.button === Qt.RightButton) + return + + if (!hoveredItem) + return; + +// var pos = mapToItem(hoveredItem, mouse.x, mouse.y); +// if (pos.x < 0 || pos.x > hoveredItem.width || pos.y < 0 || pos.y > hoveredItem.height) { +// hoveredItem = null +// previouslySelectedItemIndex = -1 +// folderModel.clearSelection() +// return +// } + } + + onReleased: pressCanceled() + onCanceled: pressCanceled() + } + + function clearPressState() { + pressedItem = null + pressX = -1 + pressY = -1 + } + + function pressCanceled() { + if (control.rubberBand) { + control.rubberBand.close() + control.rubberBand = null + + control.interactive = true + // control.cachedRectangleSelection = null + folderModel.unpinSelection() + } + + clearPressState() + } + + function rectangleSelect(x, y, width, height) { + var rows = (control.flow === GridView.FlowLeftToRight) + var axis = rows ? control.width : control.height + var step = rows ? cellWidth : cellHeight + var perStripe = Math.floor(axis / step) + var stripes = Math.ceil(control.count / perStripe) + var cWidth = control.cellWidth - (2 * Meui.Units.smallSpacing) + var cHeight = control.cellHeight - (2 * Meui.Units.smallSpacing) + var midWidth = control.cellWidth / 2 + var midHeight = control.cellHeight / 2 + var indices = [] + + for (var s = 0; s < stripes; s++) { + for (var i = 0; i < perStripe; i++) { + var index = (s * perStripe) + i + + if (index >= control.count) { + break + } + + if (folderModel.isBlank(index)) { + continue + } + + var itemX = ((rows ? i : s) * control.cellWidth) + var itemY = ((rows ? s : i) * control.cellHeight) + + if (control.effectiveLayoutDirection === Qt.RightToLeft) { + itemX -= (rows ? control.contentX : control.originX) + itemX += cWidth + itemX = (rows ? control.width : control.contentItem.width) - itemX + } + + // Check if the rubberband intersects this cell first to avoid doing more + // expensive work. + if (control.rubberBand.intersects(Qt.rect(itemX + Meui.Units.smallSpacing, itemY + Meui.Units.smallSpacing, + cWidth, cHeight))) { + var item = control.contentItem.childAt(itemX + midWidth, itemY + midHeight) + + // If this is a visible item, check for intersection with the actual + // icon or label rects for better feel. + if (item && item.iconArea) { + var iconRect = Qt.rect(itemX + item.iconArea.x, itemY + item.iconArea.y, + item.iconArea.width, item.iconArea.height) + + if (control.rubberBand.intersects(iconRect)) { + indices.push(index) + continue + } + + var labelRect = Qt.rect(itemX + item.textArea.x, itemY + item.textArea.y, + item.textArea.width, item.textArea.height) + + if (control.rubberBand.intersects(labelRect)) { + indices.push(index) + continue + } + } else { + // Otherwise be content with the cell intersection. + indices.push(index) + } + } + } + } + + control.cachedRectangleSelection = indices + } + + function calcExtraSpacing(cellSize, containerSize) { + var availableColumns = Math.floor(containerSize / cellSize) + var extraSpacing = 0 + if (availableColumns > 0) { + var allColumnSize = availableColumns * cellSize + var extraSpace = Math.max(containerSize - allColumnSize, Meui.Units.smallSpacing) + extraSpacing = extraSpace / availableColumns + } + return Math.floor(extraSpacing) + } +} diff --git a/qml/FolderIconDelegate.qml b/qml/FolderIconDelegate.qml deleted file mode 100644 index 5a9dd61..0000000 --- a/qml/FolderIconDelegate.qml +++ /dev/null @@ -1,102 +0,0 @@ -import QtQuick 2.12 -import QtQuick.Controls 2.12 -import QtQuick.Layouts 1.12 -import QtGraphicalEffects 1.0 - -import MeuiKit 1.0 as Meui - -import org.kde.plasma.core 2.0 as PlasmaCore - -Item { - id: main - - property int index: model.index - property string name: model.blank ? "" : model.display - property bool blank: model.blank - property bool isDir: model.blank ? false : model.isDir - property bool selected: model.blank ? false : model.selected - property Item frame: contentItem - property Item iconArea: icon - property Item labelArea: label - - property bool hovered: (main.GridView.view.hoveredItem === main) - - property color hoveredColor: Qt.rgba(Meui.Theme.highlightColor.r, - Meui.Theme.highlightColor.g, - Meui.Theme.highlightColor.b, 0.1) - property color selectedColor: Qt.rgba(Meui.Theme.highlightColor.r, - Meui.Theme.highlightColor.g, - Meui.Theme.highlightColor.b, 0.9) - - Accessible.name: name - Accessible.role: Accessible.Canvas - - onSelectedChanged: { - if (selected && !blank) { - contentItem.grabToImage(function(result) { - dirModel.addItemDragImage(positioner.map(index), main.x + contentItem.x, main.y + contentItem.y, contentItem.width, contentItem.height, result.image); - }); - } - } - - Rectangle { - anchors.fill: parent - anchors.margins: Meui.Units.largeSpacing - radius: Meui.Theme.bigRadius - color: selected ? selectedColor : main.hovered ? hoveredColor : "transparent" - - border.color: Qt.rgba(Meui.Theme.highlightColor.r, - Meui.Theme.highlightColor.g, - Meui.Theme.highlightColor.b, 0.3) - border.width: main.hovered || selected ? 1 : 0 - } - - Item { - id: contentItem - anchors.fill: parent - anchors.margins: Meui.Units.largeSpacing - - PlasmaCore.IconItem { - id: icon - z: 2 - - anchors.top: parent.top - anchors.topMargin: Meui.Units.smallSpacing - anchors.horizontalCenter: parent.horizontalCenter - - height: main.height * 0.55 - width: height - - animated: false - usesPlasmaTheme: false - smooth: true - source: model.blank ? "" : model.decoration - overlays: model.blank ? "" : model.overlays - } - - Label { - id: label - z: 2 - - anchors.top: icon.bottom - anchors.topMargin: Meui.Units.smallSpacing - anchors.left: parent.left - anchors.right: parent.right - anchors.bottom: parent.bottom - - width: parent.width - - textFormat: Text.PlainText - - horizontalAlignment: Qt.AlignHCenter - verticalAlignment: Qt.AlignTop - - wrapMode: Text.Wrap - elide: Text.ElideRight - color: selected ? Meui.Theme.highlightedTextColor : Meui.Theme.textColor - opacity: model.isHidden ? 0.6 : 1 - text: model.blank ? "" : model.display - font.italic: model.isLink - } - } -} diff --git a/qml/FolderIconView.qml b/qml/FolderIconView.qml deleted file mode 100644 index ff30246..0000000 --- a/qml/FolderIconView.qml +++ /dev/null @@ -1,381 +0,0 @@ -import QtQuick 2.12 -import QtQuick.Controls 2.12 - -import MeuiKit 1.0 as Meui -import Cutefish.FileManager 1.0 as FM - -GridView { - id: control - - // XXXX - property var iconSize: 130 + Meui.Units.largeSpacing - - cellWidth: { - var extraWidth = calcExtraSpacing(iconSize, control.width - leftMargin - rightMargin); - return iconSize + extraWidth; - } - - cellHeight: { - var extraHeight = calcExtraSpacing(iconSize, control.height - topMargin - bottomMargin); - return iconSize + extraHeight; - } - - ScrollBar.vertical: ScrollBar {} - clip: true - - property Item rubberBand: null - - property Item hoveredItem: null - property Item pressedItem: null - - property int pressX: -1 - property int pressY: -1 - property int dragX: -1 - property int dragY: -1 - property variant cPress: null - property bool doubleClickInProgress: false - - property int anchorIndex: 0 - property bool ctrlPressed: false - property bool shiftPressed: false - - property bool overflowing: (visibleArea.heightRatio < 1.0 || visibleArea.widthRatio < 1.0) - - property bool scrollLeft: false - property bool scrollRight: false - property bool scrollUp: false - property bool scrollDown: false - - property variant cachedRectangleSelection: null - property int previouslySelectedItemIndex: -1 - property int verticalDropHitscanOffset: 0 - - flow: GridView.FlowLeftToRight - - currentIndex: -1 - - onPressXChanged: { - cPress = mapToItem(control.contentItem, pressX, pressY); - } - - onPressYChanged: { - cPress = mapToItem(control.contentItem, pressX, pressY); - } - - MouseArea { - anchors.fill: parent - acceptedButtons: Qt.LeftButton | Qt.RightButton - // propagateComposedEvents: true - hoverEnabled: true - z: -1 - - onPressed: { - control.forceActiveFocus() - - if (mouse.source === Qt.MouseEventSynthesizedByQt) { - var index = control.indexAt(mouse.x, mouse.y) - var indexItem = control.itemAtIndex(index) - if (indexItem && indexItem.iconArea) { - control.currentIndex = index - hoveredItem = indexItem - } else { - hoveredItem = null - } - } - - pressX = mouse.x - pressY = mouse.y - - if (!hoveredItem || hoveredItem.blank) { - if (!control.ctrlPressed) { - control.currentIndex = -1; - previouslySelectedItemIndex = -1; - dirModel.clearSelection(); - } - - if (mouse.buttons & Qt.RightButton) { - clearPressState() - dirModel.openContextMenu(null, mouse.modifiers) - mouse.accepted = true - } - } else { - pressedItem = hoveredItem; - - if (control.shiftPressed && control.currentIndex !== -1) { - positioner.setRangeSelected(control.anchorIndex, hoveredItem.index); - } else { - if (!control.ctrlPressed && !dirModel.isSelected(positioner.map(hoveredItem.index))) { - previouslySelectedItemIndex = -1; - dirModel.clearSelection(); - } - - if (control.ctrlPressed) { - dirModel.toggleSelected(positioner.map(hoveredItem.index)); - } else { - dirModel.setSelected(positioner.map(hoveredItem.index)); - } - - control.currentIndex = hoveredItem.index; - - if (mouse.buttons & Qt.RightButton) { - clearPressState(); - - dirModel.openContextMenu(null, mouse.modifiers); - mouse.accepted = true; - } - } - } - } - - onPositionChanged: { - control.ctrlPressed = (mouse.modifiers & Qt.ControlModifier); - control.shiftPressed = (mouse.modifiers & Qt.ShiftModifier); - - var cPos = mapToItem(control.contentItem, mouse.x, mouse.y); - var item = control.itemAt(cPos.x, cPos.y); - var leftEdge = Math.min(control.contentX, control.originX); - - if (!item || item.blank) { - if (control.hoveredItem && !root.containsDrag) { - control.hoveredItem = null; - } - } else { - var fPos = mapToItem(item, mouse.x, mouse.y); - - if (fPos.x < 0 || fPos.y < 0 || fPos.x > item.width || fPos.y > item.height) { - control.hoveredItem = null; - } else { - control.hoveredItem = item - } - } - - // Trigger autoscroll. - if (pressX != -1) { - control.scrollLeft = (mouse.x <= 0 && control.contentX > leftEdge); - control.scrollRight = (mouse.x >= control.width - && control.contentX < control.contentItem.width - control.width); - control.scrollUp = (mouse.y <= 0 && control.contentY > 0); - control.scrollDown = (mouse.y >= control.height - && control.contentY < control.contentItem.height - control.height); - } - - // Update rubberband geometry. - if (control.rubberBand) { - var rB = control.rubberBand; - - if (cPos.x < cPress.x) { - rB.x = Math.max(leftEdge, cPos.x); - rB.width = Math.abs(rB.x - cPress.x); - } else { - rB.x = cPress.x; - var ceil = Math.max(control.width, control.contentItem.width) + leftEdge; - rB.width = Math.min(ceil - rB.x, Math.abs(rB.x - cPos.x)); - } - - if (cPos.y < cPress.y) { - rB.y = Math.max(0, cPos.y); - rB.height = Math.abs(rB.y - cPress.y); - } else { - rB.y = cPress.y; - var ceil = Math.max(control.height, control.contentItem.height); - rB.height = Math.min(ceil - rB.y, Math.abs(rB.y - cPos.y)); - } - - // Ensure rubberband is at least 1px in size or else it will become - // invisible and not match any items. - rB.width = Math.max(1, rB.width); - rB.height = Math.max(1, rB.height); - - control.rectangleSelect(rB.x, rB.y, rB.width, rB.height); - - return; - } - - // Drag initiation. - if (pressX != -1 /*&& root.isDrag(pressX, pressY, mouse.x, mouse.y)*/) { - if (pressedItem != null && dirModel.isSelected(positioner.map(pressedItem.index))) { - dragX = mouse.x; - dragY = mouse.y; - control.verticalDropHitscanOffset = pressedItem.iconArea.y + (pressedItem.iconArea.height / 2) - dirModel.dragSelected(mouse.x, mouse.y); - dragX = -1; - dragY = -1; - clearPressState(); - } else { - dirModel.pinSelection(); - control.rubberBand = rubberBandObject.createObject(control.contentItem, {x: cPress.x, y: cPress.y}) - control.interactive = false; - } - } - } - - onContainsMouseChanged: { - if (!containsMouse && !control.rubberBand) { - clearPressState(); - - if (control.hoveredItem) { - control.hoveredItem = null; - } - } - } - - onCanceled: pressCanceled() - onReleased: pressCanceled() - } - - function calcExtraSpacing(cellSize, containerSize) { - var availableColumns = Math.floor(containerSize / cellSize); - var extraSpacing = 0; - if (availableColumns > 0) { - var allColumnSize = availableColumns * cellSize; - var extraSpace = Math.max(containerSize - allColumnSize, 0); - extraSpacing = extraSpace / availableColumns; - } - return Math.floor(extraSpacing); - } - - function clearPressState() { - pressedItem = null; - pressX = -1; - pressY = -1; - } - - function rectangleSelect(x, y, width, height) { - var rows = (control.flow === GridView.FlowLeftToRight); - var axis = rows ? control.width : control.height; - var step = rows ? cellWidth : cellHeight; - var perStripe = Math.floor(axis / step); - var stripes = Math.ceil(control.count / perStripe); - var cWidth = control.cellWidth - (2 * Meui.Units.smallSpacing); - var cHeight = control.cellHeight - (2 * Meui.Units.smallSpacing); - var midWidth = control.cellWidth / 2; - var midHeight = control.cellHeight / 2; - var indices = []; - - for (var s = 0; s < stripes; s++) { - for (var i = 0; i < perStripe; i++) { - var index = (s * perStripe) + i; - - if (index >= control.count) { - break; - } - - if (positioner.isBlank(index)) { - continue; - } - - var itemX = ((rows ? i : s) * control.cellWidth); - var itemY = ((rows ? s : i) * control.cellHeight); - - if (control.effectiveLayoutDirection == Qt.RightToLeft) { - itemX -= (rows ? control.contentX : control.originX); - itemX += cWidth; - itemX = (rows ? control.width : control.contentItem.width) - itemX; - } - - // Check if the rubberband intersects this cell first to avoid doing more - // expensive work. - if (control.rubberBand.intersects(Qt.rect(itemX + Meui.Units.smallSpacing, itemY + Meui.Units.smallSpacing, - cWidth, cHeight))) { - var item = control.contentItem.childAt(itemX + midWidth, itemY + midHeight); - - // If this is a visible item, check for intersection with the actual - // icon or label rects for better feel. - if (item && item.iconArea) { - var iconRect = Qt.rect(itemX + item.iconArea.x, itemY + item.iconArea.y, - item.iconArea.width, item.iconArea.height); - - if (control.rubberBand.intersects(iconRect)) { - indices.push(index); - continue; - } - - var labelRect = Qt.rect(itemX + item.labelArea.x, itemY + item.labelArea.y, - item.labelArea.width, item.labelArea.height); - - if (control.rubberBand.intersects(labelRect)) { - indices.push(index); - continue; - } - } else { - // Otherwise be content with the cell intersection. - indices.push(index); - } - } - } - } - - control.cachedRectangleSelection = indices; - } - - onCachedRectangleSelectionChanged: { - if (cachedRectangleSelection == null) { - return; - } - - if (cachedRectangleSelection.length) { - // Set current index to start of selection. - // cachedRectangleSelection is pre-sorted. - currentIndex = cachedRectangleSelection[0]; - } - - dirModel.updateSelection(cachedRectangleSelection.map(positioner.map), control.ctrlPressed); - } - - function pressCanceled() { - if (control.rubberBand) { - control.rubberBand.close() - control.rubberBand = null - - control.interactive = true; - control.cachedRectangleSelection = null; - dirModel.unpinSelection(); - } - - clearPressState(); - control.cancelAutoscroll(); - } - - function cancelAutoscroll() { - scrollLeft = false; - scrollRight = false; - scrollUp = false; - scrollDown = false; - } - - Component { - id: rubberBandObject - - FM.RubberBand { - id: rubberBand - - width: 0 - height: 0 - z: 99999 - color: Meui.Theme.highlightColor - - function close() { - opacityAnimation.restart() - } - - OpacityAnimator { - id: opacityAnimation - target: rubberBand - to: 0 - from: 1 - duration: 150 - - easing { - bezierCurve: [0.4, 0.0, 1, 1] - type: Easing.Bezier - } - - onFinished: { - rubberBand.visible = false - rubberBand.enabled = false - rubberBand.destroy() - } - } - } - } -} diff --git a/qml/FolderListDelegate.qml b/qml/FolderListDelegate.qml deleted file mode 100644 index f556d26..0000000 --- a/qml/FolderListDelegate.qml +++ /dev/null @@ -1,79 +0,0 @@ -import QtQuick 2.4 -import QtQuick.Controls 2.4 -import QtQuick.Layouts 1.3 -import QtGraphicalEffects 1.0 - -import Cutefish.FileManager 1.0 -import MeuiKit 1.0 as Meui -import org.kde.plasma.core 2.0 as PlasmaCore - -Item { - id: control - - property int index: model.index - property string name: model.blank ? "" : model.display - property bool blank: model.blank - property bool isDir: model.blank ? false : model.isDir - property bool selected: model.blank ? false : model.selected - property Item frame: contentItem - property Item iconArea: iconItem - property Item labelArea: label1 - - property color hoveredColor: Qt.rgba(Meui.Theme.textColor.r, - Meui.Theme.textColor.g, - Meui.Theme.textColor.b, 0.1) - - Accessible.name: name - Accessible.role: Accessible.Canvas - - MouseArea { - id: _mouseArea - anchors.fill: parent - hoverEnabled: true - } - - Rectangle { - z: -1 - anchors.fill: parent - radius: Meui.Theme.bigRadius - color: selected ? Meui.Theme.highlightColor : _mouseArea.containsMouse ? control.hoveredColor : "transparent" - } - - RowLayout { - anchors.fill: parent - anchors.leftMargin: Meui.Units.smallSpacing - anchors.rightMargin: Meui.Units.smallSpacing - spacing: Meui.Units.largeSpacing - - Item { - id: iconItem - Layout.fillHeight: true - width: parent.height * 0.8 - - PlasmaCore.IconItem { - id: icon - z: 2 - - anchors.fill: parent - - animated: false - usesPlasmaTheme: false - smooth: true - source: model.blank ? "" : model.decoration - overlays: model.blank ? "" : model.overlays - } - } - - Label { - id: label1 - Layout.fillWidth: true - text: name - color: selected ? Meui.Theme.highlightedTextColor : Meui.Theme.textColor - } - - Label { - id: label2 - color: selected ? Meui.Theme.highlightedTextColor : Meui.Theme.textColor - } - } -} diff --git a/qml/FolderListItem.qml b/qml/FolderListItem.qml new file mode 100644 index 0000000..8121749 --- /dev/null +++ b/qml/FolderListItem.qml @@ -0,0 +1,91 @@ +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Layouts 1.12 +import MeuiKit 1.0 as Meui + +Item { + id: _listItem + width: ListView.view.width - ListView.view.leftMargin - ListView.view.rightMargin + height: 40 + + Accessible.name: fileName + Accessible.role: Accessible.Canvas + + property Item iconArea: _image.visible ? _image : _icon + property Item textArea: _label + + property int index: model.index + property bool hovered: ListView.view.hoveredItem === _listItem + property bool selected: model.selected + property bool blank: model.blank + + property color hoveredColor: Meui.Theme.darkMode ? Qt.lighter(Meui.Theme.backgroundColor, 1.1) + : Qt.darker(Meui.Theme.backgroundColor, 1.05) + property color selectedColor: Meui.Theme.darkMode ? Qt.lighter(Meui.Theme.backgroundColor, 1.2) + : Qt.darker(Meui.Theme.backgroundColor, 1.15) +// onSelectedChanged: { +// if (selected && !blank) { +// _listItem.grabToImage(function(result) { +// folderModel.addItemDragImage(_listItem.index, +// _listItem.x, +// _listItem.y, +// _listItem.width, _listItem.height, result.image) +// }) +// } +// } + + Rectangle { + id: _background + anchors.fill: parent + radius: Meui.Theme.smallRadius + color: selected ? selectedColor : hovered ? hoveredColor : "transparent" + visible: selected || hovered + } + + RowLayout { + id: _mainLayout + anchors.fill: parent + anchors.leftMargin: Meui.Units.smallSpacing + anchors.rightMargin: Meui.Units.smallSpacing + spacing: Meui.Units.largeSpacing + + Item { + id: iconItem + Layout.fillHeight: true + width: parent.height * 0.8 + + Image { + id: _icon + anchors.centerIn: iconItem + width: iconItem.width + height: width + sourceSize.width: width + sourceSize.height: height + source: "image://icontheme/" + model.iconName + visible: !_image.visible + asynchronous: true + } + + Image { + id: _image + width: parent.height * 0.8 + height: width + anchors.centerIn: iconItem + sourceSize: Qt.size(_icon.width, _icon.height) + source: model.thumbnail ? model.thumbnail : "" + visible: _image.status === Image.Ready + fillMode: Image.PreserveAspectFit + asynchronous: true + smooth: false + } + } + + Label { + id: _label + text: model.fileName + Layout.fillWidth: true + color: Meui.Theme.textColor + elide: Qt.ElideMiddle + } + } +} diff --git a/qml/FolderListView.qml b/qml/FolderListView.qml index a34a855..916ada8 100644 --- a/qml/FolderListView.qml +++ b/qml/FolderListView.qml @@ -1,38 +1,364 @@ import QtQuick 2.12 import QtQuick.Controls 2.12 import QtQuick.Layouts 1.12 - import MeuiKit 1.0 as Meui -import Cutefish.FileManager 1.0 as FM +import Cutefish.FileManager 1.0 ListView { id: control - signal clicked(var mouse) - signal positionChanged(var mouse) - signal pressed(var mouse) - signal released(var mouse) + property Item rubberBand: null + property Item hoveredItem: null + property Item pressedItem: null - ScrollBar.vertical: ScrollBar {} - spacing: Meui.Units.largeSpacing + property int verticalDropHitscanOffset: 0 + + property int pressX: -1 + property int pressY: -1 + + property int dragX: -1 + property int dragY: -1 + + property bool ctrlPressed: false + property bool shiftPressed: false + + property int previouslySelectedItemIndex: -1 + property variant cPress: null + property Item editor: null + property int anchorIndex: 0 + + signal keyPress(var event) + + currentIndex: -1 clip: true - snapMode: ListView.NoSnap + ScrollBar.vertical: ScrollBar { + bottomPadding: Meui.Theme.mediumRadius + } - highlightFollowsCurrentItem: true highlightMoveDuration: 0 - highlightResizeDuration : 0 + keyNavigationEnabled : true + keyNavigationWraps : true + Keys.onPressed: { + if (event.key === Qt.Key_Control) { + ctrlPressed = true + } else if (event.key === Qt.Key_Shift) { + shiftPressed = true + + if (currentIndex != -1) + anchorIndex = currentIndex + } + + control.keyPress(event) + } + Keys.onReleased: { + if (event.key === Qt.Key_Control) { + ctrlPressed = false + } else if (event.key === Qt.Key_Shift) { + shiftPressed = false + anchorIndex = 0 + } + } + + onContentXChanged: { + cancelRename() + } + + onContentYChanged: { + cancelRename() + } + + onPressXChanged: { + cPress = mapToItem(control.contentItem, pressX, pressY) + } + + onPressYChanged: { + cPress = mapToItem(control.contentItem, pressX, pressY) + } + + function rename() { + if (currentIndex !== -1) { + var renameAction = control.model.action("rename") + if (renameAction && !renameAction.enabled) + return + + if (!control.editor) + control.editor = editorComponent.createObject(control) + + control.editor.targetItem = control.currentItem + } + } + + function cancelRename() { + if (control.editor) + control.editor = null + } MouseArea { + id: _mouseArea anchors.fill: parent - z: -1 - hoverEnabled: true propagateComposedEvents: true - acceptedButtons: Qt.LeftButton | Qt.RightButton - onClicked: parent.clicked(mouse) - onPositionChanged: parent.positionChanged(mouse) - onPressed: parent.pressed(mouse) - onReleased: parent.released(mouse) - Keys.forwardTo: _listViewBrowser + preventStealing: true + acceptedButtons: Qt.RightButton | Qt.LeftButton + hoverEnabled: true + enabled: true + z: -1 + + onDoubleClicked: { + if (mouse.button === Qt.LeftButton && control.pressedItem) + folderModel.openSelected() + } + + onPressed: { + control.forceActiveFocus() + + if (control.editor && childAt(mouse.x, mouse.y) !== control.editor) + control.editor.commit() + + if (mouse.source === Qt.MouseEventSynthesizedByQt) { + var index = control.indexAt(mouse.x, mouse.y + control.contentY) + var indexItem = control.itemAtIndex(index) + if (indexItem && indexItem.iconArea) { + control.currentIndex = index + hoveredItem = indexItem + } else { + hoveredItem = null + } + } + + pressX = mouse.x + pressY = mouse.y + + if (!hoveredItem || hoveredItem.blank) { + if (!control.ctrlPressed) { + control.currentIndex = -1 + control.previouslySelectedItemIndex = -1 + folderModel.clearSelection() + } + + if (mouse.buttons & Qt.RightButton) { + clearPressState() + folderModel.openContextMenu(null, mouse.modifiers) + mouse.accepted = true + } + } else { + pressedItem = hoveredItem + + if (control.shiftPressed && control.currentIndex !== -1) { + folderModel.setRangeSelected(control.anchorIndex, hoveredItem.index) + } else { + if (!control.ctrlPressed && !folderModel.isSelected(hoveredItem.index)) { + previouslySelectedItemIndex = -1 + folderModel.clearSelection() + } + + if (control.ctrlPressed) + folderModel.toggleSelected(hoveredItem.index) + else + folderModel.setSelected(hoveredItem.index) + } + + control.currentIndex = hoveredItem.index + + if (mouse.buttons & Qt.RightButton) { + clearPressState() + folderModel.openContextMenu(null, mouse.modifiers) + mouse.accepted = true + } + } + } + + onClicked: { + clearPressState() + + if (!hoveredItem || hoveredItem.blank || control.currentIndex === -1 || control.ctrlPressed + || control.shiftPressed) { + return + } + + // TODO: rename + } + + onPositionChanged: { + control.ctrlPressed = (mouse.modifiers & Qt.ControlModifier) + control.shiftPressed = (mouse.modifiers & Qt.ShiftModifier) + + var cPos = mapToItem(control.contentItem, mouse.x, mouse.y) + var item = control.itemAt(mouse.x, mouse.y + control.contentY) + var leftEdge = Math.min(control.contentX, control.originX) + + if (!item || item.blank) { + if (control.hoveredItem) { + control.hoveredItem = null + } + } else { + control.hoveredItem = item + } + + // TODO: autoscroll + + if (control.rubberBand) { + var rB = control.rubberBand + + if (cPos.x < cPress.x) { + rB.x = Math.max(leftEdge, cPos.x) + rB.width = Math.abs(rB.x - cPress.x) + } else { + rB.x = cPress.x + var ceil = Math.max(control.width, control.contentItem.width) + leftEdge + rB.width = Math.min(ceil - rB.x, Math.abs(rB.x - cPos.x)) + } + + if (cPos.y < cPress.y) { + rB.y = Math.max(0, cPos.y) + rB.height = Math.abs(rB.y - cPress.y) + } else { + rB.y = cPress.y + var ceilValue = Math.max(control.height, control.contentItem.height) + rB.height = Math.min(ceilValue - rB.y, Math.abs(rB.y - cPos.y)) + } + + // Ensure rubberband is at least 1px in size or else it will become + // invisible and not match any items. + rB.width = Math.max(1, rB.width) + rB.height = Math.max(1, rB.height) + + control.rectangleSelect(rB.x, rB.y, rB.width, rB.height) + + return + } + + // Drag + if (pressX != -1) { + if (pressedItem != null && folderModel.isSelected(pressedItem.index)) { + control.dragX = mouse.x + control.dragY = mouse.y + control.verticalDropHitscanOffset = pressedItem.y + (pressedItem.height / 2) + folderModel.dragSelected(mouse.x, mouse.y) + control.dragX = -1 + control.dragY = -1 + clearPressState() + } else { + folderModel.pinSelection() + control.rubberBand = rubberBandObject.createObject(control.contentItem, {x: cPress.x, y: cPress.y}) + control.interactive = false + } + } + } + + onContainsMouseChanged: { + if (!containsMouse && !control.rubberBand) { + clearPressState() + + if (control.hoveredItem) { + control.hoveredItem = null + } + } + } + + onReleased: pressCanceled() + onCanceled: pressCanceled() + } + + function pressCanceled() { + if (control.rubberBand) { + control.rubberBand.close() + control.rubberBand = null + + control.interactive = true + // control.cachedRectangleSelection = null + folderModel.unpinSelection() + } + + clearPressState() + // control.cancelAutoscroll() + } + + function clearPressState() { + pressedItem = null + pressX = -1 + pressY = -1 + } + + function rectangleSelect(x, y, width, height) { + + } + + function updateSelection(modifier) { + if (modifier & Qt.ShiftModifier) { + folderModel.setRangeSelected(anchorIndex, hoveredItem) + } else { + folderModel.clear() + folderModel.setSelected(currentIndex) + if (currentIndex == -1) + previouslySelectedItemIndex = -1 + previouslySelectedItemIndex = currentIndex + } + } + + Component { + id: editorComponent + + TextArea { + id: _editor + visible: false + wrapMode: Text.NoWrap + textMargin: 0 + verticalAlignment: TextEdit.AlignVCenter + + property Item targetItem: null + + background: Item {} + + onTargetItemChanged: { + if (targetItem != null) { + var pos = control.mapFromItem(targetItem, targetItem.textArea.x, control.contentY) + width = targetItem.width - targetItem.iconArea.width * 2 + height = targetItem.height + x = pos.x + y = pos.y + text = targetItem.textArea.text + targetItem.textArea.visible = false + _editor.select(0, folderModel.fileExtensionBoundary(targetItem.index)) + visible = true + } else { + x: 0 + y: 0 + visible = false + } + } + + Keys.onPressed: { + switch (event.key) { + case Qt.Key_Return: + case Qt.Key_Enter: + commit() + break + case Qt.Key_Escape: + if (targetItem) { + targetItem.textArea.visible = true + targetItem = null + event.accepted = true + } + break + } + } + + onVisibleChanged: { + if (visible) + _editor.forceActiveFocus() + else + control.forceActiveFocus() + } + + function commit() { + if (targetItem) { + targetItem.textArea.visible = true + folderModel.rename(targetItem.index, text) + control.currentIndex = targetItem.index + targetItem = null + } + } + } } } diff --git a/qml/FolderPage.qml b/qml/FolderPage.qml new file mode 100644 index 0000000..dbebb74 --- /dev/null +++ b/qml/FolderPage.qml @@ -0,0 +1,213 @@ +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Layouts 1.12 + +import Cutefish.FileManager 1.0 +import MeuiKit 1.0 as Meui + +import "./Dialogs" + +Item { + id: folderPage + + property alias currentUrl: folderModel.url + property Item currentView: _viewLoader.item + property int statusBarHeight: 30 + Meui.Units.smallSpacing + + signal requestPathEditor() + + onCurrentUrlChanged: { + _viewLoader.item.forceActiveFocus() + } + + Rectangle { + id: _background + anchors.fill: parent + radius: Meui.Theme.smallRadius + color: Meui.Theme.backgroundColor + } + + Label { + id: _fileTips + text: qsTr("No files") + anchors.centerIn: parent + visible: false + } + + FolderModel { + id: folderModel + viewAdapter: viewAdapter + + Component.onCompleted: { + folderModel.url = folderModel.homePath() + } + } + + ItemViewAdapter { + id: viewAdapter + adapterView: _viewLoader.item + adapterModel: folderModel + adapterIconSize: 40 + adapterVisibleArea: Qt.rect(_viewLoader.item.contentX, _viewLoader.item.contentY, + _viewLoader.item.contentWidth, _viewLoader.item.contentHeight) + } + + ColumnLayout { + anchors.fill: parent + spacing: 0 + + Loader { + id: _viewLoader + Layout.fillWidth: true + Layout.fillHeight: true + sourceComponent: switch (settings.viewMethod) { + case 0: return _listViewComponent + case 1: return _gridViewComponent + } + } + + Loader { + Layout.fillWidth: true + sourceComponent: _statusBar + } + } + + Component { + id: _statusBar + + Item { + height: statusBarHeight + z: 999 + + RowLayout { + anchors.fill: parent + anchors.leftMargin: Meui.Units.largeSpacing + anchors.rightMargin: Meui.Units.largeSpacing + anchors.bottomMargin: Meui.Units.smallSpacing + + Label { + text: folderModel.statusText + Layout.alignment: Qt.AlignLeft + elide: Text.ElideMiddle + } + + Button { + Layout.fillHeight: true + Layout.alignment: Qt.AlignRight + text: qsTr("Empty Trash") + onClicked: folderModel.emptyTrash() + visible: folderModel.url === "trash:/" + } + } + } + } + + Component { + id: _gridViewComponent + + FolderGridView { + id: _gridView + model: folderModel + delegate: FolderGridItem {} + + onCountChanged: { + _fileTips.visible = count === 0 + } + } + } + + Component { + id: _listViewComponent + + FolderListView { + id: _folderListView + model: folderModel + + leftMargin: Meui.Units.largeSpacing + rightMargin: Meui.Units.largeSpacing + spacing: Meui.Units.largeSpacing + + onCountChanged: { + _fileTips.visible = count === 0 + } + + delegate: FolderListItem {} + + Component.onCompleted: { + folderModel.requestRename.connect(rename) + } + } + } + + Component { + id: rubberBandObject + + RubberBand { + id: rubberBand + + width: 0 + height: 0 + z: 99999 + color: Meui.Theme.highlightColor + + function close() { + opacityAnimation.restart() + } + + OpacityAnimator { + id: opacityAnimation + target: rubberBand + to: 0 + from: 1 + duration: 150 + + easing { + bezierCurve: [0.4, 0.0, 1, 1] + type: Easing.Bezier + } + + onFinished: { + rubberBand.visible = false + rubberBand.enabled = false + rubberBand.destroy() + } + } + } + } + + Connections { + target: _viewLoader.item + + function onKeyPress(event) { + if (event.key === Qt.Key_Enter || event.key === Qt.Key_Return) + folderModel.openSelected() + else if (event.key === Qt.Key_C && event.modifiers & Qt.ControlModifier) + folderModel.copy() + else if (event.key === Qt.Key_X && event.modifiers & Qt.ControlModifier) + folderModel.cut() + else if (event.key === Qt.Key_V && event.modifiers & Qt.ControlModifier) + folderModel.paste() + else if (event.key === Qt.Key_F2) + folderModel.requestRename() + else if (event.key === Qt.Key_L && event.modifiers & Qt.ControlModifier) + folderPage.requestPathEditor() + else if (event.key === Qt.Key_A && event.modifiers & Qt.ControlModifier) + folderModel.selectAll() + else if (event.key === Qt.Key_Backspace) + folderModel.up() + } + } + + function openUrl(url) { + folderModel.url = url + _viewLoader.item.forceActiveFocus() + } + + function goBack() { + folderModel.goBack() + } + + function goForward() { + folderModel.goForward() + } +} diff --git a/qml/GlobalSettings.qml b/qml/GlobalSettings.qml index 31de75a..8fb3d76 100644 --- a/qml/GlobalSettings.qml +++ b/qml/GlobalSettings.qml @@ -1,11 +1,11 @@ -import QtQuick 2.0 +import QtQuick 2.12 import Qt.labs.settings 1.0 Settings { - property int viewMethod: 0 // 0 = Grid, 1 = List + property int viewMethod: 0 property bool showHidden: false - property int width: 1080 - property int height: 645 + property int width: 900 + property int height: 580 property int desktopIconSize: 128 property int maximumIconSize: 256 property int minimumIconSize: 128 diff --git a/qml/IconDelegate.qml b/qml/IconDelegate.qml deleted file mode 100644 index 168d12c..0000000 --- a/qml/IconDelegate.qml +++ /dev/null @@ -1,102 +0,0 @@ -import QtQuick 2.12 -import QtQuick.Controls 2.12 -import QtQuick.Layouts 1.12 -import QtGraphicalEffects 1.0 - -import MeuiKit 1.0 as Meui - -import org.kde.plasma.core 2.0 as PlasmaCore - -Item { - id: main - - property int index: model.index - property string name: model.blank ? "" : model.display - property bool blank: model.blank - property bool isDir: model.blank ? false : model.isDir - property bool selected: model.blank ? false : model.selected - property Item frame: contentItem - property Item iconArea: icon - property Item labelArea: label - - property bool hovered: (main.GridView.view.hoveredItem === main) - - property color hoveredColor: Qt.rgba(Meui.Theme.highlightColor.r, - Meui.Theme.highlightColor.g, - Meui.Theme.highlightColor.b, 0.1) - property color selectedColor: Qt.rgba(Meui.Theme.highlightColor.r, - Meui.Theme.highlightColor.g, - Meui.Theme.highlightColor.b, 0.9) - - Accessible.name: name - Accessible.role: Accessible.Canvas - - onSelectedChanged: { - if (selected && !blank) { - contentItem.grabToImage(function(result) { - dir.addItemDragImage(positioner.map(index), main.x + contentItem.x, main.y + contentItem.y, contentItem.width, contentItem.height, result.image); - }); - } - } - - Rectangle { - anchors.fill: parent - anchors.margins: Meui.Units.largeSpacing - radius: Meui.Theme.bigRadius - color: selected ? selectedColor : main.hovered ? hoveredColor : "transparent" - - border.color: Qt.rgba(Meui.Theme.highlightColor.r, - Meui.Theme.highlightColor.g, - Meui.Theme.highlightColor.b, 0.3) - border.width: main.hovered || selected ? 1 : 0 - } - - Item { - id: contentItem - anchors.fill: parent - anchors.margins: Meui.Units.largeSpacing - - PlasmaCore.IconItem { - id: icon - z: 2 - - anchors.top: parent.top - anchors.topMargin: Meui.Units.smallSpacing - anchors.horizontalCenter: parent.horizontalCenter - - height: main.height * 0.55 - width: height - - animated: false - usesPlasmaTheme: false - smooth: true - source: model.blank ? "" : model.decoration - overlays: model.blank ? "" : model.overlays - } - - Label { - id: label - z: 2 - - anchors.top: icon.bottom - anchors.topMargin: Meui.Units.smallSpacing - anchors.left: parent.left - anchors.right: parent.right - anchors.bottom: parent.bottom - - width: parent.width - - textFormat: Text.PlainText - - horizontalAlignment: Qt.AlignHCenter - verticalAlignment: Qt.AlignTop - - wrapMode: Text.Wrap - elide: Text.ElideRight - color: Meui.Theme.textColor - opacity: model.isHidden ? 0.6 : 1 - text: model.blank ? "" : model.display - font.italic: model.isLink - } - } -} diff --git a/qml/ItemMenu.qml b/qml/ItemMenu.qml deleted file mode 100644 index edf7170..0000000 --- a/qml/ItemMenu.qml +++ /dev/null @@ -1,101 +0,0 @@ -import QtQuick 2.12 -import QtQuick.Controls 2.12 -import Cutefish.FileManager 1.0 -import MeuiKit 1.0 as Meui - -Menu { - id: control - implicitWidth: 200 - - property var item : ({}) - property int index : -1 - property bool isDir : false - property bool isExec : false - - signal openClicked(var item) - signal removeClicked(var item) - signal copyClicked(var item) - signal cutClicked(var item) - signal renameClicked(var item) - signal wallpaperClicked(var item) - signal propertiesClicked(var item) - - MenuItem { - text: qsTr("Open") - onTriggered: { - openClicked(control.item) - close() - } - } - - MenuItem { - text: qsTr("Copy") - onTriggered: { - copyClicked(control.item) - close() - } - } - - MenuItem { - text: qsTr("Cut") - onTriggered: { - cutClicked(control.item) - close() - } - } - - MenuItem { - text: qsTr("Move to Trash") - onTriggered: { - removeClicked(control.item) - close() - } - } - - MenuSeparator {} - - MenuItem { - text: qsTr("Rename") - onTriggered: { - renameClicked(control.item) - close() - } - } - - MenuItem { - text: qsTr("Open in Terminal") - } - - MenuItem { - id: wallpaperItem - text: qsTr("Set As Wallpaper") - visible: false - onTriggered: { - wallpaperClicked(control.item) - close() - } - } - - MenuItem { - id: properties - text: qsTr("Properties") - onTriggered: { - propertiesClicked(control.item) - close() - } - } - - function show(index) { - control.item = currentFMModel.get(index) - - if (item) { - control.index = index - control.isDir = item.isdir === true || item.isdir === "true" - control.isExec = item.executable === true || item.executable === "true" - wallpaperItem.visible = item.img === "true" - - popup() - } - - } -} diff --git a/qml/PathBar.qml b/qml/PathBar.qml index 9fe2999..a3b3a99 100644 --- a/qml/PathBar.qml +++ b/qml/PathBar.qml @@ -1,106 +1,80 @@ -import QtQuick 2.4 -import QtQuick.Controls 2.4 +import QtQuick 2.12 +import QtQuick.Controls 2.12 import QtGraphicalEffects 1.0 -import MeuiKit 1.0 as Meui + import Cutefish.FileManager 1.0 +import MeuiKit 1.0 as Meui Item { id: control property string url: "" - signal placeClicked(string path) - signal pathChanged(string path) - onUrlChanged: { - _pathList.path = control.url - } - - BaseModel { - id: _pathModel - list: _pathList - } - - PathList { - id: _pathList - } - - Rectangle { - anchors.fill: parent - radius: Meui.Theme.smallRadius - color: Meui.Theme.backgroundColor - } + signal itemClicked(string path) + signal editorAccepted(string path) ListView { - id: listView + id: _pathView anchors.fill: parent - model: _pathModel + model: _pathBarModel orientation: Qt.Horizontal layoutDirection: Qt.LeftToRight clip: true + leftMargin: 3 + rightMargin: 3 spacing: Meui.Units.smallSpacing onCountChanged: { - currentIndex = listView.count - 1 - listView.positionViewAtEnd() + _pathView.currentIndex = _pathView.count - 1 + _pathView.positionViewAtEnd() } - delegate: MouseArea { - id: pathBarItem - height: listView.height - width: label.width + Meui.Units.largeSpacing * 2 - - property bool selected: index === listView.count - 1 - property color pressedColor: Qt.rgba(Meui.Theme.textColor.r, - Meui.Theme.textColor.g, - Meui.Theme.textColor.b, 0.5) - - onClicked: control.placeClicked(model.path) - - Rectangle { - anchors.fill: parent - anchors.margins: 2 - color: Meui.Theme.highlightColor - radius: Meui.Theme.smallRadius - visible: selected - - layer.enabled: true - layer.effect: DropShadow { - transparentBorder: true - radius: 2 - samples: 2 - horizontalOffset: 0 - verticalOffset: 0 - color: Qt.rgba(0, 0, 0, 0.1) - } - } - - Label { - id: label - text: model.label - anchors.centerIn: parent - color: selected ? Meui.Theme.highlightedTextColor : pathBarItem.pressed - ? pressedColor : Meui.Theme.textColor - } + Rectangle { + anchors.fill: parent + color: Meui.Theme.backgroundColor + radius: Meui.Theme.smallRadius + z: -1 } MouseArea { anchors.fill: parent + acceptedButtons: Qt.LeftButton + onClicked: openEditor() + z: -1 + } + + delegate: MouseArea { + id: _item + height: ListView.view.height + width: _name.width + Meui.Units.largeSpacing z: -1 - onClicked: { - if (!addressEdit.visible) { - openEditor() - } + property bool selected: index === _pathView.count - 1 + + onClicked: control.itemClicked(model.path) + + Rectangle { + anchors.fill: parent + anchors.topMargin: 2 + anchors.bottomMargin: 2 + color: Meui.Theme.highlightColor + radius: Meui.Theme.smallRadius + visible: selected + } + + Label { + id: _name + text: model.name + color: selected ? Meui.Theme.highlightedTextColor : Meui.Theme.textColor + anchors.centerIn: parent } } } TextField { - id: addressEdit - anchors.centerIn: parent - width: parent.width - height: parent.height + id: _pathEditor + anchors.fill: parent visible: false selectByMouse: true inputMethodHints: Qt.ImhUrlCharactersOnly | Qt.ImhNoAutoUppercase @@ -108,7 +82,7 @@ Item { text: control.url onAccepted: { - control.pathChanged(text) + control.editorAccepted(text) closeEditor() } @@ -124,15 +98,25 @@ Item { } } - function closeEditor() { - addressEdit.visible = false - listView.visible = true + PathBarModel { + id: _pathBarModel + } + + function updateUrl(url) { + control.url = url + _pathBarModel.url = url } function openEditor() { - addressEdit.visible = true - addressEdit.forceActiveFocus() - addressEdit.selectAll() - listView.visible = false + _pathEditor.text = control.url + _pathEditor.visible = true + _pathEditor.forceActiveFocus() + _pathEditor.selectAll() + _pathView.visible = false + } + + function closeEditor() { + _pathEditor.visible = false + _pathView.visible = true } } diff --git a/qml/SideBar.qml b/qml/SideBar.qml index d9fe1bb..b9b1640 100644 --- a/qml/SideBar.qml +++ b/qml/SideBar.qml @@ -1,55 +1,28 @@ -import QtQuick 2.4 -import QtQuick.Layouts 1.3 -import QtQuick.Controls 2.4 +import QtQuick 2.12 +import QtQuick.Layouts 1.12 +import QtQuick.Controls 2.12 +import QtGraphicalEffects 1.0 + import MeuiKit 1.0 as Meui import Cutefish.FileManager 1.0 ListView { - id: control - implicitWidth: 200 + id: sideBar - property string currentUrl - - signal placeClicked(string path) - signal itemClicked(int index) - - onItemClicked: { - var item = placesModel.get(index) - control.placeClicked(item.url) - } - - onCurrentUrlChanged: { - syncIndex(currentUrl) - } - - Component.onCompleted: { - syncIndex(currentUrl) - } - - function syncIndex(path) { - control.currentIndex = -1 - - for (var i = 0; i < control.count; ++i) { - if (path === control.model.get(i).url) { - control.currentIndex = i - break - } - } - } + signal clicked(string path) PlacesModel { id: placesModel } + model: placesModel clip: true - spacing: Meui.Units.smallSpacing + leftMargin: Meui.Units.smallSpacing rightMargin: Meui.Units.smallSpacing - - model: placesModel + spacing: Meui.Units.largeSpacing ScrollBar.vertical: ScrollBar {} - flickableDirection: Flickable.VerticalFlick highlightFollowsCurrentItem: true highlightMoveDuration: 0 @@ -60,15 +33,71 @@ ListView { color: Meui.Theme.highlightColor } - delegate: SidebarItem { - id: listItem - checked: control.currentIndex === index + delegate: Item { + id: _item width: ListView.view.width - ListView.view.leftMargin - ListView.view.rightMargin height: 40 - onClicked: { - control.currentIndex = index - control.itemClicked(index) + property bool checked: sideBar.currentIndex === index + property color hoveredColor: Meui.Theme.darkMode ? Qt.lighter(Meui.Theme.backgroundColor, 1.1) + : Qt.darker(Meui.Theme.backgroundColor, 1.1) + MouseArea { + id: _mouseArea + anchors.fill: parent + hoverEnabled: true + acceptedButtons: Qt.LeftButton + onClicked: { + sideBar.currentIndex = index + sideBar.clicked(model.path) + } + } + + Rectangle { + anchors.fill: parent + radius: Meui.Theme.smallRadius + color: _mouseArea.containsMouse && !checked ? _item.hoveredColor : "transparent" + } + + RowLayout { + anchors.fill: parent + anchors.leftMargin: Meui.Units.smallSpacing + anchors.rightMargin: Meui.Units.smallSpacing + spacing: Meui.Units.smallSpacing + + Image { + height: _item.height * 0.55 + width: height + sourceSize: Qt.size(width, height) + source: model.iconPath ? model.iconPath : "image://icontheme/" + model.iconName + Layout.alignment: Qt.AlignVCenter + + ColorOverlay { + anchors.fill: parent + source: parent + color: _label.color + visible: Meui.Theme.darkMode && model.iconPath || checked + } + } + + Label { + id: _label + text: model.name + color: checked ? Meui.Theme.highlightedTextColor : Meui.Theme.textColor + elide: Text.ElideRight + Layout.fillWidth: true + Layout.alignment: Qt.AlignVCenter + } + } + } + + function updateSelection(path) { + sideBar.currentIndex = -1 + + for (var i = 0; i < sideBar.count; ++i) { + if (path === sideBar.model.get(i).path) { + sideBar.currentIndex = i + break + } } } } diff --git a/qml/SidebarItem.qml b/qml/SidebarItem.qml deleted file mode 100644 index ef5b7aa..0000000 --- a/qml/SidebarItem.qml +++ /dev/null @@ -1,69 +0,0 @@ -import QtQuick 2.4 -import QtQuick.Controls 2.4 -import QtQuick.Layouts 1.1 -import QtGraphicalEffects 1.0 - -import MeuiKit 1.0 as Meui - -Item { - id: item - - property bool checked: false - signal clicked - - Rectangle { - id: rect - anchors.fill: parent - radius: Meui.Theme.smallRadius - color: item.checked ? "transparent" - : mouseArea.containsMouse ? Qt.rgba(Meui.Theme.textColor.r, - Meui.Theme.textColor.g, - Meui.Theme.textColor.b, 0.1) : "transparent" - } - - MouseArea { - id: mouseArea - anchors.fill: parent - hoverEnabled: true - acceptedButtons: Qt.LeftButton - onClicked: item.clicked() - } - - RowLayout { - anchors.fill: rect - anchors.leftMargin: Meui.Units.largeSpacing - anchors.rightMargin: Meui.Units.largeSpacing - - spacing: Meui.Units.largeSpacing - - Item { - id: iconItem - height: item.height * 0.55 - width: height - - Image { - id: image - anchors.fill: parent - sourceSize: Qt.size(width, height) - source: model.iconPath ? model.iconPath : "image://icontheme/" + model.iconName - Layout.alignment: Qt.AlignVCenter - } - - ColorOverlay { - anchors.fill: parent - source: parent - color: itemTitle.color - visible: Meui.Theme.darkMode && model.iconPath || checked - } - } - - Label { - id: itemTitle - text: model.name - color: item.checked ? Meui.Theme.highlightedTextColor : Meui.Theme.textColor - elide: Text.ElideRight - Layout.alignment: Qt.AlignVCenter - Layout.fillWidth: true - } - } -} diff --git a/qml/main.qml b/qml/main.qml index 245775f..80a7855 100644 --- a/qml/main.qml +++ b/qml/main.qml @@ -1,105 +1,122 @@ import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Layouts 1.12 import QtQuick.Window 2.12 -import QtQuick.Controls 2.4 -import QtQuick.Layouts 1.3 - -import Cutefish.FileManager 1.0 - import MeuiKit 1.0 as Meui +import "./Controls" + Meui.Window { id: root width: settings.width height: settings.height minimumWidth: 900 - minimumHeight: 600 + minimumHeight: 580 visible: true title: qsTr("File Manager") - hideHeaderOnMaximize: false headerBarHeight: 35 + Meui.Units.largeSpacing backgroundColor: Meui.Theme.secondBackgroundColor property QtObject settings: GlobalSettings { } onClosing: { - settings.width = root.width - settings.height = root.height + if (root.visibility !== Window.Maximized && + root.visibility !== Window.FullScreen) { + settings.width = root.width + settings.height = root.height + } } headerBar: Item { RowLayout { anchors.fill: parent - anchors.leftMargin: Meui.Units.largeSpacing + anchors.leftMargin: Meui.Units.smallSpacing anchors.rightMargin: Meui.Units.smallSpacing - anchors.topMargin: Meui.Units.largeSpacing + anchors.topMargin: Meui.Units.smallSpacing + anchors.bottomMargin: Meui.Units.smallSpacing + spacing: Meui.Units.smallSpacing IconButton { Layout.fillHeight: true implicitWidth: height - source: Meui.Theme.darkMode ? "qrc:/images/dark/go-previous.svg" : "qrc:/images/light/go-previous.svg" - onClicked: _browserView.goBack() + source: Meui.Theme.darkMode ? "qrc:/images/dark/go-previous.svg" + : "qrc:/images/light/go-previous.svg" + onClicked: _folderPage.goBack() } IconButton { Layout.fillHeight: true implicitWidth: height - source: Meui.Theme.darkMode ? "qrc:/images/dark/go-next.svg" : "qrc:/images/light/go-next.svg" - onClicked: _browserView.goForward() + source: Meui.Theme.darkMode ? "qrc:/images/dark/go-next.svg" + : "qrc:/images/light/go-next.svg" + onClicked: _folderPage.goForward() } PathBar { - id: pathBar + id: _pathBar Layout.fillWidth: true Layout.fillHeight: true - url: _browserView.url - onPlaceClicked: _browserView.openFolder(path) - onPathChanged: _browserView.openFolder(path) + onItemClicked: _folderPage.openUrl(path) + onEditorAccepted: _folderPage.openUrl(path) } IconButton { Layout.fillHeight: true implicitWidth: height - source: Meui.Theme.darkMode ? "qrc:/images/dark/grid.svg" : "qrc:/images/light/grid.svg" - onClicked: settings.viewMethod = 1 + + property var gridSource: Meui.Theme.darkMode ? "qrc:/images/dark/grid.svg" : "qrc:/images/light/grid.svg" + property var listSource: Meui.Theme.darkMode ? "qrc:/images/dark/list.svg" : "qrc:/images/light/list.svg" + + source: settings.viewMethod === 0 ? listSource : gridSource + + onClicked: { + if (settings.viewMethod === 1) + settings.viewMethod = 0 + else + settings.viewMethod = 1 + } } - IconButton { - Layout.fillHeight: true - implicitWidth: height - source: Meui.Theme.darkMode ? "qrc:/images/dark/list.svg" : "qrc:/images/light/list.svg" - onClicked: settings.viewMethod = 0 - } +// IconButton { +// Layout.fillHeight: true +// implicitWidth: height +// source: Meui.Theme.darkMode ? "qrc:/images/dark/grid.svg" : "qrc:/images/light/grid.svg" +// onClicked: settings.viewMethod = 1 +// } + +// IconButton { +// Layout.fillHeight: true +// implicitWidth: height +// source: Meui.Theme.darkMode ? "qrc:/images/dark/list.svg" : "qrc:/images/light/list.svg" +// onClicked: settings.viewMethod = 0 +// } } } - ColumnLayout { + RowLayout { anchors.fill: parent - spacing: Meui.Units.largeSpacing + anchors.topMargin: 2 + spacing: 0 - Item { - id: bottomControls + SideBar { + id: _sideBar + Layout.fillHeight: true + width: 200 + Meui.Units.largeSpacing + onClicked: _folderPage.openUrl(path) + } + + FolderPage { + id: _folderPage Layout.fillWidth: true Layout.fillHeight: true - - RowLayout { - anchors.fill: parent - anchors.topMargin: Meui.Units.largeSpacing - spacing: 0 - - SideBar { - Layout.fillHeight: true - currentUrl: _browserView.model.url - onPlaceClicked: _browserView.model.url = path - } - - BrowserView { - id: _browserView - Layout.fillWidth: true - Layout.fillHeight: true - // onOpenPathBar: pathBar.openEditor() - } + onCurrentUrlChanged: { + _sideBar.updateSelection(currentUrl) + _pathBar.updateUrl(currentUrl) + } + onRequestPathEditor: { + _pathBar.openEditor() } } } diff --git a/src/baselist.cpp b/src/baselist.cpp deleted file mode 100644 index 4847df3..0000000 --- a/src/baselist.cpp +++ /dev/null @@ -1,86 +0,0 @@ -/* - * - * Copyright (C) 2019 camilo - * - * 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 3 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, see . - */ - -#include "baselist.h" -#include "basemodel.h" - -BaseList::BaseList(QObject *parent) - : QObject(parent) - , m_model(nullptr) -{ -} - -int BaseList::getCount() const -{ - return this->items().count(); -} - -QVariantMap BaseList::get(const int &index) const -{ - if (this->m_model) { - return this->m_model->get(index); - } - - if (index >= 0 && this->items().size() > 0 && index < this->items().size()) { - return FMH::toMap(this->items()[index]); - } - - return QVariantMap(); -} - -FMH::MODEL_LIST BaseList::getItems() const -{ - if (this->m_model && !this->m_model->getFilter().isEmpty()) { - return FMH::toModelList(this->m_model->getAll()); - } - - return this->items(); -} - -int BaseList::mappedIndex(const int &index) const -{ - if (this->m_model) - return this->m_model->mappedToSource(index); - - return index; -} - -int BaseList::mappedIndexFromSource(const int &index) const -{ - if (this->m_model) - return this->m_model->mappedFromSource(index); - - return index; -} - -bool BaseList::exists(const FMH::MODEL_KEY &key, const QString &value) const -{ - return this->indexOf(key, value) >= 0; -} - -int BaseList::indexOf(const FMH::MODEL_KEY &key, const QString &value) const -{ - const auto it = std::find_if(this->items().constBegin(), this->items().constEnd(), [&](const FMH::MODEL &item) -> bool { - return item[key] == value; - }); - - if (it != this->items().constEnd()) - return this->mappedIndexFromSource(std::distance(this->items().constBegin(), it)); - else - return -1; -} diff --git a/src/baselist.h b/src/baselist.h deleted file mode 100644 index 43e1fcb..0000000 --- a/src/baselist.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - * - * Copyright (C) 2019 camilo - * - * 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 3 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, see . - */ - -#ifndef BASELIST_H -#define BASELIST_H - -#include "fmh.h" - -#include - -/** - * @todo write docs - */ -#include - -class BaseModel; -class BaseList : public QObject, public QQmlParserStatus -{ - Q_INTERFACES(QQmlParserStatus) - - Q_OBJECT - Q_PROPERTY(int count READ getCount NOTIFY countChanged FINAL) - -public: - /** - * Default constructor - */ - explicit BaseList(QObject *parent = nullptr); - - virtual const FMH::MODEL_LIST &items() const = 0; - virtual void classBegin() override - { - } - virtual void componentComplete() override - { - } - virtual void modelHooked() {}; - - int getCount() const; - - /** - * @brief getItems - * Get all the items in the list model. If the model has been filtered or sorted those are the items that are returned - * @param index - * @return - */ - FMH::MODEL_LIST getItems() const; - - const BaseModel *m_model; // becarefull this is owned by qml engine, this is only supossed to be a viewer - -public slots: - int mappedIndex(const int &index) const; - int mappedIndexFromSource(const int &index) const; - QVariantMap get(const int &index) const; - -protected: - bool exists(const FMH::MODEL_KEY &key, const QString &value) const; - int indexOf(const FMH::MODEL_KEY &key, const QString &value) const; - -signals: - void preItemAppended(); - void preItemsAppended(uint count); - void postItemAppended(); - void preItemAppendedAt(int index); - void preItemRemoved(int index); - void postItemRemoved(); - void updateModel(int index, QVector roles); - void preListChanged(); - void postListChanged(); - - void countChanged(); -}; - -#endif // BASELIST_H diff --git a/src/basemodel.cpp b/src/basemodel.cpp deleted file mode 100644 index 437e2e4..0000000 --- a/src/basemodel.cpp +++ /dev/null @@ -1,333 +0,0 @@ -/* - * - * Copyright (C) 2019 camilo - * - * 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 3 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, see . - */ - -#include "basemodel.h" -#include "baselist.h" - -BaseModel::BaseModel(QObject *parent) - : QSortFilterProxyModel(parent) - , m_model(new PrivateAbstractListModel(this)) -{ - this->setSourceModel(this->m_model); - this->setDynamicSortFilter(true); -} - -void BaseModel::setFilterString(const QString &string) -{ - this->setFilterCaseSensitivity(Qt::CaseInsensitive); - this->setFilterFixedString(string); - // this->setFilterRegExp(QRegExp(string, Qt::CaseInsensitive)); -} - -void BaseModel::setSortOrder(const int &sortOrder) -{ - this->sort(0, static_cast(sortOrder)); -} - -QVariantMap BaseModel::get(const int &index) const -{ - QVariantMap res; - if (index >= this->rowCount() || index < 0) - return res; - - const auto roleNames = this->roleNames(); - for (const auto &role : roleNames) - res.insert(role, this->index(index, 0).data(FMH::MODEL_NAME_KEY[role]).toString()); - - return res; -} - -QVariantList BaseModel::getAll() const -{ - QVariantList res; - for (auto i = 0; i < this->rowCount(); i++) - res << this->get(i); - - return res; -} - -void BaseModel::setFilter(const QString &filter) -{ - if (this->m_filter == filter) - return; - - this->m_filter = filter; - emit this->filterChanged(this->m_filter); - this->setFilterFixedString(this->m_filter); -} - -const QString BaseModel::getFilter() const -{ - return this->m_filter; -} - -void BaseModel::setSortOrder(const Qt::SortOrder &sortOrder) -{ - if (this->m_sortOrder == sortOrder) - return; - - this->m_sortOrder = sortOrder; - emit this->sortOrderChanged(this->m_sortOrder); - this->sort(0, this->m_sortOrder); -} - -Qt::SortOrder BaseModel::getSortOrder() const -{ - return this->m_sortOrder; -} - -void BaseModel::setSort(const QString &sort) -{ - if (this->m_sort == sort) - return; - - this->m_sort = sort; - emit this->sortChanged(this->m_sort); - this->setSortRole(FMH::MODEL_NAME_KEY[sort]); - this->sort(0, this->m_sortOrder); -} - -QString BaseModel::getSort() const -{ - return this->m_sort; -} - -int BaseModel::mappedFromSource(const int &index) const -{ - return this->mapFromSource(this->m_model->index(index, 0)).row(); -} - -int BaseModel::mappedToSource(const int &index) const -{ - return this->mapToSource(this->index(index, 0)).row(); -} - -bool BaseModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const -{ - if (this->filterRole() != Qt::DisplayRole) { - QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent); - const auto data = this->sourceModel()->data(index, this->filterRole()).toString(); - return data.contains(this->filterRegExp()); - } - - const auto roleNames = this->sourceModel()->roleNames(); - for (const auto &role : roleNames) { - QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent); - const auto data = this->sourceModel()->data(index, FMH::MODEL_NAME_KEY[role]).toString(); - if (data.contains(this->filterRegExp())) - return true; - else - continue; - } - - return false; -} - -BaseList *BaseModel::getList() const -{ - return this->m_model->getList(); -} - -BaseList *BaseModel::PrivateAbstractListModel::getList() const -{ - return this->list; -} - -void BaseModel::PrivateAbstractListModel::setList(BaseList *value) -{ - beginResetModel(); - - if (this->list) - this->list->disconnect(this); - - this->list = value; - - if (this->list) { - connect( - this->list, - &BaseList::preItemAppendedAt, - this, - [=](int index) { - beginInsertRows(QModelIndex(), index, index); - }, - Qt::DirectConnection); - - connect( - this->list, - &BaseList::preItemAppended, - this, - [=]() { - const int index = this->list->items().size(); - beginInsertRows(QModelIndex(), index, index); - }, - Qt::DirectConnection); - - connect( - this->list, - &BaseList::preItemsAppended, - this, - [=](uint count) { - const int index = this->list->items().size(); - beginInsertRows(QModelIndex(), index, index + count - 1); - }, - Qt::DirectConnection); - - connect( - this->list, - &BaseList::postItemAppended, - this, - [=]() { - endInsertRows(); - }, - Qt::DirectConnection); - - connect( - this->list, - &BaseList::preItemRemoved, - this, - [=](int index) { - beginRemoveRows(QModelIndex(), index, index); - }, - Qt::DirectConnection); - - connect( - this->list, - &BaseList::postItemRemoved, - this, - [=]() { - endRemoveRows(); - }, - Qt::DirectConnection); - - connect( - this->list, - &BaseList::updateModel, - this, - [=](int index, QVector roles) { - emit this->dataChanged(this->index(index), this->index(index), roles); - }, - Qt::DirectConnection); - - connect( - this->list, - &BaseList::preListChanged, - this, - [=]() { - beginResetModel(); - }, - Qt::DirectConnection); - - connect( - this->list, - &BaseList::postListChanged, - this, - [=]() { - endResetModel(); - }, - Qt::DirectConnection); - } - - endResetModel(); -} - -void BaseModel::setList(BaseList *value) -{ - value->modelHooked(); - this->m_model->setList(value); - this->getList()->m_model = this; - emit this->listChanged(); -} - -BaseModel::PrivateAbstractListModel::PrivateAbstractListModel(BaseModel *model) - : QAbstractListModel(model) - , list(nullptr) - , m_model(model) -{ - connect( - this, - &QAbstractListModel::rowsInserted, - this, - [this](QModelIndex, int, int) { - if (this->list) { - emit this->list->countChanged(); - } - }, - Qt::DirectConnection); - - connect( - this, - &QAbstractListModel::rowsRemoved, - this, - [this](QModelIndex, int, int) { - if (this->list) { - emit this->list->countChanged(); - } - }, - Qt::DirectConnection); -} - -int BaseModel::PrivateAbstractListModel::rowCount(const QModelIndex &parent) const -{ - if (parent.isValid() || !list) - return 0; - - return list->items().size(); -} - -QVariant BaseModel::PrivateAbstractListModel::data(const QModelIndex &index, int role) const -{ - if (!index.isValid() || !list) - return QVariant(); - - auto value = list->items().at(index.row())[static_cast(role)]; - - if (role == FMH::MODEL_KEY::ADDDATE || role == FMH::MODEL_KEY::DATE || role == FMH::MODEL_KEY::MODIFIED || role == FMH::MODEL_KEY::RELEASEDATE) { - const auto date = QDateTime::fromString(value, Qt::TextDate); - if (date.isValid()) - return date; - } - - return value; -} - -bool BaseModel::PrivateAbstractListModel::setData(const QModelIndex &index, const QVariant &value, int role) -{ - Q_UNUSED(index); - Q_UNUSED(value); - Q_UNUSED(role); - - return false; -} - -Qt::ItemFlags BaseModel::PrivateAbstractListModel::flags(const QModelIndex &index) const -{ - if (!index.isValid()) - return Qt::NoItemFlags; - - return Qt::ItemIsEditable; // FIXME: Implement me! -} - -QHash BaseModel::PrivateAbstractListModel::roleNames() const -{ - QHash names; - - for (const auto &key : FMH::MODEL_NAME.keys()) - names[key] = QString(FMH::MODEL_NAME[key]).toUtf8(); - - return names; -} diff --git a/src/basemodel.h b/src/basemodel.h deleted file mode 100644 index 5f7dd87..0000000 --- a/src/basemodel.h +++ /dev/null @@ -1,181 +0,0 @@ -/* - * - * Copyright (C) 2019 camilo - * - * 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 3 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, see . - */ - -#ifndef BASEMODEL_H -#define BASEMODEL_H - -#include -#include -#include -#include - -class BaseList; - -/** - * @brief The BaseModel class - * The BaseModel is a template model to use with a BaseList, it aims to be a generic and simple data model to quickly model string based models using the FMH::MODEL_LIST and FMH::MODEL_KEY types. - * - * This type is exposed to QML to quickly create a modle that can be filtered, sorted and has another usefull functionalities. - */ -class BaseModel : public QSortFilterProxyModel -{ - Q_OBJECT - Q_PROPERTY(BaseList *list READ getList WRITE setList NOTIFY listChanged) - Q_PROPERTY(QString filter READ getFilter WRITE setFilter NOTIFY filterChanged) - Q_PROPERTY(Qt::SortOrder sortOrder READ getSortOrder WRITE setSortOrder NOTIFY sortOrderChanged) - Q_PROPERTY(QString sort READ getSort WRITE setSort NOTIFY sortChanged) - -public: - BaseModel(QObject *parent = nullptr); - - /** - * @brief getList - * The list being handled by the model - * @return - */ - BaseList *getList() const; - - /** - * @brief setList - * For the model to work you need to set a BaseList, by subclassing it and exposing it to the QML engine - * @param value - */ - void setList(BaseList *value); - - /** - * @brief getSortOrder - * The current sort order being applied - * @return - */ - Qt::SortOrder getSortOrder() const; - - /** - * @brief getSort - * The current sorting key - * @return - */ - QString getSort() const; - - /** - * @brief getFilter - * The filter being applied to the model - * @return - */ - const QString getFilter() const; - -protected: - bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override; - -private: - class PrivateAbstractListModel; - PrivateAbstractListModel *m_model; - QString m_filter; - Qt::SortOrder m_sortOrder; - QString m_sort; - - [[deprecated]] void setFilterString(const QString &string); - - [[deprecated]] void setSortOrder(const int &sortOrder); - -public slots: - /** - * @brief setFilter - * Filter the model using a simple string, to clear the filter just set it to a empty string - * @param filter - * Simple filter string - */ - void setFilter(const QString &filter); - - /** - * @brief setSortOrder - * Set the sort order, asc or dec - * @param sortOrder - */ - void setSortOrder(const Qt::SortOrder &sortOrder); - - /** - * @brief setSort - * Set the sort key. The sort keys can be found in the FMH::MODEL_KEY keys - * @param sort - */ - void setSort(const QString &sort); - - /** - * @brief get - * Returns an item in the model/list. This method correctly maps the given index in case the modle has been sorted or filtered - * @param index - * Index of the item in the list - * @return - */ - QVariantMap get(const int &index) const; - - /** - * @brief getAll - * Returns all the items in the list represented as a QVariantList to be able to be used in QML. This operation performs a transformation from FMH::MODEL_LIST to QVariantList - * @return - * All the items in the list - */ - QVariantList getAll() const; - - /** - * @brief mappedFromSource - * Maps an index from the base list to the model, incase the modle has been filtered or sorted, this gives you the right mapped index - * @param index - * @return - */ - int mappedFromSource(const int &index) const; - - /** - * @brief mappedToSource - * given an index from the filtered or sorted model it return the mapped index to the original list index - * @param index - * @return - */ - int mappedToSource(const int &index) const; -signals: - void listChanged(); - void filterChanged(QString filter); - void sortOrderChanged(Qt::SortOrder sortOrder); - void sortChanged(QString sort); -}; - -class BaseModel::PrivateAbstractListModel : public QAbstractListModel -{ - Q_OBJECT -public: - PrivateAbstractListModel(BaseModel *model); - int rowCount(const QModelIndex &parent = QModelIndex()) const override; - - QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; - - // Editable: - bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; - - Qt::ItemFlags flags(const QModelIndex &index) const override; - - virtual QHash roleNames() const override; - - BaseList *getList() const; - void setList(BaseList *value); - -private: - BaseList *list; - BaseModel *m_model; -}; - -#endif // BASEMODEL_H diff --git a/src/desktop/desktopview.h b/src/desktop/desktopview.h deleted file mode 100644 index f8c729c..0000000 --- a/src/desktop/desktopview.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef DESKTOPVIEW_H -#define DESKTOPVIEW_H - -#include - -class DesktopView : public QQuickView -{ - Q_OBJECT - Q_PROPERTY(QRect screenRect READ screenRect NOTIFY screenRectChanged) - Q_PROPERTY(QRect screenAvailableRect READ screenAvailableRect NOTIFY screenAvailableGeometryChanged) - -public: - explicit DesktopView(QQuickView *parent = nullptr); - - QRect screenRect(); - QRect screenAvailableRect(); - -signals: - void screenRectChanged(); - void screenAvailableGeometryChanged(); - -private slots: - void onGeometryChanged(); - void onAvailableGeometryChanged(const QRect &geometry); - -private: - QRect m_screenRect; - QRect m_screenAvailableRect; -}; - -#endif // DESKTOPVIEW_H diff --git a/src/fm.cpp b/src/fm.cpp deleted file mode 100644 index cef1801..0000000 --- a/src/fm.cpp +++ /dev/null @@ -1,239 +0,0 @@ -/* - * Copyright 2018 Camilo Higuita - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, 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 Library 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. - */ - -#include "fm.h" - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -FM::FM(QObject *parent) - : QObject(parent) -#ifdef COMPONENT_SYNCING - , sync(new Syncing(this)) -#endif - , dirLister(new KCoreDirLister(this)) -{ - this->dirLister->setAutoUpdate(true); - - const static auto packItems = [](const KFileItemList &items) -> FMH::MODEL_LIST { - return std::accumulate(items.constBegin(), items.constEnd(), FMH::MODEL_LIST(), [](FMH::MODEL_LIST &res, const KFileItem &item) -> FMH::MODEL_LIST { - res << FMH::getFileInfo(item); - return res; - }); - }; - - connect(dirLister, static_cast(&KCoreDirLister::completed), this, [&](QUrl url) { - qDebug() << "PATH CONTENT READY" << url; - emit this->pathContentReady(url); - }); - - connect(dirLister, static_cast(&KCoreDirLister::itemsAdded), this, [&](QUrl dirUrl, KFileItemList items) { - qDebug() << "MORE ITEMS WERE ADDED"; - emit this->pathContentItemsReady({dirUrl, packItems(items)}); - }); - - // connect(dirLister, static_cast(&KCoreDirLister::newItems), [&](KFileItemList items) - // { - // qDebug()<< "MORE NEW ITEMS WERE ADDED"; - // for(const auto &item : items) - // qDebug()<< "MORE <<" << item.url(); - // - // emit this->pathContentChanged(dirLister->url()); - // }); - - connect(dirLister, static_cast(&KCoreDirLister::itemsDeleted), this, [&](KFileItemList items) { - qDebug() << "ITEMS WERE DELETED"; - emit this->pathContentItemsRemoved({dirLister->url(), packItems(items)}); - }); - - connect(dirLister, static_cast> &items)>(&KCoreDirLister::refreshItems), this, [&](QList> items) { - qDebug() << "ITEMS WERE REFRESHED"; - - const auto res = std::accumulate( - items.constBegin(), items.constEnd(), QVector>(), [](QVector> &list, const QPair &pair) -> QVector> { - list << QPair {FMH::getFileInfo(pair.first), FMH::getFileInfo(pair.second)}; - return list; - }); - - emit this->pathContentItemsChanged(res); - }); - -#ifdef COMPONENT_SYNCING - connect(this->sync, &Syncing::listReady, [this](const FMH::MODEL_LIST &list, const QUrl &url) { - emit this->cloudServerContentReady(list, url); - }); - - connect(this->sync, &Syncing::itemReady, [this](const FMH::MODEL &item, const QUrl &url, const Syncing::SIGNAL_TYPE &signalType) { - switch (signalType) { - case Syncing::SIGNAL_TYPE::OPEN: - FMStatic::openUrl(item[FMH::MODEL_KEY::PATH]); - break; - - case Syncing::SIGNAL_TYPE::DOWNLOAD: - emit this->cloudItemReady(item, url); - break; - - case Syncing::SIGNAL_TYPE::COPY: { - QVariantMap data; - const auto keys = item.keys(); - for (auto key : keys) - data.insert(FMH::MODEL_NAME[key], item[key]); - - // this->copy(QVariantList {data}, this->sync->getCopyTo()); - break; - } - default: - return; - } - }); - - connect(this->sync, &Syncing::error, [this](const QString &message) { - emit this->warningMessage(message); - }); - - connect(this->sync, &Syncing::progress, [this](const int &percent) { - emit this->loadProgress(percent); - }); - - connect(this->sync, &Syncing::dirCreated, [this](const FMH::MODEL &dir, const QUrl &url) { - emit this->newItem(dir, url); - }); - - connect(this->sync, &Syncing::uploadReady, [this](const FMH::MODEL &item, const QUrl &url) { - emit this->newItem(item, url); - }); -#endif -} - -void FM::getPathContent(const QUrl &path, const bool &hidden, const bool &onlyDirs, const QStringList &filters, const QDirIterator::IteratorFlags &iteratorFlags) -{ - qDebug() << "Getting async path contents"; - Q_UNUSED(iteratorFlags) - this->dirLister->setShowingDotFiles(hidden); - this->dirLister->setDirOnlyMode(onlyDirs); - this->dirLister->setNameFilter(filters.join(" ")); - - if (this->dirLister->openUrl(path)) - qDebug() << "GETTING PATH CONTENT" << path; -} - -FMH::MODEL_LIST FM::getAppsPath() -{ - return FMH::MODEL_LIST {FMH::MODEL {{FMH::MODEL_KEY::ICON, "system-run"}, - {FMH::MODEL_KEY::LABEL, FMH::PATHTYPE_LABEL[FMH::PATHTYPE_KEY::APPS_PATH]}, - {FMH::MODEL_KEY::PATH, FMH::PATHTYPE_URI[FMH::PATHTYPE_KEY::APPS_PATH]}, - {FMH::MODEL_KEY::TYPE, FMH::PATHTYPE_LABEL[FMH::PATHTYPE_KEY::PLACES_PATH]}}}; -} - -bool FM::getCloudServerContent(const QUrl &path, const QStringList &filters, const int &depth) -{ -#ifdef COMPONENT_SYNCING - const auto __list = path.toString().replace("cloud:///", "/").split("/"); - - if (__list.isEmpty() || __list.size() < 2) { - qWarning() << "Could not parse username to get cloud server content"; - return false; - } - - auto user = __list[1]; - // auto data = this->get(QString("select * from clouds where user = '%1'").arg(user)); - QVariantList data; - if (data.isEmpty()) - return false; - - auto map = data.first().toMap(); - - user = map[FMH::MODEL_NAME[FMH::MODEL_KEY::USER]].toString(); - auto server = map[FMH::MODEL_NAME[FMH::MODEL_KEY::SERVER]].toString(); - auto password = map[FMH::MODEL_NAME[FMH::MODEL_KEY::PASSWORD]].toString(); - this->sync->setCredentials(server, user, password); - - this->sync->listContent(path, filters, depth); - return true; -#else - Q_UNUSED(path) - Q_UNUSED(filters) - Q_UNUSED(depth) - - return false; -#endif -} - -void FM::createCloudDir(const QString &path, const QString &name) -{ -#ifdef COMPONENT_SYNCING - this->sync->createDir(path, name); -#endif -} - -void FM::openCloudItem(const QVariantMap &item) -{ -#ifdef COMPONENT_SYNCING - FMH::MODEL data; - const auto keys = item.keys(); - for (const auto &key : keys) - data.insert(FMH::MODEL_NAME_KEY[key], item[key].toString()); - - this->sync->resolveFile(data, Syncing::SIGNAL_TYPE::OPEN); -#endif -} - -void FM::getCloudItem(const QVariantMap &item) -{ -#ifdef COMPONENT_SYNCING - this->sync->resolveFile(FMH::toModel(item), Syncing::SIGNAL_TYPE::DOWNLOAD); -#endif -} - -QString FM::resolveUserCloudCachePath(const QString &server, const QString &user) -{ - Q_UNUSED(server) - return FMH::CloudCachePath + "opendesktop/" + user; -} - -QString FM::resolveLocalCloudPath(const QString &path) -{ -#ifdef COMPONENT_SYNCING - return QString(path).replace(FMH::PATHTYPE_URI[FMH::PATHTYPE_KEY::CLOUD_PATH] + this->sync->getUser(), ""); -#else - return QString(); -#endif -} - -bool FM::cut(const QList &urls, const QUrl &where) -{ - return FMStatic::cut(urls, where); -} - -bool FM::copy(const QList &urls, const QUrl &where) -{ - return FMStatic::copy(urls, where); -} diff --git a/src/fm.h b/src/fm.h deleted file mode 100644 index 97174b1..0000000 --- a/src/fm.h +++ /dev/null @@ -1,206 +0,0 @@ -#ifndef FM_H -#define FM_H - -#include -#include -#include -#include -#include -#include - -#include "fmh.h" -#include "fmstatic.h" - -#if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID) -class KCoreDirLister; -#else -class QFileSystemWatcher; - -namespace FMH -{ -class FileLoader; -} -/** - * @brief The QDirLister class - * Placeholder for the KCoreDirLister for other system other than GNU Linux - */ -class QDirLister : public QObject -{ - Q_OBJECT -public: - explicit QDirLister(QObject *parent = nullptr); - -public slots: - /** - * @brief openUrl - * @param url - * @return - */ - bool openUrl(const QUrl &url); - - /** - * @brief setNameFilter - * @param filters - */ - void setNameFilter(QString filters); - - /** - * @brief setDirOnlyMode - * @param value - */ - void setDirOnlyMode(bool value); - - /** - * @brief setShowingDotFiles - * @param value - */ - void setShowingDotFiles(bool value); - -signals: - void itemsReady(FMH::MODEL_LIST items, QUrl url); - void itemReady(FMH::MODEL item, QUrl url); - void completed(QUrl url); - void itemsAdded(FMH::MODEL_LIST items, QUrl url); - void itemsDeleted(FMH::MODEL_LIST items, QUrl url); - void newItems(FMH::MODEL_LIST items, QUrl url); - void refreshItems(QVector> items, QUrl url); - -private: - FMH::FileLoader *m_loader; - QFileSystemWatcher *m_watcher; - - FMH::MODEL_LIST m_list; - QString m_nameFilters; - QUrl m_url; - bool m_dirOnly = false; - bool m_showDotFiles = false; - - bool m_checking = false; - - void reviewChanges(); - bool includes(const QUrl &url); - int indexOf(const FMH::MODEL_KEY &key, const QString &value) const; -}; -#endif - -class Syncing; -class Tagging; - -/** - * @brief The FM class - * File management methods with syncing and tagging integration if such components were enabled with COMPONENT_SYNCING and COMPONENT_TAGGING - */ -class FM : public QObject -{ - Q_OBJECT - -public: - FM(QObject *parent = nullptr); - - /** Syncing **/ - /** - * @brief getCloudServerContent - * Given a server URL address return the contents. This only works if the syncing component has been enabled COMPONENT_SYNCING - * @param server - * Server URL - * @param filters - * Filters to be applied - * @param depth - * How deep in the directory three go, for example 1 keeps the retrieval in the first level - * @return - */ - bool getCloudServerContent(const QUrl &server, const QStringList &filters = QStringList(), const int &depth = 0); - - /** - * @brief createCloudDir - * Creates a directory in the server. This only works if the syncing component has been enabled COMPONENT_SYNCING - * @param path - * Server address URL - * @param name - * Directory name - */ - Q_INVOKABLE void createCloudDir(const QString &path, const QString &name); - - /** - * @brief getPathContent - * Given a path URL extract the contents and return the information packaged as a model. This method is asyncronous and once items are ready signals are emitted, such as: pathContentItemsReady or pathContentReady - * @param path - * The directory path - * @param hidden - * If shoudl also pack hidden files - * @param onlyDirs - * Should only pack directories - * @param filters - * Filters to be applied to the retrieval - * @param iteratorFlags - * Directory iterator flags, for reference check QDirIterator documentation - */ - void getPathContent(const QUrl &path, const bool &hidden = false, const bool &onlyDirs = false, const QStringList &filters = QStringList(), const QDirIterator::IteratorFlags &iteratorFlags = QDirIterator::NoIteratorFlags); - - /** - * @brief resolveLocalCloudPath - * Given a server address URL resolve it to the local cache URL. This only works if the syncing component has been enabled COMPONENT_SYNCING - * @param path - * Server address - * @return - */ - QString resolveLocalCloudPath(const QString &path); - - /** - * @brief getAppsPath - * Gives the path to the applications directory. Missing integration with other system other than GNU Linux - * @return - */ - static FMH::MODEL_LIST getAppsPath(); - - /** - * @brief resolveUserCloudCachePath - * @param server - * @param user - * @return - */ - static QString resolveUserCloudCachePath(const QString &server, const QString &user); - -#ifdef COMPONENT_SYNCING - Syncing *sync; -#endif - -private: -#ifdef COMPONENT_TAGGING - Tagging *tag; -#endif - -#if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID) - KCoreDirLister *dirLister; -#else - QDirLister *dirLister; -#endif - -signals: - void cloudServerContentReady(FMH::MODEL_LIST list, const QUrl &url); - void cloudItemReady(FMH::MODEL item, QUrl path); // when a item is downloaded and ready - - void pathContentReady(QUrl path); - void pathContentItemsReady(FMH::PATH_CONTENT list); - void pathContentChanged(QUrl path); - void pathContentItemsChanged(QVector> items); - void pathContentItemsRemoved(FMH::PATH_CONTENT list); - - void warningMessage(QString message); - void loadProgress(int percent); - - void dirCreated(FMH::MODEL dir); - void newItem(FMH::MODEL item, QUrl path); // when a new item is created - -public slots: - void openCloudItem(const QVariantMap &item); - void getCloudItem(const QVariantMap &item); - - /* ACTIONS */ - bool copy(const QList &urls, const QUrl &where); - bool cut(const QList &urls, const QUrl &where); - - friend class FMStatic; -}; - -#endif // FM_H diff --git a/src/fmh.cpp b/src/fmh.cpp deleted file mode 100644 index d53c80a..0000000 --- a/src/fmh.cpp +++ /dev/null @@ -1,337 +0,0 @@ -#include "fmh.h" - -namespace FMH -{ -const QVector modelRoles(const FMH::MODEL &model) -{ - const auto keys = model.keys(); - return std::accumulate(keys.begin(), keys.end(), QVector(), [](QVector &res, const FMH::MODEL_KEY &key) { - res.append(key); - return res; - }); -} - -const QString mapValue(const QVariantMap &map, const FMH::MODEL_KEY &key) -{ - return map[FMH::MODEL_NAME[key]].toString(); -} - -const QVariantMap toMap(const FMH::MODEL &model) -{ - QVariantMap map; - for (const auto &key : model.keys()) - map.insert(FMH::MODEL_NAME[key], model[key]); - - return map; -} - -const FMH::MODEL toModel(const QVariantMap &map) -{ - FMH::MODEL model; - for (const auto &key : map.keys()) - model.insert(FMH::MODEL_NAME_KEY[key], map[key].toString()); - - return model; -} - -const FMH::MODEL_LIST toModelList(const QVariantList &list) -{ - FMH::MODEL_LIST res; - return std::accumulate(list.constBegin(), list.constEnd(), res, [](FMH::MODEL_LIST &res, const QVariant &item) -> FMH::MODEL_LIST { - res << FMH::toModel(item.toMap()); - return res; - }); -} - -const QVariantList toMapList(const FMH::MODEL_LIST &list) -{ - QVariantList res; - return std::accumulate(list.constBegin(), list.constEnd(), res, [](QVariantList &res, const FMH::MODEL &item) -> QVariantList { - res << FMH::toMap(item); - return res; - }); -} - -const FMH::MODEL filterModel(const FMH::MODEL &model, const QVector &keys) -{ - FMH::MODEL res; - return std::accumulate(keys.constBegin(), keys.constEnd(), res, [=](FMH::MODEL &res, const FMH::MODEL_KEY &key) -> FMH::MODEL { - if (model.contains(key)) - res[key] = model[key]; - return res; - }); -} - -const QStringList modelToList(const FMH::MODEL_LIST &list, const FMH::MODEL_KEY &key) -{ - QStringList res; - return std::accumulate(list.constBegin(), list.constEnd(), res, [key](QStringList &res, const FMH::MODEL &item) -> QStringList { - if (item.contains(key)) - res << item[key]; - return res; - }); -} - -bool fileExists(const QUrl &path) -{ - if (!path.isLocalFile()) { - qWarning() << "URL recived is not a local file" << path; - return false; - } - return QFileInfo::exists(path.toLocalFile()); -} - -const QString fileDir(const QUrl &path) // the directory path of the file -{ - QString res = path.toString(); - if (path.isLocalFile()) { - const QFileInfo file(path.toLocalFile()); - if (file.isDir()) - res = path.toString(); - else - res = QUrl::fromLocalFile(file.dir().absolutePath()).toString(); - } else - qWarning() << "The path is not a local one. FM::fileDir"; - - return res; -} - -const QUrl parentDir(const QUrl &path) -{ - if (!path.isLocalFile()) { - qWarning() << "URL recived is not a local file, FM::parentDir" << path; - return path; - } - - QDir dir(path.toLocalFile()); - dir.cdUp(); - return QUrl::fromLocalFile(dir.absolutePath()); -} - -const QVariantMap dirConf(const QUrl &path) -{ - if (!path.isLocalFile()) { - qWarning() << "URL recived is not a local file" << path; - return QVariantMap(); - } - - if (!fileExists(path)) - return QVariantMap(); - - QString icon, iconsize, hidden, detailview, showthumbnail, showterminal; - - uint count = 0, sortby = MODEL_KEY::MODIFIED, viewType = 0; - - bool foldersFirst = false; - -#if defined Q_OS_ANDROID || defined Q_OS_WIN || defined Q_OS_MACOS || defined Q_OS_IOS - QSettings file(path.toLocalFile(), QSettings::Format::NativeFormat); - file.beginGroup(QString("Desktop Entry")); - icon = file.value("Icon").toString(); - file.endGroup(); - - file.beginGroup(QString("Settings")); - hidden = file.value("HiddenFilesShown").toString(); - file.endGroup(); - - file.beginGroup(QString("MAUIFM")); - iconsize = file.value("IconSize").toString(); - detailview = file.value("DetailView").toString(); - showthumbnail = file.value("ShowThumbnail").toString(); - showterminal = file.value("ShowTerminal").toString(); - count = file.value("Count").toInt(); - sortby = file.value("SortBy").toInt(); - foldersFirst = file.value("FoldersFirst").toBool(); - viewType = file.value("ViewType").toInt(); - file.endGroup(); - -#else - KConfig file(path.toLocalFile()); - icon = file.entryMap(QString("Desktop Entry"))["Icon"]; - hidden = file.entryMap(QString("Settings"))["HiddenFilesShown"]; - iconsize = file.entryMap(QString("MAUIFM"))["IconSize"]; - detailview = file.entryMap(QString("MAUIFM"))["DetailView"]; - showthumbnail = file.entryMap(QString("MAUIFM"))["ShowThumbnail"]; - showterminal = file.entryMap(QString("MAUIFM"))["ShowTerminal"]; - count = file.entryMap(QString("MAUIFM"))["Count"].toInt(); - sortby = file.entryMap(QString("MAUIFM"))["SortBy"].toInt(); - foldersFirst = file.entryMap(QString("MAUIFM"))["FoldersFirst"] == "true" ? true : false; - viewType = file.entryMap(QString("MAUIFM"))["ViewType"].toInt(); -#endif - - return QVariantMap({{MODEL_NAME[MODEL_KEY::ICON], icon.isEmpty() ? "folder" : icon}, - {MODEL_NAME[MODEL_KEY::ICONSIZE], iconsize}, - {MODEL_NAME[MODEL_KEY::COUNT], count}, - {MODEL_NAME[MODEL_KEY::SHOWTERMINAL], showterminal.isEmpty() ? "false" : showterminal}, - {MODEL_NAME[MODEL_KEY::SHOWTHUMBNAIL], showthumbnail.isEmpty() ? "false" : showthumbnail}, - {MODEL_NAME[MODEL_KEY::DETAILVIEW], detailview.isEmpty() ? "false" : detailview}, - {MODEL_NAME[MODEL_KEY::HIDDEN], hidden.isEmpty() ? false : (hidden == "true" ? true : false)}, - {MODEL_NAME[MODEL_KEY::SORTBY], sortby}, - {MODEL_NAME[MODEL_KEY::FOLDERSFIRST], foldersFirst}, - {MODEL_NAME[MODEL_KEY::VIEWTYPE], viewType}}); -} - -void setDirConf(const QUrl &path, const QString &group, const QString &key, const QVariant &value) -{ - if (!path.isLocalFile()) { - qWarning() << "URL recived is not a local file" << path; - return; - } - -#if defined Q_OS_ANDROID || defined Q_OS_WIN || defined Q_OS_MACOS || defined Q_OS_IOS - QSettings file(path.toLocalFile(), QSettings::Format::IniFormat); - file.beginGroup(group); - file.setValue(key, value); - file.endGroup(); - file.sync(); -#else - KConfig file(path.toLocalFile(), KConfig::SimpleConfig); - auto kgroup = file.group(group); - kgroup.writeEntry(key, value); - // file.reparseConfiguration(); - file.sync(); -#endif -} - -const QString getIconName(const QUrl &path) -{ - if (path.isLocalFile() && QFileInfo(path.toLocalFile()).isDir()) { - if (folderIcon.contains(path.toString())) - return folderIcon[path.toString()]; - else { - const auto icon = dirConf(QString(path.toString() + "/%1").arg(".directory"))[MODEL_NAME[MODEL_KEY::ICON]].toString(); - return icon.isEmpty() ? "folder" : icon; - } - - } else { - QMimeDatabase mime; - const auto type = mime.mimeTypeForFile(path.toString()); - return type.iconName(); - -// KFileItem mime(path); -// return mime.iconName(); - } -} - -const QString getMime(const QUrl &path) -{ - if (!path.isLocalFile()) { - qWarning() << "URL recived is not a local file, getMime" << path; - return QString(); - } - - const QMimeDatabase mimedb; - return mimedb.mimeTypeForFile(path.toLocalFile()).name(); -} - -const QUrl thumbnailUrl(const QUrl &url, const QString &mimetype) -{ -#if defined Q_OS_LINUX && !defined Q_OS_ANDROID - if (checkFileType(FILTER_TYPE::DOCUMENT, mimetype) || checkFileType(FILTER_TYPE::VIDEO, mimetype)) { - return QUrl("image://thumbnailer/" + url.toString()); - } -#endif - - if (checkFileType(FILTER_TYPE::IMAGE, mimetype)) { - return url; - } - - return QUrl(); -} - -#if (!defined Q_OS_ANDROID && defined Q_OS_LINUX) || defined Q_OS_WIN -const FMH::MODEL getFileInfo(const KFileItem &kfile) -{ - return MODEL {{MODEL_KEY::LABEL, kfile.name()}, - {MODEL_KEY::NAME, kfile.name().remove(kfile.name().lastIndexOf("."), kfile.name().size())}, - {MODEL_KEY::DATE, kfile.time(KFileItem::FileTimes::CreationTime).toString(Qt::TextDate)}, - {MODEL_KEY::MODIFIED, kfile.time(KFileItem::FileTimes::ModificationTime).toString(Qt::TextDate)}, - {MODEL_KEY::LAST_READ, kfile.time(KFileItem::FileTimes::AccessTime).toString(Qt::TextDate)}, - {MODEL_KEY::PATH, kfile.mostLocalUrl().toString()}, - {MODEL_KEY::URL, kfile.mostLocalUrl().toString()}, - {MODEL_KEY::THUMBNAIL, thumbnailUrl(kfile.mostLocalUrl(), kfile.mimetype()).toString()}, - {MODEL_KEY::SYMLINK, kfile.linkDest()}, - {MODEL_KEY::IS_SYMLINK, QVariant(kfile.isLink()).toString()}, - {MODEL_KEY::HIDDEN, QVariant(kfile.isHidden()).toString()}, - {MODEL_KEY::IS_DIR, QVariant(kfile.isDir()).toString()}, - {MODEL_KEY::IS_FILE, QVariant(kfile.isFile()).toString()}, - {MODEL_KEY::WRITABLE, QVariant(kfile.isWritable()).toString()}, - {MODEL_KEY::READABLE, QVariant(kfile.isReadable()).toString()}, - {MODEL_KEY::EXECUTABLE, QVariant(kfile.isDesktopFile()).toString()}, - {MODEL_KEY::MIME, kfile.mimetype()}, - {MODEL_KEY::GROUP, kfile.group()}, - {MODEL_KEY::ICON, kfile.iconName()}, - // for set wallpaper. - {MODEL_KEY::IMG, QVariant(kfile.mimetype().startsWith("image/")).toString()}, - {MODEL_KEY::SIZE, QString::number(kfile.size())}, - {MODEL_KEY::OWNER, kfile.user()}, - {MODEL_KEY::COUNT, kfile.isLocalFile() && kfile.isDir() ? QString::number(QDir(kfile.localPath()).count()) : "0"}}; -} -#endif - -const FMH::MODEL getFileInfoModel(const QUrl &path) -{ - MODEL res; -#if defined Q_OS_ANDROID || defined Q_OS_MACOS || defined Q_OS_IOS || defined Q_OS_WIN - const QFileInfo file(path.toLocalFile()); - if (!file.exists()) - return MODEL(); - - const auto mime = getMime(path); - res = MODEL {{MODEL_KEY::GROUP, file.group()}, - {MODEL_KEY::OWNER, file.owner()}, - {MODEL_KEY::SUFFIX, file.completeSuffix()}, - {MODEL_KEY::LABEL, /*file.isDir() ? file.baseName() :*/ path == HomePath ? QStringLiteral("Home") : file.fileName()}, - {MODEL_KEY::NAME, file.fileName()}, - {MODEL_KEY::DATE, file.birthTime().toString(Qt::TextDate)}, - {MODEL_KEY::MODIFIED, file.lastModified().toString(Qt::TextDate)}, - {MODEL_KEY::LAST_READ, file.lastRead().toString(Qt::TextDate)}, - {MODEL_KEY::MIME, mime}, - {MODEL_KEY::SYMLINK, file.symLinkTarget()}, - {MODEL_KEY::IS_SYMLINK, QVariant(file.isSymLink()).toString()}, - {MODEL_KEY::IS_FILE, QVariant(file.isFile()).toString()}, - {MODEL_KEY::HIDDEN, QVariant(file.isHidden()).toString()}, - {MODEL_KEY::IS_DIR, QVariant(file.isDir()).toString()}, - {MODEL_KEY::WRITABLE, QVariant(file.isWritable()).toString()}, - {MODEL_KEY::READABLE, QVariant(file.isReadable()).toString()}, - {MODEL_KEY::EXECUTABLE, QVariant(file.suffix().endsWith(".desktop")).toString()}, - {MODEL_KEY::ICON, getIconName(path)}, - {MODEL_KEY::SIZE, QString::number(file.size()) /*locale.formattedDataSize(file.size())*/}, - {MODEL_KEY::PATH, path.toString()}, - {MODEL_KEY::URL, path.toString()}, - {MODEL_KEY::THUMBNAIL, thumbnailUrl(path, mime).toString()}, - {MODEL_KEY::COUNT, file.isDir() ? QString::number(QDir(path.toLocalFile()).count()) : "0"}}; -#else - res = getFileInfo(KFileItem(path, KFileItem::MimeTypeDetermination::NormalMimeTypeDetermination)); -#endif - return res; -} - -const QVariantMap getFileInfo(const QUrl &path) -{ - return toMap(getFileInfoModel(path)); -} - -const MODEL getDirInfoModel(const QUrl &path, const QString &type) -{ - auto res = getFileInfoModel(path); - res[MODEL_KEY::TYPE] = type; - return res; -} - -const QVariantMap getDirInfo(const QUrl &path) -{ - return toMap(getDirInfoModel(path)); -} - -PATHTYPE_KEY getPathType(const QUrl &url) -{ - return PATHTYPE_SCHEME_NAME[url.scheme()]; -} - -bool checkFileType(const FMH::FILTER_TYPE &type, const QString &mimeTypeName) -{ - return SUPPORTED_MIMETYPES[type].contains(mimeTypeName); -} - -} diff --git a/src/fmh.h b/src/fmh.h deleted file mode 100644 index 91229f3..0000000 --- a/src/fmh.h +++ /dev/null @@ -1,1019 +0,0 @@ -/* - * Copyright 2018 Camilo Higuita - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, 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 Library 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. - */ - -#ifndef FMH_H -#define FMH_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if defined(Q_OS_ANDROID) -#include "mauiandroid.h" -#elif defined Q_OS_LINUX || defined Q_OS_WIN -#include -#include -#include -#include -#endif - -/** - * A set of helpers related to file management and modeling of data - */ -namespace FMH -{ -/** - * @brief isAndroid - * @return - */ -bool isAndroid(); - -/** - * @brief isWindows - * @return - */ -bool isWindows(); - -/** - * @brief isLinux - * @return - */ -bool isLinux(); - -/** - * @brief isMac - * @return - */ -bool isMac(); - -/** - * @brief isIOS - * @return - */ -bool isIOS(); - -/** - * @brief The FILTER_TYPE enum - */ -enum FILTER_TYPE : int { AUDIO, VIDEO, TEXT, IMAGE, DOCUMENT, COMPRESSED, FONT, NONE }; - -static const QStringList AUDIO_MIMETYPES = {"audio/mpeg", "audio/mp4", "audio/flac", "audio/ogg", "audio/wav"}; - -static const QStringList VIDEO_MIMETYPES = {"video/mp4", "video/x-matroska", "video/webm", "video/avi", "video/flv", "video/mpg", "video/wmv", "video/mov", "video/ogg", "video/mpeg", "video/jpeg"}; - -static const QStringList TEXT_MIMETYPES = {"text/markdown", - "text/x-chdr", - "text/x-c++src", - "text/x-c++hdr", - "text/css", - "text/html", - "text/plain", - "text/richtext", - "text/scriptlet", - "text/x-vcard", - "text/x-go", - "text/x-cmake", - "text/x-qml", - "application/xml", - "application/javascript", - "application/json", - "application/pgp-keys", - "application/x-shellscript", - "application/x-cmakecache", - "application/x-kicad-project"}; - -static const QStringList IMAGE_MIMETYPES = {"image/bmp", "image/webp", "image/png", "image/gif", "image/jpeg", "image/web", "image/svg", "image/svg+xml"}; - -static const QStringList DOCUMENT_MIMETYPES = {"application/pdf", "application/rtf", "application/doc", "application/odf"}; - -static const QStringList COMPRESSED_MIMETYPES = - {"application/x-compress", "application/x-compressed", "application/x-xz-compressed-tar", "application/x-compressed-tar", "application/x-xz", "application/x-bzip", "application/x-gtar", "application/x-gzip", "application/zip"}; - -static const QStringList FONT_MIMETYPES = {"font/ttf", "font/otf"}; - -static const QMap SUPPORTED_MIMETYPES {{FILTER_TYPE::AUDIO, AUDIO_MIMETYPES}, - {FILTER_TYPE::VIDEO, VIDEO_MIMETYPES}, - {FILTER_TYPE::TEXT, TEXT_MIMETYPES}, - {FILTER_TYPE::IMAGE, IMAGE_MIMETYPES}, - {FILTER_TYPE::DOCUMENT, DOCUMENT_MIMETYPES}, - {FILTER_TYPE::FONT, FONT_MIMETYPES}, - {FILTER_TYPE::COMPRESSED, COMPRESSED_MIMETYPES}}; - -/** - * @brief getMimeTypeSuffixes - * @param type - * @return - */ -static QStringList getMimeTypeSuffixes(const FMH::FILTER_TYPE &type, QString (*cb)(QString)) -{ - QStringList res; - QMimeDatabase mimedb; - for (const auto &mime : SUPPORTED_MIMETYPES[type]) { - if (cb) { - const auto suffixes = mimedb.mimeTypeForName(mime).suffixes(); - for (const QString &_suffix : suffixes) { - res << cb(_suffix); - } - } else { - res << mimedb.mimeTypeForName(mime).suffixes(); - } - } - return res; -} - -static QHash FILTER_LIST = {{FILTER_TYPE::AUDIO, - getMimeTypeSuffixes(FILTER_TYPE::AUDIO, - [](QString suffix) -> QString { - return "*." + suffix; - })}, - {FILTER_TYPE::VIDEO, - getMimeTypeSuffixes(FILTER_TYPE::VIDEO, - [](QString suffix) -> QString { - return "*." + suffix; - })}, - {FILTER_TYPE::TEXT, - getMimeTypeSuffixes(FILTER_TYPE::TEXT, - [](QString suffix) -> QString { - return "*." + suffix; - })}, - {FILTER_TYPE::DOCUMENT, - getMimeTypeSuffixes(FILTER_TYPE::DOCUMENT, - [](QString suffix) -> QString { - return "*." + suffix; - })}, - {FILTER_TYPE::COMPRESSED, - getMimeTypeSuffixes(FILTER_TYPE::COMPRESSED, - [](QString suffix) -> QString { - return "*." + suffix; - })}, - {FILTER_TYPE::FONT, - getMimeTypeSuffixes(FILTER_TYPE::FONT, - [](QString suffix) -> QString { - return "*." + suffix; - })}, - {FILTER_TYPE::IMAGE, - getMimeTypeSuffixes(FILTER_TYPE::IMAGE, - [](QString suffix) -> QString { - return "*." + suffix; - })}, - {FILTER_TYPE::NONE, QStringList()}}; - -/** - * @brief The MODEL_KEY enum - */ -enum MODEL_KEY : int { - ICON, - LABEL, - PATH, - URL, - TYPE, - GROUP, - OWNER, - SUFFIX, - NAME, - DATE, - SIZE, - MODIFIED, - MIME, - TAG, - PERMISSIONS, - THUMBNAIL, - THUMBNAIL_1, - THUMBNAIL_2, - THUMBNAIL_3, - HIDDEN, - ICONSIZE, - DETAILVIEW, - SHOWTHUMBNAIL, - SHOWTERMINAL, - COUNT, - SORTBY, - USER, - PASSWORD, - SERVER, - FOLDERSFIRST, - VIEWTYPE, - ADDDATE, - FAV, - FAVORITE, - COLOR, - RATE, - FORMAT, - PLACE, - LOCATION, - ALBUM, - ARTIST, - TRACK, - DURATION, - ARTWORK, - PLAYLIST, - LYRICS, - WIKI, - MOOD, - SOURCETYPE, - GENRE, - NOTE, - COMMENT, - CONTEXT, - SOURCE, - TITLE, - ID, - PARENT_ID, - RELEASEDATE, - LICENSE, - DESCRIPTION, - BOOKMARK, - ACCOUNT, - ACCOUNTTYPE, - VERSION, - DOMAIN_M, - CATEGORY, - CONTENT, - PIN, - IMG, - PREVIEW, - LINK, - STAMP, - BOOK, - - /** ccdav keys **/ - N, - PHOTO, - GENDER, - ADR, - ADR_2, - ADR_3, - EMAIL, - EMAIL_2, - EMAIL_3, - LANG, - NICKNAME, - ORG, - PROFILE, - TZ, - TEL, - TEL_2, - TEL_3, - IM, - - /** other keys **/ - CITY, - STATE, - COUNTRY, - - /** keys from opendesktop store **/ - PACKAGE_ARCH, - PACKAGE_TYPE, - GPG_FINGERPRINT, - GPG_SIGNATURE, - PACKAGE_NAME, - PRICE, - REPOSITORY, - TAGS, - WAY, - PIC, - SMALL_PIC, - CHANGED, - COMMENTS, - CREATED, - DETAIL_PAGE, - DETAILS, - TOTAL_DOWNLOADS, - GHNS_EXCLUDED, - LANGUAGE, - PERSON_ID, - SCORE, - SUMMARY, - TYPE_ID, - TYPE_NAME, - XDG_TYPE, - - // file props - SYMLINK, - IS_SYMLINK, - IS_DIR, - IS_FILE, - IS_REMOTE, - EXECUTABLE, - READABLE, - WRITABLE, - LAST_READ, - VALUE, - KEY, - - MAC, - LOT, - APP, - URI, - DEVICE, - LASTSYNC - -}; - -static const QHash MODEL_NAME = {{MODEL_KEY::ICON, "icon"}, - {MODEL_KEY::LABEL, "label"}, - {MODEL_KEY::PATH, "path"}, - {MODEL_KEY::URL, "url"}, - {MODEL_KEY::TYPE, "type"}, - {MODEL_KEY::GROUP, "group"}, - {MODEL_KEY::OWNER, "owner"}, - {MODEL_KEY::SUFFIX, "suffix"}, - {MODEL_KEY::NAME, "name"}, - {MODEL_KEY::DATE, "date"}, - {MODEL_KEY::MODIFIED, "modified"}, - {MODEL_KEY::MIME, "mime"}, - {MODEL_KEY::SIZE, "size"}, - {MODEL_KEY::TAG, "tag"}, - {MODEL_KEY::PERMISSIONS, "permissions"}, - {MODEL_KEY::THUMBNAIL, "thumbnail"}, - {MODEL_KEY::THUMBNAIL_1, "thumbnail_1"}, - {MODEL_KEY::THUMBNAIL_2, "thumbnail_2"}, - {MODEL_KEY::THUMBNAIL_3, "thumbnail_3"}, - {MODEL_KEY::ICONSIZE, "iconsize"}, - {MODEL_KEY::HIDDEN, "hidden"}, - {MODEL_KEY::DETAILVIEW, "detailview"}, - {MODEL_KEY::SHOWTERMINAL, "showterminal"}, - {MODEL_KEY::SHOWTHUMBNAIL, "showthumbnail"}, - {MODEL_KEY::COUNT, "count"}, - {MODEL_KEY::SORTBY, "sortby"}, - {MODEL_KEY::USER, "user"}, - {MODEL_KEY::PASSWORD, "password"}, - {MODEL_KEY::SERVER, "server"}, - {MODEL_KEY::FOLDERSFIRST, "foldersfirst"}, - {MODEL_KEY::VIEWTYPE, "viewtype"}, - {MODEL_KEY::ADDDATE, "adddate"}, - {MODEL_KEY::FAV, "fav"}, - {MODEL_KEY::FAVORITE, "favorite"}, - {MODEL_KEY::COLOR, "color"}, - {MODEL_KEY::RATE, "rate"}, - {MODEL_KEY::FORMAT, "format"}, - {MODEL_KEY::PLACE, "place"}, - {MODEL_KEY::LOCATION, "location"}, - {MODEL_KEY::ALBUM, "album"}, - {MODEL_KEY::DURATION, "duration"}, - {MODEL_KEY::RELEASEDATE, "releasedate"}, - {MODEL_KEY::ARTIST, "artist"}, - {MODEL_KEY::LYRICS, "lyrics"}, - {MODEL_KEY::TRACK, "track"}, - {MODEL_KEY::GENRE, "genre"}, - {MODEL_KEY::WIKI, "wiki"}, - {MODEL_KEY::CONTEXT, "context"}, - {MODEL_KEY::SOURCETYPE, "sourcetype"}, - {MODEL_KEY::ARTWORK, "artwork"}, - {MODEL_KEY::NOTE, "note"}, - {MODEL_KEY::MOOD, "mood"}, - {MODEL_KEY::COMMENT, "comment"}, - {MODEL_KEY::PLAYLIST, "playlist"}, - {MODEL_KEY::SOURCE, "source"}, - {MODEL_KEY::TITLE, "title"}, - {MODEL_KEY::ID, "id"}, - {MODEL_KEY::PERSON_ID, "personid"}, - {MODEL_KEY::PARENT_ID, "parentid"}, - {MODEL_KEY::LICENSE, "license"}, - {MODEL_KEY::DESCRIPTION, "description"}, - {MODEL_KEY::BOOKMARK, "bookmark"}, - {MODEL_KEY::ACCOUNT, "account"}, - {MODEL_KEY::ACCOUNTTYPE, "accounttype"}, - {MODEL_KEY::VERSION, "version"}, - {MODEL_KEY::DOMAIN_M, "domain"}, - {MODEL_KEY::CATEGORY, "category"}, - {MODEL_KEY::CONTENT, "content"}, - {MODEL_KEY::PIN, "pin"}, - {MODEL_KEY::IMG, "img"}, - {MODEL_KEY::PREVIEW, "preview"}, - {MODEL_KEY::LINK, "link"}, - {MODEL_KEY::STAMP, "stamp"}, - {MODEL_KEY::BOOK, "book"}, - - /** ccdav keys **/ - {MODEL_KEY::N, "n"}, - {MODEL_KEY::IM, "im"}, - {MODEL_KEY::PHOTO, "photo"}, - {MODEL_KEY::GENDER, "gender"}, - {MODEL_KEY::ADR, "adr"}, - {MODEL_KEY::ADR_2, "adr2"}, - {MODEL_KEY::ADR_3, "adr3"}, - {MODEL_KEY::EMAIL, "email"}, - {MODEL_KEY::EMAIL_2, "email2"}, - {MODEL_KEY::EMAIL_3, "email3"}, - {MODEL_KEY::LANG, "lang"}, - {MODEL_KEY::NICKNAME, "nickname"}, - {MODEL_KEY::ORG, "org"}, - {MODEL_KEY::PROFILE, "profile"}, - {MODEL_KEY::TZ, "tz"}, - {MODEL_KEY::TEL, "tel"}, - {MODEL_KEY::TEL_2, "tel2"}, - {MODEL_KEY::TEL_3, "tel3"}, - - {MODEL_KEY::CITY, "city"}, - {MODEL_KEY::STATE, "state"}, - {MODEL_KEY::COUNTRY, "country"}, - - // opendesktop keys - {MODEL_KEY::PACKAGE_ARCH, "packagearch"}, - {MODEL_KEY::PACKAGE_TYPE, "packagetype"}, - {MODEL_KEY::GPG_FINGERPRINT, "gpgfingerprint"}, - {MODEL_KEY::GPG_SIGNATURE, "gpgsignature"}, - {MODEL_KEY::PACKAGE_NAME, "packagename"}, - {MODEL_KEY::PRICE, "price"}, - {MODEL_KEY::REPOSITORY, "repository"}, - {MODEL_KEY::TAGS, "tags"}, - {MODEL_KEY::WAY, "way"}, - {MODEL_KEY::PIC, "pic"}, - {MODEL_KEY::SMALL_PIC, "smallpic"}, - {MODEL_KEY::CHANGED, "changed"}, - {MODEL_KEY::COMMENTS, "comments"}, - {MODEL_KEY::CREATED, "created"}, - {MODEL_KEY::DETAIL_PAGE, "detailpage"}, - {MODEL_KEY::DETAILS, "details"}, - {MODEL_KEY::TOTAL_DOWNLOADS, "totaldownloads"}, - {MODEL_KEY::GHNS_EXCLUDED, "ghnsexcluded"}, - {MODEL_KEY::LANGUAGE, "language"}, - {MODEL_KEY::SCORE, "score"}, - {MODEL_KEY::SUMMARY, "summary"}, - {MODEL_KEY::TYPE_ID, "typeid"}, - {MODEL_KEY::TYPE_NAME, "typename"}, - {MODEL_KEY::XDG_TYPE, "xdgtype"}, - - // file props - {MODEL_KEY::SYMLINK, "symlink"}, - {MODEL_KEY::IS_SYMLINK, "issymlink"}, - {MODEL_KEY::LAST_READ, "lastread"}, - {MODEL_KEY::READABLE, "readable"}, - {MODEL_KEY::WRITABLE, "writeable"}, - {MODEL_KEY::IS_DIR, "isdir"}, - {MODEL_KEY::IS_FILE, "isfile"}, - {MODEL_KEY::IS_REMOTE, "isremote"}, - {MODEL_KEY::EXECUTABLE, "executable"}, - {MODEL_KEY::VALUE, "value"}, - {MODEL_KEY::KEY, "key"}, - - {MODEL_KEY::MAC, "mac"}, - {MODEL_KEY::LOT, "lot"}, - {MODEL_KEY::APP, "app"}, - {MODEL_KEY::URI, "uri"}, - {MODEL_KEY::DEVICE, "device"}, - {MODEL_KEY::LASTSYNC, "lastsync"}}; - -static const QHash MODEL_NAME_KEY = {{MODEL_NAME[MODEL_KEY::ICON], MODEL_KEY::ICON}, - {MODEL_NAME[MODEL_KEY::LABEL], MODEL_KEY::LABEL}, - {MODEL_NAME[MODEL_KEY::PATH], MODEL_KEY::PATH}, - {MODEL_NAME[MODEL_KEY::URL], MODEL_KEY::URL}, - {MODEL_NAME[MODEL_KEY::TYPE], MODEL_KEY::TYPE}, - {MODEL_NAME[MODEL_KEY::GROUP], MODEL_KEY::GROUP}, - {MODEL_NAME[MODEL_KEY::OWNER], MODEL_KEY::OWNER}, - {MODEL_NAME[MODEL_KEY::SUFFIX], MODEL_KEY::SUFFIX}, - {MODEL_NAME[MODEL_KEY::NAME], MODEL_KEY::NAME}, - {MODEL_NAME[MODEL_KEY::DATE], MODEL_KEY::DATE}, - {MODEL_NAME[MODEL_KEY::MODIFIED], MODEL_KEY::MODIFIED}, - {MODEL_NAME[MODEL_KEY::MIME], MODEL_KEY::MIME}, - { - MODEL_NAME[MODEL_KEY::SIZE], - MODEL_KEY::SIZE, - }, - {MODEL_NAME[MODEL_KEY::TAG], MODEL_KEY::TAG}, - {MODEL_NAME[MODEL_KEY::PERMISSIONS], MODEL_KEY::PERMISSIONS}, - {MODEL_NAME[MODEL_KEY::THUMBNAIL], MODEL_KEY::THUMBNAIL}, - {MODEL_NAME[MODEL_KEY::THUMBNAIL_1], MODEL_KEY::THUMBNAIL_1}, - {MODEL_NAME[MODEL_KEY::THUMBNAIL_2], MODEL_KEY::THUMBNAIL_2}, - {MODEL_NAME[MODEL_KEY::THUMBNAIL_3], MODEL_KEY::THUMBNAIL_3}, - {MODEL_NAME[MODEL_KEY::ICONSIZE], MODEL_KEY::ICONSIZE}, - {MODEL_NAME[MODEL_KEY::HIDDEN], MODEL_KEY::HIDDEN}, - {MODEL_NAME[MODEL_KEY::DETAILVIEW], MODEL_KEY::DETAILVIEW}, - {MODEL_NAME[MODEL_KEY::SHOWTERMINAL], MODEL_KEY::SHOWTERMINAL}, - {MODEL_NAME[MODEL_KEY::SHOWTHUMBNAIL], MODEL_KEY::SHOWTHUMBNAIL}, - {MODEL_NAME[MODEL_KEY::COUNT], MODEL_KEY::COUNT}, - {MODEL_NAME[MODEL_KEY::SORTBY], MODEL_KEY::SORTBY}, - {MODEL_NAME[MODEL_KEY::USER], MODEL_KEY::USER}, - {MODEL_NAME[MODEL_KEY::PASSWORD], MODEL_KEY::PASSWORD}, - {MODEL_NAME[MODEL_KEY::SERVER], MODEL_KEY::SERVER}, - {MODEL_NAME[MODEL_KEY::VIEWTYPE], MODEL_KEY::VIEWTYPE}, - {MODEL_NAME[MODEL_KEY::ADDDATE], MODEL_KEY::ADDDATE}, - {MODEL_NAME[MODEL_KEY::FAV], MODEL_KEY::FAV}, - {MODEL_NAME[MODEL_KEY::FAVORITE], MODEL_KEY::FAVORITE}, - {MODEL_NAME[MODEL_KEY::COLOR], MODEL_KEY::COLOR}, - {MODEL_NAME[MODEL_KEY::RATE], MODEL_KEY::RATE}, - {MODEL_NAME[MODEL_KEY::FORMAT], MODEL_KEY::FORMAT}, - {MODEL_NAME[MODEL_KEY::PLACE], MODEL_KEY::PLACE}, - {MODEL_NAME[MODEL_KEY::LOCATION], MODEL_KEY::LOCATION}, - {MODEL_NAME[MODEL_KEY::ALBUM], MODEL_KEY::ALBUM}, - {MODEL_NAME[MODEL_KEY::ARTIST], MODEL_KEY::ARTIST}, - {MODEL_NAME[MODEL_KEY::DURATION], MODEL_KEY::DURATION}, - {MODEL_NAME[MODEL_KEY::TRACK], MODEL_KEY::TRACK}, - {MODEL_NAME[MODEL_KEY::GENRE], MODEL_KEY::GENRE}, - {MODEL_NAME[MODEL_KEY::LYRICS], MODEL_KEY::LYRICS}, - {MODEL_NAME[MODEL_KEY::RELEASEDATE], MODEL_KEY::RELEASEDATE}, - {MODEL_NAME[MODEL_KEY::FORMAT], MODEL_KEY::FORMAT}, - {MODEL_NAME[MODEL_KEY::WIKI], MODEL_KEY::WIKI}, - {MODEL_NAME[MODEL_KEY::SOURCETYPE], MODEL_KEY::SOURCETYPE}, - {MODEL_NAME[MODEL_KEY::ARTWORK], MODEL_KEY::ARTWORK}, - {MODEL_NAME[MODEL_KEY::NOTE], MODEL_KEY::NOTE}, - {MODEL_NAME[MODEL_KEY::MOOD], MODEL_KEY::MOOD}, - {MODEL_NAME[MODEL_KEY::COMMENT], MODEL_KEY::COMMENT}, - {MODEL_NAME[MODEL_KEY::CONTEXT], MODEL_KEY::CONTEXT}, - {MODEL_NAME[MODEL_KEY::SOURCE], MODEL_KEY::SOURCE}, - {MODEL_NAME[MODEL_KEY::PLAYLIST], MODEL_KEY::PLAYLIST}, - {MODEL_NAME[MODEL_KEY::TITLE], MODEL_KEY::TITLE}, - {MODEL_NAME[MODEL_KEY::ID], MODEL_KEY::ID}, - {MODEL_NAME[MODEL_KEY::PARENT_ID], MODEL_KEY::PARENT_ID}, - {MODEL_NAME[MODEL_KEY::LICENSE], MODEL_KEY::LICENSE}, - {MODEL_NAME[MODEL_KEY::DESCRIPTION], MODEL_KEY::DESCRIPTION}, - {MODEL_NAME[MODEL_KEY::BOOKMARK], MODEL_KEY::BOOKMARK}, - {MODEL_NAME[MODEL_KEY::ACCOUNT], MODEL_KEY::ACCOUNT}, - {MODEL_NAME[MODEL_KEY::ACCOUNTTYPE], MODEL_KEY::ACCOUNTTYPE}, - {MODEL_NAME[MODEL_KEY::VERSION], MODEL_KEY::VERSION}, - {MODEL_NAME[MODEL_KEY::DOMAIN_M], MODEL_KEY::DOMAIN_M}, - {MODEL_NAME[MODEL_KEY::CATEGORY], MODEL_KEY::CATEGORY}, - {MODEL_NAME[MODEL_KEY::CONTENT], MODEL_KEY::CONTENT}, - {MODEL_NAME[MODEL_KEY::PIN], MODEL_KEY::PIN}, - {MODEL_NAME[MODEL_KEY::IMG], MODEL_KEY::IMG}, - {MODEL_NAME[MODEL_KEY::PREVIEW], MODEL_KEY::PREVIEW}, - {MODEL_NAME[MODEL_KEY::LINK], MODEL_KEY::LINK}, - {MODEL_NAME[MODEL_KEY::STAMP], MODEL_KEY::STAMP}, - {MODEL_NAME[MODEL_KEY::BOOK], MODEL_KEY::BOOK}, - - /** ccdav keys **/ - {MODEL_NAME[MODEL_KEY::N], MODEL_KEY::N}, - {MODEL_NAME[MODEL_KEY::IM], MODEL_KEY::IM}, - {MODEL_NAME[MODEL_KEY::PHOTO], MODEL_KEY::PHOTO}, - {MODEL_NAME[MODEL_KEY::GENDER], MODEL_KEY::GENDER}, - {MODEL_NAME[MODEL_KEY::ADR], MODEL_KEY::ADR}, - {MODEL_NAME[MODEL_KEY::ADR_2], MODEL_KEY::ADR_2}, - {MODEL_NAME[MODEL_KEY::ADR_3], MODEL_KEY::ADR_3}, - {MODEL_NAME[MODEL_KEY::EMAIL], MODEL_KEY::EMAIL}, - {MODEL_NAME[MODEL_KEY::EMAIL_2], MODEL_KEY::EMAIL_2}, - {MODEL_NAME[MODEL_KEY::EMAIL_3], MODEL_KEY::EMAIL_3}, - {MODEL_NAME[MODEL_KEY::LANG], MODEL_KEY::LANG}, - {MODEL_NAME[MODEL_KEY::NICKNAME], MODEL_KEY::NICKNAME}, - {MODEL_NAME[MODEL_KEY::ORG], MODEL_KEY::ORG}, - {MODEL_NAME[MODEL_KEY::PROFILE], MODEL_KEY::PROFILE}, - {MODEL_NAME[MODEL_KEY::TZ], MODEL_KEY::TZ}, - {MODEL_NAME[MODEL_KEY::TEL], MODEL_KEY::TEL}, - {MODEL_NAME[MODEL_KEY::TEL_2], MODEL_KEY::TEL_2}, - {MODEL_NAME[MODEL_KEY::TEL_3], MODEL_KEY::TEL_3}, - - {MODEL_NAME[MODEL_KEY::CITY], MODEL_KEY::CITY}, - {MODEL_NAME[MODEL_KEY::STATE], MODEL_KEY::STATE}, - {MODEL_NAME[MODEL_KEY::COUNTRY], MODEL_KEY::COUNTRY}, - - // opendesktop store keys - {MODEL_NAME[MODEL_KEY::PACKAGE_ARCH], MODEL_KEY::PACKAGE_ARCH}, - {MODEL_NAME[MODEL_KEY::PACKAGE_TYPE], MODEL_KEY::PACKAGE_TYPE}, - {MODEL_NAME[MODEL_KEY::GPG_FINGERPRINT], MODEL_KEY::GPG_FINGERPRINT}, - {MODEL_NAME[MODEL_KEY::GPG_SIGNATURE], MODEL_KEY::GPG_SIGNATURE}, - {MODEL_NAME[MODEL_KEY::PACKAGE_NAME], MODEL_KEY::PACKAGE_NAME}, - {MODEL_NAME[MODEL_KEY::PRICE], MODEL_KEY::PRICE}, - {MODEL_NAME[MODEL_KEY::REPOSITORY], MODEL_KEY::REPOSITORY}, - {MODEL_NAME[MODEL_KEY::TAGS], MODEL_KEY::TAGS}, - {MODEL_NAME[MODEL_KEY::WAY], MODEL_KEY::WAY}, - {MODEL_NAME[MODEL_KEY::PIC], MODEL_KEY::PIC}, - {MODEL_NAME[MODEL_KEY::SMALL_PIC], MODEL_KEY::SMALL_PIC}, - {MODEL_NAME[MODEL_KEY::CHANGED], MODEL_KEY::CHANGED}, - {MODEL_NAME[MODEL_KEY::COMMENTS], MODEL_KEY::COMMENTS}, - {MODEL_NAME[MODEL_KEY::CREATED], MODEL_KEY::CREATED}, - {MODEL_NAME[MODEL_KEY::DETAIL_PAGE], MODEL_KEY::DETAIL_PAGE}, - {MODEL_NAME[MODEL_KEY::DETAILS], MODEL_KEY::DETAILS}, - {MODEL_NAME[MODEL_KEY::TOTAL_DOWNLOADS], MODEL_KEY::TOTAL_DOWNLOADS}, - {MODEL_NAME[MODEL_KEY::GHNS_EXCLUDED], MODEL_KEY::GHNS_EXCLUDED}, - {MODEL_NAME[MODEL_KEY::LANGUAGE], MODEL_KEY::LANGUAGE}, - {MODEL_NAME[MODEL_KEY::PERSON_ID], MODEL_KEY::PERSON_ID}, - {MODEL_NAME[MODEL_KEY::SCORE], MODEL_KEY::SCORE}, - {MODEL_NAME[MODEL_KEY::SUMMARY], MODEL_KEY::SUMMARY}, - {MODEL_NAME[MODEL_KEY::TYPE_ID], MODEL_KEY::TYPE_ID}, - {MODEL_NAME[MODEL_KEY::TYPE_NAME], MODEL_KEY::TYPE_NAME}, - {MODEL_NAME[MODEL_KEY::XDG_TYPE], MODEL_KEY::XDG_TYPE}, - - // file props - {MODEL_NAME[MODEL_KEY::SYMLINK], MODEL_KEY::SYMLINK}, - {MODEL_NAME[MODEL_KEY::IS_SYMLINK], MODEL_KEY::IS_SYMLINK}, - {MODEL_NAME[MODEL_KEY::LAST_READ], MODEL_KEY::LAST_READ}, - {MODEL_NAME[MODEL_KEY::READABLE], MODEL_KEY::READABLE}, - {MODEL_NAME[MODEL_KEY::WRITABLE], MODEL_KEY::WRITABLE}, - {MODEL_NAME[MODEL_KEY::IS_DIR], MODEL_KEY::IS_DIR}, - {MODEL_NAME[MODEL_KEY::IS_FILE], MODEL_KEY::IS_FILE}, - {MODEL_NAME[MODEL_KEY::IS_REMOTE], MODEL_KEY::IS_REMOTE}, - {MODEL_NAME[MODEL_KEY::EXECUTABLE], MODEL_KEY::EXECUTABLE}, - {MODEL_NAME[MODEL_KEY::VALUE], MODEL_KEY::VALUE}, - {MODEL_NAME[MODEL_KEY::KEY], MODEL_KEY::KEY}, - - {MODEL_NAME[MODEL_KEY::MAC], MODEL_KEY::MAC}, - {MODEL_NAME[MODEL_KEY::LOT], MODEL_KEY::LOT}, - {MODEL_NAME[MODEL_KEY::APP], MODEL_KEY::APP}, - {MODEL_NAME[MODEL_KEY::URI], MODEL_KEY::URI}, - {MODEL_NAME[MODEL_KEY::DEVICE], MODEL_KEY::DEVICE}, - {MODEL_NAME[MODEL_KEY::LASTSYNC], MODEL_KEY::LASTSYNC}}; -/** - * @brief MODEL - */ -typedef QHash MODEL; - -/** - * @brief MODEL_LIST - */ -typedef QVector MODEL_LIST; - -/** - * @brief modelRoles - * @param model - * @return - */ -const QVector modelRoles(const MODEL &model); - -/** - * @brief mapValue - * @param map - * @param key - * @return - */ -const QString mapValue(const QVariantMap &map, const MODEL_KEY &key); - -/** - * @brief toMap - * @param model - * @return - */ -const QVariantMap toMap(const MODEL &model); - -/** - * @brief toModel - * @param map - * @return - */ -const MODEL toModel(const QVariantMap &map); - -/** - * Creates a MODEL_LIST from a QVariantList - * */ -/** - * @brief toModelList - * @param list - * @return - */ -const MODEL_LIST toModelList(const QVariantList &list); - -/** - * Creates a QVariantList from a MODEL_LIST - * */ -/** - * @brief toMapList - * @param list - * @return - */ -const QVariantList toMapList(const MODEL_LIST &list); - -/** - * Creates a new MODEL from another filtered by the given array of MODEL_KEY - * */ -/** - * @brief filterModel - * @param model - * @param keys - * @return - */ -const MODEL filterModel(const MODEL &model, const QVector &keys); - -/** - * Extracts from a MODEL_LIST the values from a given MODEL::KEY into a QStringList - * */ - -/** - * @brief modelToList - * @param list - * @param key - * @return - */ -const QStringList modelToList(const MODEL_LIST &list, const MODEL_KEY &key); - -/** - * @brief The PATH_CONTENT struct - */ -struct PATH_CONTENT { - QUrl path; // the url holding all the content - MODEL_LIST content; // the content from the url -}; - -/** - * @brief The PATHTYPE_KEY enum - */ -#if defined Q_OS_ANDROID || defined Q_OS_WIN || defined Q_OS_MACOS || defined Q_OS_IOS // for android, windows and mac use this for now -enum PATHTYPE_KEY : int { - PLACES_PATH, - REMOTE_PATH, - DRIVES_PATH, - REMOVABLE_PATH, - TAGS_PATH, - UNKNOWN_TYPE, - APPS_PATH, - TRASH_PATH, - SEARCH_PATH, - CLOUD_PATH, - FISH_PATH, - MTP_PATH, - QUICK_PATH, - BOOKMARKS_PATH, - OTHER_PATH, -}; -#else -enum PATHTYPE_KEY : int { - PLACES_PATH = KFilePlacesModel::GroupType::PlacesType, - REMOTE_PATH = KFilePlacesModel::GroupType::RemoteType, - DRIVES_PATH = KFilePlacesModel::GroupType::DevicesType, - REMOVABLE_PATH = KFilePlacesModel::GroupType::RemovableDevicesType, - TAGS_PATH = KFilePlacesModel::GroupType::TagsType, - UNKNOWN_TYPE = KFilePlacesModel::GroupType::UnknownType, - APPS_PATH = 9, - TRASH_PATH = 10, - SEARCH_PATH = 11, - CLOUD_PATH = 12, - FISH_PATH = 13, - MTP_PATH = 14, - QUICK_PATH = 15, - BOOKMARKS_PATH = 16, - OTHER_PATH = 17 -}; -#endif - -static const QHash PATHTYPE_SCHEME = {{PATHTYPE_KEY::PLACES_PATH, "file"}, - {PATHTYPE_KEY::BOOKMARKS_PATH, "file"}, - {PATHTYPE_KEY::DRIVES_PATH, "drives"}, - {PATHTYPE_KEY::APPS_PATH, "applications"}, - {PATHTYPE_KEY::REMOTE_PATH, "remote"}, - {PATHTYPE_KEY::REMOVABLE_PATH, "removable"}, - {PATHTYPE_KEY::UNKNOWN_TYPE, "Unkown"}, - {PATHTYPE_KEY::TRASH_PATH, "trash"}, - {PATHTYPE_KEY::TAGS_PATH, "tags"}, - {PATHTYPE_KEY::SEARCH_PATH, "search"}, - {PATHTYPE_KEY::CLOUD_PATH, "cloud"}, - {PATHTYPE_KEY::FISH_PATH, "fish"}, - {PATHTYPE_KEY::MTP_PATH, "mtp"}}; - -static const QHash PATHTYPE_SCHEME_NAME = {{PATHTYPE_SCHEME[PATHTYPE_KEY::PLACES_PATH], PATHTYPE_KEY::PLACES_PATH}, - {PATHTYPE_SCHEME[PATHTYPE_KEY::BOOKMARKS_PATH], PATHTYPE_KEY::BOOKMARKS_PATH}, - {PATHTYPE_SCHEME[PATHTYPE_KEY::DRIVES_PATH], PATHTYPE_KEY::DRIVES_PATH}, - {PATHTYPE_SCHEME[PATHTYPE_KEY::APPS_PATH], PATHTYPE_KEY::APPS_PATH}, - {PATHTYPE_SCHEME[PATHTYPE_KEY::REMOTE_PATH], PATHTYPE_KEY::REMOTE_PATH}, - {PATHTYPE_SCHEME[PATHTYPE_KEY::REMOVABLE_PATH], PATHTYPE_KEY::REMOVABLE_PATH}, - {PATHTYPE_SCHEME[PATHTYPE_KEY::UNKNOWN_TYPE], PATHTYPE_KEY::UNKNOWN_TYPE}, - {PATHTYPE_SCHEME[PATHTYPE_KEY::TRASH_PATH], PATHTYPE_KEY::TRASH_PATH}, - {PATHTYPE_SCHEME[PATHTYPE_KEY::TAGS_PATH], PATHTYPE_KEY::TAGS_PATH}, - {PATHTYPE_SCHEME[PATHTYPE_KEY::SEARCH_PATH], PATHTYPE_KEY::SEARCH_PATH}, - {PATHTYPE_SCHEME[PATHTYPE_KEY::CLOUD_PATH], PATHTYPE_KEY::CLOUD_PATH}, - {PATHTYPE_SCHEME[PATHTYPE_KEY::FISH_PATH], PATHTYPE_KEY::FISH_PATH}, - {PATHTYPE_SCHEME[PATHTYPE_KEY::MTP_PATH], PATHTYPE_KEY::MTP_PATH}}; - -static const QHash PATHTYPE_URI = {{PATHTYPE_KEY::PLACES_PATH, PATHTYPE_SCHEME[PATHTYPE_KEY::PLACES_PATH] + "://"}, - {PATHTYPE_KEY::BOOKMARKS_PATH, PATHTYPE_SCHEME[PATHTYPE_KEY::BOOKMARKS_PATH] + "://"}, - {PATHTYPE_KEY::DRIVES_PATH, PATHTYPE_SCHEME[PATHTYPE_KEY::DRIVES_PATH] + "://"}, - {PATHTYPE_KEY::APPS_PATH, PATHTYPE_SCHEME[PATHTYPE_KEY::APPS_PATH] + ":///"}, - {PATHTYPE_KEY::REMOTE_PATH, PATHTYPE_SCHEME[PATHTYPE_KEY::REMOTE_PATH] + "://"}, - {PATHTYPE_KEY::REMOVABLE_PATH, PATHTYPE_SCHEME[PATHTYPE_KEY::REMOVABLE_PATH] + "://"}, - {PATHTYPE_KEY::UNKNOWN_TYPE, PATHTYPE_SCHEME[PATHTYPE_KEY::UNKNOWN_TYPE] + "://"}, - {PATHTYPE_KEY::TRASH_PATH, PATHTYPE_SCHEME[PATHTYPE_KEY::TRASH_PATH] + "://"}, - {PATHTYPE_KEY::TAGS_PATH, PATHTYPE_SCHEME[PATHTYPE_KEY::TAGS_PATH] + ":///"}, - {PATHTYPE_KEY::SEARCH_PATH, PATHTYPE_SCHEME[PATHTYPE_KEY::SEARCH_PATH] + "://"}, - {PATHTYPE_KEY::CLOUD_PATH, PATHTYPE_SCHEME[PATHTYPE_KEY::CLOUD_PATH] + ":///"}, - {PATHTYPE_KEY::FISH_PATH, PATHTYPE_SCHEME[PATHTYPE_KEY::FISH_PATH] + "://"}, - {PATHTYPE_KEY::MTP_PATH, PATHTYPE_SCHEME[PATHTYPE_KEY::MTP_PATH] + "://"}}; - -static const QHash PATHTYPE_LABEL = {{PATHTYPE_KEY::PLACES_PATH, ("Places")}, - {PATHTYPE_KEY::BOOKMARKS_PATH, ("Bookmarks")}, - {PATHTYPE_KEY::DRIVES_PATH, ("Drives")}, - {PATHTYPE_KEY::APPS_PATH, ("Apps")}, - {PATHTYPE_KEY::REMOTE_PATH, ("Remote")}, - {PATHTYPE_KEY::REMOVABLE_PATH, ("Removable")}, - {PATHTYPE_KEY::UNKNOWN_TYPE, ("Unknown")}, - {PATHTYPE_KEY::TRASH_PATH, ("Trash")}, - {PATHTYPE_KEY::TAGS_PATH, ("Tags")}, - {PATHTYPE_KEY::SEARCH_PATH, ("Search")}, - {PATHTYPE_KEY::CLOUD_PATH, ("Cloud")}, - {PATHTYPE_KEY::FISH_PATH, ("Remote")}, - {PATHTYPE_KEY::MTP_PATH, ("Drives")}, - {PATHTYPE_KEY::OTHER_PATH, ("Others")}, - {PATHTYPE_KEY::QUICK_PATH, ("Quick")}}; - -/** - * @brief DataPath - */ -static const QString DataPath = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation); - -/** - * @brief ConfigPath - */ -static const QString ConfigPath = QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::ConfigLocation)).toString(); - -/** - * @brief CloudCachePath - */ -static const QString CloudCachePath = DataPath + "/Cloud/"; - -/** - * @brief DesktopPath - */ -static const QString DesktopPath = QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::DesktopLocation)).toString(); - -/** - * @brief AppsPath - */ -static const QString AppsPath = QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation)).toString(); - -/** - * @brief RootPath - */ -static const QString RootPath = QUrl::fromLocalFile("/").toString(); - -#if defined(Q_OS_ANDROID) -static const QString PicturesPath = QUrl::fromLocalFile(PATHS::PicturesPath).toString(); -static const QString DownloadsPath = QUrl::fromLocalFile(PATHS::DownloadsPath).toString(); -static const QString DocumentsPath = QUrl::fromLocalFile(PATHS::DocumentsPath).toString(); -static const QString HomePath = QUrl::fromLocalFile(PATHS::HomePath).toString(); -static const QString MusicPath = QUrl::fromLocalFile(PATHS::MusicPath).toString(); -static const QString VideosPath = QUrl::fromLocalFile(PATHS::VideosPath).toString(); - -static const QStringList defaultPaths = {HomePath, DocumentsPath, PicturesPath, MusicPath, VideosPath, DownloadsPath}; - -#else -static const QString PicturesPath = QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation)).toString(); -static const QString DownloadsPath = QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::DownloadLocation)).toString(); -static const QString DocumentsPath = QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)).toString(); -static const QString HomePath = QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::HomeLocation)).toString(); -static const QString MusicPath = QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::MusicLocation)).toString(); -static const QString VideosPath = QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::MoviesLocation)).toString(); -static const QString TrashPath = QStringLiteral("trash:/"); - -static const QStringList defaultPaths = { - HomePath, - DesktopPath, - DocumentsPath, - PicturesPath, - MusicPath, - VideosPath, - DownloadsPath /*, - RootPath, - TrashPath*/ -}; - -#endif - -static const QMap folderIcon {{PicturesPath, "folder-pictures"}, - {DownloadsPath, "folder-download"}, - {DocumentsPath, "folder-documents"}, - {HomePath, "user-home"}, - {MusicPath, "folder-music"}, - {VideosPath, "folder-videos"}, - {DesktopPath, "user-desktop"}, - {AppsPath, "system-run"}, - {RootPath, "folder-root"}}; - -/** - * Checks if a local file exists. - * The URL must represent a local file path, by using the scheme file:// - **/ -/** - * @brief fileExists - * @param path - * @return - */ -bool fileExists(const QUrl &path); - -/** - * @brief fileDir - * @param path - * @return - */ -const QString fileDir(const QUrl &path); - -/** - * @brief parentDir - * @param path - * @return - */ -const QUrl parentDir(const QUrl &path); - -/** - * Return the configuration of a single directory represented - * by a QVariantMap. - * The passed path must be a local file URL. - **/ -/** - * @brief dirConf - * @param path - * @return - */ -const QVariantMap dirConf(const QUrl &path); - -/** - * @brief setDirConf - * @param path - * @param group - * @param key - * @param value - */ -void setDirConf(const QUrl &path, const QString &group, const QString &key, const QVariant &value); - -/** - * @brief getIconName - * Returns the icon name for certain file. - * The file path must be represented as a local file URL. - * It also looks into the directory config file to get custom set icons - * @param path - * @return - */ -const QString getIconName(const QUrl &path); - -/** - * @brief getMime - * @param path - * @return - */ -const QString getMime(const QUrl &path); - -/** - * @brief checkFileType - * @param type - * @param mimeTypeName - * @return - */ -bool checkFileType(const FILTER_TYPE &type, const QString &mimeTypeName); - -/** - * @brief thumbnailUrl - * Returns a valid thumbnail Url to an image provider if supported, otherwise an empty URL - * @param url - * @return - */ -const QUrl thumbnailUrl(const QUrl &url, const QString &mimetype); - -#if (!defined Q_OS_ANDROID && defined Q_OS_LINUX) || defined Q_OS_WIN -/** - * @brief packFileInfo - * @param item - * @return - */ -const MODEL getFileInfo(const KFileItem &kfile); -#endif - -/** - * @brief getFileInfoModel - * @param path - * @return - */ -const MODEL getFileInfoModel(const QUrl &path); - -/** - * @brief getFileInfo - * @param path - * @return - */ -const QVariantMap getFileInfo(const QUrl &path); - -/** - * @brief getDirInfoModel - * @param path - * @param type - * @return - */ -const MODEL getDirInfoModel(const QUrl &path, const QString &type = QString()); - -/** - * @brief getDirInfo - * @param path - * @param type - * @return - */ -const QVariantMap getDirInfo(const QUrl &path); - -/** - * @brief getPathType - * @param url - * @return - */ -PATHTYPE_KEY getPathType(const QUrl &url); -} - -#endif // FMH_H diff --git a/src/fmlist.cpp b/src/fmlist.cpp deleted file mode 100644 index af5ba93..0000000 --- a/src/fmlist.cpp +++ /dev/null @@ -1,626 +0,0 @@ -/* - * - * Copyright (C) 2018 camilo higuita - * - * 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 3 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, see . - */ - -#include "fmlist.h" -#include "fm.h" - -#if defined Q_OS_LINUX && !defined Q_OS_ANDROID -#include -#endif - -#include -#include -#include -#include -#include - -FMList::FMList(QObject *parent) - : BaseList(parent) - , fm(new FM(this)) -{ - qRegisterMetaType("const FMList*"); // this is needed for QML to know of FMList in the search method - connect(this->fm, &FM::cloudServerContentReady, [&](const FMH::MODEL_LIST &list, const QUrl &url) { - if (this->path == url) { - this->assignList(list); - } - }); - - connect(this->fm, &FM::pathContentReady, [&](QUrl) { - emit this->preListChanged(); - this->sortList(); - this->setStatus({STATUS_CODE::READY, this->list.isEmpty() ? "Nothing here!" : "", this->list.isEmpty() ? "This place seems to be empty" : "", this->list.isEmpty() ? "folder-add" : "", this->list.isEmpty(), true}); - emit this->postListChanged(); - }); - - connect(this->fm, &FM::pathContentItemsChanged, [&](QVector> res) { - for (const auto &item : qAsConst(res)) { - const auto index = this->indexOf(FMH::MODEL_KEY::PATH, item.first[FMH::MODEL_KEY::PATH]); - - if (index >= this->list.size() || index < 0) - return; - - this->list[index] = item.second; - emit this->updateModel(index, FMH::modelRoles(item.second)); - } - }); - - connect(this->fm, &FM::pathContentItemsReady, [&](FMH::PATH_CONTENT res) { - if (res.path != this->path) - return; - - this->appendToList(res.content); - }); - - connect(this->fm, &FM::pathContentItemsRemoved, [&](FMH::PATH_CONTENT res) { - if (res.path != this->path) - return; - - if (!FMH::fileExists(res.path)) { - this->setStatus({STATUS_CODE::ERROR, "Error", "This URL cannot be listed", "documentinfo", true, false}); - return; - } - - for (const auto &item : qAsConst(res.content)) { - const auto index = this->indexOf(FMH::MODEL_KEY::PATH, item[FMH::MODEL_KEY::PATH]); - qDebug() << "SUPOSSED TO REMOVED THIS FORM THE LIST" << index << this->list.count() << item[FMH::MODEL_KEY::PATH]; - - this->remove(index); - } - - this->setStatus({STATUS_CODE::READY, this->list.isEmpty() ? "Nothing here!" : "", this->list.isEmpty() ? "This place seems to be empty" : "", this->list.isEmpty() ? "folder-add" : "", this->list.isEmpty(), true}); - }); - - connect(this->fm, &FM::warningMessage, [&](const QString &message) { - emit this->warning(message); - }); - - connect(this->fm, &FM::loadProgress, [&](const int &percent) { - emit this->progress(percent); - }); - - connect(this->fm, &FM::pathContentChanged, [&](const QUrl &path) { - qDebug() << "FOLDER PATH CHANGED" << path; - if (path != this->path) - return; - this->sortList(); - }); - - connect(this->fm, &FM::newItem, [&](const FMH::MODEL &item, const QUrl &url) { - if (this->path == url) { - emit this->preItemAppended(); - this->list << item; - emit this->postItemAppended(); - } - }); -} - -void FMList::assignList(const FMH::MODEL_LIST &list) -{ - emit this->preListChanged(); - this->list = list; - this->sortList(); - this->setStatus({STATUS_CODE::READY, this->list.isEmpty() ? "Nothing here!" : "", this->list.isEmpty() ? "This place seems to be empty" : "", this->list.isEmpty() ? "folder-add" : "", this->list.isEmpty(), true}); - emit this->postListChanged(); -} - -void FMList::appendToList(const FMH::MODEL_LIST &list) -{ - emit this->preItemsAppended(list.size()); - this->list << list; - emit this->postItemAppended(); -} - -void FMList::clear() -{ - emit this->preListChanged(); - this->list.clear(); - emit this->postListChanged(); -} - -void FMList::setList() -{ - qDebug() << "PATHTYPE FOR URL" << pathType << this->path.toString() << this->filters << this; - this->clear(); - - switch (this->pathType) { - case FMList::PATHTYPE::CLOUD_PATH: - this->fm->getCloudServerContent(this->path.toString(), this->filters, this->cloudDepth); - break; // ASYNC - - default: { - const bool exists = this->path.isLocalFile() ? FMH::fileExists(this->path) : true; - if (!exists) - this->setStatus({STATUS_CODE::ERROR, "Error", "This URL cannot be listed", "documentinfo", this->list.isEmpty(), exists}); - else { - this->fm->getPathContent(this->path, this->hidden, this->onlyDirs, QStringList() << this->filters << FMH::FILTER_LIST[static_cast(this->filterType)]); - } - break; // ASYNC - } - } -} - -void FMList::reset() -{ - this->setList(); -} - -const FMH::MODEL_LIST &FMList::items() const -{ - return this->list; -} - -FMList::SORTBY FMList::getSortBy() const -{ - return this->sort; -} - -void FMList::setSortBy(const FMList::SORTBY &key) -{ - if (this->sort == key) - return; - - emit this->preListChanged(); - - this->sort = key; - this->sortList(); - - emit this->sortByChanged(); - emit this->postListChanged(); -} - -void FMList::sortList() -{ - const FMH::MODEL_KEY key = static_cast(this->sort); - auto index = 0; - - if (this->foldersFirst) { - qSort(this->list.begin(), this->list.end(), [](const FMH::MODEL &e1, const FMH::MODEL &e2) -> bool { - Q_UNUSED(e2) - const auto key = FMH::MODEL_KEY::MIME; - return e1[key] == "inode/directory"; - }); - - for (const auto &item : qAsConst(this->list)) - if (item[FMH::MODEL_KEY::MIME] == "inode/directory") - index++; - else - break; - - std::sort(this->list.begin(), this->list.begin() + index, [&key](const FMH::MODEL &e1, const FMH::MODEL &e2) -> bool { - switch (key) { - case FMH::MODEL_KEY::SIZE: { - if (e1[key].toDouble() > e2[key].toDouble()) - return true; - break; - } - - case FMH::MODEL_KEY::MODIFIED: - case FMH::MODEL_KEY::DATE: { - auto currentTime = QDateTime::currentDateTime(); - - auto date1 = QDateTime::fromString(e1[key], Qt::TextDate); - auto date2 = QDateTime::fromString(e2[key], Qt::TextDate); - - if (date1.secsTo(currentTime) < date2.secsTo(currentTime)) - return true; - - break; - } - - case FMH::MODEL_KEY::LABEL: { - const auto str1 = QString(e1[key]).toLower(); - const auto str2 = QString(e2[key]).toLower(); - - if (str1 < str2) - return true; - break; - } - - default: - if (e1[key] < e2[key]) - return true; - } - - return false; - }); - } - - std::sort(this->list.begin() + index, this->list.end(), [key](const FMH::MODEL &e1, const FMH::MODEL &e2) -> bool { - switch (key) { - case FMH::MODEL_KEY::MIME: - if (e1[key] == "inode/directory") - return true; - break; - - case FMH::MODEL_KEY::SIZE: { - if (e1[key].toDouble() > e2[key].toDouble()) - return true; - break; - } - - case FMH::MODEL_KEY::MODIFIED: - case FMH::MODEL_KEY::DATE: { - auto currentTime = QDateTime::currentDateTime(); - - auto date1 = QDateTime::fromString(e1[key], Qt::TextDate); - auto date2 = QDateTime::fromString(e2[key], Qt::TextDate); - - if (date1.secsTo(currentTime) < date2.secsTo(currentTime)) - return true; - - break; - } - - case FMH::MODEL_KEY::LABEL: { - const auto str1 = QString(e1[key]).toLower(); - const auto str2 = QString(e2[key]).toLower(); - - if (str1 < str2) - return true; - break; - } - - default: - if (e1[key] < e2[key]) - return true; - } - - return false; - }); -} - -QString FMList::getPathName() const -{ - return this->pathName; -} - -QUrl FMList::getPath() const -{ - return this->path; -} - -void FMList::setPath(const QUrl &path) -{ - QUrl path_ = QUrl::fromUserInput(path.toString().trimmed()); - - if (this->path == path_) - return; - - this->path = path_; - m_navHistory.appendPath(this->path); - - this->setStatus({STATUS_CODE::LOADING, "Loading content", "Almost ready!", "view-refresh", true, false}); - - const auto __scheme = this->path.scheme(); - this->pathName = QDir(this->path.toLocalFile()).dirName(); - - if (__scheme == FMH::PATHTYPE_SCHEME[FMH::PATHTYPE_KEY::CLOUD_PATH]) { - this->pathType = FMList::PATHTYPE::CLOUD_PATH; - - } else if (__scheme == FMH::PATHTYPE_SCHEME[FMH::PATHTYPE_KEY::APPS_PATH]) { - this->pathType = FMList::PATHTYPE::APPS_PATH; - - } else if (__scheme == FMH::PATHTYPE_SCHEME[FMH::PATHTYPE_KEY::TAGS_PATH]) { - this->pathType = FMList::PATHTYPE::TAGS_PATH; - this->pathName = this->path.path(); - - } else if (__scheme == FMH::PATHTYPE_SCHEME[FMH::PATHTYPE_KEY::TRASH_PATH]) { - this->pathType = FMList::PATHTYPE::TRASH_PATH; - this->pathName = "Trash"; - - } else if (__scheme == FMH::PATHTYPE_SCHEME[FMH::PATHTYPE_KEY::PLACES_PATH]) { - this->pathType = FMList::PATHTYPE::PLACES_PATH; - - } else if (__scheme == FMH::PATHTYPE_SCHEME[FMH::PATHTYPE_KEY::MTP_PATH]) { - this->pathType = FMList::PATHTYPE::MTP_PATH; - - } else if (__scheme == FMH::PATHTYPE_SCHEME[FMH::PATHTYPE_KEY::FISH_PATH]) { - this->pathType = FMList::PATHTYPE::FISH_PATH; - - } else if (__scheme == FMH::PATHTYPE_SCHEME[FMH::PATHTYPE_KEY::REMOTE_PATH]) { - this->pathType = FMList::PATHTYPE::REMOTE_PATH; - - } else if (__scheme == FMH::PATHTYPE_SCHEME[FMH::PATHTYPE_KEY::DRIVES_PATH]) { - this->pathType = FMList::PATHTYPE::DRIVES_PATH; - } else { - this->pathType = FMList::PATHTYPE::OTHER_PATH; - } - - emit this->pathNameChanged(); - emit this->pathTypeChanged(); - emit this->pathChanged(); -} - -FMList::PATHTYPE FMList::getPathType() const -{ - return this->pathType; -} - -QStringList FMList::getFilters() const -{ - return this->filters; -} - -void FMList::setFilters(const QStringList &filters) -{ - if (this->filters == filters) - return; - - this->filters = filters; - - emit this->filtersChanged(); -} - -FMList::FILTER FMList::getFilterType() const -{ - return this->filterType; -} - -void FMList::setFilterType(const FMList::FILTER &type) -{ - if (this->filterType == type) - return; - - this->filterType = type; - - emit this->filterTypeChanged(); -} - -bool FMList::getHidden() const -{ - return this->hidden; -} - -void FMList::setHidden(const bool &state) -{ - if (this->hidden == state) - return; - - this->hidden = state; - - emit this->hiddenChanged(); -} - -bool FMList::getOnlyDirs() const -{ - return this->onlyDirs; -} - -void FMList::setOnlyDirs(const bool &state) -{ - if (this->onlyDirs == state) - return; - - this->onlyDirs = state; - - emit this->onlyDirsChanged(); -} - -void FMList::refresh() -{ - emit this->pathChanged(); -} - -void FMList::createDir(const QString &name) -{ - if (this->pathType == FMList::PATHTYPE::CLOUD_PATH) { -#ifdef COMPONENT_SYNCING - this->fm->createCloudDir(QString(this->path.toString()).replace(FMH::PATHTYPE_SCHEME[FMH::PATHTYPE_KEY::CLOUD_PATH] + "/" + this->fm->sync->getUser(), ""), name); -#endif - } else { - FMStatic::createDir(this->path, name); - } -} - -void FMList::copyInto(const QStringList &urls) -{ - this->fm->copy(QUrl::fromStringList(urls), this->path); -} - -void FMList::cutInto(const QStringList &urls) -{ - this->fm->cut(QUrl::fromStringList(urls), this->path); - // else if(this->pathType == FMList::PATHTYPE::CLOUD_PATH) - // { - // this->fm->createCloudDir(QString(this->path).replace(FMH::PATHTYPE_NAME[FMList::PATHTYPE::CLOUD_PATH]+"/"+this->fm->sync->getUser(), ""), name); - // } -} - -void FMList::setDirIcon(const int &index, const QString &iconName) -{ - if (index >= this->list.size() || index < 0) - return; - - // const auto index_ = this->mappedIndex(index); - - const auto path = QUrl(this->list.at(index)[FMH::MODEL_KEY::PATH]); - - if (!FMStatic::isDir(path)) - return; - - FMH::setDirConf(path.toString() + "/.directory", "Desktop Entry", "Icon", iconName); - - this->list[index][FMH::MODEL_KEY::ICON] = iconName; - emit this->updateModel(index, QVector {FMH::MODEL_KEY::ICON}); -} - -const QUrl FMList::getParentPath() -{ - switch (this->pathType) { - case FMList::PATHTYPE::PLACES_PATH: - return FMStatic::parentDir(this->path).toString(); - default: - return this->previousPath(); - } -} - -const QUrl FMList::posteriorPath() -{ - const auto url = m_navHistory.getPosteriorPath(); - - if (url.isEmpty()) - return this->path; - - return url; -} - -const QUrl FMList::previousPath() -{ - const auto url = m_navHistory.getPreviousPath(); - - if (url.isEmpty()) - return this->path; - - return url; -} - -bool FMList::getFoldersFirst() const -{ - return this->foldersFirst; -} - -void FMList::setFoldersFirst(const bool &value) -{ - if (this->foldersFirst == value) - return; - - emit this->preListChanged(); - - this->foldersFirst = value; - - emit this->foldersFirstChanged(); - - this->sortList(); - - emit this->postListChanged(); -} - -void FMList::search(const QString &query, const FMList *currentFMList) -{ - this->search(query, currentFMList->getPath(), currentFMList->getHidden(), currentFMList->getOnlyDirs(), currentFMList->getFilters()); -} - -void FMList::componentComplete() -{ - connect(this, &FMList::pathChanged, this, &FMList::setList); - connect(this, &FMList::filtersChanged, this, &FMList::setList); - connect(this, &FMList::filterTypeChanged, this, &FMList::setList); - connect(this, &FMList::hiddenChanged, this, &FMList::setList); - connect(this, &FMList::onlyDirsChanged, this, &FMList::setList); - - this->setList(); -} - -void FMList::search(const QString &query, const QUrl &path, const bool &hidden, const bool &onlyDirs, const QStringList &filters) -{ - qDebug() << "SEARCHING FOR" << query << path; - - if (!path.isLocalFile()) { - qWarning() << "URL recived is not a local file. So search will only filter the content" << path; - this->filterContent(query, path); - return; - } - - QFutureWatcher *watcher = new QFutureWatcher; - connect(watcher, &QFutureWatcher::finished, [=]() { - const auto res = watcher->future().result(); - - this->assignList(res.content); - emit this->searchResultReady(); - - watcher->deleteLater(); - }); - - QFuture t1 = QtConcurrent::run([=]() -> FMH::PATH_CONTENT { - FMH::PATH_CONTENT res; - res.path = path.toString(); - res.content = FMStatic::search(query, path, hidden, onlyDirs, filters); - return res; - }); - watcher->setFuture(t1); -} - -void FMList::filterContent(const QString &query, const QUrl &path) -{ - if (this->list.isEmpty()) { - qDebug() << "Can not filter content. List is empty"; - return; - } - - QFutureWatcher *watcher = new QFutureWatcher; - connect(watcher, &QFutureWatcher::finished, [=]() { - const auto res = watcher->future().result(); - - this->assignList(res.content); - emit this->searchResultReady(); - - watcher->deleteLater(); - }); - - QFuture t1 = QtConcurrent::run([=]() -> FMH::PATH_CONTENT { - FMH::MODEL_LIST m_content; - FMH::PATH_CONTENT res; - - for (const auto &item : qAsConst(this->list)) { - if (item[FMH::MODEL_KEY::LABEL].contains(query, Qt::CaseInsensitive) || item[FMH::MODEL_KEY::SUFFIX].contains(query, Qt::CaseInsensitive) || item[FMH::MODEL_KEY::MIME].contains(query, Qt::CaseInsensitive)) { - m_content << item; - } - } - - res.path = path.toString(); - res.content = m_content; - return res; - }); - watcher->setFuture(t1); -} - -int FMList::getCloudDepth() const -{ - return this->cloudDepth; -} - -void FMList::setCloudDepth(const int &value) -{ - if (this->cloudDepth == value) - return; - - this->cloudDepth = value; - - emit this->cloudDepthChanged(); -} - -PathStatus FMList::getStatus() const -{ - return this->m_status; -} - -void FMList::setStatus(const PathStatus &status) -{ - this->m_status = status; - emit this->statusChanged(); -} - -void FMList::remove(const int &index) -{ - if (index >= this->list.size() || index < 0) - return; - - emit this->preItemRemoved(index); - this->list.remove(index); - emit this->postItemRemoved(); -} diff --git a/src/fmlist.h b/src/fmlist.h deleted file mode 100644 index 1e312e2..0000000 --- a/src/fmlist.h +++ /dev/null @@ -1,450 +0,0 @@ -/* - * - * Copyright (C) 2018 Camilo Higuita - * - * 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 3 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, see . - */ - -#ifndef FMLIST_H -#define FMLIST_H - -#include "fmh.h" -#include "baselist.h" -#include - -class FM; - -enum STATUS_CODE : uint_fast8_t { LOADING, ERROR, READY }; - -/** - * @brief The PathStatus class - * Represents the status of a directory, be it non existance, loading or empty. - */ -class PathStatus -{ - Q_GADGET - - Q_PROPERTY(STATUS_CODE code MEMBER m_code) - Q_PROPERTY(QString title MEMBER m_title) - Q_PROPERTY(QString message MEMBER m_message) - Q_PROPERTY(QString icon MEMBER m_icon) - Q_PROPERTY(bool empty MEMBER m_empty) - Q_PROPERTY(bool exists MEMBER m_exists) - -public: - STATUS_CODE m_code; - QString m_title; - QString m_message; - QString m_icon; - bool m_empty = false; - bool m_exists = false; -}; -Q_DECLARE_METATYPE(PathStatus) - -struct NavHistory { - void appendPath(const QUrl &path) - { - this->prev_history.append(path); - } - - QUrl getPosteriorPath() - { - if (this->post_history.isEmpty()) - return QUrl(); - - return this->post_history.takeLast(); - } - - QUrl getPreviousPath() - { - if (this->prev_history.isEmpty()) - return QUrl(); - - if (this->prev_history.length() < 2) - return this->prev_history.at(0); - - this->post_history.append(this->prev_history.takeLast()); - - return this->prev_history.takeLast(); - } - -private: - QVector prev_history; - QVector post_history; -}; - -/** - * @brief The FMList class - * Model for listing the file system files and directories and perfom relevant actions upon it - */ -class FMList : public BaseList -{ - Q_OBJECT - - // writable - Q_PROPERTY(QUrl path READ getPath WRITE setPath NOTIFY pathChanged) - Q_PROPERTY(bool hidden READ getHidden WRITE setHidden NOTIFY hiddenChanged) - Q_PROPERTY(bool onlyDirs READ getOnlyDirs WRITE setOnlyDirs NOTIFY onlyDirsChanged) - Q_PROPERTY(bool foldersFirst READ getFoldersFirst WRITE setFoldersFirst NOTIFY foldersFirstChanged) - Q_PROPERTY(int cloudDepth READ getCloudDepth WRITE setCloudDepth NOTIFY cloudDepthChanged) - - Q_PROPERTY(QStringList filters READ getFilters WRITE setFilters NOTIFY filtersChanged) - Q_PROPERTY(FMList::FILTER filterType READ getFilterType WRITE setFilterType NOTIFY filterTypeChanged) - Q_PROPERTY(FMList::SORTBY sortBy READ getSortBy WRITE setSortBy NOTIFY sortByChanged) - - // readonly - Q_PROPERTY(QString pathName READ getPathName NOTIFY pathNameChanged FINAL) - Q_PROPERTY(FMList::PATHTYPE pathType READ getPathType NOTIFY pathTypeChanged FINAL) - - Q_PROPERTY(PathStatus status READ getStatus NOTIFY statusChanged FINAL) - - Q_PROPERTY(QUrl parentPath READ getParentPath NOTIFY pathChanged) - -public: - enum SORTBY : uint_fast8_t { - SIZE = FMH::MODEL_KEY::SIZE, - MODIFIED = FMH::MODEL_KEY::MODIFIED, - DATE = FMH::MODEL_KEY::DATE, - LABEL = FMH::MODEL_KEY::LABEL, - MIME = FMH::MODEL_KEY::MIME, - ADDDATE = FMH::MODEL_KEY::MIME, - TITLE = FMH::MODEL_KEY::TITLE, - PLACE = FMH::MODEL_KEY::PLACE, - FORMAT = FMH::MODEL_KEY::FORMAT - - }; - Q_ENUM(SORTBY) - - enum FILTER : uint_fast8_t { - AUDIO = FMH::FILTER_TYPE::AUDIO, - VIDEO = FMH::FILTER_TYPE::VIDEO, - TEXT = FMH::FILTER_TYPE::TEXT, - IMAGE = FMH::FILTER_TYPE::IMAGE, - DOCUMENT = FMH::FILTER_TYPE::DOCUMENT, - COMPRESSED = FMH::FILTER_TYPE::COMPRESSED, - FONT = FMH::FILTER_TYPE::FONT, - NONE = FMH::FILTER_TYPE::NONE - }; - Q_ENUM(FILTER) - - enum PATHTYPE : uint_fast8_t { - PLACES_PATH = FMH::PATHTYPE_KEY::PLACES_PATH, - FISH_PATH = FMH::PATHTYPE_KEY::FISH_PATH, - MTP_PATH = FMH::PATHTYPE_KEY::MTP_PATH, - REMOTE_PATH = FMH::PATHTYPE_KEY::REMOTE_PATH, - DRIVES_PATH = FMH::PATHTYPE_KEY::DRIVES_PATH, - REMOVABLE_PATH = FMH::PATHTYPE_KEY::REMOVABLE_PATH, - TAGS_PATH = FMH::PATHTYPE_KEY::TAGS_PATH, - APPS_PATH = FMH::PATHTYPE_KEY::APPS_PATH, - TRASH_PATH = FMH::PATHTYPE_KEY::TRASH_PATH, - CLOUD_PATH = FMH::PATHTYPE_KEY::CLOUD_PATH, - QUICK_PATH = FMH::PATHTYPE_KEY::QUICK_PATH, - OTHER_PATH = FMH::PATHTYPE_KEY::OTHER_PATH - - }; - Q_ENUM(PATHTYPE) - - enum VIEW_TYPE : uint_fast8_t { - ICON_VIEW, - LIST_VIEW, - MILLERS_VIEW - - }; - Q_ENUM(VIEW_TYPE) - - Q_ENUM(STATUS_CODE) - - /** - * @brief FMList - * @param parent - */ - FMList(QObject *parent = nullptr); - - /** - * @brief items - * @return - */ - const FMH::MODEL_LIST &items() const final override; - - /** - * @brief getSortBy - * @return - */ - FMList::SORTBY getSortBy() const; - - /** - * @brief setSortBy - * @param key - */ - void setSortBy(const FMList::SORTBY &key); - - /** - * @brief componentComplete - */ - void componentComplete() override final; - - /** - * @brief getPath - * Current path being watched and model - * @return - * Directory URL - */ - QUrl getPath() const; - - /** - * @brief setPath - * Set the directory path to be model - * @param path - * Directory URL - */ - void setPath(const QUrl &path); - - /** - * @brief getPathName - * The short name of the current directory - * @return - */ - QString getPathName() const; - - /** - * @brief getPathType - * The type of the current path, be it LOCAl, TAGS, CLOUD, APPS, DEVICE or others - * @return - * Path type value - */ - FMList::PATHTYPE getPathType() const; - - /** - * @brief getFilters - * The filters being applied to the current directory - * @return - * List of filters - */ - QStringList getFilters() const; - - /** - * @brief setFilters - * FIlters to be applied as regular expressions - * @param filters - */ - void setFilters(const QStringList &filters); - - /** - * @brief getFilterType - * Filter typebeing applied, for example, filtering by AUDIO or IMAGES etc... - * @return - */ - FMList::FILTER getFilterType() const; - - /** - * @brief setFilterType - * Apply a filter type, this a quick shortcut for applying a filter on a file type such as AUDIO, IMAGE, DOCUMENT - * @param type - */ - void setFilterType(const FMList::FILTER &type); - - /** - * @brief getHidden - * Returns if the current model is including hidden files - * @return - */ - bool getHidden() const; - - /** - * @brief setHidden - * List hidden files in the model - * @param state - */ - void setHidden(const bool &state); - - /** - * @brief getOnlyDirs - * Returns if the current model is including only directories or not - * @return - */ - bool getOnlyDirs() const; - - /** - * @brief setOnlyDirs - * Only list directories when modeling a directory - * @param state - */ - void setOnlyDirs(const bool &state); - - /** - * @brief getParentPath - * Returns a URL to the parent directory of the current directory being modeled or the previous directory if the current URL is not a local file - * @return - */ - const QUrl getParentPath(); - - /** - * @brief getFoldersFirst - * Returns whether directories are listed first before other files - * @return - */ - bool getFoldersFirst() const; - - /** - * @brief setFoldersFirst - * List directories first - * @param value - */ - void setFoldersFirst(const bool &value); - - /** - * @brief getCloudDepth - * @return - */ - int getCloudDepth() const; - - /** - * @brief setCloudDepth - * @param value - */ - void setCloudDepth(const int &value); - - /** - * @brief getStatus - * Get the current status of the current path - * @return - */ - PathStatus getStatus() const; - -private: - FM *fm; - - void clear(); - void reset(); - void setList(); - void assignList(const FMH::MODEL_LIST &list); - void appendToList(const FMH::MODEL_LIST &list); - void sortList(); - void search(const QString &query, const QUrl &path, const bool &hidden = false, const bool &onlyDirs = false, const QStringList &filters = QStringList()); - void filterContent(const QString &query, const QUrl &path); - void setStatus(const PathStatus &status); - - FMH::MODEL_LIST list = {{}}; - - QUrl path; - QString pathName = QString(); - QStringList filters = {}; - - bool onlyDirs = false; - bool hidden = false; - - bool foldersFirst = false; - int cloudDepth = 1; - - PathStatus m_status; - - FMList::SORTBY sort = FMList::SORTBY::MODIFIED; - FMList::FILTER filterType = FMList::FILTER::NONE; - FMList::PATHTYPE pathType = FMList::PATHTYPE::PLACES_PATH; - - NavHistory m_navHistory; - -public slots: - - /** - * @brief refresh - * Refresh the model for new changes - */ - void refresh(); - - /** - * @brief createDir - * Create a new directory within the current directory - * @param name - * Name of the directory - */ - void createDir(const QString &name); - - /** - * @brief copyInto - * Copy a list of file URls into the current directory - * @param urls - * List of files - */ - void copyInto(const QStringList &urls); - - /** - * @brief cutInto - * Cut/move a list of file URLs to the current directory - * @param urls - * List of files - */ - void cutInto(const QStringList &urls); - - /** - * @brief setDirIcon - * Changes the icon of a directory by making use of the directory config file - * @param index - * Index of the directory in the model - * @param iconName - * Name of the new icon - */ - void setDirIcon(const int &index, const QString &iconName); - - /** - * @brief remove - * Remove an item from the model, this does not remove the file from the file system - * @param index - */ - void remove(const int &index); - - /** - * @brief search - * Perform a search on the current directory. The search is perfrom in another model than the current one - * @param query - * Query for the search - * @param currentFMList - * The information of the model where the search is going to be performed - */ - void search(const QString &query, const FMList *currentFMList); - - /** - * @brief previousPath - * Inmediate previous path - * @return - */ - const QUrl previousPath(); - - /** - * @brief posteriorPath - * Inmediate posterior path - * @return - */ - const QUrl posteriorPath(); - -signals: - void pathChanged(); - void pathNameChanged(); - void pathTypeChanged(); - void filtersChanged(); - void filterTypeChanged(); - void hiddenChanged(); - void onlyDirsChanged(); - void sortByChanged(); - void foldersFirstChanged(); - void statusChanged(); - void cloudDepthChanged(); - - void warning(QString message); - void progress(int percent); - - void searchResultReady(); -}; - -#endif // FMLIST_H diff --git a/src/fmstatic.cpp b/src/fmstatic.cpp deleted file mode 100644 index 1d0908b..0000000 --- a/src/fmstatic.cpp +++ /dev/null @@ -1,312 +0,0 @@ -#include "fmstatic.h" - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -FMStatic::FMStatic(QObject *parent) - : QObject(parent) -{ -} - -FMH::MODEL_LIST FMStatic::packItems(const QStringList &items, const QString &type) -{ - FMH::MODEL_LIST data; - - for (const auto &path : items) { - if (QUrl(path).isLocalFile() && !FMH::fileExists(path)) - continue; - - auto model = FMH::getFileInfoModel(path); - model.insert(FMH::MODEL_KEY::TYPE, type); - data << model; - } - - return data; -} - -FMH::MODEL_LIST FMStatic::getDefaultPaths() -{ - return FMStatic::packItems(FMH::defaultPaths, FMH::PATHTYPE_LABEL[FMH::PATHTYPE_KEY::PLACES_PATH]); -} - -FMH::MODEL_LIST FMStatic::search(const QString &query, const QUrl &path, const bool &hidden, const bool &onlyDirs, const QStringList &filters) -{ - FMH::MODEL_LIST content; - - if (!path.isLocalFile()) { - qWarning() << "URL recived is not a local file. FM::search" << path; - return content; - } - - if (FMStatic::isDir(path)) { - QDir::Filters dirFilter; - - dirFilter = (onlyDirs ? QDir::AllDirs | QDir::NoDotDot | QDir::NoDot : QDir::Files | QDir::AllDirs | QDir::NoDotDot | QDir::NoDot); - - if (hidden) - dirFilter = dirFilter | QDir::Hidden | QDir::System; - - QDirIterator it(path.toLocalFile(), filters, dirFilter, QDirIterator::Subdirectories); - while (it.hasNext()) { - auto url = it.next(); - if (it.fileName().contains(query, Qt::CaseInsensitive)) { - content << FMH::getFileInfoModel(QUrl::fromLocalFile(url)); - } - } - } else - qWarning() << "Search path does not exists" << path; - - qDebug() << content; - return content; -} - -FMH::MODEL_LIST FMStatic::getDevices() -{ - FMH::MODEL_LIST drives; - - return drives; -} - -QVariantMap FMStatic::getDirInfo(const QUrl &path) -{ - return FMH::getDirInfo(path); -} - -QVariantMap FMStatic::getFileInfo(const QUrl &path) -{ - return FMH::getFileInfo(path); -} - -bool FMStatic::isDefaultPath(const QString &path) -{ - return FMH::defaultPaths.contains(path); -} - -QUrl FMStatic::parentDir(const QUrl &path) -{ - return FMH::parentDir(path); -} - -bool FMStatic::isDir(const QUrl &path) -{ - if (!path.isLocalFile()) { - // qWarning() << "URL recived is not a local file. FM::isDir" << path; - return false; - } - - const QFileInfo file(path.toLocalFile()); - return file.isDir(); -} - -bool FMStatic::isCloud(const QUrl &path) -{ - return path.scheme() == FMH::PATHTYPE_SCHEME[FMH::PATHTYPE_KEY::CLOUD_PATH]; -} - -bool FMStatic::fileExists(const QUrl &path) -{ - return FMH::fileExists(path); -} - -QString FMStatic::fileDir(const QUrl &path) // the directory path of the file -{ - return FMH::fileDir(path); -} - -QString FMStatic::formatSize(const int &size) -{ - const QLocale locale; - return locale.formattedDataSize(size); -} - -QString FMStatic::formatDate(const QString &dateStr, const QString &format, const QString &initFormat) -{ - if (initFormat.isEmpty()) - return QDateTime::fromString(dateStr, Qt::TextDate).toString(format); - else - return QDateTime::fromString(dateStr, initFormat).toString(format); -} - -QString FMStatic::systemFormatDate(const QString &dateStr) -{ - return QLocale::system().toString(QDateTime::fromString(dateStr, Qt::TextDate), - QLocale::ShortFormat); -} - -QString FMStatic::formatTime(const qint64 &value) -{ - QString tStr; - if (value) { - QTime time((value / 3600) % 60, (value / 60) % 60, value % 60, (value * 1000) % 1000); - QString format = "mm:ss"; - if (value > 3600) - format = "hh:mm:ss"; - tStr = time.toString(format); - } - - return tStr.isEmpty() ? "00:00" : tStr; -} - -QString FMStatic::homePath() -{ - return FMH::HomePath; -} - -bool FMStatic::copy(const QList &urls, const QUrl &destinationDir) -{ - auto job = KIO::copy(urls, destinationDir); - job->start(); - return true; -} - -bool FMStatic::cut(const QList &urls, const QUrl &where) -{ - return FMStatic::cut(urls, where, QString()); -} - -bool FMStatic::cut(const QList &urls, const QUrl &where, const QString &name) -{ - QUrl _where = where; - if (!name.isEmpty()) - _where = QUrl(where.toString() + "/" + name); - - auto job = KIO::move(urls, _where, KIO::HideProgressInfo); - job->start(); - return true; -} - -bool FMStatic::removeFiles(const QList &urls) -{ - auto job = KIO::del(urls); - job->start(); - return true; -} - -void FMStatic::moveToTrash(const QList &urls) -{ - auto job = KIO::trash(urls); - job->start(); -} - -void FMStatic::emptyTrash() -{ - auto job = KIO::emptyTrash(); - job->start(); -} - -bool FMStatic::removeDir(const QUrl &path) -{ - bool result = true; - QDir dir(path.toLocalFile()); - qDebug() << "TRYING TO REMOVE DIR" << path << path.toLocalFile(); - if (dir.exists()) { - Q_FOREACH (QFileInfo info, dir.entryInfoList(QDir::NoDotAndDotDot | QDir::System | QDir::Hidden | QDir::AllDirs | QDir::Files, QDir::DirsFirst)) { - if (info.isDir()) { - result = removeDir(QUrl::fromLocalFile(info.absoluteFilePath())); - } else { - result = QFile::remove(info.absoluteFilePath()); - } - - if (!result) { - return result; - } - } - result = dir.rmdir(path.toLocalFile()); - } - - return result; -} - -bool FMStatic::rename(const QUrl &url, const QString &name) -{ - return FMStatic::cut({url}, QUrl(url.toString().left(url.toString().lastIndexOf("/"))), name); -} - -bool FMStatic::createDir(const QUrl &path, const QString &name) -{ - auto job = KIO::mkdir(name.isEmpty() ? path : QUrl(path.toString() + "/" + name)); - job->start(); - return true; -} - -bool FMStatic::createFile(const QUrl &path, const QString &name) -{ - QFile file(path.toLocalFile() + "/" + name); - - if (file.open(QIODevice::ReadWrite)) { - file.close(); - return true; - } - - return false; -} - -bool FMStatic::createSymlink(const QUrl &path, const QUrl &where) -{ - qDebug() << "trying to create symlink" << path << where; - const auto job = KIO::link({path}, where); - job->start(); - return true; -} - -bool FMStatic::openUrl(const QUrl &url) -{ - KRun::runUrl(url, FMH::getFileInfoModel(url)[FMH::MODEL_KEY::MIME], nullptr, false, KRun::RunFlag::DeleteTemporaryFiles); - return true; -} - -void FMStatic::openLocation(const QStringList &urls) -{ - for (const auto &url : qAsConst(urls)) - QDesktopServices::openUrl(QUrl::fromLocalFile(QFileInfo(url).dir().absolutePath())); -} - -const QVariantMap FMStatic::dirConf(const QUrl &path) -{ - return FMH::dirConf(path); -} - -void FMStatic::setDirConf(const QUrl &path, const QString &group, const QString &key, const QVariant &value) -{ - FMH::setDirConf(path, group, key, value); -} - -bool FMStatic::checkFileType(const int &type, const QString &mimeTypeName) -{ - return FMH::checkFileType(static_cast(type), mimeTypeName); -} - -static bool doNameFilter(const QString &name, const QStringList &filters) -{ - const auto filtersAccumulate = std::accumulate(filters.constBegin(), filters.constEnd(), QVector {}, [](QVector &res, const QString &filter) -> QVector { - res.append(QRegExp(filter, Qt::CaseInsensitive, QRegExp::Wildcard)); - return res; - }); - - for (const auto &filter : filtersAccumulate) { - if (filter.exactMatch(name)) { - return true; - } - } - return false; -} - -QStringList FMStatic::nameFilters(const int &type) -{ - return FMH::FILTER_LIST[static_cast(type)]; -} - -QString FMStatic::iconName(const QString &value) -{ - return FMH::getIconName(value); -} diff --git a/src/fmstatic.h b/src/fmstatic.h deleted file mode 100644 index d1c67f9..0000000 --- a/src/fmstatic.h +++ /dev/null @@ -1,353 +0,0 @@ -#ifndef FMSTATIC_H -#define FMSTATIC_H - -#include "fmh.h" -#include - -/** - * @brief The FMStatic class - * STatic file management methods, this class has a constructor only to register to QML, however all methods are static. - */ -class FMStatic : public QObject -{ - Q_OBJECT -public: - explicit FMStatic(QObject *parent = nullptr); - -public slots: - /** - * @brief search - * Search for files in a path using filters - * @param query - * Term to be searched, such as ".qml" or "music" - * @param path - * The path to perform the search upon - * @param hidden - * If should also search for hidden files - * @param onlyDirs - * If only searching for directories and not files - * @param filters - * List of filter patterns such as {"*.qml"}, it can use regular expressions - * @return - * The search results are returned as a FMH::MODEL_LIST - */ - static FMH::MODEL_LIST search(const QString &query, const QUrl &path, const bool &hidden = false, const bool &onlyDirs = false, const QStringList &filters = QStringList()); - - /** - * @brief getDevices - * Devices mounted to the file system - * @return - * Represented as a FMH::MODEL_LIST - */ - static FMH::MODEL_LIST getDevices(); - - /** - * @brief getDefaultPaths - * A model list of the default paths in most systems, such as Home, Pictures, Video, Downloads, Music and Documents folders - * @return - */ - static FMH::MODEL_LIST getDefaultPaths(); - - /** - * @brief packItems - * Given a list of path URLs pack all the info of such files as a FMH::MODEL_LIST - * @param items - * List of local URLs - * @param type - * The type of the list of urls, such as local, remote etc. This value is inserted with the key FMH::MODEL_KEY::TYPE - * @return - */ - static FMH::MODEL_LIST packItems(const QStringList &items, const QString &type); - - /** - * @brief copy - * Perfom a copy of the files to the passed destination - * @param urls - * List of URLs to be copy - * @param destinationDir - * Destination - * @return - * Return if the operation has been succesfull - */ - static bool copy(const QList &urls, const QUrl &destinationDir); - - /** - * @brief cut - * Perform a move/cut of a list of files to a destination. This function also moves the associated tags if the tags component has been enabled COMPONENT_TAGGING - * @param urls - * List of URLs to be moved - * @param where - * Destination path - * @return - * If the operation has been sucessfull - */ - static bool cut(const QList &urls, const QUrl &where); - - /** - * @brief cut - * @param urls - * @param where - * @param name - * New name of the files to be moved - * @return - */ - static bool cut(const QList &urls, const QUrl &where, const QString &name); - - /** - * @brief removeFiles - * List of files to be removed completely. This function also removes the assciated tags to the files if the tagging component has been enabled COMPONENT_TAGGING - * @param urls - * @return - * If the operation has been sucessfull - */ - static bool removeFiles(const QList &urls); - - /** - * @brief removeDir - * Remove a directory recursively - * @param path - * Path URL to be rmeoved - * @return - * If the operation has been sucessfull - */ - static bool removeDir(const QUrl &path); - - /** - * @brief formatSize - * Format a file size - * @param size - * size in bytes - * @return - * Formated into a readable string - */ - static QString formatSize(const int &size); - - /** - * @brief formatTime - * Format a milliseconds value to a readable format - * @param value - * Milliseconds - * @return - * Readable formated value - */ - static QString formatTime(const qint64 &value); - - /** - * @brief formatDate - * Given a date string, a format and a intended format return a readable string - * @param dateStr - * Date format - * @param format - * Intended format, by default "dd/MM/yyyy" - * @param initFormat - * Date format - * @return - */ - static QString formatDate(const QString &dateStr, const QString &format = QString("dd/MM/yyyy"), const QString &initFormat = QString()); - - static QString systemFormatDate(const QString &dateStr); - - /** - * @brief homePath - * The default home path in different systems - * @return - */ - static QString homePath(); - - /** - * @brief parentDir - * Given a file url return its parent directory - * @param path - * The file URL - * @return - * The parent directory URL if it exists otherwise returns the passed URL - */ - static QUrl parentDir(const QUrl &path); - - /** - * @brief getDirInfo - * Get info of a directory packed as a QVariantMap model - * @param path - * Path URL - * @return - */ - static QVariantMap getDirInfo(const QUrl &path); - - /** - * @brief getFileInfo - * Get file info - * @param path - * @return - * File info packed as a QVariantMap model - */ - static QVariantMap getFileInfo(const QUrl &path); - - /** - * @brief isDefaultPath - * Checks if a given path URL is a default path as in returned by the defaultPaths method - * @param path - * @return - */ - static bool isDefaultPath(const QString &path); - - /** - * @brief isDir - * If a local file URL is a directory - * @param path - * File URL - * @return - */ - static bool isDir(const QUrl &path); - - /** - * @brief isCloud - * If a path is a URL server instead of a local file - * @param path - * @return - */ - static bool isCloud(const QUrl &path); - - /** - * @brief fileExists - * Checks if a local file exists in the file system - * @param path - * File URL - * @return - * Existance - */ - static bool fileExists(const QUrl &path); - - /** - * if the url is a file path then it returns its directory - * and if it is a directory returns the same path - * */ - /** - * @brief fileDir - * Gives the directory URL path of a file, and if it is a directory returns the same path - * @param path - * File path URL - * @return - * The directory URL - */ - static QString fileDir(const QUrl &path); - - /** - * @brief dirConf - * The config values of a directory, such values can be any from iconname to specific ones. The config file is stored in the directory as .dir - * @param path - * @return - */ - static const QVariantMap dirConf(const QUrl &path); - - /** - * @brief setDirConf - * Write a config key-value to the directory config file - * @param path - * @param group - * @param key - * @param value - */ - static void setDirConf(const QUrl &path, const QString &group, const QString &key, const QVariant &value); - - /** - * @brief checkFileType - * Checks if a mimetype belongs to a file type, for example image/jpg belong to the type FMH::FILTER_TYPE - * @param type - * FMH::FILTER_TYPE value - * @param mimeTypeName - * @return - */ - static bool checkFileType(const int &type, const QString &mimeTypeName); - - /** - * @brief moveToTrash - * Moves to the trash can the file URLs. The associated tags are kept in case the files are restored. - * @param urls - */ - static void moveToTrash(const QList &urls); - - /** - * @brief emptyTrash - * Empty the trash casn - */ - static void emptyTrash(); - - /** - * @brief rename - * Rename a file to a new name - * @param url - * File URL to be renamed - * @param name - * The short new name of the file, not the new URL, for setting a new URl use cut instead. - * @return - */ - static bool rename(const QUrl &url, const QString &name); - - /** - * @brief createDir - * Creates a directory given a base path and a directory name - * @param path - * Base directory path - * @param name - * New directory name - * @return - * If the operation was sucessfull - */ - static bool createDir(const QUrl &path, const QString &name); - - /** - * @brief createFile - * Creates a file given the base directory path and a short file name - * @param path - * Base directory path - * @param name - * Name of the new file to be created with the extension - * @return - */ - static bool createFile(const QUrl &path, const QString &name); - - /** - * @brief createSymlink - * Creates a symlink - * @param path - * File to be symlinked - * @param where - * Destination of the symlink - * @return - */ - static bool createSymlink(const QUrl &path, const QUrl &where); - - /** - * @brief openUrl - * Given a URL it tries to open it using the default app associated to it - * @param url - * The URL to be open - * @return - */ - static bool openUrl(const QUrl &url); - - /** - * @brief openLocation - * Open with the default file manager a list of URLs - * @param urls - */ - static void openLocation(const QStringList &urls); - - /** - * @brief nameFilters - * Given a filter type return a list of associated name filters, as in suffixes. - * @param type - * The filter type to be mapped to a FMH::FILTER_TYPE - */ - static QStringList nameFilters(const int &type); - - /** - * @brief iconName - * Get the icon name associated to the file or name. - * @param value - * The file path or file name - */ - static QString iconName(const QString &value); -}; - -#endif // FMSTATIC_H diff --git a/src/handy.cpp b/src/handy.cpp deleted file mode 100644 index 2554e13..0000000 --- a/src/handy.cpp +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright 2018 Camilo Higuita - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, 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 Library 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. - */ - -#include "handy.h" -#include "fmh.h" - -#include -#include -#include -#include -#include -#include -#include - -Handy::Handy(QObject *parent) - : QObject(parent) -{ - -} - -QVariantMap Handy::userInfo() -{ - QString name = qgetenv("USER"); - if (name.isEmpty()) - name = qgetenv("USERNAME"); - - return QVariantMap({{FMH::MODEL_NAME[FMH::MODEL_KEY::NAME], name}}); -} - -QString Handy::getClipboardText() -{ - auto clipbopard = QApplication::clipboard(); - - auto mime = clipbopard->mimeData(); - if (mime->hasText()) - return clipbopard->text(); - - return QString(); -} - -QVariantMap Handy::getClipboard() -{ - QVariantMap res; - - auto clipboard = QApplication::clipboard(); - - auto mime = clipboard->mimeData(); - if (mime->hasUrls()) - res.insert("urls", QUrl::toStringList(mime->urls())); - - if (mime->hasText()) - res.insert("text", mime->text()); - - const QByteArray a = mime->data(QStringLiteral("application/x-kde-cutselection")); - res.insert("cut", (!a.isEmpty() && a.at(0) == '1')); - return res; -} - -bool Handy::copyToClipboard(const QVariantMap &value, const bool &cut) -{ - auto clipboard = QApplication::clipboard(); - QMimeData *mimeData = new QMimeData(); - - if (value.contains("urls")) - mimeData->setUrls(QUrl::fromStringList(value["urls"].toStringList())); - - if (value.contains("text")) - mimeData->setText(value["text"].toString()); - - mimeData->setData(QStringLiteral("application/x-kde-cutselection"), cut ? "1" : "0"); - clipboard->setMimeData(mimeData); - - return true; -} - -void Handy::setAsWallpaper(const QUrl &url) -{ - if (!url.isLocalFile()) - return; - - QDBusInterface iface("org.cutefish.Settings", "/Theme", - "org.cutefish.Theme", - QDBusConnection::sessionBus(), nullptr); - if (iface.isValid()) - iface.call("setWallpaper", url.toLocalFile()); -} - -bool Handy::copyTextToClipboard(const QString &text) -{ - QApplication::clipboard()->setText(text); - return true; -} - -int Handy::version() -{ - return QOperatingSystemVersion::current().majorVersion(); -} diff --git a/src/handy.h b/src/handy.h deleted file mode 100644 index 3794631..0000000 --- a/src/handy.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright 2018 Camilo Higuita - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, 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 Library 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. - */ - -#ifndef HANDY_H -#define HANDY_H - -#include - -#include - -/*! - * \brief The Handy class - * Contains useful static methods to be used as an attached property to the Maui application - */ -class Handy : public QObject -{ - Q_OBJECT - -public: - Handy(QObject *parent = nullptr); - -public slots: - /*! - * \brief Returns the major version of the current OS - * - * This function is static. - * \return Major OS version - */ - static int version(); - - /*! - * \brief Returns a QVariantMap containing basic information about the current user - * - * The pairs keys for the information returned are: - * "name" - * \return QVariantMap with user info - */ - static QVariantMap userInfo(); - - /*! - * \brief Returns the text contained in the clipboard - * \return QString containing clipboard text - */ - static QString getClipboardText(); - static QVariantMap getClipboard(); - - /*! - * \brief Copies text to the clipboard - * \param text text to be copied to the clipboard - * \return - */ - static bool copyTextToClipboard(const QString &text); - - /** - * @brief copyToClipboard - * @param value - * @param cut - * @return - */ - static bool copyToClipboard(const QVariantMap &value, const bool &cut = false); - - static void setAsWallpaper(const QUrl &url); -}; - -#endif // HANDY_H diff --git a/src/iconthemeprovider.cpp b/src/iconthemeprovider.cpp deleted file mode 100644 index 7cb0312..0000000 --- a/src/iconthemeprovider.cpp +++ /dev/null @@ -1,33 +0,0 @@ -#include "iconthemeprovider.h" -#include - -IconThemeProvider::IconThemeProvider() - : QQuickImageProvider(QQuickImageProvider::Pixmap) -{ -} - -QPixmap IconThemeProvider::requestPixmap(const QString &id, QSize *realSize, - const QSize &requestedSize) -{ - // Sanitize requested size - QSize size(requestedSize); - if (size.width() < 1) - size.setWidth(1); - if (size.height() < 1) - size.setHeight(1); - - // Return real size - if (realSize) - *realSize = size; - - // Is it a path? - if (id.startsWith(QLatin1Char('/'))) - return QPixmap(id).scaled(size); - - // Return icon from theme or fallback to a generic icon - QIcon icon = QIcon::fromTheme(id); - if (icon.isNull()) - icon = QIcon::fromTheme(QLatin1String("application-x-desktop")); - - return icon.pixmap(size); -} diff --git a/src/iconthemeprovider.h b/src/iconthemeprovider.h deleted file mode 100644 index 1f62f86..0000000 --- a/src/iconthemeprovider.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef ICONTHEMEPROVIDER_H -#define ICONTHEMEPROVIDER_H - -#include - -class IconThemeProvider : public QQuickImageProvider -{ -public: - IconThemeProvider(); - - QPixmap requestPixmap(const QString &id, QSize *realSize, const QSize &requestedSize); -}; - -#endif // ICONTHEMEPROVIDER_H diff --git a/src/lib/fileitemactions.cpp b/src/lib/fileitemactions.cpp deleted file mode 100644 index 88887a2..0000000 --- a/src/lib/fileitemactions.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include "fileitemactions.h" -#include - -FileItemActions::FileItemActions(QObject *parent) - : QObject(parent) -{ - -} - -KService::List FileItemActions::associatedApplications(const QStringList &mimeTypeList, const QString &traderConstraint) -{ - const KService::List firstOffers = KMimeTypeTrader::self()->query(mimeTypeList.first(), "Application", traderConstraint); - QStringList serviceList; - - for (int i = 0; i < firstOffers.count(); ++i) { - - } - - return KService::List(); -} diff --git a/src/lib/fileitemactions.h b/src/lib/fileitemactions.h deleted file mode 100644 index d366390..0000000 --- a/src/lib/fileitemactions.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef FILEITEMACTIONS_H -#define FILEITEMACTIONS_H - -#include -#include - -class FileItemActions : public QObject -{ - Q_OBJECT - -public: - explicit FileItemActions(QObject *parent = nullptr); - - static KService::List associatedApplications(const QStringList& mimeTypeList, const QString& traderConstraint); - -}; - -#endif // FILEITEMACTIONS_H diff --git a/src/lib/foldermodel.cpp b/src/lib/foldermodel.cpp deleted file mode 100644 index 35ab493..0000000 --- a/src/lib/foldermodel.cpp +++ /dev/null @@ -1,2150 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2006 David Faure * - * Copyright (C) 2008 Fredrik Höglund * - * Copyright (C) 2008 Rafael Fernández López * - * Copyright (C) 2011 Marco Martin * - * Copyright (C) 2014 by Eike Hein * - * Copyright (C) 2021 by Reven Martin * - * * - * 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 . * - ***************************************************************************/ - -#include "foldermodel.h" -#include "itemviewadapter.h" -#include "positioner.h" -#include "../dialogs/propertiesdialog.h" - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -Q_LOGGING_CATEGORY(FOLDERMODEL, "cutefish.desktop.folder.foldermodel") - -DirLister::DirLister(QObject *parent) - : KDirLister(parent) -{ -} - -DirLister::~DirLister() -{ -} - -void DirLister::handleError(KIO::Job *job) -{ - if (!autoErrorHandlingEnabled()) { - emit error(job->errorString()); - return; - } - - KDirLister::handleError(job); -} - -FolderModel::FolderModel(QObject *parent) - : QSortFilterProxyModel(parent) - , m_dirWatch(nullptr) - , m_dragInProgress(false) - , m_urlChangedWhileDragging(false) - , m_dropTargetPositionsCleanup(new QTimer(this)) - , m_previewGenerator(nullptr) - , m_viewAdapter(nullptr) - , m_actionCollection(this) - , m_newMenu(nullptr) - , m_fileItemActions(nullptr) - , m_usedByContainment(false) - , m_locked(true) - , m_sortMode(0) - , m_sortDesc(false) - , m_sortDirsFirst(true) - , m_parseDesktopFiles(false) - , m_previews(false) - , m_filterMode(NoFilter) - , m_filterPatternMatchAll(true) - , m_screenUsed(false) - // , m_screenMapper(ScreenMapper::instance()) - , m_complete(false) -{ - // needed to pass the job around with qml - // qmlRegisterType(); - - qmlRegisterAnonymousType("Cutefish.FileManager", 1); - - DirLister *dirLister = new DirLister(this); - dirLister->setDelayedMimeTypes(true); - dirLister->setAutoErrorHandlingEnabled(false, nullptr); - - connect(dirLister, &DirLister::error, this, &FolderModel::dirListFailed); - connect(dirLister, &KCoreDirLister::itemsDeleted, this, &FolderModel::evictFromIsDirCache); - connect(dirLister, &KCoreDirLister::started, this, std::bind(&FolderModel::setStatus, this, Status::Listing)); - - void (KCoreDirLister::*myCompletedSignal)() = &KCoreDirLister::completed; - QObject::connect(dirLister, myCompletedSignal, this, [this] { - setStatus(Status::Ready); - emit listingCompleted(); - }); - - void (KCoreDirLister::*myCanceledSignal)() = &KCoreDirLister::canceled; - QObject::connect(dirLister, myCanceledSignal, this, [this] { - setStatus(Status::Canceled); - emit listingCanceled(); - }); - - m_dirModel = new KDirModel(this); - m_dirModel->setDirLister(dirLister); - m_dirModel->setDropsAllowed(KDirModel::DropOnDirectory | KDirModel::DropOnLocalExecutable); - - // If we have dropped items queued for moving, go unsorted now. - connect(this, &QAbstractItemModel::rowsAboutToBeInserted, this, [this]() { - if (!m_dropTargetPositions.isEmpty()) { - setSortMode(-1); - } - }); - - // Position dropped items at the desired target position. - connect(this, &QAbstractItemModel::rowsInserted, this, [this](const QModelIndex &parent, int first, int last) { - for (int i = first; i <= last; ++i) { - const auto idx = index(i, 0, parent); - const auto url = itemForIndex(idx).url(); - auto it = m_dropTargetPositions.find(url.fileName()); - if (it != m_dropTargetPositions.end()) { - const auto pos = it.value(); - m_dropTargetPositions.erase(it); - emit move(pos.x(), pos.y(), {url}); - } - } - }); - - /* - * Dropped files may not actually show up as new files, e.g. when we overwrite - * an existing file. Or files that fail to be listed by the dirLister, or... - * To ensure we don't grow the map indefinitely, clean it up periodically. - * The cleanup timer is (re)started whenever we modify the map. We use a quite - * high interval of 10s. This should ensure, that we don't accidentally wipe - * the mapping when we actually still want to use it. Since the time between - * adding an entry in the map and it showing up in the model should be - * small, this should rarely, if ever happen. - */ - m_dropTargetPositionsCleanup->setInterval(10000); - m_dropTargetPositionsCleanup->setSingleShot(true); - connect(m_dropTargetPositionsCleanup, &QTimer::timeout, this, [this]() { - if (!m_dropTargetPositions.isEmpty()) { - qCDebug(FOLDERMODEL) << "clearing drop target positions after timeout:" << m_dropTargetPositions; - m_dropTargetPositions.clear(); - } - }); - - m_selectionModel = new QItemSelectionModel(this, this); - connect(m_selectionModel, &QItemSelectionModel::selectionChanged, this, &FolderModel::selectionChanged); - - setSourceModel(m_dirModel); - - setSortLocaleAware(true); - setFilterCaseSensitivity(Qt::CaseInsensitive); - setDynamicSortFilter(true); - - sort(m_sortMode, m_sortDesc ? Qt::DescendingOrder : Qt::AscendingOrder); - - createActions(); -} - -FolderModel::~FolderModel() -{ -// if (m_usedByContainment) { -// // disconnect so we don't handle signals from the screen mapper when -// // removeScreen is called -// m_screenMapper->disconnect(this); -// m_screenMapper->removeScreen(m_screen, resolvedUrl()); -// } -} - -QHash FolderModel::roleNames() const -{ - return staticRoleNames(); -} - -QHash FolderModel::staticRoleNames() -{ - QHash roleNames; - roleNames[Qt::DisplayRole] = "display"; - roleNames[Qt::DecorationRole] = "decoration"; - roleNames[BlankRole] = "blank"; - roleNames[OverlaysRole] = "overlays"; - roleNames[SelectedRole] = "selected"; - roleNames[IsDirRole] = "isDir"; - roleNames[IsLinkRole] = "isLink"; - roleNames[IsHiddenRole] = "isHidden"; - roleNames[UrlRole] = "url"; - roleNames[LinkDestinationUrl] = "linkDestinationUrl"; - roleNames[SizeRole] = "size"; - roleNames[TypeRole] = "type"; - - return roleNames; -} - -void FolderModel::classBegin() -{ -} - -void FolderModel::componentComplete() -{ - m_complete = true; - invalidate(); -} - -void FolderModel::invalidateIfComplete() -{ - if (!m_complete) { - return; - } - - invalidate(); -} - -void FolderModel::invalidateFilterIfComplete() -{ - if (!m_complete) { - return; - } - - invalidateFilter(); -} - -void FolderModel::newFileMenuItemCreated(const QUrl &url) -{ - Q_UNUSED(url) - -// if (m_usedByContainment && !m_screenMapper->sharedDesktops()) { -// m_screenMapper->addMapping(url, m_screen, ScreenMapper::DelayedSignal); -// m_dropTargetPositions.insert(url.fileName(), m_menuPosition); -// m_menuPosition = {}; -// m_dropTargetPositionsCleanup->start(); -// } -} - -QString FolderModel::url() const -{ - return m_url; -} - -void FolderModel::setUrl(const QString &url) -{ - const QUrl &resolvedNewUrl = resolve(url); - - if (url == m_url) { - m_dirModel->dirLister()->updateDirectory(resolvedNewUrl); - return; - } - - const auto oldUrl = resolvedUrl(); - - beginResetModel(); - m_url = url; - m_isDirCache.clear(); - m_dirModel->dirLister()->openUrl(resolvedNewUrl); - clearDragImages(); - m_dragIndexes.clear(); - endResetModel(); - - emit urlChanged(); - emit resolvedUrlChanged(); - - m_errorString.clear(); - emit errorStringChanged(); - - if (m_dirWatch) { - delete m_dirWatch; - m_dirWatch = nullptr; - } - - if (resolvedNewUrl.isValid()) { - m_dirWatch = new KDirWatch(this); - connect(m_dirWatch, &KDirWatch::created, this, &FolderModel::iconNameChanged); - connect(m_dirWatch, &KDirWatch::dirty, this, &FolderModel::iconNameChanged); - m_dirWatch->addFile(resolvedNewUrl.toLocalFile() + QLatin1String("/.directory")); - } - - if (m_dragInProgress) { - m_urlChangedWhileDragging = true; - } - - emit iconNameChanged(); - -// if (m_usedByContainment && !m_screenMapper->sharedDesktops()) { -// m_screenMapper->removeScreen(m_screen, oldUrl); -// m_screenMapper->addScreen(m_screen, resolvedUrl()); -// } -} - -QUrl FolderModel::resolvedUrl() const -{ - return m_dirModel->dirLister()->url(); -} - -QUrl FolderModel::resolve(const QString &url) -{ - QUrl resolvedUrl; - - if (url.startsWith(QLatin1Char('~'))) { - resolvedUrl = QUrl::fromLocalFile(KShell::tildeExpand(url)); - } else { - resolvedUrl = QUrl::fromUserInput(url); - } - - return resolvedUrl; -} - -QString FolderModel::iconName() const -{ - const KFileItem rootItem(m_dirModel->dirLister()->url()); - - if (!rootItem.isFinalIconKnown()) { - rootItem.determineMimeType(); - } - - return rootItem.iconName(); -} - -FolderModel::Status FolderModel::status() const -{ - return m_status; -} - -void FolderModel::setStatus(Status status) -{ - if (m_status != status) { - m_status = status; - emit statusChanged(); - } -} - -QString FolderModel::errorString() const -{ - return m_errorString; -} - -bool FolderModel::dragging() const -{ - return m_dragInProgress; -} - -bool FolderModel::usedByContainment() const -{ - return m_usedByContainment; -} - -void FolderModel::setUsedByContainment(bool used) -{ - Q_UNUSED(used) - -// if (m_usedByContainment != used) { -// m_usedByContainment = used; - -// QAction *action = m_actionCollection.action(QStringLiteral("refresh")); - -// if (action) { -// action->setText(m_usedByContainment ? tr("&Refresh Desktop") : tr("&Refresh View")); -// action->setIcon(m_usedByContainment ? QIcon::fromTheme(QStringLiteral("user-desktop")) : QIcon::fromTheme(QStringLiteral("view-refresh"))); -// } - -// m_screenMapper->disconnect(this); -// connect(m_screenMapper, &ScreenMapper::screensChanged, this, &FolderModel::invalidateFilterIfComplete); -// connect(m_screenMapper, &ScreenMapper::screenMappingChanged, this, &FolderModel::invalidateFilterIfComplete); - -// emit usedByContainmentChanged(); -// } -} - -bool FolderModel::locked() const -{ - return m_locked; -} - -void FolderModel::setLocked(bool locked) -{ - if (m_locked != locked) { - m_locked = locked; - - emit lockedChanged(); - } -} - -void FolderModel::dirListFailed(const QString &error) -{ - m_errorString = error; - emit errorStringChanged(); -} - -int FolderModel::sortMode() const -{ - return m_sortMode; -} - -void FolderModel::setSortMode(int mode) -{ - if (m_sortMode != mode) { - m_sortMode = mode; - - if (mode == -1 /* Unsorted */) { - setDynamicSortFilter(false); - } else { - invalidateIfComplete(); - sort(m_sortMode, m_sortDesc ? Qt::DescendingOrder : Qt::AscendingOrder); - setDynamicSortFilter(true); - } - - emit sortModeChanged(); - } -} - -bool FolderModel::sortDesc() const -{ - return m_sortDesc; -} - -void FolderModel::setSortDesc(bool desc) -{ - if (m_sortDesc != desc) { - m_sortDesc = desc; - - if (m_sortMode != -1 /* Unsorted */) { - invalidateIfComplete(); - sort(m_sortMode, m_sortDesc ? Qt::DescendingOrder : Qt::AscendingOrder); - } - - emit sortDescChanged(); - } -} - -bool FolderModel::sortDirsFirst() const -{ - return m_sortDirsFirst; -} - -void FolderModel::setSortDirsFirst(bool enable) -{ - if (m_sortDirsFirst != enable) { - m_sortDirsFirst = enable; - - if (m_sortMode != -1 /* Unsorted */) { - invalidateIfComplete(); - sort(m_sortMode, m_sortDesc ? Qt::DescendingOrder : Qt::AscendingOrder); - } - - emit sortDirsFirstChanged(); - } -} - -bool FolderModel::parseDesktopFiles() const -{ - return m_parseDesktopFiles; -} - -void FolderModel::setParseDesktopFiles(bool enable) -{ - if (m_parseDesktopFiles != enable) { - m_parseDesktopFiles = enable; - emit parseDesktopFilesChanged(); - } -} - -QObject *FolderModel::viewAdapter() const -{ - return m_viewAdapter; -} - -void FolderModel::setViewAdapter(QObject *adapter) -{ - if (m_viewAdapter != adapter) { - KAbstractViewAdapter *abstractViewAdapter = dynamic_cast(adapter); - - m_viewAdapter = abstractViewAdapter; - - if (m_viewAdapter && !m_previewGenerator) { - m_previewGenerator = new KFilePreviewGenerator(abstractViewAdapter, this); - m_previewGenerator->setPreviewShown(m_previews); - m_previewGenerator->setEnabledPlugins(m_effectivePreviewPlugins); - } - - emit viewAdapterChanged(); - } -} - -bool FolderModel::previews() const -{ - return m_previews; -} - -void FolderModel::setPreviews(bool previews) -{ - if (m_previews != previews) { - m_previews = previews; - - if (m_previewGenerator) { - m_previewGenerator->setPreviewShown(m_previews); - } - - emit previewsChanged(); - } -} - -QStringList FolderModel::previewPlugins() const -{ - return m_previewPlugins; -} - -void FolderModel::setPreviewPlugins(const QStringList &previewPlugins) -{ - QStringList effectivePlugins = previewPlugins; - if (effectivePlugins.isEmpty()) { - effectivePlugins = KIO::PreviewJob::defaultPlugins(); - } - - if (m_effectivePreviewPlugins != effectivePlugins) { - m_effectivePreviewPlugins = effectivePlugins; - - if (m_previewGenerator) { - m_previewGenerator->setPreviewShown(false); - m_previewGenerator->setEnabledPlugins(m_effectivePreviewPlugins); - m_previewGenerator->setPreviewShown(true); - } - } - - if (m_previewPlugins != previewPlugins) { - m_previewPlugins = previewPlugins; - emit previewPluginsChanged(); - } -} - -int FolderModel::filterMode() const -{ - return m_filterMode; -} - -void FolderModel::setFilterMode(int filterMode) -{ - if (m_filterMode != (FilterMode)filterMode) { - m_filterMode = (FilterMode)filterMode; - - invalidateFilterIfComplete(); - - emit filterModeChanged(); - } -} - -QString FolderModel::filterPattern() const -{ - return m_filterPattern; -} - -void FolderModel::setFilterPattern(const QString &pattern) -{ - if (m_filterPattern == pattern) { - return; - } - - m_filterPattern = pattern; - m_filterPatternMatchAll = (pattern == QLatin1String("*")); - - const QStringList patterns = pattern.split(QLatin1Char(' ')); - m_regExps.clear(); - m_regExps.reserve(patterns.count()); - - foreach (const QString &pattern, patterns) { - QRegExp rx(pattern); - rx.setPatternSyntax(QRegExp::Wildcard); - rx.setCaseSensitivity(Qt::CaseInsensitive); - m_regExps.append(rx); - } - - invalidateFilterIfComplete(); - - emit filterPatternChanged(); -} - -QStringList FolderModel::filterMimeTypes() const -{ - return m_mimeSet.values(); -} - -void FolderModel::setFilterMimeTypes(const QStringList &mimeList) -{ - const QSet set(mimeList.constBegin(), mimeList.constEnd()); - - if (m_mimeSet != set) { - m_mimeSet = set; - - invalidateFilterIfComplete(); - - emit filterMimeTypesChanged(); - } -} - -void FolderModel::setScreen(int screen) -{ - m_screenUsed = (screen != -1); - - if (!m_screenUsed || m_screen == screen) - return; - - m_screen = screen; -// if (m_usedByContainment && !m_screenMapper->sharedDesktops()) { -// m_screenMapper->addScreen(screen, resolvedUrl()); -// } - emit screenChanged(); -} - -bool FolderModel::eventFilter(QObject *watched, QEvent *event) -{ - Q_UNUSED(watched) - - // Catching Shift modifier usage on open context menus to swap the - // Trash/Delete actions. - if (event->type() == QEvent::KeyPress) { - QKeyEvent *keyEvent = static_cast(event); - - if (keyEvent->key() == Qt::Key_Shift) { - m_actionCollection.action(QStringLiteral("trash"))->setVisible(false); - m_actionCollection.action(QStringLiteral("del"))->setVisible(true); - } - } else if (event->type() == QEvent::KeyRelease) { - QKeyEvent *keyEvent = static_cast(event); - - if (keyEvent->key() == Qt::Key_Shift) { - m_actionCollection.action(QStringLiteral("trash"))->setVisible(true); - m_actionCollection.action(QStringLiteral("del"))->setVisible(false); - } - } - - return false; -} - -KFileItem FolderModel::rootItem() const -{ - return m_dirModel->dirLister()->rootItem(); -} - -void FolderModel::up() -{ - const QUrl &up = KIO::upUrl(resolvedUrl()); - - if (up.isValid()) { - setUrl(up.toString()); - } -} - -void FolderModel::cd(int row) -{ - if (row < 0) { - return; - } - - const QModelIndex idx = index(row, 0); - bool isDir = data(idx, IsDirRole).toBool(); - - if (isDir) { - const KFileItem item = itemForIndex(idx); - if (m_parseDesktopFiles && item.isDesktopFile()) { - const KDesktopFile file(item.targetUrl().path()); - if (file.hasLinkType()) { - setUrl(file.readUrl()); - } - } else { - setUrl(item.targetUrl().toString()); - } - } -} - -void FolderModel::run(int row) -{ - if (row < 0) { - return; - } - - KFileItem item = itemForIndex(index(row, 0)); - - QUrl url(item.targetUrl()); - - // FIXME TODO: This can go once we depend on a KIO w/ fe1f50caaf2. - if (url.scheme().isEmpty()) { - url.setScheme(QStringLiteral("file")); - } - - KRun *run = new KRun(url, nullptr); - // On desktop:/ we want to be able to run .desktop files right away, - // otherwise ask for security reasons. We also don't use the targetUrl() - // from above since we don't want the resolved /home/foo/Desktop URL. - run->setShowScriptExecutionPrompt(item.url().scheme() != QLatin1String("desktop") - || item.url().adjusted(QUrl::RemoveFilename).path() != QLatin1String("/")); -} - -void FolderModel::runSelected() -{ - if (!m_selectionModel->hasSelection()) { - return; - } - - if (m_selectionModel->selectedIndexes().count() == 1) { - run(m_selectionModel->selectedIndexes().constFirst().row()); - return; - } - - KFileItemActions fileItemActions(this); - KFileItemList items; - - foreach (const QModelIndex &index, m_selectionModel->selectedIndexes()) { - // Skip over directories. - if (!index.data(IsDirRole).toBool()) { - items << itemForIndex(index); - } - } - - fileItemActions.runPreferredApplications(items, QString()); -} - -void FolderModel::rename(int row, const QString &name) -{ - if (row < 0) { - return; - } - - QModelIndex idx = index(row, 0); - m_dirModel->setData(mapToSource(idx), name, Qt::EditRole); -} - -int FolderModel::fileExtensionBoundary(int row) -{ - const QModelIndex idx = index(row, 0); - const QString &name = data(idx, Qt::DisplayRole).toString(); - - int boundary = name.length(); - - if (data(idx, IsDirRole).toBool()) { - return boundary; - } - - QMimeDatabase db; - const QString &ext = db.suffixForFileName(name); - - if (ext.isEmpty()) { - boundary = name.lastIndexOf(QLatin1Char('.')); - - if (boundary < 1) { - boundary = name.length(); - } - } else { - boundary -= ext.length() + 1; - } - - return boundary; -} - -bool FolderModel::hasSelection() const -{ - return m_selectionModel->hasSelection(); -} - -bool FolderModel::isSelected(int row) -{ - if (row < 0) { - return false; - } - - return m_selectionModel->isSelected(index(row, 0)); -} - -void FolderModel::setSelected(int row) -{ - if (row < 0) { - return; - } - - m_selectionModel->select(index(row, 0), QItemSelectionModel::Select); -} - -void FolderModel::selectAll() -{ - setRangeSelected(0, rowCount() - 1); -} - -void FolderModel::toggleSelected(int row) -{ - if (row < 0) { - return; - } - - m_selectionModel->select(index(row, 0), QItemSelectionModel::Toggle); -} - -void FolderModel::setRangeSelected(int anchor, int to) -{ - if (anchor < 0 || to < 0) { - return; - } - - QItemSelection selection(index(anchor, 0), index(to, 0)); - m_selectionModel->select(selection, QItemSelectionModel::ClearAndSelect); -} - -void FolderModel::updateSelection(const QVariantList &rows, bool toggle) -{ - QItemSelection newSelection; - - int iRow = -1; - - foreach (const QVariant &row, rows) { - iRow = row.toInt(); - - if (iRow < 0) { - return; - } - - const QModelIndex &idx = index(iRow, 0); - newSelection.select(idx, idx); - } - - if (toggle) { - QItemSelection pinnedSelection = m_pinnedSelection; - pinnedSelection.merge(newSelection, QItemSelectionModel::Toggle); - m_selectionModel->select(pinnedSelection, QItemSelectionModel::ClearAndSelect); - } else { - m_selectionModel->select(newSelection, QItemSelectionModel::ClearAndSelect); - } -} - -void FolderModel::clearSelection() -{ - if (m_selectionModel->hasSelection()) { - m_selectionModel->clear(); - } -} - -void FolderModel::pinSelection() -{ - m_pinnedSelection = m_selectionModel->selection(); -} - -void FolderModel::unpinSelection() -{ - m_pinnedSelection = QItemSelection(); -} - -void FolderModel::addItemDragImage(int row, int x, int y, int width, int height, const QVariant &image) -{ - if (row < 0) { - return; - } - - delete m_dragImages.take(row); - - DragImage *dragImage = new DragImage(); - dragImage->row = row; - dragImage->rect = QRect(x, y, width, height); - dragImage->image = image.value(); - dragImage->blank = false; - - m_dragImages.insert(row, dragImage); -} - -void FolderModel::clearDragImages() -{ - qDeleteAll(m_dragImages); - m_dragImages.clear(); -} - -void FolderModel::setDragHotSpotScrollOffset(int x, int y) -{ - m_dragHotSpotScrollOffset.setX(x); - m_dragHotSpotScrollOffset.setY(y); -} - -QPoint FolderModel::dragCursorOffset(int row) -{ - DragImage *image = m_dragImages.value(row); - if (!image) { - return QPoint(0, 0); - } - - return image->cursorOffset; -} - -void FolderModel::addDragImage(QDrag *drag, int x, int y) -{ - if (!drag || m_dragImages.isEmpty()) { - return; - } - - QRegion region; - - foreach (DragImage *image, m_dragImages) { - image->blank = isBlank(image->row); - image->rect.translate(-m_dragHotSpotScrollOffset.x(), -m_dragHotSpotScrollOffset.y()); - if (!image->blank && !image->image.isNull()) { - region = region.united(image->rect); - } - } - - QRect rect = region.boundingRect(); - QPoint offset = rect.topLeft(); - rect.translate(-offset.x(), -offset.y()); - - QImage dragImage(rect.size(), QImage::Format_RGBA8888); - dragImage.fill(Qt::transparent); - - QPainter painter(&dragImage); - - QPoint pos; - - foreach (DragImage *image, m_dragImages) { - if (!image->blank && !image->image.isNull()) { - pos = image->rect.translated(-offset.x(), -offset.y()).topLeft(); - image->cursorOffset.setX(pos.x() - (x - offset.x())); - image->cursorOffset.setY(pos.y() - (y - offset.y())); - - painter.drawImage(pos, image->image); - } - - // FIXME HACK: Operate on copy. - image->rect.translate(m_dragHotSpotScrollOffset.x(), m_dragHotSpotScrollOffset.y()); - } - - drag->setPixmap(QPixmap::fromImage(dragImage)); - drag->setHotSpot(QPoint(x - offset.x(), y - offset.y())); -} - -void FolderModel::dragSelected(int x, int y) -{ - if (m_dragInProgress) { - return; - } - - m_dragInProgress = true; - emit draggingChanged(); - m_urlChangedWhileDragging = false; - - // Avoid starting a drag synchronously in a mouse handler or interferes with - // child event filtering in parent items (and thus e.g. press-and-hold hand- - // ling in a containment). - QMetaObject::invokeMethod(this, "dragSelectedInternal", Qt::QueuedConnection, Q_ARG(int, x), Q_ARG(int, y)); -} - -void FolderModel::dragSelectedInternal(int x, int y) -{ - if (!m_viewAdapter || !m_selectionModel->hasSelection()) { - m_dragInProgress = false; - emit draggingChanged(); - return; - } - - ItemViewAdapter *adapter = qobject_cast(m_viewAdapter); - QQuickItem *item = qobject_cast(adapter->adapterView()); - - QDrag *drag = new QDrag(item); - - addDragImage(drag, x, y); - - m_dragIndexes = m_selectionModel->selectedIndexes(); - - std::sort(m_dragIndexes.begin(), m_dragIndexes.end()); - - // TODO: Optimize to emit contiguous groups. - emit dataChanged(m_dragIndexes.first(), m_dragIndexes.last(), QVector() << BlankRole); - - QModelIndexList sourceDragIndexes; - sourceDragIndexes.reserve(m_dragIndexes.count()); - foreach (const QModelIndex &index, m_dragIndexes) { - sourceDragIndexes.append(mapToSource(index)); - } - - drag->setMimeData(m_dirModel->mimeData(sourceDragIndexes)); - - // Due to spring-loading (aka auto-expand), the URL might change - // while the drag is in-flight - in that case we don't want to - // unnecessarily emit dataChanged() for (possibly invalid) indices - // after it ends. - const QUrl currentUrl(m_dirModel->dirLister()->url()); - - item->grabMouse(); - drag->exec(supportedDragActions()); - - item->ungrabMouse(); - - m_dragInProgress = false; - emit draggingChanged(); - m_urlChangedWhileDragging = false; - - if (m_dirModel->dirLister()->url() == currentUrl) { - const QModelIndex first(m_dragIndexes.first()); - const QModelIndex last(m_dragIndexes.last()); - m_dragIndexes.clear(); - // TODO: Optimize to emit contiguous groups. - emit dataChanged(first, last, QVector() << BlankRole); - } -} - -static bool isDropBetweenSharedViews(const QList &urls, const QUrl &folderUrl) -{ - for (const auto &url : urls) { - if (folderUrl.adjusted(QUrl::StripTrailingSlash) != url.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash)) { - return false; - } - } - return true; -} - -void FolderModel::drop(QQuickItem *target, QObject *dropEvent, int row, bool showMenuManually) -{ - QMimeData *mimeData = qobject_cast(dropEvent->property("mimeData").value()); - - if (!mimeData) { - return; - } - - QModelIndex idx; - KFileItem item; - - if (row > -1 && row < rowCount()) { - idx = index(row, 0); - item = itemForIndex(idx); - } - - QUrl dropTargetUrl; - - // So we get to run mostLocalUrl() over the current URL. - if (item.isNull()) { - item = rootItem(); - } - - if (item.isNull()) { - dropTargetUrl = m_dirModel->dirLister()->url(); - } else if (m_parseDesktopFiles && item.isDesktopFile()) { - const KDesktopFile file(item.targetUrl().path()); - - if (file.hasLinkType()) { - dropTargetUrl = QUrl(file.readUrl()); - } else { - dropTargetUrl = item.mostLocalUrl(); - } - } else { - dropTargetUrl = item.mostLocalUrl(); - } - - auto dropTargetFolderUrl = dropTargetUrl; - if (dropTargetFolderUrl.fileName() == QLatin1Char('.')) { - // the target URL for desktop:/ is e.g. 'file://home/user/Desktop/.' - dropTargetFolderUrl = dropTargetFolderUrl.adjusted(QUrl::RemoveFilename); - } - - // use dropTargetUrl to resolve desktop:/ to the actual file location which is also used by the mime data - /* QMimeData operates on local URLs, but the dir lister and thus screen mapper and positioner may - * use a fancy scheme like desktop:/ instead. Ensure we always use the latter to properly map URLs, - * i.e. go from file:///home/user/Desktop/file to desktop:/file - */ -// auto mappableUrl = [this, dropTargetFolderUrl](const QUrl &url) -> QUrl { -// if (dropTargetFolderUrl != m_dirModel->dirLister()->url()) { -// QString mappedUrl = url.toString(); -// const auto local = dropTargetFolderUrl.toString(); -// const auto internal = m_dirModel->dirLister()->url().toString(); -// if (mappedUrl.startsWith(local)) { -// mappedUrl.replace(0, local.size(), internal); -// } -// return ScreenMapper::stringToUrl(mappedUrl); -// } -// return url; -// }; - - const int x = dropEvent->property("x").toInt(); - const int y = dropEvent->property("y").toInt(); - const QPoint dropPos = {x, y}; - - if (m_dragInProgress && row == -1 && !m_urlChangedWhileDragging) { - if (m_locked || mimeData->urls().isEmpty()) { - return; - } - - setSortMode(-1); - - for (const auto &url : mimeData->urls()) { - m_dropTargetPositions.insert(url.fileName(), dropPos); -// m_screenMapper->addMapping(mappableUrl(url), m_screen, ScreenMapper::DelayedSignal); -// m_screenMapper->removeItemFromDisabledScreen(mappableUrl(url)); - } - emit move(x, y, mimeData->urls()); - - return; - } - - if (mimeData->hasFormat(QStringLiteral("application/x-kde-ark-dndextract-service")) - && mimeData->hasFormat(QStringLiteral("application/x-kde-ark-dndextract-path"))) { - const QString remoteDBusClient = mimeData->data(QStringLiteral("application/x-kde-ark-dndextract-service")); - const QString remoteDBusPath = mimeData->data(QStringLiteral("application/x-kde-ark-dndextract-path")); - - QDBusMessage message = QDBusMessage::createMethodCall(remoteDBusClient, - remoteDBusPath, - QStringLiteral("org.kde.ark.DndExtract"), - QStringLiteral("extractSelectedFilesTo")); - message.setArguments({dropTargetUrl.toDisplayString(QUrl::PreferLocalFile)}); - - QDBusConnection::sessionBus().call(message, QDBus::NoBlock); - - return; - } - - if (idx.isValid() && !(flags(idx) & Qt::ItemIsDropEnabled)) { - return; - } - - // Catch drops from a Task Manager and convert to usable URL. - if (!mimeData->hasUrls() && mimeData->hasFormat(QStringLiteral("text/x-orgkdeplasmataskmanager_taskurl"))) { - QList urls = {QUrl(QString::fromUtf8(mimeData->data(QStringLiteral("text/x-orgkdeplasmataskmanager_taskurl"))))}; - mimeData->setUrls(urls); - } - - if (m_usedByContainment /*&& !m_screenMapper->sharedDesktops()*/) { - if (isDropBetweenSharedViews(mimeData->urls(), dropTargetFolderUrl)) { - setSortMode(-1); - const QList urls = mimeData->urls(); - for (const auto &url : urls) { - m_dropTargetPositions.insert(url.fileName(), dropPos); -// m_screenMapper->addMapping(mappableUrl(url), m_screen, ScreenMapper::DelayedSignal); -// m_screenMapper->removeItemFromDisabledScreen(mappableUrl(url)); - } - m_dropTargetPositionsCleanup->start(); - return; - } - } - - Qt::DropAction proposedAction((Qt::DropAction)dropEvent->property("proposedAction").toInt()); - Qt::DropActions possibleActions(dropEvent->property("possibleActions").toInt()); - Qt::MouseButtons buttons(dropEvent->property("buttons").toInt()); - Qt::KeyboardModifiers modifiers(dropEvent->property("modifiers").toInt()); - - auto pos = target->mapToScene(dropPos).toPoint(); - pos = target->window()->mapToGlobal(pos); - QDropEvent ev(pos, possibleActions, mimeData, buttons, modifiers); - ev.setDropAction(proposedAction); - - KIO::DropJobFlag flag = showMenuManually ? KIO::ShowMenuManually : KIO::DropJobDefaultFlags; - KIO::DropJob *dropJob = KIO::drop(&ev, dropTargetUrl, flag); - dropJob->uiDelegate()->setAutoErrorHandlingEnabled(true); - - // The QMimeData we extract from the DropArea's drop event is deleted as soon as this method - // ends but we need to keep a copy for when popupMenuAboutToShow fires. - QMimeData *mimeCopy = new QMimeData(); - const QStringList formats = mimeData->formats(); - for (const QString &format : formats) { - mimeCopy->setData(format, mimeData->data(format)); - } - - connect(dropJob, &KIO::DropJob::popupMenuAboutToShow, this, [this, mimeCopy, x, y, dropJob](const KFileItemListProperties &) { - emit popupMenuAboutToShow(dropJob, mimeCopy, x, y); - mimeCopy->deleteLater(); - }); - - /* - * Position files that come from a drag'n'drop event at the drop event - * target position. To do so, we first listen to copy job to figure out - * the target URL. Then we store the position of this drop event in the - * hash and eventually trigger a move request when we get notified about - * the new file event from the source model. - */ - connect(dropJob, &KIO::DropJob::copyJobStarted, this, [this, dropPos, dropTargetUrl](KIO::CopyJob *copyJob) { - auto map = [this, dropPos, dropTargetUrl](const QUrl &targetUrl) { - m_dropTargetPositions.insert(targetUrl.fileName(), dropPos); - m_dropTargetPositionsCleanup->start(); - - if (m_usedByContainment /*&& !m_screenMapper->sharedDesktops()*/) { - // assign a screen for the item before the copy is actually done, so - // filterAcceptsRow doesn't assign the default screen to it - QUrl url = resolvedUrl(); - // if the folderview's folder is a standard path, just use the targetUrl for mapping - if (targetUrl.toString().startsWith(url.toString())) { -// m_screenMapper->addMapping(targetUrl, m_screen, ScreenMapper::DelayedSignal); - } else if (targetUrl.toString().startsWith(dropTargetUrl.toString())) { - // if the folderview's folder is a special path, like desktop:// , we need to convert - // the targetUrl file:// path to a desktop:/ path for mapping - auto destPath = dropTargetUrl.path(); - auto filePath = targetUrl.path(); - if (filePath.startsWith(destPath)) { - url.setPath(filePath.remove(0, destPath.length())); -// m_screenMapper->addMapping(url, m_screen, ScreenMapper::DelayedSignal); - } - } - } - }; - // remember drop target position for target URL and forget about the source URL - connect(copyJob, &KIO::CopyJob::copyingDone, this, [map](KIO::Job *, const QUrl &, const QUrl &targetUrl, const QDateTime &, bool, bool) { - map(targetUrl); - }); - connect(copyJob, &KIO::CopyJob::copyingLinkDone, this, [map](KIO::Job *, const QUrl &, const QString &, const QUrl &targetUrl) { - map(targetUrl); - }); - }); -} - -void FolderModel::dropCwd(QObject *dropEvent) -{ - QMimeData *mimeData = qobject_cast(dropEvent->property("mimeData").value()); - - if (!mimeData) { - return; - } - - if (mimeData->hasFormat(QStringLiteral("application/x-kde-ark-dndextract-service")) - && mimeData->hasFormat(QStringLiteral("application/x-kde-ark-dndextract-path"))) { - const QString remoteDBusClient = mimeData->data(QStringLiteral("application/x-kde-ark-dndextract-service")); - const QString remoteDBusPath = mimeData->data(QStringLiteral("application/x-kde-ark-dndextract-path")); - - QDBusMessage message = QDBusMessage::createMethodCall(remoteDBusClient, - remoteDBusPath, - QStringLiteral("org.kde.ark.DndExtract"), - QStringLiteral("extractSelectedFilesTo")); - message.setArguments(QVariantList() << m_dirModel->dirLister()->url().adjusted(QUrl::PreferLocalFile).toString()); - - QDBusConnection::sessionBus().call(message, QDBus::NoBlock); - } else { - Qt::DropAction proposedAction((Qt::DropAction)dropEvent->property("proposedAction").toInt()); - Qt::DropActions possibleActions(dropEvent->property("possibleActions").toInt()); - Qt::MouseButtons buttons(dropEvent->property("buttons").toInt()); - Qt::KeyboardModifiers modifiers(dropEvent->property("modifiers").toInt()); - - QDropEvent ev(QPoint(), possibleActions, mimeData, buttons, modifiers); - ev.setDropAction(proposedAction); - - KIO::DropJob *dropJob = KIO::drop(&ev, m_dirModel->dirLister()->url().adjusted(QUrl::PreferLocalFile)); - dropJob->uiDelegate()->setAutoErrorHandlingEnabled(true); - } -} - -void FolderModel::selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) -{ - QModelIndexList indices = selected.indexes(); - indices.append(deselected.indexes()); - - QVector roles; - roles.append(SelectedRole); - - foreach (const QModelIndex &index, indices) { - emit dataChanged(index, index, roles); - } - - if (!m_selectionModel->hasSelection()) { - clearDragImages(); - } else { - foreach (const QModelIndex &idx, deselected.indexes()) { - delete m_dragImages.take(idx.row()); - } - } - - updateActions(); -} - -bool FolderModel::isBlank(int row) const -{ - if (row < 0) { - return true; - } - - return data(index(row, 0), BlankRole).toBool(); -} - -QVariant FolderModel::data(const QModelIndex &index, int role) const -{ - if (!index.isValid()) { - return QVariant(); - } - - if (role == BlankRole) { - return m_dragIndexes.contains(index); - } else if (role == OverlaysRole) { - const KFileItem item = itemForIndex(index); - return item.overlays(); - } else if (role == SelectedRole) { - return m_selectionModel->isSelected(index); - } else if (role == IsDirRole) { - return isDir(mapToSource(index), m_dirModel); - } else if (role == IsLinkRole) { - const KFileItem item = itemForIndex(index); - return item.isLink(); - } else if (role == IsHiddenRole) { - const KFileItem item = itemForIndex(index); - return item.isHidden(); - } else if (role == UrlRole) { - return itemForIndex(index).url(); - } else if (role == LinkDestinationUrl) { - const KFileItem item = itemForIndex(index); - - if (m_parseDesktopFiles && item.isDesktopFile()) { - const KDesktopFile file(item.targetUrl().path()); - - if (file.hasLinkType()) { - return file.readUrl(); - } - } - - return item.targetUrl(); - } else if (role == SizeRole) { - bool isDir = data(index, IsDirRole).toBool(); - - if (!isDir) { - return m_dirModel->data(mapToSource(QSortFilterProxyModel::index(index.row(), 1)), Qt::DisplayRole); - } - } else if (role == TypeRole) { - return m_dirModel->data(mapToSource(QSortFilterProxyModel::index(index.row(), 6)), Qt::DisplayRole); - } else if (role == FileNameRole) { - return itemForIndex(index).url().fileName(); - } - - return QSortFilterProxyModel::data(index, role); -} - -int FolderModel::indexForUrl(const QUrl &url) const -{ - return mapFromSource(m_dirModel->indexForUrl(url)).row(); -} - -KFileItem FolderModel::itemForIndex(const QModelIndex &index) const -{ - return m_dirModel->itemForIndex(mapToSource(index)); -} - -bool FolderModel::isDir(const QModelIndex &index, const KDirModel *dirModel) const -{ - KFileItem item = dirModel->itemForIndex(index); - if (item.isDir()) { - return true; - } - - auto it = m_isDirCache.constFind(item.url()); - if (it != m_isDirCache.constEnd()) { - return *it; - } - - if (m_parseDesktopFiles && item.isDesktopFile()) { - // Check if the desktop file is a link to a directory - KDesktopFile file(item.targetUrl().path()); - - if (!file.hasLinkType()) { - return false; - } - - const QUrl url(file.readUrl()); - - // Check if we already have a running StatJob for this URL. - if (m_isDirJobs.contains(item.url())) { - return false; - } - - // Assume the root folder of a protocol is always a folder. - // This avoids spinning up e.g. trash KIO slave just to check whether trash:/ is a folder. - if (url.path() == QLatin1String("/")) { - m_isDirCache.insert(item.url(), true); - return true; - } - - if (KProtocolInfo::protocolClass(url.scheme()) != QLatin1String(":local")) { - return false; - } - - KIO::StatJob *job = KIO::stat(url, KIO::HideProgressInfo); - job->setProperty("org.kde.plasma.folder_url", item.url()); - job->setSide(KIO::StatJob::SourceSide); - job->setDetails(0); - connect(job, &KJob::result, this, &FolderModel::statResult); - m_isDirJobs.insert(item.url(), job); - } - - return false; -} - -void FolderModel::statResult(KJob *job) -{ - KIO::StatJob *statJob = static_cast(job); - - const QUrl &url = statJob->property("org.kde.plasma.folder_url").toUrl(); - const QModelIndex &idx = index(indexForUrl(url), 0); - - if (idx.isValid() && statJob->error() == KJob::NoError) { - m_isDirCache[url] = statJob->statResult().isDir(); - - emit dataChanged(idx, idx, QVector() << IsDirRole); - } - - m_isDirJobs.remove(url); -} - -void FolderModel::evictFromIsDirCache(const KFileItemList &items) -{ - foreach (const KFileItem &item, items) { -// m_screenMapper->removeFromMap(item.url()); - m_isDirCache.remove(item.url()); - } -} - -bool FolderModel::lessThan(const QModelIndex &left, const QModelIndex &right) const -{ - const KDirModel *dirModel = static_cast(sourceModel()); - - if (m_sortDirsFirst || left.column() == KDirModel::Size) { - bool leftIsDir = isDir(left, dirModel); - bool rightIsDir = isDir(right, dirModel); - - if (leftIsDir && !rightIsDir) { - return (sortOrder() == Qt::AscendingOrder); - } - - if (!leftIsDir && rightIsDir) { - return (sortOrder() == Qt::DescendingOrder); - } - } - - const KFileItem leftItem = dirModel->data(left, KDirModel::FileItemRole).value(); - const KFileItem rightItem = dirModel->data(right, KDirModel::FileItemRole).value(); - const int column = left.column(); - int result = 0; - - switch (column) { - case KDirModel::Size: { - if (isDir(left, dirModel) && isDir(right, dirModel)) { - const int leftChildCount = dirModel->data(left, KDirModel::ChildCountRole).toInt(); - const int rightChildCount = dirModel->data(right, KDirModel::ChildCountRole).toInt(); - if (leftChildCount < rightChildCount) - result = -1; - else if (leftChildCount > rightChildCount) - result = +1; - } else { - const KIO::filesize_t leftSize = leftItem.size(); - const KIO::filesize_t rightSize = rightItem.size(); - if (leftSize < rightSize) - result = -1; - else if (leftSize > rightSize) - result = +1; - } - - break; - } - case KDirModel::ModifiedTime: { - const long long leftTime = leftItem.entry().numberValue(KIO::UDSEntry::UDS_MODIFICATION_TIME, -1); - const long long rightTime = rightItem.entry().numberValue(KIO::UDSEntry::UDS_MODIFICATION_TIME, -1); - if (leftTime < rightTime) - result = -1; - else if (leftTime > rightTime) - result = +1; - - break; - } - case KDirModel::Type: - result = QString::compare(dirModel->data(left, Qt::DisplayRole).toString(), dirModel->data(right, Qt::DisplayRole).toString()); - break; - - default: - break; - } - - if (result != 0) - return result < 0; - - QCollator collator; - - result = collator.compare(leftItem.text(), rightItem.text()); - - if (result != 0) - return result < 0; - - result = collator.compare(leftItem.name(), rightItem.name()); - - if (result != 0) - return result < 0; - - return QString::compare(leftItem.url().url(), rightItem.url().url(), Qt::CaseSensitive); -} - -Qt::DropActions FolderModel::supportedDragActions() const -{ - return Qt::CopyAction | Qt::MoveAction | Qt::LinkAction; -} - -Qt::DropActions FolderModel::supportedDropActions() const -{ - return Qt::CopyAction | Qt::MoveAction | Qt::LinkAction; -} - -inline bool FolderModel::matchMimeType(const KFileItem &item) const -{ - if (m_mimeSet.isEmpty()) { - return false; - } - - if (m_mimeSet.contains(QLatin1String("all/all")) || m_mimeSet.contains(QLatin1String("all/allfiles"))) { - return true; - } - - const QString mimeType = item.determineMimeType().name(); - return m_mimeSet.contains(mimeType); -} - -inline bool FolderModel::matchPattern(const KFileItem &item) const -{ - if (m_filterPatternMatchAll) { - return true; - } - - const QString name = item.name(); - QListIterator i(m_regExps); - while (i.hasNext()) { - if (i.next().exactMatch(name)) { - return true; - } - } - - return false; -} - -bool FolderModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const -{ - const KDirModel *dirModel = static_cast(sourceModel()); - const KFileItem item = dirModel->itemForIndex(dirModel->index(sourceRow, KDirModel::Name, sourceParent)); - -// if (m_usedByContainment && !m_screenMapper->sharedDesktops()) { -// const QUrl url = item.url(); -// const int screen = m_screenMapper->screenForItem(url); -// // don't do anything if the folderview is not associated with a screen -// if (m_screenUsed && screen == -1) { -// // The item is not associated with a screen, probably because this is the first -// // time we see it or the folderview was previously used as a regular applet. -// // Associated with this folderview if the view is on the first available screen -// if (m_screen == m_screenMapper->firstAvailableScreen(resolvedUrl())) { -// m_screenMapper->addMapping(url, m_screen, ScreenMapper::DelayedSignal); -// } else { -// return false; -// } -// } else if (m_screen != screen) { -// // the item belongs to a different screen, filter it out -// return false; -// } -// } - - if (m_filterMode == NoFilter) { - return true; - } - - if (m_filterMode == FilterShowMatches) { - return (matchPattern(item) && matchMimeType(item)); - } else { - return !(matchPattern(item) && matchMimeType(item)); - } -} - -void FolderModel::createActions() -{ - KIO::FileUndoManager *manager = KIO::FileUndoManager::self(); - - QAction *cut = new QAction(tr("Cut"), this); - connect(cut, &QAction::triggered, this, &FolderModel::cut); - - QAction *copy = new QAction(tr("Copy"), this); - connect(copy, &QAction::triggered, this, &FolderModel::copy); - - QAction *undo = new QAction(tr("Undo"), this); - undo->setEnabled(manager->undoAvailable()); - connect(undo, &QAction::triggered, manager, &KIO::FileUndoManager::undo); - - connect(manager, SIGNAL(undoAvailable(bool)), undo, SLOT(setEnabled(bool))); - // revenmartin: not needed for now. - // connect(manager, &KIO::FileUndoManager::undoTextChanged, this, &FolderModel::undoTextChanged); - - QAction *paste = new QAction(tr("Paste"), this); - connect(paste, &QAction::triggered, this, &FolderModel::paste); - - // QAction *refresh = new QAction(tr("&Refresh View"), this); - // refresh->setShortcut(QKeySequence(QKeySequence::Refresh)); - // connect(refresh, &QAction::triggered, this, &FolderModel::refresh); - - QAction *newFolderAction = new QAction(tr("New Folder")); - connect(newFolderAction, &QAction::triggered, this, &FolderModel::createFolder); - - QAction *newDocAction = new QAction(tr("New Documents")); - connect(newDocAction, &QAction::triggered, this, [=] { - m_newMenu->setPopupFiles(QList() << m_dirModel->dirLister()->url()); - m_newMenu->createFile(); - }); - - QAction *rename = new QAction(tr("Rename"), this); - connect(rename, &QAction::triggered, this, &FolderModel::requestRename); - - QAction *trash = new QAction(tr("Move To Trash"), this); - connect(trash, &QAction::triggered, this, &FolderModel::moveSelectedToTrash); - - QAction *emptyTrash = new QAction(tr("&Empty Trash"), this); - connect(emptyTrash, &QAction::triggered, this, &FolderModel::emptyTrashBin); - - QAction *restoreFromTrash = new QAction(tr("Restore from trash"), this); - connect(restoreFromTrash, &QAction::triggered, this, &FolderModel::restoreSelectedFromTrash); - - QAction *del = new QAction(tr("Delete"), this); - connect(del, &QAction::triggered, this, &FolderModel::deleteSelected); - - QAction *actOpen = new QAction(tr("&Open"), this); - connect(actOpen, &QAction::triggered, this, &FolderModel::openSelected); - - QAction *propertiesAction = new QAction(tr("Properties"), this); - QObject::connect(propertiesAction, &QAction::triggered, this, &FolderModel::openPropertiesDialog); - - m_actionCollection.addAction(QStringLiteral("open"), actOpen); - m_actionCollection.addAction(QStringLiteral("cut"), cut); - m_actionCollection.addAction(QStringLiteral("undo"), undo); - m_actionCollection.addAction(QStringLiteral("copy"), copy); - m_actionCollection.addAction(QStringLiteral("paste"), paste); - // m_actionCollection.addAction(QStringLiteral("refresh"), refresh); - m_actionCollection.addAction(QStringLiteral("rename"), rename); - m_actionCollection.addAction(QStringLiteral("trash"), trash); - m_actionCollection.addAction(QStringLiteral("del"), del); - m_actionCollection.addAction(QStringLiteral("restoreFromTrash"), restoreFromTrash); - m_actionCollection.addAction(QStringLiteral("emptyTrash"), emptyTrash); - m_actionCollection.addAction(QStringLiteral("new_folder"), newFolderAction); - m_actionCollection.addAction(QStringLiteral("new_documents"), newDocAction); - m_actionCollection.addAction(QStringLiteral("properties"), propertiesAction); - - m_newMenu = new KNewFileMenu(&m_actionCollection, QStringLiteral("newMenu"), this); - m_newMenu->setModal(false); - connect(m_newMenu, &KNewFileMenu::directoryCreated, this, &FolderModel::newFileMenuItemCreated); - connect(m_newMenu, &KNewFileMenu::fileCreated, this, &FolderModel::newFileMenuItemCreated); - - m_copyToMenu = new KFileCopyToMenu(nullptr); -} - -QAction *FolderModel::action(const QString &name) const -{ - return m_actionCollection.action(name); -} - -QObject *FolderModel::newMenu() const -{ - return m_newMenu->menu(); -} - -void FolderModel::updateActions() -{ - const QModelIndexList indexes = m_selectionModel->selectedIndexes(); - - KFileItemList items; - QList urls; - bool hasRemoteFiles = false; - bool isTrashLink = false; - const bool isTrash = (resolvedUrl().scheme() == QLatin1String("trash")); - - if (indexes.isEmpty()) { - items << rootItem(); - } else { - items.reserve(indexes.count()); - urls.reserve(indexes.count()); - for (const QModelIndex &index : indexes) { - KFileItem item = itemForIndex(index); - if (!item.isNull()) { - hasRemoteFiles |= item.localPath().isEmpty(); - items.append(item); - urls.append(item.url()); - } - } - } - - KFileItemListProperties itemProperties(items); - // Check if we're showing the menu for the trash link - if (items.count() == 1 && items.at(0).isDesktopFile()) { - KDesktopFile file(items.at(0).localPath()); - if (file.hasLinkType() && file.readUrl() == QLatin1String("trash:/")) { - isTrashLink = true; - } - } - - if (m_newMenu) { - m_newMenu->checkUpToDate(); - m_newMenu->setPopupFiles(QList() << m_dirModel->dirLister()->url()); - // we need to set here as well, when the menu is shown via AppletInterface::eventFilter - m_menuPosition = QCursor::pos(); - - if (QAction *newMenuAction = m_actionCollection.action(QStringLiteral("newMenu"))) { - newMenuAction->setEnabled(itemProperties.supportsWriting()); - newMenuAction->setVisible(!isTrash); - } - } - - if (QAction *emptyTrash = m_actionCollection.action(QStringLiteral("emptyTrash"))) { - if (isTrash || isTrashLink) { - emptyTrash->setVisible(true); - emptyTrash->setEnabled(!isTrashEmpty()); - } else { - emptyTrash->setVisible(false); - } - } - - if (QAction *newFolder = m_actionCollection.action(QStringLiteral("new_folder"))) { - newFolder->setVisible(!isTrash); - } - - if (QAction *newDocuments = m_actionCollection.action(QStringLiteral("new_documents"))) { - newDocuments->setVisible(!isTrash); - } - - if (QAction *restoreFromTrash = m_actionCollection.action(QStringLiteral("restoreFromTrash"))) { - restoreFromTrash->setVisible(isTrash); - } - - if (QAction *moveToTrash = m_actionCollection.action(QStringLiteral("trash"))) { - moveToTrash->setVisible(!hasRemoteFiles && itemProperties.supportsMoving()); - } - - if (QAction *del = m_actionCollection.action(QStringLiteral("del"))) { - del->setVisible(itemProperties.supportsDeleting()); - } - - if (QAction *cut = m_actionCollection.action(QStringLiteral("cut"))) { - cut->setEnabled(itemProperties.supportsDeleting()); - cut->setVisible(!isTrash); - } - - if (QAction *paste = m_actionCollection.action(QStringLiteral("paste"))) { - bool enable = false; - - QList urls = KUrlMimeData::urlsFromMimeData(QApplication::clipboard()->mimeData()); - - if (!urls.isEmpty() && rootItem().isWritable()) { - enable = rootItem().isWritable(); - } - - paste->setEnabled(enable); - - } - - if (QAction *rename = m_actionCollection.action(QStringLiteral("rename"))) { - rename->setEnabled(itemProperties.supportsMoving()); - rename->setVisible(!isTrash); - } -} - -void FolderModel::openContextMenu(QQuickItem *visualParent, Qt::KeyboardModifiers modifiers) -{ - if (m_usedByContainment && !KAuthorized::authorize(QStringLiteral("action/kdesktop_rmb"))) { - return; - } - - updateActions(); - - const QModelIndexList indexes = m_selectionModel->selectedIndexes(); - QMenu *menu = new QMenu(); - - if (!m_fileItemActions) { - m_fileItemActions = new KFileItemActions(this); - m_fileItemActions->setParentWidget(QApplication::desktop()); - } - - if (indexes.isEmpty()) { - QAction *selectAll = new QAction(tr("Select All"), this); - connect(selectAll, &QAction::triggered, this, &FolderModel::selectAll); - - if (KFileItem(url()).isWritable()) - menu->addAction(m_actionCollection.action(QStringLiteral("new_folder"))); - - if (KFileItem(url()).isWritable()) - menu->addAction(m_actionCollection.action(QStringLiteral("new_documents"))); - - // menu->addAction(m_actionCollection.action(QStringLiteral("newMenu"))); - menu->addSeparator(); - menu->addAction(m_actionCollection.action(QStringLiteral("paste"))); - menu->addAction(m_actionCollection.action(QStringLiteral("undo"))); - menu->addAction(selectAll); - menu->addAction(m_actionCollection.action(QStringLiteral("emptyTrash"))); - - if (m_isDesktopView) { - QAction *setWallpaper = new QAction(tr("Change Wallpaper"), this); - connect(setWallpaper, &QAction::triggered, this, [=] { openSettings("wallpaper"); }); - menu->addSeparator(); - menu->addAction(setWallpaper); - } - - menu->addSeparator(); - menu->addAction(m_actionCollection.action(QStringLiteral("properties"))); - } else { - KFileItemList items; - QList urls; - - items.reserve(indexes.count()); - urls.reserve(indexes.count()); - for (const QModelIndex &index : indexes) { - KFileItem item = itemForIndex(index); - if (!item.isNull()) { - items.append(item); - urls.append(item.url()); - } - } - - KFileItemListProperties itemProperties(items); - - // Start adding the actions: - // "Open" and "Open with" actions - m_fileItemActions->setItemListProperties(itemProperties); - m_fileItemActions->addOpenWithActionsTo(menu); - - menu->addSeparator(); - menu->addAction(m_actionCollection.action(QStringLiteral("cut"))); - menu->addAction(m_actionCollection.action(QStringLiteral("copy"))); - - menu->addAction(m_actionCollection.action(QStringLiteral("rename"))); - menu->addSeparator(); - menu->addAction(m_actionCollection.action(QStringLiteral("restoreFromTrash"))); - - menu->addAction(m_actionCollection.action(QStringLiteral("emptyTrash"))); - - QAction *trashAction = m_actionCollection.action(QStringLiteral("trash")); - menu->addAction(trashAction); - trashAction->setVisible(!modifiers.testFlag(Qt::ShiftModifier)); - - QAction *deleteAction = m_actionCollection.action(QStringLiteral("del")); - menu->addAction(deleteAction); - - deleteAction->setVisible(!trashAction->isVisible()); - - menu->addSeparator(); - - // Set as Wallpaper - if (indexes.count() == 1 && items.first().mimetype().startsWith("image/")) { - QAction *setAswallpaper = new QAction(tr("Set as Wallpaper"), this); - connect(setAswallpaper, &QAction::triggered, this, &FolderModel::setAsWallpaper); - menu->addAction(setAswallpaper); - } - - menu->addSeparator(); - menu->addAction(m_actionCollection.action(QStringLiteral("properties"))); - } - - if (visualParent) { - m_menuPosition = visualParent->mapToGlobal(QPointF(0, visualParent->height())).toPoint(); - } else { - m_menuPosition = QCursor::pos(); - } - - // Used to monitor Shift modifier usage while the menu is open, to - // swap the Trash and Delete actions. - menu->installEventFilter(this); - - menu->setAttribute(Qt::WA_TranslucentBackground); - menu->winId(); // force surface creation before ensurePolish call in menu::Popup which happens before show - menu->popup(m_menuPosition); - connect(menu, &QMenu::aboutToHide, [menu]() { - menu->deleteLater(); - }); -} - -void FolderModel::openPropertiesDialog() -{ - const QModelIndexList indexes = m_selectionModel->selectedIndexes(); - - if (indexes.isEmpty()) { - PropertiesDialog::showDialog(QUrl::fromLocalFile(url())); - return; - } - - KFileItemList items; - items.reserve(indexes.count()); - for (const QModelIndex &index : indexes) { - KFileItem item = itemForIndex(index); - if (!item.isNull()) { - items.append(item); - } - } - - PropertiesDialog::showDialog(items); -} - -QString FolderModel::desktopPath() const -{ - return QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); -} - -QString FolderModel::homePath() const -{ - return QStandardPaths::writableLocation(QStandardPaths::HomeLocation); -} - -void FolderModel::linkHere(const QUrl &sourceUrl) -{ - KIO::CopyJob *job = KIO::link(sourceUrl, m_dirModel->dirLister()->url(), KIO::HideProgressInfo); - KIO::FileUndoManager::self()->recordCopyJob(job); -} - -QList FolderModel::selectedUrls() const -{ - const auto indexes = m_selectionModel->selectedIndexes(); - - QList urls; - urls.reserve(indexes.count()); - - for (const QModelIndex &index : indexes) { - urls.append(itemForIndex(index).url()); - } - - return urls; -} - -void FolderModel::copy() -{ - if (!m_selectionModel->hasSelection()) { - return; - } - - if (QAction *action = m_actionCollection.action(QStringLiteral("copy"))) { - if (!action->isEnabled()) { - return; - } - } - - QMimeData *mimeData = QSortFilterProxyModel::mimeData(m_selectionModel->selectedIndexes()); - QApplication::clipboard()->setMimeData(mimeData); -} - -void FolderModel::cut() -{ - if (!m_selectionModel->hasSelection()) { - return; - } - - if (QAction *action = m_actionCollection.action(QStringLiteral("cut"))) { - if (!action->isEnabled()) { - return; - } - } - - QMimeData *mimeData = QSortFilterProxyModel::mimeData(m_selectionModel->selectedIndexes()); - KIO::setClipboardDataCut(mimeData, true); - QApplication::clipboard()->setMimeData(mimeData); -} - -void FolderModel::paste() -{ - if (QAction *action = m_actionCollection.action(QStringLiteral("paste"))) { - if (!action->isEnabled()) { - return; - } - } - - KIO::paste(QApplication::clipboard()->mimeData(), m_dirModel->dirLister()->url()); -} - -void FolderModel::pasteTo() -{ - const QList urls = selectedUrls(); - Q_ASSERT(urls.count() == 1); - KIO::paste(QApplication::clipboard()->mimeData(), urls.first()); -} - -void FolderModel::refresh() -{ - m_errorString.clear(); - emit errorStringChanged(); - - m_dirModel->dirLister()->updateDirectory(m_dirModel->dirLister()->url()); -} - -void FolderModel::moveSelectedToTrash() -{ - if (!m_selectionModel->hasSelection()) { - return; - } - - if (QAction *action = m_actionCollection.action(QStringLiteral("trash"))) { - if (!action->isEnabled()) { - return; - } - } - - const QList urls = selectedUrls(); - KIO::JobUiDelegate uiDelegate; - - if (uiDelegate.askDeleteConfirmation(urls, KIO::JobUiDelegate::Trash, KIO::JobUiDelegate::DefaultConfirmation)) { - KIO::Job *job = KIO::trash(urls); - job->uiDelegate()->setAutoErrorHandlingEnabled(true); - KIO::FileUndoManager::self()->recordJob(KIO::FileUndoManager::Trash, urls, QUrl(QStringLiteral("trash:/")), job); - } -} - -void FolderModel::deleteSelected() -{ - if (!m_selectionModel->hasSelection()) { - return; - } - - if (QAction *action = m_actionCollection.action(QStringLiteral("del"))) { - if (!action->isEnabled()) { - return; - } - } - - const QList urls = selectedUrls(); - KIO::JobUiDelegate uiDelegate; - - if (uiDelegate.askDeleteConfirmation(urls, KIO::JobUiDelegate::Delete, KIO::JobUiDelegate::DefaultConfirmation)) { - KIO::Job *job = KIO::del(urls); - job->uiDelegate()->setAutoErrorHandlingEnabled(true); - } -} - -void FolderModel::openSelected() -{ - if (!m_selectionModel->hasSelection()) { - return; - } - - const QList urls = selectedUrls(); - for (const QUrl &url : urls) { - (void)new KRun(url, nullptr); - } -} - -void FolderModel::undo() -{ - if (QAction *action = m_actionCollection.action(QStringLiteral("undo"))) { - // trigger() doesn't check enabled and would crash if invoked nonetheless. - if (action->isEnabled()) { - action->trigger(); - } - } -} - -void FolderModel::emptyTrashBin() -{ - KIO::JobUiDelegate uiDelegate; - uiDelegate.setWindow(QApplication::desktop()); - - if (uiDelegate.askDeleteConfirmation(QList(), KIO::JobUiDelegate::EmptyTrash, KIO::JobUiDelegate::DefaultConfirmation)) { - KIO::Job *job = KIO::emptyTrash(); - job->uiDelegate()->setAutoErrorHandlingEnabled(true); - } -} - -void FolderModel::restoreSelectedFromTrash() -{ - if (!m_selectionModel->hasSelection()) { - return; - } - - const auto &urls = selectedUrls(); - - KIO::RestoreJob *job = KIO::restoreFromTrash(urls); - job->uiDelegate()->setAutoErrorHandlingEnabled(true); -} - -bool FolderModel::isTrashEmpty() -{ - KConfig trashConfig(QStringLiteral("trashrc"), KConfig::SimpleConfig); - return trashConfig.group("Status").readEntry("Empty", true); -} - -void FolderModel::undoTextChanged(const QString &text) -{ - if (QAction *action = m_actionCollection.action(QStringLiteral("undo"))) { - action->setText(text); - } -} - -void FolderModel::createFolder() -{ - m_newMenu->setPopupFiles(QList() << m_dirModel->dirLister()->url()); - m_newMenu->createDirectory(); -} - -void FolderModel::setAsWallpaper() -{ - if (!m_selectionModel) - return; - - QUrl url = selectedUrls().first(); - - if (!url.isLocalFile()) - return; - - QDBusInterface iface("org.cutefish.Settings", "/Theme", - "org.cutefish.Theme", - QDBusConnection::sessionBus(), nullptr); - if (iface.isValid()) - iface.call("setWallpaper", url.toLocalFile()); -} - -void FolderModel::openSettings(const QString &itemName) -{ - QProcess process; - process.startDetached("cutefish-settings", QStringList() << "-m" << itemName); -} - -bool FolderModel::desktopView() const -{ - return m_isDesktopView; -} - -void FolderModel::setDesktopView(bool value) -{ - if (m_isDesktopView != value) { - m_isDesktopView = value; - emit desktopViewChanged(); - } -} diff --git a/src/lib/foldermodel.h b/src/lib/foldermodel.h deleted file mode 100644 index 266c443..0000000 --- a/src/lib/foldermodel.h +++ /dev/null @@ -1,372 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2008 Fredrik Höglund * - * Copyright (C) 2011 Marco Martin * - * Copyright (C) 2014 by Eike Hein * - * * - * 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 . * - ***************************************************************************/ - -#ifndef FOLDERMODEL_H -#define FOLDERMODEL_H - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include - -class QDrag; -class QItemSelectionModel; -class QQuickItem; - -class KFileCopyToMenu; -class KActionCollection; -class KDirModel; -class KDirWatch; -class KFileItem; -class KFileItemActions; -class KJob; -class KNewFileMenu; - -namespace KIO -{ -class DropJob; -class StatJob; -} - -class ScreenMapper; - -class DirLister : public KDirLister -{ - Q_OBJECT - -public: - explicit DirLister(QObject *parent = nullptr); - ~DirLister() override; - -Q_SIGNALS: - void error(const QString &string); - -protected: - void handleError(KIO::Job *job) override; -}; - -class FolderModel : public QSortFilterProxyModel, public QQmlParserStatus -{ - Q_OBJECT - Q_INTERFACES(QQmlParserStatus) - - Q_PROPERTY(QString url READ url WRITE setUrl NOTIFY urlChanged) - Q_PROPERTY(QString iconName READ iconName NOTIFY iconNameChanged) - Q_PROPERTY(QUrl resolvedUrl READ resolvedUrl NOTIFY resolvedUrlChanged) - Q_PROPERTY(Status status READ status NOTIFY statusChanged) - Q_PROPERTY(QString errorString READ errorString NOTIFY errorStringChanged) - Q_PROPERTY(bool dragging READ dragging NOTIFY draggingChanged) - Q_PROPERTY(bool usedByContainment READ usedByContainment WRITE setUsedByContainment NOTIFY usedByContainmentChanged) - Q_PROPERTY(bool locked READ locked WRITE setLocked NOTIFY lockedChanged) - Q_PROPERTY(int sortMode READ sortMode WRITE setSortMode NOTIFY sortModeChanged) - Q_PROPERTY(bool sortDesc READ sortDesc WRITE setSortDesc NOTIFY sortDescChanged) - Q_PROPERTY(bool sortDirsFirst READ sortDirsFirst WRITE setSortDirsFirst NOTIFY sortDirsFirstChanged) - Q_PROPERTY(bool parseDesktopFiles READ parseDesktopFiles WRITE setParseDesktopFiles NOTIFY parseDesktopFilesChanged) - Q_PROPERTY(QObject *viewAdapter READ viewAdapter WRITE setViewAdapter NOTIFY viewAdapterChanged) - Q_PROPERTY(bool previews READ previews WRITE setPreviews NOTIFY previewsChanged) - Q_PROPERTY(QStringList previewPlugins READ previewPlugins WRITE setPreviewPlugins NOTIFY previewPluginsChanged) - Q_PROPERTY(int filterMode READ filterMode WRITE setFilterMode NOTIFY filterModeChanged) - Q_PROPERTY(QString filterPattern READ filterPattern WRITE setFilterPattern NOTIFY filterPatternChanged) - Q_PROPERTY(QStringList filterMimeTypes READ filterMimeTypes WRITE setFilterMimeTypes NOTIFY filterMimeTypesChanged) - Q_PROPERTY(QObject *newMenu READ newMenu CONSTANT) - Q_PROPERTY(bool desktopView READ desktopView WRITE setDesktopView NOTIFY desktopViewChanged) - -public: - enum DataRole { - BlankRole = Qt::UserRole + 1, - OverlaysRole, - SelectedRole, - IsDirRole, - IsLinkRole, - IsHiddenRole, - UrlRole, - LinkDestinationUrl, - SizeRole, - TypeRole, - FileNameRole, - }; - - enum FilterMode { - NoFilter = 0, - FilterShowMatches, - FilterHideMatches, - }; - - enum Status { - None, - Ready, - Listing, - Canceled, - }; - Q_ENUM(Status) - - explicit FolderModel(QObject *parent = nullptr); - ~FolderModel() override; - - QHash roleNames() const override; - static QHash staticRoleNames(); - - void classBegin() override; - void componentComplete() override; - - QString url() const; - void setUrl(const QString &url); - - QString iconName() const; - - QUrl resolvedUrl() const; - Q_INVOKABLE QUrl resolve(const QString &url); - - Status status() const; - - QString errorString() const; - - bool dragging() const; - - bool usedByContainment() const; - void setUsedByContainment(bool used); - - bool locked() const; - void setLocked(bool locked); - - int sortMode() const; - void setSortMode(int mode); - - bool sortDesc() const; - void setSortDesc(bool desc); - - bool sortDirsFirst() const; - void setSortDirsFirst(bool enable); - - bool parseDesktopFiles() const; - void setParseDesktopFiles(bool enable); - - QObject *viewAdapter() const; - void setViewAdapter(QObject *adapter); - - bool previews() const; - void setPreviews(bool previews); - - QStringList previewPlugins() const; - void setPreviewPlugins(const QStringList &previewPlugins); - - int filterMode() const; - void setFilterMode(int filterMode); - - QString filterPattern() const; - void setFilterPattern(const QString &pattern); - - QStringList filterMimeTypes() const; - void setFilterMimeTypes(const QStringList &mimeList); - - KFileItem rootItem() const; - - Q_INVOKABLE void up(); - Q_INVOKABLE void cd(int row); - - Q_INVOKABLE void run(int row); - Q_INVOKABLE void runSelected(); - - Q_INVOKABLE void rename(int row, const QString &name); - Q_INVOKABLE int fileExtensionBoundary(int row); - - Q_INVOKABLE bool hasSelection() const; - Q_INVOKABLE bool isSelected(int row); - Q_INVOKABLE void setSelected(int row); - Q_INVOKABLE void selectAll(); - Q_INVOKABLE void toggleSelected(int row); - Q_INVOKABLE void setRangeSelected(int anchor, int to); - Q_INVOKABLE void updateSelection(const QVariantList &rows, bool toggle); - Q_INVOKABLE void clearSelection(); - Q_INVOKABLE void pinSelection(); - Q_INVOKABLE void unpinSelection(); - - Q_INVOKABLE void addItemDragImage(int row, int x, int y, int width, int height, const QVariant &image); - Q_INVOKABLE void clearDragImages(); - Q_INVOKABLE void setDragHotSpotScrollOffset(int x, int y); // FIXME TODO: Propify. - Q_INVOKABLE QPoint dragCursorOffset(int row); - Q_INVOKABLE void dragSelected(int x, int y); - Q_INVOKABLE void drop(QQuickItem *target, QObject *dropEvent, int row, bool showMenuManually = false); - Q_INVOKABLE void dropCwd(QObject *dropEvent); - - Q_INVOKABLE bool isBlank(int row) const; - - Q_INVOKABLE QAction *action(const QString &name) const; - QObject *newMenu() const; - Q_INVOKABLE void updateActions(); - Q_INVOKABLE void openContextMenu(QQuickItem *visualParent = nullptr, Qt::KeyboardModifiers modifiers = Qt::NoModifier); - - Q_INVOKABLE void linkHere(const QUrl &sourceUrl); - - Q_INVOKABLE void openPropertiesDialog(); - - Q_INVOKABLE QString desktopPath() const; - Q_INVOKABLE QString homePath() const; - - QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; - int indexForUrl(const QUrl &url) const; - KFileItem itemForIndex(const QModelIndex &index) const; - bool isDir(const QModelIndex &index, const KDirModel *dirModel) const; - bool lessThan(const QModelIndex &left, const QModelIndex &right) const override; - Qt::DropActions supportedDragActions() const override; - Qt::DropActions supportedDropActions() const override; - - Q_INVOKABLE void paste(); - Q_INVOKABLE void copy(); - Q_INVOKABLE void cut(); - Q_INVOKABLE void deleteSelected(); - Q_INVOKABLE void openSelected(); - Q_INVOKABLE void undo(); - Q_INVOKABLE void refresh(); - Q_INVOKABLE void createFolder(); - Q_INVOKABLE void setAsWallpaper(); - Q_INVOKABLE void openSettings(const QString &itemName); - - bool desktopView() const; - void setDesktopView(bool value); - - void setScreen(int screen); - - bool eventFilter(QObject *watched, QEvent *event) override; - -Q_SIGNALS: - void urlChanged() const; - void listingCompleted() const; - void listingCanceled() const; - void iconNameChanged() const; - void resolvedUrlChanged() const; - void statusChanged() const; - void errorStringChanged() const; - void draggingChanged() const; - void usedByContainmentChanged() const; - void lockedChanged() const; - void sortModeChanged() const; - void sortDescChanged() const; - void sortDirsFirstChanged() const; - void parseDesktopFilesChanged() const; - void viewAdapterChanged(); - void previewsChanged() const; - void previewPluginsChanged() const; - void filterModeChanged() const; - void filterPatternChanged() const; - void filterMimeTypesChanged() const; - void screenChanged() const; - void requestRename() const; - void move(int x, int y, QList urls); - void popupMenuAboutToShow(KIO::DropJob *dropJob, QMimeData *mimeData, int x, int y); - - void desktopViewChanged(); - -protected: - bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override; - bool matchMimeType(const KFileItem &item) const; - bool matchPattern(const KFileItem &item) const; - -private Q_SLOTS: - void dragSelectedInternal(int x, int y); - void dirListFailed(const QString &error); - void statResult(KJob *job); - void evictFromIsDirCache(const KFileItemList &items); - void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected); - void pasteTo(); - void moveSelectedToTrash(); - void emptyTrashBin(); - void restoreSelectedFromTrash(); - void undoTextChanged(const QString &text); - void invalidateIfComplete(); - void invalidateFilterIfComplete(); - void newFileMenuItemCreated(const QUrl &url); - -private: - struct DragImage { - int row; - QRect rect; - QPoint cursorOffset; - QImage image; - bool blank; - }; - - void createActions(); - void addDragImage(QDrag *drag, int x, int y); - void setStatus(Status status); - static bool isTrashEmpty(); - QList selectedUrls() const; - KDirModel *m_dirModel; - KDirWatch *m_dirWatch; - QString m_url; - mutable QHash m_isDirCache; - mutable QHash m_isDirJobs; - QItemSelectionModel *m_selectionModel; - QItemSelection m_pinnedSelection; - QModelIndexList m_dragIndexes; - QHash m_dragImages; - QPoint m_dragHotSpotScrollOffset; - bool m_dragInProgress; - bool m_urlChangedWhileDragging; - // target filename to target position of a drop event, note that this deliberately - // is not using the URL to easily support desktop:/ URL schemes - QHash m_dropTargetPositions; - QTimer *m_dropTargetPositionsCleanup; - QPointer m_previewGenerator; - QPointer m_viewAdapter; - KActionCollection m_actionCollection; - KNewFileMenu *m_newMenu; - KFileItemActions *m_fileItemActions; - KFileCopyToMenu *m_copyToMenu; - Status m_status = Status::None; - QString m_errorString; - bool m_usedByContainment; - bool m_locked; - int m_sortMode; // FIXME TODO: Enumify. - bool m_sortDesc; - bool m_sortDirsFirst; - bool m_parseDesktopFiles; - bool m_previews; - // An empty previewPlugin list means use default. - // We don't want to leak that fact to the QML side, however, so the property stays empty - // and internally we operate on effectivePreviewPlugins instead. - QStringList m_previewPlugins; - QStringList m_effectivePreviewPlugins; - FilterMode m_filterMode; - QString m_filterPattern; - bool m_filterPatternMatchAll; - QSet m_mimeSet; - QList m_regExps; - int m_screen = -1; - bool m_screenUsed; - bool m_complete; - QPoint m_menuPosition; - - bool m_isDesktopView; -}; - -#endif diff --git a/src/lib/itemviewadapter.h b/src/lib/itemviewadapter.h deleted file mode 100644 index 5ec1d97..0000000 --- a/src/lib/itemviewadapter.h +++ /dev/null @@ -1,72 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2014 by Eike Hein * - * * - * 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 . * - ***************************************************************************/ - -#ifndef ITEMVIEWADAPTER_H -#define ITEMVIEWADAPTER_H - -#include - -#include - -class ItemViewAdapter : public KAbstractViewAdapter -{ - Q_OBJECT - - Q_PROPERTY(QObject *adapterView READ adapterView WRITE setAdapterView NOTIFY adapterViewChanged) - Q_PROPERTY(QAbstractItemModel *adapterModel READ adapterModel WRITE setAdapterModel NOTIFY adapterModelChanged) - Q_PROPERTY(int adapterIconSize READ adapterIconSize WRITE setAdapterIconSize NOTIFY adapterIconSizeChanged) - Q_PROPERTY(QRect adapterVisibleArea READ adapterVisibleArea WRITE setAdapterVisibleArea NOTIFY adapterVisibleAreaChanged) - -public: - explicit ItemViewAdapter(QObject *parent = nullptr); - - QAbstractItemModel *model() const override; - QSize iconSize() const override; - QPalette palette() const override; - QRect visibleArea() const override; - QRect visualRect(const QModelIndex &index) const override; - void connect(Signal signal, QObject *receiver, const char *slot) override; - - QObject *adapterView() const; - void setAdapterView(QObject *view); - - QAbstractItemModel *adapterModel() const; - void setAdapterModel(QAbstractItemModel *model); - - int adapterIconSize() const; - void setAdapterIconSize(int size); - - QRect adapterVisibleArea() const; - void setAdapterVisibleArea(QRect rect); - -Q_SIGNALS: - void viewScrolled() const; - void adapterViewChanged() const; - void adapterModelChanged() const; - void adapterIconSizeChanged() const; - void adapterVisibleAreaChanged() const; - -private: - QObject *m_adapterView; - QAbstractItemModel *m_adapterModel; - int m_adapterIconSize; - QRect m_adapterVisibleArea; -}; - -#endif diff --git a/src/pathlist.cpp b/src/pathlist.cpp deleted file mode 100644 index 47bbaca..0000000 --- a/src/pathlist.cpp +++ /dev/null @@ -1,126 +0,0 @@ -/* - * - * Copyright (C) 2019 camilo - * - * 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 3 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, see . - */ - -#include "pathlist.h" - -PathList::PathList(QObject *parent) - : BaseList(parent) -{ -} - -QVariantMap PathList::get(const int &index) const -{ - if (this->list.isEmpty() || index >= this->list.size() || index < 0) { - return QVariantMap(); - } - - const auto model = this->list.at(index); - return FMH::toMap(model); -} - -QString PathList::getPath() const -{ - return this->m_path; -} - -const FMH::MODEL_LIST &PathList::items() const -{ - return this->list; -} - -void PathList::setList() -{ - const auto paths = PathList::splitPath(m_path); - - if (this->list.isEmpty()) { - emit this->preListChanged(); - this->list << paths; - emit this->postListChanged(); - } else { - const int index = [&]() -> int { - int i = 0; - for (const auto &item : qAsConst(list)) { - if (i < paths.size()) { - if (item[FMH::MODEL_KEY::PATH] != paths[i][FMH::MODEL_KEY::PATH]) { - break; - } else - i++; - } else - break; - } - return i; - }(); - - for (auto i = this->list.size() - 1; i >= index; i--) { - emit preItemRemoved(i); - this->list.removeAt(i); - emit postItemRemoved(); - } - - for (auto i = index; i < paths.size(); i++) { - emit preItemAppended(); - this->list << paths[i]; - emit postItemAppended(); - } - } -} - -void PathList::setPath(const QString &path) -{ - if (path == this->m_path) - return; - - this->m_path = path; - this->setList(); - - emit this->pathChanged(); - - qDebug() << this->list; -} - -FMH::MODEL_LIST PathList::splitPath(const QString &path) -{ - FMH::MODEL_LIST res; - - QString _url = path; - - while (_url.endsWith("/")) - _url.chop(1); - - _url += "/"; - - const auto count = _url.count("/"); - - for (auto i = 0; i < count; i++) { - _url = QString(_url).left(_url.lastIndexOf("/")); - auto label = QString(_url).right(_url.length() - _url.lastIndexOf("/") - 1); - - if (label.isEmpty()) - continue; - - if (label.contains(":") && i == count - 1) // handle the protocol - { - res << FMH::MODEL {{FMH::MODEL_KEY::LABEL, "/"}, {FMH::MODEL_KEY::PATH, _url + "///"}}; - break; - } - - res << FMH::MODEL {{FMH::MODEL_KEY::LABEL, label}, {FMH::MODEL_KEY::PATH, _url}}; - } - std::reverse(res.begin(), res.end()); - return res; -} diff --git a/src/pathlist.h b/src/pathlist.h deleted file mode 100644 index afc7d37..0000000 --- a/src/pathlist.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - * - * Copyright (C) 2019 camilo - * - * 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 3 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, see . - */ - -#ifndef PATHLIST_H -#define PATHLIST_H - -#include "baselist.h" - -/** - * @brief The PathList class - */ -class PathList : public BaseList -{ - Q_OBJECT - - Q_PROPERTY(QString path READ getPath WRITE setPath NOTIFY pathChanged) - -public: - PathList(QObject *parent = nullptr); - - const FMH::MODEL_LIST &items() const override; - - /** - * @brief setPath - * @param path - */ - void setPath(const QString &path); - - /** - * @brief getPath - * @return - */ - QString getPath() const; - - /** - * @brief get - * @param index - * @return - */ - QVariantMap get(const int &index) const; - -private: - FMH::MODEL_LIST list; - QString m_path; - - static FMH::MODEL_LIST splitPath(const QString &path); - void setList(); - -signals: - /** - * @brief pathChanged - */ - void pathChanged(); -}; - -#endif // PATHLIST_H diff --git a/src/placeslist.cpp b/src/placeslist.cpp deleted file mode 100644 index ab402ab..0000000 --- a/src/placeslist.cpp +++ /dev/null @@ -1,233 +0,0 @@ -/* - * - * Copyright (C) 2018 camilo - * - * 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 3 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, see . - */ - -#include "placeslist.h" -#include "fm.h" - -#include -#include -#include -#include - -#include - -PlacesList::PlacesList(QObject *parent) - : BaseList(parent) - , fm(new FM(this)) - , model(new KFilePlacesModel(this)) - , watcher(new QFileSystemWatcher(this)) -{ - /* - * The watcher signal returns a local file URL withouth a scheme, and the model is using a local file URL with file:// scheme. - * So those need to be correctly mapped - * */ - connect(watcher, &QFileSystemWatcher::directoryChanged, [&](const QString &path) { - if (this->count.contains(QUrl::fromLocalFile(path).toString())) { - const auto oldCount = this->count[QUrl::fromLocalFile(path).toString()]; - const auto index = this->indexOf(FMH::MODEL_KEY::PATH, QUrl::fromLocalFile(path).toString()); - const QDir dir(path); - const auto newCount = dir.count(); - int count = newCount - oldCount; - - this->list[index][FMH::MODEL_KEY::COUNT] = QString::number(std::max(0, count)); - emit this->updateModel(index, {FMH::MODEL_KEY::COUNT}); - } - }); - - connect(this->model, &KFilePlacesModel::reloaded, [this]() { - this->setList(); - }); - - connect(this->model, &KFilePlacesModel::rowsInserted, [this](const QModelIndex, int, int) { - this->setList(); - emit this->bookmarksChanged(); - - /*emit this->preListChanged(); - - for (int i = first; i <= last; i++) - { - const QModelIndex index = model->index(i, 0); - - if(this->groups.contains(model->groupType(index))) - { - this->list << getGroup(*this->model, static_cast(model->groupType(index))); - } - } - emit this->postListChanged(); */ - }); // TODO improve the usage of the model -} - -void PlacesList::watchPath(const QString &path) -{ - if (path.isEmpty() || !FMH::fileExists(path) || !QUrl(path).isLocalFile()) - return; - - this->watcher->addPath(QUrl(path).toLocalFile()); -} - -void PlacesList::componentComplete() -{ - connect(this, &PlacesList::groupsChanged, this, &PlacesList::setList); - this->setList(); -} - -const FMH::MODEL_LIST &PlacesList::items() const -{ - return this->list; -} - -FMH::MODEL_LIST PlacesList::getGroup(const KFilePlacesModel &model, const FMH::PATHTYPE_KEY &type) -{ - FMH::MODEL_LIST res; - - if (type == FMH::PATHTYPE_KEY::QUICK_PATH) { - res << FMH::MODEL {{FMH::MODEL_KEY::PATH, FMH::PATHTYPE_URI[FMH::PATHTYPE_KEY::TAGS_PATH] + "fav"}, {FMH::MODEL_KEY::ICON, "love"}, {FMH::MODEL_KEY::LABEL, "Favorite"}, {FMH::MODEL_KEY::TYPE, "Quick"}}; - -#if defined Q_OS_LINUX && !defined Q_OS_ANDROID - res << FMH::MODEL {{FMH::MODEL_KEY::PATH, "recentdocuments:///"}, {FMH::MODEL_KEY::ICON, "view-media-recent"}, {FMH::MODEL_KEY::LABEL, "Recent"}, {FMH::MODEL_KEY::TYPE, "Quick"}}; -#endif - - return res; - } - - if (type == FMH::PATHTYPE_KEY::PLACES_PATH) { - res << FMStatic::getDefaultPaths(); - } - - const auto group = model.groupIndexes(static_cast(type)); - res << std::accumulate(group.constBegin(), group.constEnd(), FMH::MODEL_LIST(), [&model, &type](FMH::MODEL_LIST &list, const QModelIndex &index) -> FMH::MODEL_LIST { - const QUrl url = model.url(index); - if (type == FMH::PATHTYPE_KEY::PLACES_PATH && FMH::defaultPaths.contains(url.toString())) - return list; - - if (type == FMH::PATHTYPE_KEY::PLACES_PATH && url.isLocalFile() && !FMH::fileExists(url)) - return list; - - list << FMH::MODEL {{FMH::MODEL_KEY::PATH, url.toString()}, - {FMH::MODEL_KEY::URL, url.toString()}, - {FMH::MODEL_KEY::ICON, model.icon(index).name()}, - {FMH::MODEL_KEY::LABEL, model.text(index)}, - {FMH::MODEL_KEY::NAME, model.text(index)}, - {FMH::MODEL_KEY::TYPE, type == FMH::PATHTYPE_KEY::PLACES_PATH ? FMH::PATHTYPE_LABEL[FMH::PATHTYPE_KEY::BOOKMARKS_PATH] : FMH::PATHTYPE_LABEL[type]}}; - - return list; - }); - - return res; -} - -void PlacesList::setList() -{ - if (this->groups.isEmpty()) - return; - - emit this->preListChanged(); - - this->list.clear(); - - for (const auto &group : qAsConst(this->groups)) { - switch (group) { - case FMH::PATHTYPE_KEY::PLACES_PATH: - this->list << getGroup(*this->model, FMH::PATHTYPE_KEY::PLACES_PATH); - break; - - case FMH::PATHTYPE_KEY::QUICK_PATH: - this->list << getGroup(*this->model, FMH::PATHTYPE_KEY::QUICK_PATH); - break; - - case FMH::PATHTYPE_KEY::APPS_PATH: - this->list << FM::getAppsPath(); - break; - - case FMH::PATHTYPE_KEY::DRIVES_PATH: - this->list << getGroup(*this->model, FMH::PATHTYPE_KEY::DRIVES_PATH); - break; - - case FMH::PATHTYPE_KEY::REMOTE_PATH: - this->list << getGroup(*this->model, FMH::PATHTYPE_KEY::REMOTE_PATH); - break; - - case FMH::PATHTYPE_KEY::REMOVABLE_PATH: - this->list << getGroup(*this->model, FMH::PATHTYPE_KEY::REMOVABLE_PATH); - break; - } - } - - this->setCount(); - emit this->postListChanged(); -} - -void PlacesList::setCount() -{ - this->watcher->removePaths(this->watcher->directories()); - for (auto &data : this->list) { - const auto path = data[FMH::MODEL_KEY::URL]; - if (FMStatic::isDir(path)) { - data.insert(FMH::MODEL_KEY::COUNT, "0"); - QDir dir(QUrl(path).toLocalFile()); - const auto count = dir.count(); - this->count.insert(path, count); - this->watchPath(path); - } - } -} - -QList PlacesList::getGroups() const -{ - return this->groups; -} - -void PlacesList::setGroups(const QList &value) -{ - if (this->groups == value) - return; - - this->groups = value; - emit this->groupsChanged(); -} - -QVariantMap PlacesList::get(const int &index) const -{ - if (index >= this->list.size() || index < 0) - return QVariantMap(); - - const auto model = this->list.at(index); - return FMH::toMap(model); -} - -void PlacesList::clearBadgeCount(const int &index) -{ - this->list[index][FMH::MODEL_KEY::COUNT] = "0"; - emit this->updateModel(index, {FMH::MODEL_KEY::COUNT}); -} - -void PlacesList::removePlace(const int &index) -{ - if (index >= this->list.size() || index < 0) - return; - - emit this->preItemRemoved(index); - this->model->removePlace(this->model->closestItem(this->list.at(index)[FMH::MODEL_KEY::PATH])); - this->list.removeAt(index); - emit this->postItemRemoved(); -} - -bool PlacesList::contains(const QUrl &path) -{ - return this->exists(FMH::MODEL_KEY::PATH, path.toString()); -} diff --git a/src/placeslist.h b/src/placeslist.h deleted file mode 100644 index e050286..0000000 --- a/src/placeslist.h +++ /dev/null @@ -1,102 +0,0 @@ -/* - * - * Copyright (C) 2018 camilo - * - * 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 3 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, see . - */ - -#ifndef PLACESLIST_H -#define PLACESLIST_H - -#include "baselist.h" -#include - -class FM; -class KFilePlacesModel; -class QFileSystemWatcher; -class PlacesList : public BaseList -{ - Q_OBJECT - Q_PROPERTY(QList groups READ getGroups WRITE setGroups NOTIFY groupsChanged) - -public: - PlacesList(QObject *parent = nullptr); - - const FMH::MODEL_LIST &items() const override; - - QList getGroups() const; - void setGroups(const QList &value); - - void componentComplete() override final; - - /** - * @brief get - * Gets a item in the model. - * @param index - * Index of the item in the model. The given index is not mapped to a filtered or sorted model - * @return - * The data of the place - */ - QVariantMap get(const int &index) const; - -protected: - void setList(); - void reset(); - -public slots: - /** - * @brief clearBadgeCount - * Clears the count associated to a place at a given index in the model - * @param index - */ - void clearBadgeCount(const int &index); - - /** - * @brief removePlace - * Removes a place from the model and if the data at the given index is a file URL bookmark then it gets removed from the bookmarks. - * @param index - * Index of the item to be removed in the model - */ - void removePlace(const int &index); - - /** - * @brief contains - * Checks of a file URL exists in the places model - * @param path - * File URL to be checked - * @return - * True if it exists otherwise false - */ - bool contains(const QUrl &path); - -private: - FM *fm; - FMH::MODEL_LIST list; - KFilePlacesModel *model; - QHash count; - - QList groups; - - QFileSystemWatcher *watcher; - void watchPath(const QString &path); - - void setCount(); - - static FMH::MODEL_LIST getGroup(const KFilePlacesModel &model, const FMH::PATHTYPE_KEY &type); - -signals: - void groupsChanged(); - void bookmarksChanged(); -}; -#endif // PLACESLIST_H diff --git a/src/rubberband.h b/src/rubberband.h deleted file mode 100644 index 24ee2d2..0000000 --- a/src/rubberband.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef RUBBERBAND_H -#define RUBBERBAND_H - -#include - -class RubberBand : public QQuickPaintedItem -{ - Q_OBJECT - Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged) - -public: - explicit RubberBand(QQuickItem *parent = nullptr); - ~RubberBand() override; - - void paint(QPainter *painter) override; - - Q_INVOKABLE bool intersects(const QRectF &rect) const; - - QColor color() const; - void setColor(QColor color); - -signals: - void colorChanged(); - -protected: - void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) override; - -private: - QRectF m_geometry; - QColor m_color; -}; - -#endif diff --git a/translations/en_US.ts b/translations/en_US.ts deleted file mode 100644 index 6721291..0000000 --- a/translations/en_US.ts +++ /dev/null @@ -1,283 +0,0 @@ - - - - - BrowserMenu - - - New Folder - - - - - Paste - - - - - Open in Terminal - - - - - Select All - - - - - Properties - - - - - Empty Trash - - - - - BrowserView - - - No Files - - - - - DesktopView - - - Desktop - - - - - FolderModel - - - Cut - - - - - Copy - - - - - Undo - - - - - Paste - - - - - New Folder - - - - - New Documents - - - - - Rename - - - - - Move To Trash - - - - - &Empty Trash - - - - - Restore from trash - - - - - Delete - - - - - &Open - - - - - Select All - - - - - Change Wallpaper - - - - - Properties - - - - - Set as Wallpaper - - - - - &Properties - - - - - ItemMenu - - - Open - - - - - Copy - - - - - Cut - - - - - Move to Trash - - - - - Rename - - - - - Open in Terminal - - - - - Set As Wallpaper - - - - - Properties - - - - - PlacesModel - - - Home - - - - - Desktop - - - - - Documents - - - - - Downloads - - - - - Music - - - - - Pictures - - - - - Videos - - - - - Trash - - - - - PropertiesDialog - - - Properties - - - - - Type: - - - - - Location: - - - - - Size: - - - - - Created: - - - - - Modified: - - - - - Accessed: - - - - - Cancel - - - - - OK - - - - - main - - - File Manager - - - - diff --git a/translations/zh_CN.ts b/translations/zh_CN.ts index a0d81c5..823db24 100644 --- a/translations/zh_CN.ts +++ b/translations/zh_CN.ts @@ -1,51 +1,10 @@ - - BrowserMenu - - - New Folder - 新建文件夹 - - - - Paste - 粘贴 - - - - Open in Terminal - 在终端中打开 - - - - Select All - 全选 - - - - Properties - 属性 - - - - Empty Trash - 清空回收站 - - - - BrowserView - - - No Files - 没有文件 - - DesktopView - + Desktop 桌面 @@ -53,173 +12,138 @@ FolderModel - - Cut - 剪切 + + %1 selected + 选中了 %1 项 - - Copy - 拷贝 + + %1 item + %1 项 - - Undo - 撤销 + + %1 items + %1 项 - - Paste - 粘贴 - - - - New Folder - 新建文件夹 - - - - New Documents - 新建文档 - - - - Rename - 重命名 - - - - Move To Trash - 移到回收站 - - - - &Empty Trash - &清空回收站 - - - - Restore from trash - 从回收站恢复 - - - - Delete - 删除 - - - - &Open - &打开 - - - + Select All 全选 - - Change Wallpaper - 更改壁纸 - - - - Properties - 属性 - - - - Set as Wallpaper - 设置为壁纸 - - - - &Properties - &属性 - - - - ItemMenu - - + Open 打开 - - Copy - 拷贝 - - - + Cut 剪切 - - Move to Trash - 移到回收站 + + Copy + 复制 - + + Paste + 粘贴 + + + + New Folder + 新建文件夹 + + + + Move To Trash + 移动到回收站 + + + + Empty Trash + 清空回收站 + + + + Delete + 删除 + + + Rename 重命名 - + Open in Terminal 在终端中打开 - - Set As Wallpaper + + Set as Wallpaper 设置为壁纸 - + Properties 属性 + + FolderPage + + + No files + 无文件 + + + + Empty Trash + 清空回收站 + + PlacesModel - + Home 主文件夹 - + Desktop 桌面 - + Documents 文档 - + Downloads 下载 - + Music 音乐 - + Pictures 图片 - + Videos 视频 - + Trash 回收站 @@ -232,50 +156,60 @@ 属性 - + Type: 类型: - + Location: 位置: - + Size: 大小: - + + Calculating... + 计算中... + + + Created: 创建时间: - + Modified: 修改时间: - + Accessed: 访问时间: - + Cancel 取消 - + OK 确定 + + + %1 files + %1 项 + main - + File Manager 文件管理器 diff --git a/src/lib/itemviewadapter.cpp b/widgets/itemviewadapter.cpp similarity index 69% rename from src/lib/itemviewadapter.cpp rename to widgets/itemviewadapter.cpp index 9493250..f12b7fe 100644 --- a/src/lib/itemviewadapter.cpp +++ b/widgets/itemviewadapter.cpp @@ -1,22 +1,3 @@ -/* - * Copyright © 2008 Fredrik Höglund - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - #include "itemviewadapter.h" #include @@ -24,7 +5,7 @@ #include ItemViewAdapter::ItemViewAdapter(QObject *parent) - : KAbstractViewAdapter(parent) + : QObject(parent) , m_adapterView(nullptr) , m_adapterModel(nullptr) , m_adapterIconSize(-1) @@ -55,7 +36,7 @@ QRect ItemViewAdapter::visualRect(const QModelIndex &index) const { // FIXME TODO: Implemented on DND branch. - Q_UNUSED(index) + Q_UNUSED(index); return QRect(); } diff --git a/widgets/itemviewadapter.h b/widgets/itemviewadapter.h new file mode 100644 index 0000000..48c23a0 --- /dev/null +++ b/widgets/itemviewadapter.h @@ -0,0 +1,54 @@ +#ifndef ITEMVIEWADAPTER_H +#define ITEMVIEWADAPTER_H + +#include +#include + +class ItemViewAdapter : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QObject *adapterView READ adapterView WRITE setAdapterView NOTIFY adapterViewChanged) + Q_PROPERTY(QAbstractItemModel *adapterModel READ adapterModel WRITE setAdapterModel NOTIFY adapterModelChanged) + Q_PROPERTY(int adapterIconSize READ adapterIconSize WRITE setAdapterIconSize NOTIFY adapterIconSizeChanged) + Q_PROPERTY(QRect adapterVisibleArea READ adapterVisibleArea WRITE setAdapterVisibleArea NOTIFY adapterVisibleAreaChanged) + +public: + enum Signal { ScrollBarValueChanged, IconSizeChanged }; + + explicit ItemViewAdapter(QObject *parent = nullptr); + + QAbstractItemModel *model() const; + QSize iconSize() const; + QPalette palette() const; + QRect visibleArea() const; + QRect visualRect(const QModelIndex &index) const; + void connect(Signal signal, QObject *receiver, const char *slot); + + QObject *adapterView() const; + void setAdapterView(QObject *view); + + QAbstractItemModel *adapterModel() const; + void setAdapterModel(QAbstractItemModel *model); + + int adapterIconSize() const; + void setAdapterIconSize(int size); + + QRect adapterVisibleArea() const; + void setAdapterVisibleArea(QRect rect); + +Q_SIGNALS: + void viewScrolled() const; + void adapterViewChanged() const; + void adapterModelChanged() const; + void adapterIconSizeChanged() const; + void adapterVisibleAreaChanged() const; + +private: + QObject *m_adapterView; + QAbstractItemModel *m_adapterModel; + int m_adapterIconSize; + QRect m_adapterVisibleArea; +}; + +#endif diff --git a/src/rubberband.cpp b/widgets/rubberband.cpp similarity index 63% rename from src/rubberband.cpp rename to widgets/rubberband.cpp index 3bd1048..be540c3 100644 --- a/src/rubberband.cpp +++ b/widgets/rubberband.cpp @@ -1,3 +1,22 @@ +/* + * Copyright (C) 2021 CutefishOS Team. + * + * Author: revenmartin + * + * 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 3 of the License, or + * 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, see . + */ + #include "rubberband.h" #include diff --git a/widgets/rubberband.h b/widgets/rubberband.h new file mode 100644 index 0000000..3996b34 --- /dev/null +++ b/widgets/rubberband.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2021 CutefishOS Team. + * + * Author: revenmartin + * + * 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 3 of the License, or + * 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, see . + */ + +#ifndef RUBBERBAND_H +#define RUBBERBAND_H + +#include + +class RubberBand : public QQuickPaintedItem +{ + Q_OBJECT + Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged) + +public: + explicit RubberBand(QQuickItem *parent = nullptr); + ~RubberBand() override; + + void paint(QPainter *painter) override; + + Q_INVOKABLE bool intersects(const QRectF &rect) const; + + QColor color() const; + void setColor(QColor color); + +signals: + void colorChanged(); + +protected: + void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) override; + +private: + QRectF m_geometry; + QColor m_color; +}; + +#endif