Refactor the code
This commit is contained in:
parent
aa449f52ad
commit
c3d7c6eedf
90 changed files with 4047 additions and 10926 deletions
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
```
|
||||
|
|
|
@ -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
1
debian/control
vendored
|
@ -6,6 +6,7 @@ Build-Depends: cmake,
|
|||
debhelper (>= 9),
|
||||
extra-cmake-modules,
|
||||
libkf5kio-dev,
|
||||
libkf5solid-dev,
|
||||
qtbase5-dev,
|
||||
qtbase5-private-dev,
|
||||
qtdeclarative5-dev,
|
||||
|
|
|
@ -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>
|
|
@ -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
|
||||
|
|
@ -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
50
desktop/desktopview.h
Normal 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
53
desktopiconprovider.cpp
Normal 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
33
desktopiconprovider.h
Normal 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
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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
34
helper/pathhistory.cpp
Normal 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
23
helper/pathhistory.h
Normal 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
37
helper/thumbnailer.cpp
Normal 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
25
helper/thumbnailer.h
Normal 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
|
|
@ -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 |
|
@ -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
39
model/dirlister.cpp
Normal 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
41
model/dirlister.h
Normal 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
1126
model/foldermodel.cpp
Normal file
File diff suppressed because it is too large
Load diff
202
model/foldermodel.h
Normal file
202
model/foldermodel.h
Normal 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
139
model/pathbarmodel.cpp
Normal 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
67
model/pathbarmodel.h
Normal 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
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
|
|
@ -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()
|
|
@ -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
|
||||
|
|
@ -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
31
qml.qrc
|
@ -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>
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
111
qml/Desktop/main.qml
Normal 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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
12
qml/Dialogs/CreateFolderDialog.qml
Normal file
12
qml/Dialogs/CreateFolderDialog.qml
Normal 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
|
||||
}
|
|
@ -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
102
qml/FolderGridItem.qml
Normal 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
380
qml/FolderGridView.qml
Normal 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)
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
91
qml/FolderListItem.qml
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
213
qml/FolderPage.qml
Normal 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()
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
101
qml/ItemMenu.qml
101
qml/ItemMenu.qml
|
@ -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()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
136
qml/PathBar.qml
136
qml/PathBar.qml
|
@ -1,106 +1,80 @@
|
|||
import QtQuick 2.4
|
||||
import QtQuick.Controls 2.4
|
||||
import QtQuick 2.12
|
||||
import QtQuick.Controls 2.12
|
||||
import QtGraphicalEffects 1.0
|
||||
import MeuiKit 1.0 as Meui
|
||||
|
||||
import Cutefish.FileManager 1.0
|
||||
import MeuiKit 1.0 as Meui
|
||||
|
||||
Item {
|
||||
id: control
|
||||
|
||||
property string url: ""
|
||||
signal placeClicked(string path)
|
||||
signal pathChanged(string path)
|
||||
|
||||
onUrlChanged: {
|
||||
_pathList.path = control.url
|
||||
}
|
||||
|
||||
BaseModel {
|
||||
id: _pathModel
|
||||
list: _pathList
|
||||
}
|
||||
|
||||
PathList {
|
||||
id: _pathList
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
radius: Meui.Theme.smallRadius
|
||||
color: Meui.Theme.backgroundColor
|
||||
}
|
||||
signal itemClicked(string path)
|
||||
signal editorAccepted(string path)
|
||||
|
||||
ListView {
|
||||
id: listView
|
||||
id: _pathView
|
||||
anchors.fill: parent
|
||||
model: _pathModel
|
||||
model: _pathBarModel
|
||||
orientation: Qt.Horizontal
|
||||
layoutDirection: Qt.LeftToRight
|
||||
clip: true
|
||||
|
||||
leftMargin: 3
|
||||
rightMargin: 3
|
||||
spacing: Meui.Units.smallSpacing
|
||||
|
||||
onCountChanged: {
|
||||
currentIndex = listView.count - 1
|
||||
listView.positionViewAtEnd()
|
||||
_pathView.currentIndex = _pathView.count - 1
|
||||
_pathView.positionViewAtEnd()
|
||||
}
|
||||
|
||||
delegate: MouseArea {
|
||||
id: pathBarItem
|
||||
height: listView.height
|
||||
width: label.width + Meui.Units.largeSpacing * 2
|
||||
|
||||
property bool selected: index === listView.count - 1
|
||||
property color pressedColor: Qt.rgba(Meui.Theme.textColor.r,
|
||||
Meui.Theme.textColor.g,
|
||||
Meui.Theme.textColor.b, 0.5)
|
||||
|
||||
onClicked: control.placeClicked(model.path)
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 2
|
||||
color: Meui.Theme.highlightColor
|
||||
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
|
||||
}
|
||||
}
|
||||
|
|
113
qml/SideBar.qml
113
qml/SideBar.qml
|
@ -1,55 +1,28 @@
|
|||
import QtQuick 2.4
|
||||
import QtQuick.Layouts 1.3
|
||||
import QtQuick.Controls 2.4
|
||||
import QtQuick 2.12
|
||||
import QtQuick.Layouts 1.12
|
||||
import QtQuick.Controls 2.12
|
||||
import QtGraphicalEffects 1.0
|
||||
|
||||
import MeuiKit 1.0 as Meui
|
||||
import Cutefish.FileManager 1.0
|
||||
|
||||
ListView {
|
||||
id: control
|
||||
implicitWidth: 200
|
||||
id: sideBar
|
||||
|
||||
property string currentUrl
|
||||
|
||||
signal placeClicked(string path)
|
||||
signal itemClicked(int index)
|
||||
|
||||
onItemClicked: {
|
||||
var item = placesModel.get(index)
|
||||
control.placeClicked(item.url)
|
||||
}
|
||||
|
||||
onCurrentUrlChanged: {
|
||||
syncIndex(currentUrl)
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
syncIndex(currentUrl)
|
||||
}
|
||||
|
||||
function syncIndex(path) {
|
||||
control.currentIndex = -1
|
||||
|
||||
for (var i = 0; i < control.count; ++i) {
|
||||
if (path === control.model.get(i).url) {
|
||||
control.currentIndex = i
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
signal clicked(string path)
|
||||
|
||||
PlacesModel {
|
||||
id: placesModel
|
||||
}
|
||||
|
||||
model: placesModel
|
||||
clip: true
|
||||
spacing: Meui.Units.smallSpacing
|
||||
|
||||
leftMargin: Meui.Units.smallSpacing
|
||||
rightMargin: Meui.Units.smallSpacing
|
||||
|
||||
model: placesModel
|
||||
spacing: Meui.Units.largeSpacing
|
||||
|
||||
ScrollBar.vertical: ScrollBar {}
|
||||
flickableDirection: Flickable.VerticalFlick
|
||||
|
||||
highlightFollowsCurrentItem: true
|
||||
highlightMoveDuration: 0
|
||||
|
@ -60,15 +33,71 @@ ListView {
|
|||
color: Meui.Theme.highlightColor
|
||||
}
|
||||
|
||||
delegate: SidebarItem {
|
||||
id: listItem
|
||||
checked: control.currentIndex === index
|
||||
delegate: Item {
|
||||
id: _item
|
||||
width: ListView.view.width - ListView.view.leftMargin - ListView.view.rightMargin
|
||||
height: 40
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
95
qml/main.qml
95
qml/main.qml
|
@ -1,105 +1,122 @@
|
|||
import QtQuick 2.12
|
||||
import QtQuick.Controls 2.12
|
||||
import QtQuick.Layouts 1.12
|
||||
import QtQuick.Window 2.12
|
||||
import QtQuick.Controls 2.4
|
||||
import QtQuick.Layouts 1.3
|
||||
|
||||
import Cutefish.FileManager 1.0
|
||||
|
||||
import MeuiKit 1.0 as Meui
|
||||
|
||||
import "./Controls"
|
||||
|
||||
Meui.Window {
|
||||
id: root
|
||||
width: settings.width
|
||||
height: settings.height
|
||||
minimumWidth: 900
|
||||
minimumHeight: 600
|
||||
minimumHeight: 580
|
||||
visible: true
|
||||
title: qsTr("File Manager")
|
||||
|
||||
hideHeaderOnMaximize: false
|
||||
headerBarHeight: 35 + Meui.Units.largeSpacing
|
||||
backgroundColor: Meui.Theme.secondBackgroundColor
|
||||
|
||||
property QtObject settings: GlobalSettings { }
|
||||
|
||||
onClosing: {
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
181
src/basemodel.h
181
src/basemodel.h
|
@ -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
|
|
@ -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
|
239
src/fm.cpp
239
src/fm.cpp
|
@ -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
206
src/fm.h
|
@ -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
|
337
src/fmh.cpp
337
src/fmh.cpp
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
626
src/fmlist.cpp
626
src/fmlist.cpp
|
@ -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();
|
||||
}
|
450
src/fmlist.h
450
src/fmlist.h
|
@ -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
|
312
src/fmstatic.cpp
312
src/fmstatic.cpp
|
@ -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);
|
||||
}
|
353
src/fmstatic.h
353
src/fmstatic.h
|
@ -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
|
113
src/handy.cpp
113
src/handy.cpp
|
@ -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();
|
||||
}
|
81
src/handy.h
81
src/handy.h
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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();
|
||||
}
|
|
@ -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
|
@ -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
|
|
@ -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
|
126
src/pathlist.cpp
126
src/pathlist.cpp
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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());
|
||||
}
|
102
src/placeslist.h
102
src/placeslist.h
|
@ -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
|
|
@ -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
|
|
@ -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>&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>&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>&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>
|
|
@ -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>&Empty Trash</source>
|
||||
<translation>&清空回收站</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>&Open</source>
|
||||
<translation>&打开</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>&Properties</source>
|
||||
<translation>&属性</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>
|
||||
|
|
|
@ -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
54
widgets/itemviewadapter.h
Normal 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
|
|
@ -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
52
widgets/rubberband.h
Normal 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
|
Loading…
Reference in a new issue