Refactor the code

This commit is contained in:
cutefishd 2021-03-29 16:51:34 +08:00
parent aa449f52ad
commit c3d7c6eedf
90 changed files with 4047 additions and 10926 deletions

View file

@ -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
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)

View file

@ -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
```

View file

@ -1,7 +1,7 @@
[Desktop Entry]
Type=Application
Name=File Manager
Name[zh_CN]=
Name[zh_CN]=
GenericName=File Manager
Comment=Cutefish File Manager
Exec=cutefish-filemanager %U

1
debian/control vendored
View file

@ -6,6 +6,7 @@ Build-Depends: cmake,
debhelper (>= 9),
extra-cmake-modules,
libkf5kio-dev,
libkf5solid-dev,
qtbase5-dev,
qtbase5-private-dev,
qtdeclarative5-dev,

View file

@ -1,3 +1,22 @@
/*
* Copyright (C) 2021 CutefishOS Team.
*
* Author: revenmartin <revenmartin@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "desktopsettings.h"
#include <QDBusServiceWatcher>

View file

@ -1,3 +1,22 @@
/*
* Copyright (C) 2021 CutefishOS Team.
*
* Author: revenmartin <revenmartin@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef SETTINGS_H
#define SETTINGS_H

View file

@ -1,3 +1,22 @@
/*
* Copyright (C) 2021 CutefishOS Team.
*
* Author: revenmartin <revenmartin@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "desktopview.h"
#include <QQmlEngine>
@ -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();

50
desktop/desktopview.h Normal file
View file

@ -0,0 +1,50 @@
/*
* Copyright (C) 2021 CutefishOS Team.
*
* Author: revenmartin <revenmartin@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef DESKTOPVIEW_H
#define DESKTOPVIEW_H
#include <QQuickView>
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

53
desktopiconprovider.cpp Normal file
View file

@ -0,0 +1,53 @@
/*
* Copyright (C) 2021 CutefishOS Team.
*
* Author: revenmartin <revenmartin@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "desktopiconprovider.h"
#include <QIcon>
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);
}

33
desktopiconprovider.h Normal file
View file

@ -0,0 +1,33 @@
/*
* Copyright (C) 2021 CutefishOS Team.
*
* Author: revenmartin <revenmartin@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef DESKTOPICONPROVIDER_H
#define DESKTOPICONPROVIDER_H
#include <QtQuick/QQuickImageProvider>
class DesktopIconProvider : public QQuickImageProvider
{
public:
DesktopIconProvider();
QPixmap requestPixmap(const QString &id, QSize *realSize, const QSize &requestedSize);
};
#endif // DESKTOPICONPROVIDER_H

View file

@ -1,5 +1,27 @@
/*
* Copyright (C) 2021 CutefishOS Team.
*
* Author: revenmartin <revenmartin@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "propertiesdialog.h"
#include "../iconthemeprovider.h"
#include "../desktopiconprovider.h"
#include <KFileItemListProperties>
#include <KIO/CopyJob>
#include <QDir>
#include <QFileInfo>
@ -9,6 +31,19 @@
#include <QQmlContext>
#include <QDebug>
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,7 +75,7 @@ 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"));
}
@ -49,7 +84,7 @@ 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();
}
}

View file

@ -1,3 +1,22 @@
/*
* Copyright (C) 2021 CutefishOS Team.
*
* Author: revenmartin <revenmartin@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#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();

34
helper/pathhistory.cpp Normal file
View file

@ -0,0 +1,34 @@
#include "pathhistory.h"
#include <QUrl>
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();
}

23
helper/pathhistory.h Normal file
View file

@ -0,0 +1,23 @@
#ifndef PATHHISTORY_H
#define PATHHISTORY_H
#include <QObject>
class PathHistory : public QObject
{
Q_OBJECT
public:
explicit PathHistory(QObject *parent = nullptr);
void append(const QUrl &path);
QUrl posteriorPath();
QUrl previousPath();
private:
QVector<QUrl> m_prevHistory;
QVector<QUrl> m_postHistory;
};
#endif // PATHHISTORY_H

37
helper/thumbnailer.cpp Normal file
View file

@ -0,0 +1,37 @@
#include "thumbnailer.h"
#include <KIO/PreviewJob>
#include <QDebug>
#include <QImage>
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);
}

25
helper/thumbnailer.h Normal file
View file

@ -0,0 +1,25 @@
#ifndef THUMBNAILER_H
#define THUMBNAILER_H
#include <QObject>
#include <QQuickImageProvider>
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

View file

@ -1,6 +1,120 @@
<svg width="22" height="22" version="1.1" xmlns="http://www.w3.org/2000/svg">
<defs>
<style id="current-color-scheme" type="text/css">.ColorScheme-Text { color:#363636; }</style>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="22"
height="22"
version="1.1"
id="svg7"
sodipodi:docname="folder-document.svg"
inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)">
<metadata
id="metadata11">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="2160"
inkscape:window-height="1304"
id="namedview9"
showgrid="false"
inkscape:zoom="47.24759"
inkscape:cx="13.724761"
inkscape:cy="13.430708"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="g12-3"
inkscape:document-rotation="0" />
<defs
id="defs3">
<style
id="current-color-scheme"
type="text/css">.ColorScheme-Text { color:#363636; }</style>
</defs>
<path class="ColorScheme-Text" d="m10 3c-1.108 0-2 0.892-2 2v1h-2c-1.108 0-2 0.892-2 2v9c0 1.108 0.892 2 2 2h6c1.108 0 2-0.892 2-2v-1h2c1.108 0 2-0.892 2-2v-6l-5-5h-1zm0 1h2v5h5v5c0 0.554-0.446 1-1 1h-2v-4l-5-5v-1c0-0.1385 0.027656-0.27091 0.078125-0.39062 0.15141-0.35916 0.50638-0.60938 0.92188-0.60938zm3 0.41406 3.5859 3.5859h-3.5859zm-7 2.5859h2v5h5v5c0 0.554-0.446 1-1 1h-6c-0.554 0-1-0.446-1-1v-9c0-0.1385 0.027656-0.27091 0.078125-0.39062 0.15141-0.35916 0.50638-0.60938 0.92188-0.60938zm3 0.41406 3.5859 3.5859h-3.5859z" fill="currentColor"/>
<g
id="g12-3"
transform="matrix(0.03076923,0,0,0.03076923,3.1230769,3.1230769)"
style="fill:#363636;fill-opacity:1">
<path
d="M 446.605,124.392 326.608,4.395 C 323.807,1.593 319.984,0 316,0 H 106 C 81.187,0 61,20.187 61,45 v 422 c 0,24.813 20.187,45 45,45 h 300 c 24.813,0 45,-20.187 45,-45 V 135 c 0,-4.09 -1.717,-7.931 -4.395,-10.608 z M 331,51.213 399.787,120 H 346 c -8.271,0 -15,-6.729 -15,-15 z M 406,482 H 106 c -8.271,0 -15,-6.729 -15,-15 V 45 c 0,-8.271 6.729,-15 15,-15 h 195 v 75 c 0,24.813 20.187,45 45,45 h 75 v 317 c 0,8.271 -6.729,15 -15,15 z"
id="path2-6"
style="fill:#363636;fill-opacity:1" />
<path
d="M 346,272 H 166 c -8.284,0 -15,6.716 -15,15 0,8.284 6.716,15 15,15 h 180 c 8.284,0 15,-6.716 15,-15 0,-8.284 -6.716,-15 -15,-15 z"
id="path6"
style="fill:#363636;fill-opacity:1" />
<path
d="M 346,332 H 166 c -8.284,0 -15,6.716 -15,15 0,8.284 6.716,15 15,15 h 180 c 8.284,0 15,-6.716 15,-15 0,-8.284 -6.716,-15 -15,-15 z"
id="path8-7"
style="fill:#363636;fill-opacity:1" />
<path
d="M 286,392 H 166 c -8.284,0 -15,6.716 -15,15 0,8.284 6.716,15 15,15 h 120 c 8.284,0 15,-6.716 15,-15 0,-8.284 -6.716,-15 -15,-15 z"
id="path10"
style="fill:#363636;fill-opacity:1" />
</g>
<g
id="g32"
transform="matrix(0.02922392,0,0,0.02922392,3.5186765,3.5186765)" />
<g
id="g34"
transform="matrix(0.02922392,0,0,0.02922392,3.5186765,3.5186765)" />
<g
id="g36"
transform="matrix(0.02922392,0,0,0.02922392,3.5186765,3.5186765)" />
<g
id="g38"
transform="matrix(0.02922392,0,0,0.02922392,3.5186765,3.5186765)" />
<g
id="g40"
transform="matrix(0.02922392,0,0,0.02922392,3.5186765,3.5186765)" />
<g
id="g42"
transform="matrix(0.02922392,0,0,0.02922392,3.5186765,3.5186765)" />
<g
id="g44"
transform="matrix(0.02922392,0,0,0.02922392,3.5186765,3.5186765)" />
<g
id="g46"
transform="matrix(0.02922392,0,0,0.02922392,3.5186765,3.5186765)" />
<g
id="g48"
transform="matrix(0.02922392,0,0,0.02922392,3.5186765,3.5186765)" />
<g
id="g50"
transform="matrix(0.02922392,0,0,0.02922392,3.5186765,3.5186765)" />
<g
id="g52"
transform="matrix(0.02922392,0,0,0.02922392,3.5186765,3.5186765)" />
<g
id="g54"
transform="matrix(0.02922392,0,0,0.02922392,3.5186765,3.5186765)" />
<g
id="g56"
transform="matrix(0.02922392,0,0,0.02922392,3.5186765,3.5186765)" />
<g
id="g58"
transform="matrix(0.02922392,0,0,0.02922392,3.5186765,3.5186765)" />
<g
id="g60"
transform="matrix(0.02922392,0,0,0.02922392,3.5186765,3.5186765)" />
</svg>

Before

Width:  |  Height:  |  Size: 751 B

After

Width:  |  Height:  |  Size: 4.2 KiB

View file

@ -1,7 +1,7 @@
/*
* Copyright (C) 2021 CutefishOS Team.
*
* Author: rekols <revenmartin@gmail.com>
* Author: revenmartin <revenmartin@gmail.com>
*
* 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 <QApplication>
#include <QCommandLineParser>
#include <QQmlApplicationEngine>
#include <QTranslator>
#include <QLocale>
#include <QAction>
#include <QCommandLineParser>
#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<PlacesModel>(uri, 1, 0, "PlacesModel");
qmlRegisterType<FolderModel>(uri, 1, 0, "FolderModel");
qmlRegisterType<PathBarModel>(uri, 1, 0, "PathBarModel");
qmlRegisterType<RubberBand>(uri, 1, 0, "RubberBand");
qmlRegisterType<ItemViewAdapter>(uri, 1, 0, "ItemViewAdapter");
qmlRegisterType<DesktopSettings>(uri, 1, 0, "DesktopSettings");
qmlRegisterAnonymousType<QAction>(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<QAction>(uri, 1);
qmlRegisterType<DesktopSettings>(uri, 1, 0, "DesktopSettings");
qmlRegisterType<RubberBand>(uri, 1, 0, "RubberBand");
qmlRegisterType<FolderModel>(uri, 1, 0, "FolderModel");
qmlRegisterType<ItemViewAdapter>(uri, 1, 0, "ItemViewAdapter");
qmlRegisterType<Positioner>(uri, 1, 0, "Positioner");
qmlRegisterType<PlacesModel>(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<BaseList>(uri, 1); // ABSTRACT BASE LIST
qmlRegisterType<BaseModel>(uri, 1, 0, "BaseModel"); // BASE MODEL
qmlRegisterType<PlacesList>(uri, 1, 0, "PlacesList");
qmlRegisterType<PathList>(uri, 1, 0, "PathList");
qmlRegisterType<FMList>(uri, 1, 0, "FMList");
qmlRegisterSingletonType<FMStatic>(uri, 1, 0, "FM", [](QQmlEngine *engine, QJSEngine *scriptEngine) -> QObject * {
Q_UNUSED(engine)
Q_UNUSED(scriptEngine)
return new FMStatic;
});
qmlRegisterSingletonType<Handy>(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();
}

39
model/dirlister.cpp Normal file
View file

@ -0,0 +1,39 @@
/*
* Copyright (C) 2021 CutefishOS Team.
*
* Author: revenmartin <revenmartin@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#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);
}

41
model/dirlister.h Normal file
View file

@ -0,0 +1,41 @@
/*
* Copyright (C) 2021 CutefishOS Team.
*
* Author: revenmartin <revenmartin@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef DIRLISTER_H
#define DIRLISTER_H
#include <KDirLister>
#include <KIO/Job>
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

1126
model/foldermodel.cpp Normal file

File diff suppressed because it is too large Load diff

202
model/foldermodel.h Normal file
View file

@ -0,0 +1,202 @@
#ifndef FOLDERMODEL_H
#define FOLDERMODEL_H
#include "../widgets/itemviewadapter.h"
#include "../helper/pathhistory.h"
#include <QSortFilterProxyModel>
#include <QItemSelectionModel>
#include <QQmlParserStatus>
#include <QQuickItem>
#include <KDirLister>
#include <KDirModel>
#include <KDirWatch>
#include <KActionCollection>
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<int, QByteArray> roleNames() const override;
static QHash<int, QByteArray> staticRoleNames();
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
KFileItem itemForIndex(const QModelIndex &index) const;
QList<QUrl> 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<int, DragImage *> m_dragImages;
QModelIndexList m_dragIndexes;
QPoint m_dragHotSpotScrollOffset;
bool m_dragInProgress;
QPointer<ItemViewAdapter> m_viewAdapter;
// Save path history
PathHistory m_pathHistory;
};
#endif // FOLDERMODEL_H

139
model/pathbarmodel.cpp Normal file
View file

@ -0,0 +1,139 @@
/*
* Copyright (C) 2021 CutefishOS Team.
*
* Author: revenmartin <revenmartin@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "pathbarmodel.h"
PathBarModel::PathBarModel(QObject *parent)
: QAbstractItemModel(parent)
{
}
PathBarModel::~PathBarModel()
{
}
QString PathBarModel::url() const
{
return m_url;
}
#include<QDebug>
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<int, QByteArray> PathBarModel::roleNames() const
{
QHash<int, QByteArray> 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();
}

67
model/pathbarmodel.h Normal file
View file

@ -0,0 +1,67 @@
/*
* Copyright (C) 2021 CutefishOS Team.
*
* Author: revenmartin <revenmartin@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef PATHBARMODEL_H
#define PATHBARMODEL_H
#include <QAbstractItemModel>
#include <QUrl>
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<int, QByteArray> 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<PathBarItem *> m_pathList;
};
#endif // PATHBARMODEL_H

View file

@ -1,13 +1,30 @@
/*
* Copyright (C) 2021 CutefishOS Team.
*
* Author: revenmartin <revenmartin@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "placesitem.h"
#include <QDebug>
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);
}

View file

@ -1,3 +1,22 @@
/*
* Copyright (C) 2021 CutefishOS Team.
*
* Author: revenmartin <revenmartin@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#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);

View file

@ -1,63 +1,111 @@
/*
* Copyright (C) 2021 CutefishOS Team.
*
* Author: revenmartin <revenmartin@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "placesmodel.h"
#include <QStandardPaths>
#include <QDir>
#include <QDebug>
#include <Solid/Device>
#include <Solid/DeviceNotifier>
#include <Solid/StorageDrive>
#include <Solid/StorageAccess>
#include <Solid/Predicate>
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<Solid::Device> &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()

View file

@ -1,3 +1,22 @@
/*
* Copyright (C) 2021 CutefishOS Team.
*
* Author: revenmartin <revenmartin@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef PLACESMODEL_H
#define PLACESMODEL_H

View file

@ -23,13 +23,10 @@
#include <QAbstractItemModel>
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<int, int> proxyToSourceMapping() const
{
return m_proxyToSource;
}
QHash<int, int> 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<int> &roles);
@ -137,4 +123,4 @@ private:
bool m_beginInsertRowsCalled = false; // used to sync the amount of begin/endInsertRows calls
};
#endif
#endif // POSITIONER_H

31
qml.qrc
View file

@ -1,11 +1,12 @@
<RCC>
<qresource prefix="/">
<file>qml/main.qml</file>
<file>qml/FolderPage.qml</file>
<file>qml/SideBar.qml</file>
<file>qml/SidebarItem.qml</file>
<file>qml/FolderListItem.qml</file>
<file>qml/Dialogs/PropertiesDialog.qml</file>
<file>qml/FolderListView.qml</file>
<file>qml/PathBar.qml</file>
<file>qml/FolderListDelegate.qml</file>
<file>qml/Controls/IconButton.qml</file>
<file>images/dark/go-next.svg</file>
<file>images/dark/go-previous.svg</file>
<file>images/dark/grid.svg</file>
@ -14,27 +15,19 @@
<file>images/light/go-previous.svg</file>
<file>images/light/grid.svg</file>
<file>images/light/list.svg</file>
<file>qml/IconButton.qml</file>
<file>images/folder-desktop.svg</file>
<file>images/folder-document.svg</file>
<file>images/folder-download.svg</file>
<file>images/folder-home.svg</file>
<file>images/folder-music.svg</file>
<file>images/folder-picture.svg</file>
<file>images/folder-video.svg</file>
<file>images/folder-document.svg</file>
<file>qml/GlobalSettings.qml</file>
<file>qml/FolderIconView.qml</file>
<file>qml/FolderIconDelegate.qml</file>
<file>qml/BrowserView.qml</file>
<file>qml/ItemMenu.qml</file>
<file>qml/BrowserMenu.qml</file>
<file>qml/Desktop/DesktopFolderView.qml</file>
<file>qml/Desktop/FolderViewDropArea.qml</file>
<file>qml/Desktop/FolderItemDelegate.qml</file>
<file>qml/Desktop/Desktop.qml</file>
<file>qml/Desktop/FolderTools.js</file>
<file>qml/Dialogs/PropertiesDialog.qml</file>
<file>qml/IconDelegate.qml</file>
<file>images/folder-desktop.svg</file>
<file>images/user-trash.svg</file>
<file>qml/PathBar.qml</file>
<file>qml/Desktop/main.qml</file>
<file>qml/FolderGridView.qml</file>
<file>qml/GlobalSettings.qml</file>
<file>qml/FolderGridItem.qml</file>
<file>qml/Dialogs/CreateFolderDialog.qml</file>
</qresource>
</RCC>

View file

@ -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)
}
}

View file

@ -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
}
}

View file

@ -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"
}
}

File diff suppressed because it is too large Load diff

View file

@ -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
}
}
}

View file

@ -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
}
}
}

View file

@ -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));
}
}
}

111
qml/Desktop/main.qml Normal file
View file

@ -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()
}
}
}
}
}

View file

@ -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
}

View file

@ -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
}
}

102
qml/FolderGridItem.qml Normal file
View file

@ -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
}
}
}
}

380
qml/FolderGridView.qml Normal file
View file

@ -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)
}
}

View file

@ -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
}
}
}

View file

@ -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()
}
}
}
}
}

View file

@ -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
}
}
}

91
qml/FolderListItem.qml Normal file
View file

@ -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
}
}
}

View file

@ -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
}
}
}
}
}

213
qml/FolderPage.qml Normal file
View file

@ -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()
}
}

View file

@ -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

View file

@ -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
}
}
}

View file

@ -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()
}
}
}

View file

@ -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
color: Meui.Theme.backgroundColor
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
}
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
}
}

View file

@ -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
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: {
control.currentIndex = index
control.itemClicked(index)
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
}
}
}
}

View file

@ -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
}
}
}

View file

@ -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: {
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
}
IconButton {
Layout.fillHeight: true
implicitWidth: height
source: Meui.Theme.darkMode ? "qrc:/images/dark/list.svg" : "qrc:/images/light/list.svg"
onClicked: settings.viewMethod = 0
}
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
}
}
ColumnLayout {
anchors.fill: parent
spacing: Meui.Units.largeSpacing
// IconButton {
// Layout.fillHeight: true
// implicitWidth: height
// source: Meui.Theme.darkMode ? "qrc:/images/dark/grid.svg" : "qrc:/images/light/grid.svg"
// onClicked: settings.viewMethod = 1
// }
Item {
id: bottomControls
Layout.fillWidth: true
Layout.fillHeight: true
// IconButton {
// Layout.fillHeight: true
// implicitWidth: height
// source: Meui.Theme.darkMode ? "qrc:/images/dark/list.svg" : "qrc:/images/light/list.svg"
// onClicked: settings.viewMethod = 0
// }
}
}
RowLayout {
anchors.fill: parent
anchors.topMargin: Meui.Units.largeSpacing
anchors.topMargin: 2
spacing: 0
SideBar {
id: _sideBar
Layout.fillHeight: true
currentUrl: _browserView.model.url
onPlaceClicked: _browserView.model.url = path
width: 200 + Meui.Units.largeSpacing
onClicked: _folderPage.openUrl(path)
}
BrowserView {
id: _browserView
FolderPage {
id: _folderPage
Layout.fillWidth: true
Layout.fillHeight: true
// onOpenPathBar: pathBar.openEditor()
onCurrentUrlChanged: {
_sideBar.updateSelection(currentUrl)
_pathBar.updateUrl(currentUrl)
}
onRequestPathEditor: {
_pathBar.openEditor()
}
}
}

View file

@ -1,86 +0,0 @@
/*
* <one line to give the program's name and a brief idea of what it does.>
* Copyright (C) 2019 camilo <chiguitar@unal.edu.co>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#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;
}

View file

@ -1,89 +0,0 @@
/*
* <one line to give the program's name and a brief idea of what it does.>
* Copyright (C) 2019 camilo <chiguitar@unal.edu.co>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef BASELIST_H
#define BASELIST_H
#include "fmh.h"
#include <QQmlParserStatus>
/**
* @todo write docs
*/
#include <QObject>
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<int> roles);
void preListChanged();
void postListChanged();
void countChanged();
};
#endif // BASELIST_H

View file

@ -1,333 +0,0 @@
/*
* <one line to give the program's name and a brief idea of what it does.>
* Copyright (C) 2019 camilo <chiguitar@unal.edu.co>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#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<Qt::SortOrder>(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<int> 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<FMH::MODEL_KEY>(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<int, QByteArray> BaseModel::PrivateAbstractListModel::roleNames() const
{
QHash<int, QByteArray> names;
for (const auto &key : FMH::MODEL_NAME.keys())
names[key] = QString(FMH::MODEL_NAME[key]).toUtf8();
return names;
}

View file

@ -1,181 +0,0 @@
/*
* <one line to give the program's name and a brief idea of what it does.>
* Copyright (C) 2019 camilo <chiguitar@unal.edu.co>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef BASEMODEL_H
#define BASEMODEL_H
#include <QAbstractListModel>
#include <QList>
#include <QObject>
#include <QSortFilterProxyModel>
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<int, QByteArray> roleNames() const override;
BaseList *getList() const;
void setList(BaseList *value);
private:
BaseList *list;
BaseModel *m_model;
};
#endif // BASEMODEL_H

View file

@ -1,31 +0,0 @@
#ifndef DESKTOPVIEW_H
#define DESKTOPVIEW_H
#include <QQuickView>
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

View file

@ -1,239 +0,0 @@
/*
* Copyright 2018 Camilo Higuita <milo.h@aol.com>
*
* 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 <QDateTime>
#include <QFileInfo>
#include <QLocale>
#include <QRegularExpression>
#include <QUrl>
#include <KCoreDirLister>
#include <KFileItem>
#include <KFilePlacesModel>
#include <KIO/CopyJob>
#include <KIO/DeleteJob>
#include <KIO/MkdirJob>
#include <KIO/SimpleJob>
#include <QIcon>
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<void (KCoreDirLister::*)(const QUrl &)>(&KCoreDirLister::completed), this, [&](QUrl url) {
qDebug() << "PATH CONTENT READY" << url;
emit this->pathContentReady(url);
});
connect(dirLister, static_cast<void (KCoreDirLister::*)(const QUrl &, const KFileItemList &items)>(&KCoreDirLister::itemsAdded), this, [&](QUrl dirUrl, KFileItemList items) {
qDebug() << "MORE ITEMS WERE ADDED";
emit this->pathContentItemsReady({dirUrl, packItems(items)});
});
// connect(dirLister, static_cast<void (KCoreDirLister::*)(const KFileItemList &items)>(&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<void (KCoreDirLister::*)(const KFileItemList &items)>(&KCoreDirLister::itemsDeleted), this, [&](KFileItemList items) {
qDebug() << "ITEMS WERE DELETED";
emit this->pathContentItemsRemoved({dirLister->url(), packItems(items)});
});
connect(dirLister, static_cast<void (KCoreDirLister::*)(const QList<QPair<KFileItem, KFileItem>> &items)>(&KCoreDirLister::refreshItems), this, [&](QList<QPair<KFileItem, KFileItem>> items) {
qDebug() << "ITEMS WERE REFRESHED";
const auto res = std::accumulate(
items.constBegin(), items.constEnd(), QVector<QPair<FMH::MODEL, FMH::MODEL>>(), [](QVector<QPair<FMH::MODEL, FMH::MODEL>> &list, const QPair<KFileItem, KFileItem> &pair) -> QVector<QPair<FMH::MODEL, FMH::MODEL>> {
list << QPair<FMH::MODEL, FMH::MODEL> {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<QUrl> &urls, const QUrl &where)
{
return FMStatic::cut(urls, where);
}
bool FM::copy(const QList<QUrl> &urls, const QUrl &where)
{
return FMStatic::copy(urls, where);
}

206
src/fm.h
View file

@ -1,206 +0,0 @@
#ifndef FM_H
#define FM_H
#include <QHash>
#include <QObject>
#include <QStorageInfo>
#include <QStringList>
#include <QVariantList>
#include <QVector>
#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<QPair<FMH::MODEL, FMH::MODEL>> 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<QPair<FMH::MODEL, FMH::MODEL>> 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<QUrl> &urls, const QUrl &where);
bool cut(const QList<QUrl> &urls, const QUrl &where);
friend class FMStatic;
};
#endif // FM_H

View file

@ -1,337 +0,0 @@
#include "fmh.h"
namespace FMH
{
const QVector<int> modelRoles(const FMH::MODEL &model)
{
const auto keys = model.keys();
return std::accumulate(keys.begin(), keys.end(), QVector<int>(), [](QVector<int> &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<FMH::MODEL_KEY> &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);
}
}

1019
src/fmh.h

File diff suppressed because it is too large Load diff

View file

@ -1,626 +0,0 @@
/*
* <one line to give the program's name and a brief idea of what it does.>
* Copyright (C) 2018 camilo higuita <milo.h@aol.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "fmlist.h"
#include "fm.h"
#if defined Q_OS_LINUX && !defined Q_OS_ANDROID
#include <KIO/EmptyTrashJob>
#endif
#include <QFuture>
#include <QObject>
#include <QThread>
#include <QtConcurrent/QtConcurrentRun>
#include <QtConcurrent>
FMList::FMList(QObject *parent)
: BaseList(parent)
, fm(new FM(this))
{
qRegisterMetaType<FMList *>("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<QPair<FMH::MODEL, FMH::MODEL>> 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<FMH::FILTER_TYPE>(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<FMH::MODEL_KEY>(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<int> {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<FMH::PATH_CONTENT> *watcher = new QFutureWatcher<FMH::PATH_CONTENT>;
connect(watcher, &QFutureWatcher<FMH::MODEL_LIST>::finished, [=]() {
const auto res = watcher->future().result();
this->assignList(res.content);
emit this->searchResultReady();
watcher->deleteLater();
});
QFuture<FMH::PATH_CONTENT> 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<FMH::PATH_CONTENT> *watcher = new QFutureWatcher<FMH::PATH_CONTENT>;
connect(watcher, &QFutureWatcher<FMH::MODEL_LIST>::finished, [=]() {
const auto res = watcher->future().result();
this->assignList(res.content);
emit this->searchResultReady();
watcher->deleteLater();
});
QFuture<FMH::PATH_CONTENT> 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();
}

View file

@ -1,450 +0,0 @@
/*
* <one line to give the program's name and a brief idea of what it does.>
* Copyright (C) 2018 Camilo Higuita <email>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef FMLIST_H
#define FMLIST_H
#include "fmh.h"
#include "baselist.h"
#include <QObject>
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<QUrl> prev_history;
QVector<QUrl> 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

View file

@ -1,312 +0,0 @@
#include "fmstatic.h"
#include <QDesktopServices>
#include <KRun>
#include <KCoreDirLister>
#include <KFileItem>
#include <KFilePlacesModel>
#include <KIO/CopyJob>
#include <KIO/DeleteJob>
#include <KIO/EmptyTrashJob>
#include <KIO/MkdirJob>
#include <KIO/SimpleJob>
#include <QIcon>
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<QUrl> &urls, const QUrl &destinationDir)
{
auto job = KIO::copy(urls, destinationDir);
job->start();
return true;
}
bool FMStatic::cut(const QList<QUrl> &urls, const QUrl &where)
{
return FMStatic::cut(urls, where, QString());
}
bool FMStatic::cut(const QList<QUrl> &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<QUrl> &urls)
{
auto job = KIO::del(urls);
job->start();
return true;
}
void FMStatic::moveToTrash(const QList<QUrl> &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<FMH::FILTER_TYPE>(type), mimeTypeName);
}
static bool doNameFilter(const QString &name, const QStringList &filters)
{
const auto filtersAccumulate = std::accumulate(filters.constBegin(), filters.constEnd(), QVector<QRegExp> {}, [](QVector<QRegExp> &res, const QString &filter) -> QVector<QRegExp> {
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<FMH::FILTER_TYPE>(type)];
}
QString FMStatic::iconName(const QString &value)
{
return FMH::getIconName(value);
}

View file

@ -1,353 +0,0 @@
#ifndef FMSTATIC_H
#define FMSTATIC_H
#include "fmh.h"
#include <QObject>
/**
* @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<QUrl> &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<QUrl> &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<QUrl> &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<QUrl> &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<QUrl> &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

View file

@ -1,113 +0,0 @@
/*
* Copyright 2018 Camilo Higuita <milo.h@aol.com>
*
* 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 <QApplication>
#include <QClipboard>
#include <QDebug>
#include <QIcon>
#include <QMimeData>
#include <QOperatingSystemVersion>
#include <QDBusInterface>
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();
}

View file

@ -1,81 +0,0 @@
/*
* Copyright 2018 Camilo Higuita <milo.h@aol.com>
*
* 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 <QObject>
#include <QVariantMap>
/*!
* \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

View file

@ -1,33 +0,0 @@
#include "iconthemeprovider.h"
#include <QIcon>
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);
}

View file

@ -1,14 +0,0 @@
#ifndef ICONTHEMEPROVIDER_H
#define ICONTHEMEPROVIDER_H
#include <QtQuick/QQuickImageProvider>
class IconThemeProvider : public QQuickImageProvider
{
public:
IconThemeProvider();
QPixmap requestPixmap(const QString &id, QSize *realSize, const QSize &requestedSize);
};
#endif // ICONTHEMEPROVIDER_H

View file

@ -1,20 +0,0 @@
#include "fileitemactions.h"
#include <KMimeTypeTrader>
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();
}

View file

@ -1,18 +0,0 @@
#ifndef FILEITEMACTIONS_H
#define FILEITEMACTIONS_H
#include <QObject>
#include <KService>
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

File diff suppressed because it is too large Load diff

View file

@ -1,372 +0,0 @@
/***************************************************************************
* Copyright (C) 2008 Fredrik Höglund <fredrik@kde.org> *
* Copyright (C) 2011 Marco Martin <mart@kde.org> *
* Copyright (C) 2014 by Eike Hein <hein@kde.org> *
* *
* 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 <QImage>
#include <QItemSelection>
#include <QPointer>
#include <QQmlParserStatus>
#include <QRegExp>
#include <QSet>
#include <QSortFilterProxyModel>
#include <QStringList>
#include <KAbstractViewAdapter>
#include <KActionCollection>
#include <KDirLister>
#include <KFilePreviewGenerator>
#include <KNewFileMenu>
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<int, QByteArray> roleNames() const override;
static QHash<int, QByteArray> 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<QUrl> 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<QUrl> selectedUrls() const;
KDirModel *m_dirModel;
KDirWatch *m_dirWatch;
QString m_url;
mutable QHash<QUrl, bool> m_isDirCache;
mutable QHash<QUrl, KIO::StatJob *> m_isDirJobs;
QItemSelectionModel *m_selectionModel;
QItemSelection m_pinnedSelection;
QModelIndexList m_dragIndexes;
QHash<int, DragImage *> 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<QString, QPoint> m_dropTargetPositions;
QTimer *m_dropTargetPositionsCleanup;
QPointer<KFilePreviewGenerator> m_previewGenerator;
QPointer<KAbstractViewAdapter> 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<QString> m_mimeSet;
QList<QRegExp> m_regExps;
int m_screen = -1;
bool m_screenUsed;
bool m_complete;
QPoint m_menuPosition;
bool m_isDesktopView;
};
#endif

View file

@ -1,72 +0,0 @@
/***************************************************************************
* Copyright (C) 2014 by Eike Hein <hein@kde.org> *
* *
* 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 <QRect>
#include <KAbstractViewAdapter>
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

View file

@ -1,126 +0,0 @@
/*
* <one line to give the program's name and a brief idea of what it does.>
* Copyright (C) 2019 camilo <chiguitar@unal.edu.co>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#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;
}

View file

@ -1,71 +0,0 @@
/*
* <one line to give the program's name and a brief idea of what it does.>
* Copyright (C) 2019 camilo <chiguitar@unal.edu.co>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#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

View file

@ -1,233 +0,0 @@
/*
* <one line to give the program's name and a brief idea of what it does.>
* Copyright (C) 2018 camilo <email>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "placeslist.h"
#include "fm.h"
#include <QEventLoop>
#include <QFileSystemWatcher>
#include <QIcon>
#include <QTimer>
#include <KFilePlacesModel>
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<FMH::PATHTYPE_KEY>(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<KFilePlacesModel::GroupType>(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<int> PlacesList::getGroups() const
{
return this->groups;
}
void PlacesList::setGroups(const QList<int> &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());
}

View file

@ -1,102 +0,0 @@
/*
* <one line to give the program's name and a brief idea of what it does.>
* Copyright (C) 2018 camilo <email>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef PLACESLIST_H
#define PLACESLIST_H
#include "baselist.h"
#include <QObject>
class FM;
class KFilePlacesModel;
class QFileSystemWatcher;
class PlacesList : public BaseList
{
Q_OBJECT
Q_PROPERTY(QList<int> groups READ getGroups WRITE setGroups NOTIFY groupsChanged)
public:
PlacesList(QObject *parent = nullptr);
const FMH::MODEL_LIST &items() const override;
QList<int> getGroups() const;
void setGroups(const QList<int> &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<QString, int> count;
QList<int> 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

View file

@ -1,33 +0,0 @@
#ifndef RUBBERBAND_H
#define RUBBERBAND_H
#include <QQuickPaintedItem>
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

View file

@ -1,283 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1">
<context>
<name>BrowserMenu</name>
<message>
<location filename="../qml/BrowserMenu.qml" line="17"/>
<source>New Folder</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/BrowserMenu.qml" line="27"/>
<source>Paste</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/BrowserMenu.qml" line="39"/>
<source>Open in Terminal</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/BrowserMenu.qml" line="33"/>
<source>Select All</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/BrowserMenu.qml" line="44"/>
<source>Properties</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/BrowserMenu.qml" line="53"/>
<source>Empty Trash</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>BrowserView</name>
<message>
<location filename="../qml/BrowserView.qml" line="44"/>
<source>No Files</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>DesktopView</name>
<message>
<location filename="../src/desktop/desktopview.cpp" line="19"/>
<source>Desktop</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FolderModel</name>
<message>
<location filename="../src/lib/foldermodel.cpp" line="1603"/>
<source>Cut</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/lib/foldermodel.cpp" line="1606"/>
<source>Copy</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/lib/foldermodel.cpp" line="1609"/>
<source>Undo</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/lib/foldermodel.cpp" line="1617"/>
<source>Paste</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/lib/foldermodel.cpp" line="1624"/>
<source>New Folder</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/lib/foldermodel.cpp" line="1627"/>
<source>New Documents</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/lib/foldermodel.cpp" line="1633"/>
<source>Rename</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/lib/foldermodel.cpp" line="1636"/>
<source>Move To Trash</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/lib/foldermodel.cpp" line="1639"/>
<source>&amp;Empty Trash</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/lib/foldermodel.cpp" line="1642"/>
<source>Restore from trash</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/lib/foldermodel.cpp" line="1645"/>
<source>Delete</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/lib/foldermodel.cpp" line="1648"/>
<source>&amp;Open</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/lib/foldermodel.cpp" line="1799"/>
<source>Select All</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/lib/foldermodel.cpp" line="1816"/>
<source>Change Wallpaper</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/lib/foldermodel.cpp" line="1825"/>
<source>Properties</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/lib/foldermodel.cpp" line="1873"/>
<source>Set as Wallpaper</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/lib/foldermodel.cpp" line="1881"/>
<source>&amp;Properties</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ItemMenu</name>
<message>
<location filename="../qml/ItemMenu.qml" line="24"/>
<source>Open</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/ItemMenu.qml" line="32"/>
<source>Copy</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/ItemMenu.qml" line="40"/>
<source>Cut</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/ItemMenu.qml" line="48"/>
<source>Move to Trash</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/ItemMenu.qml" line="58"/>
<source>Rename</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/ItemMenu.qml" line="66"/>
<source>Open in Terminal</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/ItemMenu.qml" line="71"/>
<source>Set As Wallpaper</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/ItemMenu.qml" line="81"/>
<source>Properties</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PlacesModel</name>
<message>
<location filename="../src/lib/placesmodel.cpp" line="11"/>
<source>Home</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/lib/placesmodel.cpp" line="18"/>
<source>Desktop</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/lib/placesmodel.cpp" line="25"/>
<source>Documents</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/lib/placesmodel.cpp" line="32"/>
<source>Downloads</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/lib/placesmodel.cpp" line="39"/>
<source>Music</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/lib/placesmodel.cpp" line="46"/>
<source>Pictures</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/lib/placesmodel.cpp" line="53"/>
<source>Videos</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/lib/placesmodel.cpp" line="58"/>
<source>Trash</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PropertiesDialog</name>
<message>
<location filename="../qml/Dialogs/PropertiesDialog.qml" line="9"/>
<source>Properties</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/Dialogs/PropertiesDialog.qml" line="71"/>
<source>Type:</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/Dialogs/PropertiesDialog.qml" line="80"/>
<source>Location:</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/Dialogs/PropertiesDialog.qml" line="90"/>
<source>Size:</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/Dialogs/PropertiesDialog.qml" line="102"/>
<source>Created:</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/Dialogs/PropertiesDialog.qml" line="114"/>
<source>Modified:</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/Dialogs/PropertiesDialog.qml" line="126"/>
<source>Accessed:</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/Dialogs/PropertiesDialog.qml" line="147"/>
<source>Cancel</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/Dialogs/PropertiesDialog.qml" line="152"/>
<source>OK</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>main</name>
<message>
<location filename="../qml/main.qml" line="17"/>
<source>File Manager</source>
<translation type="unfinished"></translation>
</message>
</context>
</TS>

View file

@ -1,51 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="zh_CN">
<context>
<name>BrowserMenu</name>
<message>
<location filename="../qml/BrowserMenu.qml" line="17"/>
<source>New Folder</source>
<translation></translation>
</message>
<message>
<location filename="../qml/BrowserMenu.qml" line="27"/>
<source>Paste</source>
<translation></translation>
</message>
<message>
<location filename="../qml/BrowserMenu.qml" line="39"/>
<source>Open in Terminal</source>
<translation></translation>
</message>
<message>
<location filename="../qml/BrowserMenu.qml" line="33"/>
<source>Select All</source>
<translation></translation>
</message>
<message>
<location filename="../qml/BrowserMenu.qml" line="44"/>
<source>Properties</source>
<translation></translation>
</message>
<message>
<location filename="../qml/BrowserMenu.qml" line="53"/>
<source>Empty Trash</source>
<translation></translation>
</message>
</context>
<context>
<name>BrowserView</name>
<message>
<location filename="../qml/BrowserView.qml" line="44"/>
<source>No Files</source>
<translation></translation>
</message>
</context>
<context>
<name>DesktopView</name>
<message>
<location filename="../src/desktop/desktopview.cpp" line="19"/>
<location filename="../desktop/desktopview.cpp" line="37"/>
<source>Desktop</source>
<translation></translation>
</message>
@ -53,173 +12,138 @@
<context>
<name>FolderModel</name>
<message>
<location filename="../src/lib/foldermodel.cpp" line="1603"/>
<source>Cut</source>
<translation></translation>
<location filename="../model/foldermodel.cpp" line="407"/>
<source>%1 selected</source>
<translation> %1 </translation>
</message>
<message>
<location filename="../src/lib/foldermodel.cpp" line="1606"/>
<source>Copy</source>
<translation></translation>
<location filename="../model/foldermodel.cpp" line="411"/>
<source>%1 item</source>
<translation>%1 </translation>
</message>
<message>
<location filename="../src/lib/foldermodel.cpp" line="1609"/>
<source>Undo</source>
<translation></translation>
<location filename="../model/foldermodel.cpp" line="415"/>
<source>%1 items</source>
<translation>%1 </translation>
</message>
<message>
<location filename="../src/lib/foldermodel.cpp" line="1617"/>
<source>Paste</source>
<translation></translation>
</message>
<message>
<location filename="../src/lib/foldermodel.cpp" line="1624"/>
<source>New Folder</source>
<translation></translation>
</message>
<message>
<location filename="../src/lib/foldermodel.cpp" line="1627"/>
<source>New Documents</source>
<translation></translation>
</message>
<message>
<location filename="../src/lib/foldermodel.cpp" line="1633"/>
<source>Rename</source>
<translation></translation>
</message>
<message>
<location filename="../src/lib/foldermodel.cpp" line="1636"/>
<source>Move To Trash</source>
<translation></translation>
</message>
<message>
<location filename="../src/lib/foldermodel.cpp" line="1639"/>
<source>&amp;Empty Trash</source>
<translation>&amp;</translation>
</message>
<message>
<location filename="../src/lib/foldermodel.cpp" line="1642"/>
<source>Restore from trash</source>
<translation></translation>
</message>
<message>
<location filename="../src/lib/foldermodel.cpp" line="1645"/>
<source>Delete</source>
<translation></translation>
</message>
<message>
<location filename="../src/lib/foldermodel.cpp" line="1648"/>
<source>&amp;Open</source>
<translation>&amp;</translation>
</message>
<message>
<location filename="../src/lib/foldermodel.cpp" line="1799"/>
<location filename="../model/foldermodel.cpp" line="746"/>
<source>Select All</source>
<translation></translation>
</message>
<message>
<location filename="../src/lib/foldermodel.cpp" line="1816"/>
<source>Change Wallpaper</source>
<translation></translation>
</message>
<message>
<location filename="../src/lib/foldermodel.cpp" line="1825"/>
<source>Properties</source>
<translation></translation>
</message>
<message>
<location filename="../src/lib/foldermodel.cpp" line="1873"/>
<source>Set as Wallpaper</source>
<translation></translation>
</message>
<message>
<location filename="../src/lib/foldermodel.cpp" line="1881"/>
<source>&amp;Properties</source>
<translation>&amp;</translation>
</message>
</context>
<context>
<name>ItemMenu</name>
<message>
<location filename="../qml/ItemMenu.qml" line="24"/>
<location filename="../model/foldermodel.cpp" line="924"/>
<source>Open</source>
<translation></translation>
</message>
<message>
<location filename="../qml/ItemMenu.qml" line="32"/>
<source>Copy</source>
<translation></translation>
</message>
<message>
<location filename="../qml/ItemMenu.qml" line="40"/>
<location filename="../model/foldermodel.cpp" line="927"/>
<source>Cut</source>
<translation></translation>
</message>
<message>
<location filename="../qml/ItemMenu.qml" line="48"/>
<source>Move to Trash</source>
<translation></translation>
<location filename="../model/foldermodel.cpp" line="930"/>
<source>Copy</source>
<translation></translation>
</message>
<message>
<location filename="../qml/ItemMenu.qml" line="58"/>
<location filename="../model/foldermodel.cpp" line="933"/>
<source>Paste</source>
<translation></translation>
</message>
<message>
<location filename="../model/foldermodel.cpp" line="936"/>
<source>New Folder</source>
<translation></translation>
</message>
<message>
<location filename="../model/foldermodel.cpp" line="938"/>
<source>Move To Trash</source>
<translation></translation>
</message>
<message>
<location filename="../model/foldermodel.cpp" line="941"/>
<source>Empty Trash</source>
<translation></translation>
</message>
<message>
<location filename="../model/foldermodel.cpp" line="944"/>
<source>Delete</source>
<translation></translation>
</message>
<message>
<location filename="../model/foldermodel.cpp" line="947"/>
<source>Rename</source>
<translation></translation>
</message>
<message>
<location filename="../qml/ItemMenu.qml" line="66"/>
<location filename="../model/foldermodel.cpp" line="950"/>
<source>Open in Terminal</source>
<translation></translation>
</message>
<message>
<location filename="../qml/ItemMenu.qml" line="71"/>
<source>Set As Wallpaper</source>
<location filename="../model/foldermodel.cpp" line="953"/>
<source>Set as Wallpaper</source>
<translation></translation>
</message>
<message>
<location filename="../qml/ItemMenu.qml" line="81"/>
<location filename="../model/foldermodel.cpp" line="956"/>
<source>Properties</source>
<translation></translation>
</message>
</context>
<context>
<name>FolderPage</name>
<message>
<location filename="../qml/FolderPage.qml" line="32"/>
<source>No files</source>
<translation></translation>
</message>
<message>
<location filename="../qml/FolderPage.qml" line="97"/>
<source>Empty Trash</source>
<translation></translation>
</message>
</context>
<context>
<name>PlacesModel</name>
<message>
<location filename="../src/lib/placesmodel.cpp" line="11"/>
<location filename="../model/placesmodel.cpp" line="38"/>
<source>Home</source>
<translation></translation>
</message>
<message>
<location filename="../src/lib/placesmodel.cpp" line="18"/>
<location filename="../model/placesmodel.cpp" line="45"/>
<source>Desktop</source>
<translation></translation>
</message>
<message>
<location filename="../src/lib/placesmodel.cpp" line="25"/>
<location filename="../model/placesmodel.cpp" line="52"/>
<source>Documents</source>
<translation></translation>
</message>
<message>
<location filename="../src/lib/placesmodel.cpp" line="32"/>
<location filename="../model/placesmodel.cpp" line="59"/>
<source>Downloads</source>
<translation></translation>
</message>
<message>
<location filename="../src/lib/placesmodel.cpp" line="39"/>
<location filename="../model/placesmodel.cpp" line="66"/>
<source>Music</source>
<translation></translation>
</message>
<message>
<location filename="../src/lib/placesmodel.cpp" line="46"/>
<location filename="../model/placesmodel.cpp" line="73"/>
<source>Pictures</source>
<translation></translation>
</message>
<message>
<location filename="../src/lib/placesmodel.cpp" line="53"/>
<location filename="../model/placesmodel.cpp" line="80"/>
<source>Videos</source>
<translation></translation>
</message>
<message>
<location filename="../src/lib/placesmodel.cpp" line="58"/>
<location filename="../model/placesmodel.cpp" line="85"/>
<source>Trash</source>
<translation></translation>
</message>
@ -232,50 +156,60 @@
<translation></translation>
</message>
<message>
<location filename="../qml/Dialogs/PropertiesDialog.qml" line="71"/>
<location filename="../qml/Dialogs/PropertiesDialog.qml" line="84"/>
<source>Type:</source>
<translation>:</translation>
</message>
<message>
<location filename="../qml/Dialogs/PropertiesDialog.qml" line="80"/>
<location filename="../qml/Dialogs/PropertiesDialog.qml" line="97"/>
<source>Location:</source>
<translation>:</translation>
</message>
<message>
<location filename="../qml/Dialogs/PropertiesDialog.qml" line="90"/>
<location filename="../qml/Dialogs/PropertiesDialog.qml" line="108"/>
<source>Size:</source>
<translation>:</translation>
</message>
<message>
<location filename="../qml/Dialogs/PropertiesDialog.qml" line="102"/>
<location filename="../qml/Dialogs/PropertiesDialog.qml" line="116"/>
<source>Calculating...</source>
<translation>...</translation>
</message>
<message>
<location filename="../qml/Dialogs/PropertiesDialog.qml" line="121"/>
<source>Created:</source>
<translation>:</translation>
</message>
<message>
<location filename="../qml/Dialogs/PropertiesDialog.qml" line="114"/>
<location filename="../qml/Dialogs/PropertiesDialog.qml" line="134"/>
<source>Modified:</source>
<translation>:</translation>
</message>
<message>
<location filename="../qml/Dialogs/PropertiesDialog.qml" line="126"/>
<location filename="../qml/Dialogs/PropertiesDialog.qml" line="147"/>
<source>Accessed:</source>
<translation>访:</translation>
</message>
<message>
<location filename="../qml/Dialogs/PropertiesDialog.qml" line="147"/>
<location filename="../qml/Dialogs/PropertiesDialog.qml" line="169"/>
<source>Cancel</source>
<translation></translation>
</message>
<message>
<location filename="../qml/Dialogs/PropertiesDialog.qml" line="152"/>
<location filename="../qml/Dialogs/PropertiesDialog.qml" line="178"/>
<source>OK</source>
<translation></translation>
</message>
<message>
<location filename="../dialogs/propertiesdialog.cpp" line="209"/>
<source>%1 files</source>
<translation>%1 </translation>
</message>
</context>
<context>
<name>main</name>
<message>
<location filename="../qml/main.qml" line="17"/>
<location filename="../qml/main.qml" line="16"/>
<source>File Manager</source>
<translation></translation>
</message>

View file

@ -1,22 +1,3 @@
/*
* Copyright © 2008 Fredrik Höglund <fredrik@kde.org>
*
* 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 <QModelIndex>
@ -24,7 +5,7 @@
#include <QSize>
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();
}

54
widgets/itemviewadapter.h Normal file
View file

@ -0,0 +1,54 @@
#ifndef ITEMVIEWADAPTER_H
#define ITEMVIEWADAPTER_H
#include <QRect>
#include <QAbstractItemModel>
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

View file

@ -1,3 +1,22 @@
/*
* Copyright (C) 2021 CutefishOS Team.
*
* Author: revenmartin <revenmartin@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "rubberband.h"
#include <QApplication>

52
widgets/rubberband.h Normal file
View file

@ -0,0 +1,52 @@
/*
* Copyright (C) 2021 CutefishOS Team.
*
* Author: revenmartin <revenmartin@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef RUBBERBAND_H
#define RUBBERBAND_H
#include <QQuickPaintedItem>
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