From 9616be2a08f6c1a45d9ae2c89d44f972b35ccd48 Mon Sep 17 00:00:00 2001 From: Sergey Chupligin Date: Thu, 26 Dec 2024 17:39:35 +0300 Subject: [PATCH 01/12] Start moving from gdocgallery --- src/CMakeLists.txt | 5 +- src/editorplugin/plugin.cpp | 4 +- src/plugin/CMakeLists.txt | 60 +++++++ src/plugin/gallerymodel.cpp | 41 +++++ src/plugin/gallerymodel.h | 40 +++++ src/plugin/glacier.gallery.json | 0 src/plugin/plugin.cpp | 30 ++++ src/plugin/plugin.h | 34 ++++ .../api => plugin/qml}/GalleryDelegate.qml | 0 src/{qml/api => plugin/qml}/GalleryView.qml | 0 src/{qml/api => plugin/qml}/qmldir | 4 +- src/qml/api/GalleryModel.qml | 160 ------------------ src/qml/components/DownListView.qml | 2 - src/qml/components/ImageContainer.qml | 2 - src/qml/pages/MainPage.qml | 6 +- 15 files changed, 212 insertions(+), 176 deletions(-) create mode 100644 src/plugin/CMakeLists.txt create mode 100644 src/plugin/gallerymodel.cpp create mode 100644 src/plugin/gallerymodel.h create mode 100644 src/plugin/glacier.gallery.json create mode 100644 src/plugin/plugin.cpp create mode 100644 src/plugin/plugin.h rename src/{qml/api => plugin/qml}/GalleryDelegate.qml (100%) rename src/{qml/api => plugin/qml}/GalleryView.qml (100%) rename src/{qml/api => plugin/qml}/qmldir (58%) delete mode 100644 src/qml/api/GalleryModel.qml diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 53ae06e..b77108c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -20,6 +20,7 @@ set(SRC qt_add_resources(RESOURCES qml/glacier-gallery.qrc) add_subdirectory(editorplugin) +add_subdirectory(plugin) add_executable(glacier-gallery ${SRC} ${RESOURCES}) @@ -34,7 +35,3 @@ install(TARGETS glacier-gallery RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) FindQtInstallQml() - -install(DIRECTORY qml/api/ - DESTINATION - ${QT_INSTALL_QML}/org/nemomobile/gallery) diff --git a/src/editorplugin/plugin.cpp b/src/editorplugin/plugin.cpp index e6e90f6..0f26e29 100644 --- a/src/editorplugin/plugin.cpp +++ b/src/editorplugin/plugin.cpp @@ -24,11 +24,11 @@ #include "editableimage.h" -class Q_DECL_EXPORT GlacierPackageManagerPlugin : public QQmlExtensionPlugin { +class Q_DECL_EXPORT GlacierImageEditorPlugin : public QQmlExtensionPlugin { Q_OBJECT Q_PLUGIN_METADATA(IID "org.glacier.imageeditor") public: - virtual ~GlacierPackageManagerPlugin() { } + virtual ~GlacierImageEditorPlugin() { } void initializeEngine(QQmlEngine*, const char* uri) { diff --git a/src/plugin/CMakeLists.txt b/src/plugin/CMakeLists.txt new file mode 100644 index 0000000..824bf68 --- /dev/null +++ b/src/plugin/CMakeLists.txt @@ -0,0 +1,60 @@ +### Sets QT_INSTALL_QML to the directory where QML Plugins should be installed +function(FindQtInstallQml) + find_program(QMAKE NAMES qmake6 qmake) + if(NOT QMAKE) + message(FATAL_ERROR "qmake not found") + endif() + execute_process( + COMMAND ${QMAKE} -query QT_INSTALL_QML + OUTPUT_VARIABLE PROC_RESULT + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + set(QT_INSTALL_QML ${PROC_RESULT} PARENT_SCOPE) +endfunction() + +set(SRC + plugin.cpp + gallerymodel.cpp) + +set(HEADERS + plugin.h + gallerymodel.h) + +set(QML + qml/GalleryDelegate.qml + qml/GalleryView.qml) + +add_library(glaciergalleryplugin SHARED ${SRC}) + +qt6_add_qml_module(glaciergalleryplugin + URI Glacier.Gallery + VERSION 1.0 + PLUGIN_TARGET glaciergalleryplugin + NO_GENERATE_PLUGIN_SOURCE + SOURCES + ${SOURCES} + ${HEADERS} + QML_FILES + ${QML} + OUTPUT_DIRECTORY + ${CMAKE_BINARY_DIR}/Glacier/Gallery + RESOURCES qml/qmldir + SOURCES gallerymodel.h gallerymodel.cpp +) + +target_link_libraries(glaciergalleryplugin PUBLIC + Qt6::Core + Qt6::Gui + Qt6::Qml + Qt6::Quick) + +FindQtInstallQml() + +install(TARGETS glaciergalleryplugin + RUNTIME DESTINATION "${QT_INSTALL_QML}/Glacier/Gallery" + BUNDLE DESTINATION "${QT_INSTALL_QML}/Glacier/Gallery" + LIBRARY DESTINATION "${QT_INSTALL_QML}/Glacier/Gallery" +) + +install(DIRECTORY qml/ + DESTINATION ${QT_INSTALL_QML}/Glacier/Gallery) diff --git a/src/plugin/gallerymodel.cpp b/src/plugin/gallerymodel.cpp new file mode 100644 index 0000000..120a799 --- /dev/null +++ b/src/plugin/gallerymodel.cpp @@ -0,0 +1,41 @@ +#include "gallerymodel.h" + +GalleryModel::GalleryModel(QObject *parent) + : QAbstractListModel{parent} + , m_loading(false) + , m_error(true) +{ +} + +int GalleryModel::rowCount(const QModelIndex &parent) const +{ + return -1; +} + +QVariant GalleryModel::data(const QModelIndex &index, int role) const +{ + return QVariant(); +} + +QStringList GalleryModel::sortProperties() const +{ + return m_sortProperties; +} + +void GalleryModel::setSortProperties(const QStringList &newSortProperties) +{ + if (m_sortProperties == newSortProperties) + return; + m_sortProperties = newSortProperties; + emit sortPropertiesChanged(); +} + +bool GalleryModel::loading() const +{ + return m_loading; +} + +bool GalleryModel::error() const +{ + return m_error; +} diff --git a/src/plugin/gallerymodel.h b/src/plugin/gallerymodel.h new file mode 100644 index 0000000..ee87a79 --- /dev/null +++ b/src/plugin/gallerymodel.h @@ -0,0 +1,40 @@ +#ifndef GALLERYMODEL_H +#define GALLERYMODEL_H + +#include + +class GalleryModel : public QAbstractListModel +{ + Q_OBJECT + Q_PROPERTY(QStringList sortProperties READ sortProperties WRITE setSortProperties NOTIFY sortPropertiesChanged FINAL) + Q_PROPERTY(bool loading READ loading NOTIFY loadingChanged FINAL) + Q_PROPERTY(bool error READ error NOTIFY errorChanged FINAL) + +public: + explicit GalleryModel(QObject *parent = nullptr); + int rowCount(const QModelIndex& parent = QModelIndex()) const; + QVariant data(const QModelIndex& index, int role) const; + QHash roleNames() const { return m_hash; } + + QStringList sortProperties() const; + void setSortProperties(const QStringList &newSortProperties); + + bool loading() const; + + bool error() const; + +signals: + void sortPropertiesChanged(); + + void loadingChanged(); + + void errorChanged(); + +private: + QHash m_hash; + QStringList m_sortProperties; + bool m_loading; + bool m_error; +}; + +#endif // GALLERYMODEL_H diff --git a/src/plugin/glacier.gallery.json b/src/plugin/glacier.gallery.json new file mode 100644 index 0000000..e69de29 diff --git a/src/plugin/plugin.cpp b/src/plugin/plugin.cpp new file mode 100644 index 0000000..665c3a0 --- /dev/null +++ b/src/plugin/plugin.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2024 Chupligin Sergey + * + * 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 "plugin.h" +#include "gallerymodel.h" +#include + +void QQuickNemoControlsExtensionPlugin::registerTypes(const char* uri) +{ + Q_ASSERT(uri == QLatin1String("Glacier.Gallery")); + qmlRegisterModule(uri, 1, 0); + //@uri Glacier.Gallery + qmlRegisterType(uri, 1, 0, "GalleryModel"); +} diff --git a/src/plugin/plugin.h b/src/plugin/plugin.h new file mode 100644 index 0000000..71fb081 --- /dev/null +++ b/src/plugin/plugin.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2024 Chupligin Sergey + * + * 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. + */ + + +#ifndef PLUGIN_H +#define PLUGIN_H + +#include + +class QQuickNemoControlsExtensionPlugin : public QQmlExtensionPlugin { +public: + Q_OBJECT + Q_PLUGIN_METADATA(IID QQmlEngineExtensionInterface_iid FILE "glacier.gallery.json") +public: + void registerTypes(const char* uri); +}; + +#endif // PLUGIN_H diff --git a/src/qml/api/GalleryDelegate.qml b/src/plugin/qml/GalleryDelegate.qml similarity index 100% rename from src/qml/api/GalleryDelegate.qml rename to src/plugin/qml/GalleryDelegate.qml diff --git a/src/qml/api/GalleryView.qml b/src/plugin/qml/GalleryView.qml similarity index 100% rename from src/qml/api/GalleryView.qml rename to src/plugin/qml/GalleryView.qml diff --git a/src/qml/api/qmldir b/src/plugin/qml/qmldir similarity index 58% rename from src/qml/api/qmldir rename to src/plugin/qml/qmldir index 6f94f7b..d5d3a1a 100644 --- a/src/qml/api/qmldir +++ b/src/plugin/qml/qmldir @@ -1,3 +1,5 @@ +module Glacier.Gallery +plugin glaciergalleryplugin + GalleryDelegate 1.0 GalleryDelegate.qml -GalleryModel 1.0 GalleryModel.qml GalleryView 1.0 GalleryView.qml diff --git a/src/qml/api/GalleryModel.qml b/src/qml/api/GalleryModel.qml deleted file mode 100644 index 4595a67..0000000 --- a/src/qml/api/GalleryModel.qml +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright (C) 2012 Andrea Bernabei - * Copyright (C) 2022-2023 Chupligin Sergey - * - * You may use this file under the terms of the BSD license as follows: - * - * "Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Nemo Mobile nor the names of its contributors - * may be used to endorse or promote products derived from this - * software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." - */ -import QtQuick -import QtQml.Models 2.15 -import QtDocGallery 5.0 - -ListModel { - id: mainModel; - - function sourceModelsChanged() { - copyReady = false; - var i, item; - mainModel.clear() - for (i = 0; i < pictureGallery.count; i++) { - item = pictureGallery.get(i) - item.url = (String)(item.url); - mainModel.append(item); - } - - for (i = 0; i < videoGallery.count; i++) { - item = videoGallery.get(i) - item.url = (String)(item.url); - mainModel.append(item); - } - copyReady = true; - } - - //destroying the old filter before the new one is assigned makes the gallery model misbehave! - function assignNewDestroyCurrent(newFilter) { - var old = gallery.filter - gallery.filter = newFilter - } - - //this is to create single filters dynamically - function createFilter(parentItem, name, filterType, keyToFilter, value){ - var myFilter = Qt.createQmlObject('import QtDocGallery 5.0;' + filterType + '{property: "' +keyToFilter + '"; value: "' + value + '" }', - parentItem, name); - return myFilter - } - - //this is to create group filters, such as union and intersection ones - function createFiltersArray(parentItem, name, filterType, filtersArray){ - var myFilter = Qt.createQmlObject('import QtDocGallery 5.0;' + filterType + '{ }', - parentItem, name); - myFilter.filters = filtersArray - return myFilter - } - - //this is to know if the item at index "index" is a video - function isVideo(index) { - //elements have index == -1 when the gallery model is being rebuilt (e.g. filters are changed) - //In that case, we must not access model data - //NOTE: this will still return a value, best thing would be to add a check in the other components - //every time before using this method! - if (index !== -1) - { - var mimeString = get(index).mimeType.toString() - return (mimeString.substring(0,5) === "video") - } - } - - - property variant sortProperties: [] - property variant filter; - property bool copyReady: true; - property bool loading: pictureGallery.status != DocumentGalleryModel.Finished || videoGallery.status != DocumentGalleryModel.Finished || !copyReady; - property bool error: pictureGallery.status == DocumentGalleryModel.Error - - property variant pictures: - DocumentGalleryModel { - id: pictureGallery; - properties: [ - "fileName", - "fileSize", - "lastModified", - "mimeType", - "url", - "orientation", - "latitude", - "longitude", - "altitude", - "dateTaken", - "created", - "exposureTime", - "fNumber", - "flashEnabled", - "focalLength", - "meteringMode", - "whiteBalance", - "cameraManufacturer", - "cameraModel", - ] - - autoUpdate: true - rootType: DocumentGallery.Image - onCountChanged: { - sourceModelsChanged(); - } - - } - - property variant videos: DocumentGalleryModel { - id: videoGallery; - properties: [ - "fileName", - "fileSize", - "lastModified", - "mimeType", - "url", - "duration", - "frameRate", - "created", - ] - autoUpdate: true - rootType: DocumentGallery.Video - onCountChanged: { - sourceModelsChanged(); - } - } - - - onSortPropertiesChanged: { - pictureGallery.sortProperties = sortProperties; - videoGallery.sortProperties = sortProperties; - } - - onFilterChanged: { - pictureGallery.filter = filter; - videoGallery.filter = filter; - } -} diff --git a/src/qml/components/DownListView.qml b/src/qml/components/DownListView.qml index 3e006f6..ed738bf 100644 --- a/src/qml/components/DownListView.qml +++ b/src/qml/components/DownListView.qml @@ -35,8 +35,6 @@ import QtQuick.Controls import Nemo import Nemo.Controls -import QtDocGallery 5.0 - Item{ id: downListView property alias model: littleImagesListView.model diff --git a/src/qml/components/ImageContainer.qml b/src/qml/components/ImageContainer.qml index 7f80831..d66d854 100644 --- a/src/qml/components/ImageContainer.qml +++ b/src/qml/components/ImageContainer.qml @@ -35,8 +35,6 @@ import QtQuick.Controls import Nemo import Nemo.Controls -import QtDocGallery 5.0 - Item { id: imgContainer property int index: -1 diff --git a/src/qml/pages/MainPage.qml b/src/qml/pages/MainPage.qml index e54c1fe..3e8dec3 100644 --- a/src/qml/pages/MainPage.qml +++ b/src/qml/pages/MainPage.qml @@ -37,14 +37,10 @@ import QtQuick.Layouts import Nemo import Nemo.Controls -import org.nemomobile.gallery 1.0 -import QtDocGallery 5.0 - +import Glacier.Gallery Page { id: mainPage - width: parent.width; - height: parent.height; headerTools: mainTools GalleryView { From c575491423acf662c8b746e2f2e31d1f558276a7 Mon Sep 17 00:00:00 2001 From: Sergey Chupligin Date: Sat, 28 Dec 2024 12:18:50 +0300 Subject: [PATCH 02/12] Implemet GalleryModel --- src/plugin/gallerymodel.cpp | 149 ++++++++++++++++++++++++--- src/plugin/gallerymodel.h | 61 +++++++++-- src/plugin/qml/GalleryDelegate.qml | 4 +- src/qml/pages/ImageInfoPage.qml | 3 +- src/qml/pages/ImagePage.qml | 4 +- src/qml/pages/ImageSlideshowPage.qml | 4 +- src/qml/pages/MainPage.qml | 2 +- src/qml/pages/SingleImagePage.qml | 4 +- 8 files changed, 197 insertions(+), 34 deletions(-) diff --git a/src/plugin/gallerymodel.cpp b/src/plugin/gallerymodel.cpp index 120a799..4ceb7aa 100644 --- a/src/plugin/gallerymodel.cpp +++ b/src/plugin/gallerymodel.cpp @@ -1,41 +1,166 @@ +/* + * Copyright (C) 2024 Chupligin Sergey + * + * 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 "gallerymodel.h" +#include +#include +#include GalleryModel::GalleryModel(QObject *parent) : QAbstractListModel{parent} , m_loading(false) - , m_error(true) + , m_error(false) + , m_filter(FilterMode::All) + , m_fileSystemWatcher(new QFileSystemWatcher) +{ + m_hash.insert(Qt::UserRole, QByteArray("url")); + formatMimeTypes(); + + connect(this, &GalleryModel::urlsChanged, this, &GalleryModel::onUrlsChanged); + connect(m_fileSystemWatcher, &QFileSystemWatcher::fileChanged, this, &GalleryModel::onFileSystemChanged); + connect(m_fileSystemWatcher, &QFileSystemWatcher::directoryChanged, this, &GalleryModel::onFileSystemChanged); + + formatFileList(); +} + +GalleryModel::~GalleryModel() { + if(m_fileSystemWatcher != nullptr) { + delete m_fileSystemWatcher; + } } int GalleryModel::rowCount(const QModelIndex &parent) const { - return -1; + return m_files.count(); } QVariant GalleryModel::data(const QModelIndex &index, int role) const { + if (!index.isValid()) { + return QVariant(); + } + + if (role == Qt::UserRole) { + return m_files.at(index.row()); + } + return QVariant(); } -QStringList GalleryModel::sortProperties() const +bool GalleryModel::loading() const +{ + return m_loading; +} + +bool GalleryModel::error() const +{ + return m_error; +} + +GalleryModel::FilterMode GalleryModel::filter() const { - return m_sortProperties; + return m_filter; } -void GalleryModel::setSortProperties(const QStringList &newSortProperties) +void GalleryModel::setFilter(FilterMode newFilter) { - if (m_sortProperties == newSortProperties) + if (m_filter == newFilter) return; - m_sortProperties = newSortProperties; - emit sortPropertiesChanged(); + m_filter = newFilter; + formatMimeTypes(); + emit filterChanged(); } -bool GalleryModel::loading() const +void GalleryModel::addPath(QString url) { - return m_loading; + if(!m_urls.contains(url)) { + m_urls.append(url); + emit urlsChanged(); + } + + if(url.isEmpty()) { + m_urls.append(QStandardPaths::standardLocations(QStandardPaths::PicturesLocation)); + emit urlsChanged(); + } } -bool GalleryModel::error() const +void GalleryModel::removePatch(QString url) { - return m_error; + if(url.isEmpty()) { + m_urls.clear(); + emit urlsChanged(); + } else if(m_urls.contains(url)) { + m_urls.removeAll(url); + emit urlsChanged(); + } +} + +void GalleryModel::onUrlsChanged() +{ + m_fileSystemWatcher->removePaths(m_fileSystemWatcher->directories()); + m_fileSystemWatcher->addPaths(m_urls); +} + +void GalleryModel::formatFileList() +{ + QMimeDatabase db; + + if(m_urls.empty()) { + addPath(); + } + + foreach (const QString &dirString, m_urls) { + QDir dir(dirString); + dir.setFilter(QDir::Files | QDir::NoDotAndDotDot | QDir::NoSymLinks); + dir.setSorting(QDir::Time | QDir::Reversed); + + QFileInfoList filelistinfo = dir.entryInfoList(); + foreach (const QFileInfo &fileinfo, filelistinfo) { + if(m_mimeTypes.contains(db.mimeTypeForFile(fileinfo.absoluteFilePath()).name())) { + m_files.append(fileinfo.absoluteFilePath()); + } + } + } +} + +void GalleryModel::onFileSystemChanged(QString path) +{ + qDebug() << Q_FUNC_INFO << path; +} + +void GalleryModel::formatMimeTypes() +{ + QMimeDatabase db; + QList mimeList = db.allMimeTypes(); + + m_mimeTypes << "inode/directory"; + + for (const QMimeType &mime : std::as_const(mimeList)) { + if(m_filter == FilterMode::All) { + if (mime.name().startsWith(QStringLiteral("image/")) || mime.name().startsWith(QStringLiteral("video/"))) { + m_mimeTypes << mime.name(); + } + } else if (m_filter == FilterMode::Images && mime.name().startsWith(QStringLiteral("image/"))) { + m_mimeTypes << mime.name(); + } else if (m_filter == FilterMode::Video && mime.name().startsWith(QStringLiteral("video/"))) { + m_mimeTypes << mime.name(); + } + } } diff --git a/src/plugin/gallerymodel.h b/src/plugin/gallerymodel.h index ee87a79..b1bebd9 100644 --- a/src/plugin/gallerymodel.h +++ b/src/plugin/gallerymodel.h @@ -1,40 +1,85 @@ +/* + * Copyright (C) 2024 Chupligin Sergey + * + * 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. + */ + #ifndef GALLERYMODEL_H #define GALLERYMODEL_H #include +#include class GalleryModel : public QAbstractListModel { Q_OBJECT - Q_PROPERTY(QStringList sortProperties READ sortProperties WRITE setSortProperties NOTIFY sortPropertiesChanged FINAL) Q_PROPERTY(bool loading READ loading NOTIFY loadingChanged FINAL) Q_PROPERTY(bool error READ error NOTIFY errorChanged FINAL) + Q_PROPERTY(GalleryModel::FilterMode filter READ filter WRITE setFilter NOTIFY filterChanged FINAL) public: + enum FilterMode{ + All, + Images, + Video + }; + Q_ENUMS(Filter) + explicit GalleryModel(QObject *parent = nullptr); + virtual ~GalleryModel(); + int rowCount(const QModelIndex& parent = QModelIndex()) const; QVariant data(const QModelIndex& index, int role) const; QHash roleNames() const { return m_hash; } - QStringList sortProperties() const; - void setSortProperties(const QStringList &newSortProperties); - bool loading() const; - bool error() const; + FilterMode filter() const; + void setFilter(FilterMode newFilter); + + void addPath(QString url = ""); + void removePatch(QString url = ""); + signals: void sortPropertiesChanged(); - void loadingChanged(); - void errorChanged(); + void filterChanged(); + + void urlsChanged(); + +private slots: + void onUrlsChanged(); + void onFileSystemChanged(QString path); + void formatFileList(); private: QHash m_hash; - QStringList m_sortProperties; bool m_loading; bool m_error; + GalleryModel::FilterMode m_filter; + + QStringList m_mimeTypes; + QStringList m_urls; + QList m_files; + + QFileSystemWatcher* m_fileSystemWatcher; + + void formatMimeTypes(); }; #endif // GALLERYMODEL_H diff --git a/src/plugin/qml/GalleryDelegate.qml b/src/plugin/qml/GalleryDelegate.qml index ad45713..ab9d1a6 100644 --- a/src/plugin/qml/GalleryDelegate.qml +++ b/src/plugin/qml/GalleryDelegate.qml @@ -42,7 +42,7 @@ Image { asynchronous: true //index is -1 when filters the model is reinitialized (e.g. filters change) so we have to treat that case too - source: (index == -1) ? "" : (GridView.view.model.isVideo(index) ? "file:///usr/share/glacier-gallery/images/GridVideoThumbnail.jpg" : "image://nemoThumbnail/" + url) - + //source: (index == -1) ? "" : (GridView.view.model.isVideo(index) ? "file:///usr/share/glacier-gallery/images/GridVideoThumbnail.jpg" : "image://nemoThumbnail/" + url) + source: "image://nemoThumbnail/" + url } diff --git a/src/qml/pages/ImageInfoPage.qml b/src/qml/pages/ImageInfoPage.qml index e7f2355..1bbec83 100644 --- a/src/qml/pages/ImageInfoPage.qml +++ b/src/qml/pages/ImageInfoPage.qml @@ -1,6 +1,6 @@ /* * Copyright (C) 2012 Andrea Bernabei - * Copyright (C) 2023 Chupligin Sergey + * Copyright (C) 2023-2024 Chupligin Sergey * * You may use this file under the terms of the BSD license as follows: * @@ -36,7 +36,6 @@ import QtQuick.Controls import Nemo import Nemo.Controls - Page { id: imageEditor diff --git a/src/qml/pages/ImagePage.qml b/src/qml/pages/ImagePage.qml index 445e038..f0670b1 100644 --- a/src/qml/pages/ImagePage.qml +++ b/src/qml/pages/ImagePage.qml @@ -1,6 +1,6 @@ /* * Copyright (C) 2012 Andrea Bernabei - * Copyright (C) 2017-2023 Chupligin Sergey + * Copyright (C) 2017-2024 Chupligin Sergey * * You may use this file under the terms of the BSD license as follows: * @@ -36,8 +36,6 @@ import QtQuick.Controls import Nemo import Nemo.Controls -import QtDocGallery 5.0 - import "../components" Page { diff --git a/src/qml/pages/ImageSlideshowPage.qml b/src/qml/pages/ImageSlideshowPage.qml index 329a17a..97217ca 100644 --- a/src/qml/pages/ImageSlideshowPage.qml +++ b/src/qml/pages/ImageSlideshowPage.qml @@ -1,6 +1,6 @@ /* * Copyright (C) 2012 Antti Seppälä - * Copyright (C) 2017-2023 Chupligin Sergey + * Copyright (C) 2017-2024 Chupligin Sergey * * You may use this file under the terms of the BSD license as follows: * @@ -36,8 +36,6 @@ import QtQuick.Controls import Nemo import Nemo.Controls -import QtDocGallery 5.0 - Page { id: imageSlideshow width: parent.width; diff --git a/src/qml/pages/MainPage.qml b/src/qml/pages/MainPage.qml index 3e8dec3..32aad04 100644 --- a/src/qml/pages/MainPage.qml +++ b/src/qml/pages/MainPage.qml @@ -1,6 +1,6 @@ /* * Copyright (C) 2012 Andrea Bernabei - * Copyright (C) 2017-2023 Chupligin Sergey + * Copyright (C) 2017-2024 Chupligin Sergey * * You may use this file under the terms of the BSD license as follows: * diff --git a/src/qml/pages/SingleImagePage.qml b/src/qml/pages/SingleImagePage.qml index 20bf47c..a5d1c69 100644 --- a/src/qml/pages/SingleImagePage.qml +++ b/src/qml/pages/SingleImagePage.qml @@ -1,6 +1,6 @@ /* * Copyright (C) 2012 Andrea Bernabei - * Copyright (C) 2023 Chupligin Sergey + * Copyright (C) 2023-2024 Chupligin Sergey * You may use this file under the terms of the BSD license as follows: * * "Redistribution and use in source and binary forms, with or without @@ -35,8 +35,6 @@ import QtQuick.Controls import Nemo import Nemo.Controls -import QtDocGallery 5.0 - import "../components" Page { From f05f3a6e3957c28676d3ff1ab0403659a5055298 Mon Sep 17 00:00:00 2001 From: Sergey Chupligin Date: Sat, 28 Dec 2024 12:20:51 +0300 Subject: [PATCH 03/12] Fixup codestyle --- src/plugin/gallerymodel.cpp | 30 +++++++++++++++--------------- src/plugin/gallerymodel.h | 7 +++---- src/plugin/plugin.h | 1 - 3 files changed, 18 insertions(+), 20 deletions(-) diff --git a/src/plugin/gallerymodel.cpp b/src/plugin/gallerymodel.cpp index 4ceb7aa..8dd4a07 100644 --- a/src/plugin/gallerymodel.cpp +++ b/src/plugin/gallerymodel.cpp @@ -22,8 +22,8 @@ #include #include -GalleryModel::GalleryModel(QObject *parent) - : QAbstractListModel{parent} +GalleryModel::GalleryModel(QObject* parent) + : QAbstractListModel { parent } , m_loading(false) , m_error(false) , m_filter(FilterMode::All) @@ -41,17 +41,17 @@ GalleryModel::GalleryModel(QObject *parent) GalleryModel::~GalleryModel() { - if(m_fileSystemWatcher != nullptr) { + if (m_fileSystemWatcher != nullptr) { delete m_fileSystemWatcher; } } -int GalleryModel::rowCount(const QModelIndex &parent) const +int GalleryModel::rowCount(const QModelIndex& parent) const { return m_files.count(); } -QVariant GalleryModel::data(const QModelIndex &index, int role) const +QVariant GalleryModel::data(const QModelIndex& index, int role) const { if (!index.isValid()) { return QVariant(); @@ -90,12 +90,12 @@ void GalleryModel::setFilter(FilterMode newFilter) void GalleryModel::addPath(QString url) { - if(!m_urls.contains(url)) { + if (!m_urls.contains(url)) { m_urls.append(url); emit urlsChanged(); } - if(url.isEmpty()) { + if (url.isEmpty()) { m_urls.append(QStandardPaths::standardLocations(QStandardPaths::PicturesLocation)); emit urlsChanged(); } @@ -103,10 +103,10 @@ void GalleryModel::addPath(QString url) void GalleryModel::removePatch(QString url) { - if(url.isEmpty()) { + if (url.isEmpty()) { m_urls.clear(); emit urlsChanged(); - } else if(m_urls.contains(url)) { + } else if (m_urls.contains(url)) { m_urls.removeAll(url); emit urlsChanged(); } @@ -122,18 +122,18 @@ void GalleryModel::formatFileList() { QMimeDatabase db; - if(m_urls.empty()) { + if (m_urls.empty()) { addPath(); } - foreach (const QString &dirString, m_urls) { + foreach (const QString& dirString, m_urls) { QDir dir(dirString); dir.setFilter(QDir::Files | QDir::NoDotAndDotDot | QDir::NoSymLinks); dir.setSorting(QDir::Time | QDir::Reversed); QFileInfoList filelistinfo = dir.entryInfoList(); - foreach (const QFileInfo &fileinfo, filelistinfo) { - if(m_mimeTypes.contains(db.mimeTypeForFile(fileinfo.absoluteFilePath()).name())) { + foreach (const QFileInfo& fileinfo, filelistinfo) { + if (m_mimeTypes.contains(db.mimeTypeForFile(fileinfo.absoluteFilePath()).name())) { m_files.append(fileinfo.absoluteFilePath()); } } @@ -152,8 +152,8 @@ void GalleryModel::formatMimeTypes() m_mimeTypes << "inode/directory"; - for (const QMimeType &mime : std::as_const(mimeList)) { - if(m_filter == FilterMode::All) { + for (const QMimeType& mime : std::as_const(mimeList)) { + if (m_filter == FilterMode::All) { if (mime.name().startsWith(QStringLiteral("image/")) || mime.name().startsWith(QStringLiteral("video/"))) { m_mimeTypes << mime.name(); } diff --git a/src/plugin/gallerymodel.h b/src/plugin/gallerymodel.h index b1bebd9..05b78e5 100644 --- a/src/plugin/gallerymodel.h +++ b/src/plugin/gallerymodel.h @@ -23,22 +23,21 @@ #include #include -class GalleryModel : public QAbstractListModel -{ +class GalleryModel : public QAbstractListModel { Q_OBJECT Q_PROPERTY(bool loading READ loading NOTIFY loadingChanged FINAL) Q_PROPERTY(bool error READ error NOTIFY errorChanged FINAL) Q_PROPERTY(GalleryModel::FilterMode filter READ filter WRITE setFilter NOTIFY filterChanged FINAL) public: - enum FilterMode{ + enum FilterMode { All, Images, Video }; Q_ENUMS(Filter) - explicit GalleryModel(QObject *parent = nullptr); + explicit GalleryModel(QObject* parent = nullptr); virtual ~GalleryModel(); int rowCount(const QModelIndex& parent = QModelIndex()) const; diff --git a/src/plugin/plugin.h b/src/plugin/plugin.h index 71fb081..3e1ae28 100644 --- a/src/plugin/plugin.h +++ b/src/plugin/plugin.h @@ -17,7 +17,6 @@ * Boston, MA 02110-1301, USA. */ - #ifndef PLUGIN_H #define PLUGIN_H From 7f90b9a2a5e5a983448322c8c79cf970e2048ddc Mon Sep 17 00:00:00 2001 From: Sergey Chupligin Date: Wed, 1 Jan 2025 15:47:24 +0300 Subject: [PATCH 04/12] [MainPage] Fixup filters --- src/plugin/gallerymodel.cpp | 59 ++++++++++++++++++++++++++++++++----- src/plugin/gallerymodel.h | 27 +++++++++++++---- src/qml/pages/MainPage.qml | 45 ++++++++++++++++++++-------- 3 files changed, 104 insertions(+), 27 deletions(-) diff --git a/src/plugin/gallerymodel.cpp b/src/plugin/gallerymodel.cpp index 8dd4a07..f2b3700 100644 --- a/src/plugin/gallerymodel.cpp +++ b/src/plugin/gallerymodel.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2024 Chupligin Sergey + * Copyright (C) 2024-2025 Chupligin Sergey * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -26,7 +26,8 @@ GalleryModel::GalleryModel(QObject* parent) : QAbstractListModel { parent } , m_loading(false) , m_error(false) - , m_filter(FilterMode::All) + , m_filter(FilterMode::AllFiles) + , m_sortMode(SortMode::SortByTime) , m_fileSystemWatcher(new QFileSystemWatcher) { m_hash.insert(Qt::UserRole, QByteArray("url")); @@ -84,6 +85,7 @@ void GalleryModel::setFilter(FilterMode newFilter) if (m_filter == newFilter) return; m_filter = newFilter; + formatMimeTypes(); emit filterChanged(); } @@ -120,16 +122,35 @@ void GalleryModel::onUrlsChanged() void GalleryModel::formatFileList() { + beginResetModel(); QMimeDatabase db; if (m_urls.empty()) { addPath(); } + m_files.clear(); + foreach (const QString& dirString, m_urls) { QDir dir(dirString); dir.setFilter(QDir::Files | QDir::NoDotAndDotDot | QDir::NoSymLinks); - dir.setSorting(QDir::Time | QDir::Reversed); + switch (m_sortMode) { + case SortByName: + dir.setSorting(QDir::Name); + break; + case SortByTime: + dir.setSorting(QDir::Time); + break; + case SortBySize: + dir.setSorting(QDir::Size); + break; + case SortByType: + dir.setSorting(QDir::Type); + break; + default: + dir.setSorting(QDir::Unsorted); + break; + } QFileInfoList filelistinfo = dir.entryInfoList(); foreach (const QFileInfo& fileinfo, filelistinfo) { @@ -138,6 +159,7 @@ void GalleryModel::formatFileList() } } } + endResetModel(); } void GalleryModel::onFileSystemChanged(QString path) @@ -150,17 +172,38 @@ void GalleryModel::formatMimeTypes() QMimeDatabase db; QList mimeList = db.allMimeTypes(); + m_mimeTypes.clear(); m_mimeTypes << "inode/directory"; for (const QMimeType& mime : std::as_const(mimeList)) { - if (m_filter == FilterMode::All) { + if (m_filter == FilterMode::AllFiles) { if (mime.name().startsWith(QStringLiteral("image/")) || mime.name().startsWith(QStringLiteral("video/"))) { m_mimeTypes << mime.name(); } - } else if (m_filter == FilterMode::Images && mime.name().startsWith(QStringLiteral("image/"))) { - m_mimeTypes << mime.name(); - } else if (m_filter == FilterMode::Video && mime.name().startsWith(QStringLiteral("video/"))) { - m_mimeTypes << mime.name(); + } else if (m_filter == FilterMode::OnlyImages) { + if (mime.name().startsWith(QStringLiteral("image/"))) { + m_mimeTypes << mime.name(); + } + } else if (m_filter == FilterMode::OnlyVideo) { + if (mime.name().startsWith(QStringLiteral("video/"))) { + m_mimeTypes << mime.name(); + } } } + formatFileList(); +} + +GalleryModel::SortMode GalleryModel::sortMode() const +{ + return m_sortMode; +} + +void GalleryModel::setSortMode(const GalleryModel::SortMode& newSort) +{ + if (m_sortMode == newSort) + return; + m_sortMode = newSort; + emit sortModeChanged(); + + formatFileList(); } diff --git a/src/plugin/gallerymodel.h b/src/plugin/gallerymodel.h index 05b78e5..b822b23 100644 --- a/src/plugin/gallerymodel.h +++ b/src/plugin/gallerymodel.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2024 Chupligin Sergey + * Copyright (C) 2024-2025 Chupligin Sergey * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -25,17 +25,28 @@ class GalleryModel : public QAbstractListModel { Q_OBJECT + Q_PROPERTY(bool loading READ loading NOTIFY loadingChanged FINAL) Q_PROPERTY(bool error READ error NOTIFY errorChanged FINAL) Q_PROPERTY(GalleryModel::FilterMode filter READ filter WRITE setFilter NOTIFY filterChanged FINAL) + Q_PROPERTY(GalleryModel::SortMode sortMode READ sortMode WRITE setSortMode NOTIFY sortModeChanged FINAL) public: enum FilterMode { - All, - Images, - Video + AllFiles, + OnlyImages, + OnlyVideo + }; + + enum SortMode { + SortByName = 0, + SortByTime, + SortBySize, + SortByType, + Unsorted = 255 }; - Q_ENUMS(Filter) + Q_ENUMS(FilterMode) + Q_ENUMS(SortMode) explicit GalleryModel(QObject* parent = nullptr); virtual ~GalleryModel(); @@ -53,13 +64,16 @@ class GalleryModel : public QAbstractListModel { void addPath(QString url = ""); void removePatch(QString url = ""); + GalleryModel::SortMode sortMode() const; + void setSortMode(const GalleryModel::SortMode& newSort); + signals: void sortPropertiesChanged(); void loadingChanged(); void errorChanged(); void filterChanged(); - void urlsChanged(); + void sortModeChanged(); private slots: void onUrlsChanged(); @@ -71,6 +85,7 @@ private slots: bool m_loading; bool m_error; GalleryModel::FilterMode m_filter; + GalleryModel::SortMode m_sortMode; QStringList m_mimeTypes; QStringList m_urls; diff --git a/src/qml/pages/MainPage.qml b/src/qml/pages/MainPage.qml index 32aad04..4a0b3c4 100644 --- a/src/qml/pages/MainPage.qml +++ b/src/qml/pages/MainPage.qml @@ -1,6 +1,6 @@ /* * Copyright (C) 2012 Andrea Bernabei - * Copyright (C) 2017-2024 Chupligin Sergey + * Copyright (C) 2017-2025 Chupligin Sergey * * You may use this file under the terms of the BSD license as follows: * @@ -81,9 +81,22 @@ Page { property int currentSort: -1 ListModel { id: sortModel - ListElement { name: qsTr("None"); sortProperty: ""; ascending: false } // dummy - ListElement { name: qsTr("Name"); sortProperty: "fileName"; ascending: true } - ListElement { name: qsTr("Modified"); sortProperty: "lastModified"; ascending: true } + ListElement { + name: qsTr("None"); + sortProperty: "none"; + } + ListElement { + name: qsTr("Name"); + sortProperty: "name"; + } + ListElement { + name: qsTr("Modified"); + sortProperty: "time"; + } + ListElement { + name: qsTr("Size"); + sortProperty: "size"; + } } HeaderToolsLayout { @@ -117,18 +130,13 @@ Page { onCurrentIndexChanged: { switch (filterButtons.currentIndex) { case 0: - var videoFilter = gallery.createFilter(gallery, "videosfilter", "GalleryStartsWithFilter", "mimeType", "video/") - var imageFilter = gallery.createFilter(gallery, "imagesfilter", "GalleryStartsWithFilter", "mimeType", "image/") - var bothFilter = gallery.createFiltersArray(gallery, "arraysFilter", "GalleryFilterUnion", [videoFilter, imageFilter]) - gallery.assignNewDestroyCurrent(bothFilter) + gallery.filter = GalleryModel.AllFiles break case 1: - var vidFilter = gallery.createFilter(gallery, "videosfilter", "GalleryStartsWithFilter", "mimeType", "video/") - gallery.assignNewDestroyCurrent(vidFilter) + gallery.filter = GalleryModel.OnlyVideo break case 2: - var imgFilter = gallery.createFilter(gallery, "imagesfilter", "GalleryStartsWithFilter", "mimeType", "image/") - gallery.assignNewDestroyCurrent(imgFilter) + gallery.filter = GalleryModel.OnlyImages break } } @@ -152,7 +160,18 @@ Page { } onCurrentIndexChanged: { - gallery.sortProperties = [ sortModel.get(sortButtons.currentIndex).sortProperty ]; + if(sortModel.get(sortButtons.currentIndex).sortProperty == "none") { + gallery.sortMode = GalleryModel.Unsorted; + } + if(sortModel.get(sortButtons.currentIndex).sortProperty == "name") { + gallery.sortMode = GalleryModel.SortByName; + } + if(sortModel.get(sortButtons.currentIndex).sortProperty == "size") { + gallery.sortMode = GalleryModel.SortBySize; + } + if(sortModel.get(sortButtons.currentIndex).sortProperty == "time") { + gallery.sortMode = GalleryModel.SortByTime; + } } } } From d01d166cd173392df0fe2c22ae2f0b66bbe4ec99 Mon Sep 17 00:00:00 2001 From: Sergey Chupligin Date: Wed, 1 Jan 2025 18:25:36 +0300 Subject: [PATCH 05/12] [Gallery] rework isVideo functions --- src/gallery.cpp | 36 +++++------------------------- src/gallery.h | 9 ++++---- src/plugin/gallerymodel.cpp | 13 +++++++++++ src/plugin/gallerymodel.h | 2 ++ src/plugin/qml/GalleryDelegate.qml | 3 +-- 5 files changed, 26 insertions(+), 37 deletions(-) diff --git a/src/gallery.cpp b/src/gallery.cpp index 7a8ae10..f5e37b0 100644 --- a/src/gallery.cpp +++ b/src/gallery.cpp @@ -1,5 +1,5 @@ /* Copyright (C) 2012 John Brooks - * Copyright (C) 2022 Chupligin Sergey (NeoChapay) + * Copyright (C) 2022-2025 Chupligin Sergey (NeoChapay) * You may use this file under the terms of the BSD license as follows: * * Redistribution and use in source and binary forms, with or without @@ -46,10 +46,11 @@ Gallery::Gallery(QObject* parent) { if (QCoreApplication::arguments().length() > 1) { QString cmd = QCoreApplication::arguments().at(1); - if (!cmd.isEmpty()) { - if (isVideo(cmd) != -1) { - m_fileToOpen = cmd; - } + QMimeDatabase db; + QFileInfo fileInfo(cmd); + + if (fileInfo.exists() && fileInfo.isFile() && (db.mimeTypeForFile(fileInfo.absoluteFilePath()).name().startsWith("video/") || db.mimeTypeForFile(fileInfo.absoluteFilePath()).name().startsWith("image/"))) { + m_fileToOpen = cmd; } } } @@ -67,28 +68,3 @@ void Gallery::acquireVideoResources() m_resources->update(); m_resources->acquire(); } - -int Gallery::isVideo(QUrl fileUrl) -{ - if (fileUrl.isEmpty()) { - return -1; - } - - // RETURN VALUES - //-1: ERROR, 0: IMAGE, 1: VIDEO - QString filePath = fileUrl.toLocalFile(); - - QFileInfo testFile(filePath); - if (testFile.exists()) { - QImageReader reader(filePath); - QByteArray format = reader.format(); - if (format.isNull() && reader.error() == QImageReader::UnsupportedFormatError) { - // we assume it's a video - return 1; - } else { - return 0; - } - } - qDebug() << filePath << " exists" << testFile.exists(); - return -1; -} diff --git a/src/gallery.h b/src/gallery.h index a5d9ce4..6e6ccfd 100644 --- a/src/gallery.h +++ b/src/gallery.h @@ -1,8 +1,5 @@ -#ifndef GALLERY_H -#define GALLERY_H - /* Copyright (C) 2012 John Brooks - * Copyright (C) 2022 Chupligin Sergey (NeoChapay) + * Copyright (C) 2022-2025 Chupligin Sergey (NeoChapay) * * You may use this file under the terms of the BSD license as follows: * @@ -32,6 +29,9 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#ifndef GALLERY_H +#define GALLERY_H + #include #include @@ -46,7 +46,6 @@ class Gallery : public QObject { public slots: void acquireVideoResources(); - int isVideo(QUrl fileName); QString fileToOpen() { return m_fileToOpen; } private: diff --git a/src/plugin/gallerymodel.cpp b/src/plugin/gallerymodel.cpp index f2b3700..7aa642b 100644 --- a/src/plugin/gallerymodel.cpp +++ b/src/plugin/gallerymodel.cpp @@ -207,3 +207,16 @@ void GalleryModel::setSortMode(const GalleryModel::SortMode& newSort) formatFileList(); } + +bool GalleryModel::isVideo(int index) +{ + QMimeDatabase db; + if (index < 0 || index > m_files.count()) { + return false; + } + QFileInfo fileInfo(m_files.at(index)); + if (db.mimeTypeForFile(fileInfo.absoluteFilePath()).name().startsWith("video/")) { + return true; + } + return false; +} diff --git a/src/plugin/gallerymodel.h b/src/plugin/gallerymodel.h index b822b23..dd97b02 100644 --- a/src/plugin/gallerymodel.h +++ b/src/plugin/gallerymodel.h @@ -67,6 +67,8 @@ class GalleryModel : public QAbstractListModel { GalleryModel::SortMode sortMode() const; void setSortMode(const GalleryModel::SortMode& newSort); + Q_INVOKABLE bool isVideo(int index); + signals: void sortPropertiesChanged(); void loadingChanged(); diff --git a/src/plugin/qml/GalleryDelegate.qml b/src/plugin/qml/GalleryDelegate.qml index ab9d1a6..778065a 100644 --- a/src/plugin/qml/GalleryDelegate.qml +++ b/src/plugin/qml/GalleryDelegate.qml @@ -42,7 +42,6 @@ Image { asynchronous: true //index is -1 when filters the model is reinitialized (e.g. filters change) so we have to treat that case too - //source: (index == -1) ? "" : (GridView.view.model.isVideo(index) ? "file:///usr/share/glacier-gallery/images/GridVideoThumbnail.jpg" : "image://nemoThumbnail/" + url) - source: "image://nemoThumbnail/" + url + source: (index == -1) ? "" : (GridView.view.model.isVideo(index) ? "file:///usr/share/glacier-gallery/images/GridVideoThumbnail.jpg" : "image://nemoThumbnail/" + url) } From 14920426f036f6082c706423058bbaae9e56c5e7 Mon Sep 17 00:00:00 2001 From: Sergey Chupligin Date: Wed, 1 Jan 2025 21:33:29 +0300 Subject: [PATCH 06/12] [gallery] Move indexing into separate thread --- src/plugin/CMakeLists.txt | 8 ++-- src/plugin/filesystemworker.cpp | 43 +++++++++++++++++++ src/plugin/filesystemworker.h | 45 +++++++++++++++++++ src/plugin/gallerymodel.cpp | 76 +++++++++++++++++++++++++++------ src/plugin/gallerymodel.h | 5 ++- 5 files changed, 159 insertions(+), 18 deletions(-) create mode 100644 src/plugin/filesystemworker.cpp create mode 100644 src/plugin/filesystemworker.h diff --git a/src/plugin/CMakeLists.txt b/src/plugin/CMakeLists.txt index 824bf68..3724c62 100644 --- a/src/plugin/CMakeLists.txt +++ b/src/plugin/CMakeLists.txt @@ -14,11 +14,13 @@ endfunction() set(SRC plugin.cpp - gallerymodel.cpp) + gallerymodel.cpp + filesystemworker.cpp) set(HEADERS plugin.h - gallerymodel.h) + gallerymodel.h + filesystemworker.h) set(QML qml/GalleryDelegate.qml @@ -39,7 +41,7 @@ qt6_add_qml_module(glaciergalleryplugin OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/Glacier/Gallery RESOURCES qml/qmldir - SOURCES gallerymodel.h gallerymodel.cpp + SOURCES ${SOURCES} ) target_link_libraries(glaciergalleryplugin PUBLIC diff --git a/src/plugin/filesystemworker.cpp b/src/plugin/filesystemworker.cpp new file mode 100644 index 0000000..1c7be9b --- /dev/null +++ b/src/plugin/filesystemworker.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2025 Chupligin Sergey + * + * 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 "filesystemworker.h" + +#include +#include + +FileSystemWorker::FileSystemWorker(QStringList dirList, QStringList suffixes, QObject *parent) + : QObject{parent} + , m_dirs(dirList) + , m_suffixes(suffixes) + , m_busy(false) +{ +} + +void FileSystemWorker::start() +{ + m_busy = true; + foreach (const QString& dirString, m_dirs) { + QDirIterator it(dirString, m_suffixes, QDir::Files, QDirIterator::Subdirectories); + while (it.hasNext()) { + emit foundFile(it.next()); + } + } + m_busy = false; +} diff --git a/src/plugin/filesystemworker.h b/src/plugin/filesystemworker.h new file mode 100644 index 0000000..cf775a8 --- /dev/null +++ b/src/plugin/filesystemworker.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2025 Chupligin Sergey + * + * 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. + */ + +#ifndef FILESYSTEMWORKER_H +#define FILESYSTEMWORKER_H + +#include + +class FileSystemWorker : public QObject +{ + Q_OBJECT +public: + explicit FileSystemWorker(QStringList dirList, QStringList suffixes, QObject *parent = nullptr); + + bool busy() {return m_busy;} + + void start(); + void stop(); + +signals: + void foundFile(QString path); + +private: + QStringList m_dirs; + QStringList m_suffixes; + bool m_busy; +}; + +#endif // FILESYSTEMWORKER_H diff --git a/src/plugin/gallerymodel.cpp b/src/plugin/gallerymodel.cpp index 7aa642b..93ab821 100644 --- a/src/plugin/gallerymodel.cpp +++ b/src/plugin/gallerymodel.cpp @@ -18,9 +18,13 @@ */ #include "gallerymodel.h" +#include "filesystemworker.h" + #include #include #include +#include +#include GalleryModel::GalleryModel(QObject* parent) : QAbstractListModel { parent } @@ -98,7 +102,7 @@ void GalleryModel::addPath(QString url) } if (url.isEmpty()) { - m_urls.append(QStandardPaths::standardLocations(QStandardPaths::PicturesLocation)); + m_urls.append(QStandardPaths::standardLocations(QStandardPaths::HomeLocation)); emit urlsChanged(); } } @@ -131,7 +135,22 @@ void GalleryModel::formatFileList() m_files.clear(); - foreach (const QString& dirString, m_urls) { + QStringList suffixes; + foreach (const QMimeType mType, m_mimeTypes) { + foreach (QString suff, mType.suffixes()) { + suffixes << "*." + suff; + } + }; + + FileSystemWorker* work = new FileSystemWorker(m_urls, suffixes); + QThread* scanTread = new QThread; + connect(scanTread, &QThread::started, work, &FileSystemWorker::start); + connect(work, &FileSystemWorker::foundFile, this, &GalleryModel::appendFiles); + + work->moveToThread(scanTread); + scanTread->start(); + + /*foreach (const QString& dirString, m_urls) { QDir dir(dirString); dir.setFilter(QDir::Files | QDir::NoDotAndDotDot | QDir::NoSymLinks); switch (m_sortMode) { @@ -152,16 +171,26 @@ void GalleryModel::formatFileList() break; } - QFileInfoList filelistinfo = dir.entryInfoList(); - foreach (const QFileInfo& fileinfo, filelistinfo) { - if (m_mimeTypes.contains(db.mimeTypeForFile(fileinfo.absoluteFilePath()).name())) { - m_files.append(fileinfo.absoluteFilePath()); - } + + + /*QDirIterator it(dirString, suffixes, QDir::Files, QDirIterator::Subdirectories); + while (it.hasNext()) { + qDebug() << it.next(); } - } + + }*/ endResetModel(); } +void GalleryModel::appendFiles(QString path) +{ + beginInsertRows(QModelIndex(), m_files.count(), m_files.count()); + if(!m_files.contains(path)) { + m_files.push_back(path); + } + endInsertRows(); +} + void GalleryModel::onFileSystemChanged(QString path) { qDebug() << Q_FUNC_INFO << path; @@ -173,20 +202,19 @@ void GalleryModel::formatMimeTypes() QList mimeList = db.allMimeTypes(); m_mimeTypes.clear(); - m_mimeTypes << "inode/directory"; for (const QMimeType& mime : std::as_const(mimeList)) { if (m_filter == FilterMode::AllFiles) { if (mime.name().startsWith(QStringLiteral("image/")) || mime.name().startsWith(QStringLiteral("video/"))) { - m_mimeTypes << mime.name(); + m_mimeTypes << mime; } } else if (m_filter == FilterMode::OnlyImages) { if (mime.name().startsWith(QStringLiteral("image/"))) { - m_mimeTypes << mime.name(); + m_mimeTypes << mime; } } else if (m_filter == FilterMode::OnlyVideo) { if (mime.name().startsWith(QStringLiteral("video/"))) { - m_mimeTypes << mime.name(); + m_mimeTypes << mime; } } } @@ -208,13 +236,33 @@ void GalleryModel::setSortMode(const GalleryModel::SortMode& newSort) formatFileList(); } +QString GalleryModel::sizeTotext(float size) +{ + QStringList list; + list << tr("kb") << tr("mb") << tr("gb") << tr("tb"); + + QStringListIterator i(list); + QString unit("bytes"); + + while (size >= 1024.0 && i.hasNext()) { + unit = i.next(); + size /= 1024.0; + } + return QString().setNum(size, 'f', 2) + " " + unit; +} + bool GalleryModel::isVideo(int index) { QMimeDatabase db; - if (index < 0 || index > m_files.count()) { + if (index < 0 || index >= m_files.count()) { return false; } - QFileInfo fileInfo(m_files.at(index)); + QString url = m_files.at(index); + if(url.isEmpty()) { + return false; + } + + QFileInfo fileInfo(url); if (db.mimeTypeForFile(fileInfo.absoluteFilePath()).name().startsWith("video/")) { return true; } diff --git a/src/plugin/gallerymodel.h b/src/plugin/gallerymodel.h index dd97b02..72f7fb6 100644 --- a/src/plugin/gallerymodel.h +++ b/src/plugin/gallerymodel.h @@ -22,6 +22,7 @@ #include #include +#include class GalleryModel : public QAbstractListModel { Q_OBJECT @@ -67,6 +68,7 @@ class GalleryModel : public QAbstractListModel { GalleryModel::SortMode sortMode() const; void setSortMode(const GalleryModel::SortMode& newSort); + Q_INVOKABLE QString sizeTotext(float size); Q_INVOKABLE bool isVideo(int index); signals: @@ -81,6 +83,7 @@ private slots: void onUrlsChanged(); void onFileSystemChanged(QString path); void formatFileList(); + void appendFiles(QString path); private: QHash m_hash; @@ -89,7 +92,7 @@ private slots: GalleryModel::FilterMode m_filter; GalleryModel::SortMode m_sortMode; - QStringList m_mimeTypes; + QList m_mimeTypes; QStringList m_urls; QList m_files; From 5d75cd28537d923bf29376fbdb4659ac9cda89c5 Mon Sep 17 00:00:00 2001 From: Sergey Chupligin Date: Wed, 1 Jan 2025 21:49:20 +0300 Subject: [PATCH 07/12] Fixup image page --- src/gallery.cpp | 4 ++++ src/gallery.h | 1 + src/plugin/gallerymodel.cpp | 14 ++++++++++++++ src/plugin/gallerymodel.h | 3 +++ src/plugin/qml/GalleryDelegate.qml | 7 +++++-- src/qml/components/DownListView.qml | 4 ++-- src/qml/components/ImageContainer.qml | 4 ++-- 7 files changed, 31 insertions(+), 6 deletions(-) diff --git a/src/gallery.cpp b/src/gallery.cpp index f5e37b0..b68347e 100644 --- a/src/gallery.cpp +++ b/src/gallery.cpp @@ -68,3 +68,7 @@ void Gallery::acquireVideoResources() m_resources->update(); m_resources->acquire(); } +bool Gallery::isVideo(QString url) +{ + return false; +} diff --git a/src/gallery.h b/src/gallery.h index 6e6ccfd..14957dc 100644 --- a/src/gallery.h +++ b/src/gallery.h @@ -43,6 +43,7 @@ class Gallery : public QObject { public: explicit Gallery(QObject* parent = 0); + Q_INVOKABLE bool isVideo(QString url); public slots: void acquireVideoResources(); diff --git a/src/plugin/gallerymodel.cpp b/src/plugin/gallerymodel.cpp index 93ab821..c5f35a4 100644 --- a/src/plugin/gallerymodel.cpp +++ b/src/plugin/gallerymodel.cpp @@ -268,3 +268,17 @@ bool GalleryModel::isVideo(int index) } return false; } + +QVariant GalleryModel::get(const int idx) +{ + if (idx >= m_files.size()) { + return QVariant(); + } + + QMap itemData; + QString item = m_files.at(idx); + + itemData.insert("url", item); + + return QVariant(itemData); +} diff --git a/src/plugin/gallerymodel.h b/src/plugin/gallerymodel.h index 72f7fb6..433a7e6 100644 --- a/src/plugin/gallerymodel.h +++ b/src/plugin/gallerymodel.h @@ -71,6 +71,9 @@ class GalleryModel : public QAbstractListModel { Q_INVOKABLE QString sizeTotext(float size); Q_INVOKABLE bool isVideo(int index); +public slots: + QVariant get(const int idx); + signals: void sortPropertiesChanged(); void loadingChanged(); diff --git a/src/plugin/qml/GalleryDelegate.qml b/src/plugin/qml/GalleryDelegate.qml index 778065a..361336d 100644 --- a/src/plugin/qml/GalleryDelegate.qml +++ b/src/plugin/qml/GalleryDelegate.qml @@ -1,6 +1,6 @@ /* * Copyright (C) 2012 John Brooks - * Copyright (C) 2017-2023 Chupligin Sergey + * Copyright (C) 2017-2025 Chupligin Sergey * * You may use this file under the terms of the BSD license as follows: * @@ -42,6 +42,9 @@ Image { asynchronous: true //index is -1 when filters the model is reinitialized (e.g. filters change) so we have to treat that case too - source: (index == -1) ? "" : (GridView.view.model.isVideo(index) ? "file:///usr/share/glacier-gallery/images/GridVideoThumbnail.jpg" : "image://nemoThumbnail/" + url) + source: (url == undefined) ? "file:///usr/share/glacier-gallery/images/GridVideoThumbnail.jpg" + : (index == -1) ? "" + : (GridView.view.model.isVideo(index) ? "file:///usr/share/glacier-gallery/images/GridVideoThumbnail.jpg" + : "image://nemoThumbnail/" + url) } diff --git a/src/qml/components/DownListView.qml b/src/qml/components/DownListView.qml index ed738bf..3b6ac45 100644 --- a/src/qml/components/DownListView.qml +++ b/src/qml/components/DownListView.qml @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022-2023 Chupligin Sergey + * Copyright (C) 2022-2025 Chupligin Sergey * * You may use this file under the terms of the BSD license as follows: * @@ -63,7 +63,7 @@ Item{ anchors.centerIn: parent sourceSize.width: Theme.itemHeightLarge sourceSize.height: Theme.itemHeightLarge - source: gallery.isVideo(url) ? "file:///usr/share/glacier-gallery/images/DefaultVideoThumbnail.jpg" : url + source: gallery.isVideo(url) ? "file:///usr/share/glacier-gallery/images/DefaultVideoThumbnail.jpg" : "file://"+url fillMode: Image.PreserveAspectFit height: index === currentIndex ? parent.width : parent.width*0.8 width: parent.height diff --git a/src/qml/components/ImageContainer.qml b/src/qml/components/ImageContainer.qml index d66d854..9083087 100644 --- a/src/qml/components/ImageContainer.qml +++ b/src/qml/components/ImageContainer.qml @@ -1,6 +1,6 @@ /* * Copyright (C) 2012 Andrea Bernabei - * Copyright (C) 2023 Chupligin Sergey + * Copyright (C) 2023-2025 Chupligin Sergey * You may use this file under the terms of the BSD license as follows: * * "Redistribution and use in source and binary forms, with or without @@ -95,7 +95,7 @@ Item { height: imgContainer.height fillMode: Image.PreserveAspectFit - source: isVideo ? "file:///usr/share/glacier-gallery/images/DefaultVideoThumbnail.jpg" : imgContainer.source + source: isVideo ? "file:///usr/share/glacier-gallery/images/DefaultVideoThumbnail.jpg" : "file://"+ imgContainer.source MouseArea { anchors.fill: parent From 1b5def7a0001ca9fa1baaeea2b191cbba9b32d44 Mon Sep 17 00:00:00 2001 From: Sergey Chupligin Date: Wed, 1 Jan 2025 21:50:41 +0300 Subject: [PATCH 08/12] Fix codestyle --- src/plugin/filesystemworker.cpp | 6 +++--- src/plugin/filesystemworker.h | 7 +++---- src/plugin/gallerymodel.cpp | 6 +++--- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/plugin/filesystemworker.cpp b/src/plugin/filesystemworker.cpp index 1c7be9b..a759a1c 100644 --- a/src/plugin/filesystemworker.cpp +++ b/src/plugin/filesystemworker.cpp @@ -22,8 +22,8 @@ #include #include -FileSystemWorker::FileSystemWorker(QStringList dirList, QStringList suffixes, QObject *parent) - : QObject{parent} +FileSystemWorker::FileSystemWorker(QStringList dirList, QStringList suffixes, QObject* parent) + : QObject { parent } , m_dirs(dirList) , m_suffixes(suffixes) , m_busy(false) @@ -34,7 +34,7 @@ void FileSystemWorker::start() { m_busy = true; foreach (const QString& dirString, m_dirs) { - QDirIterator it(dirString, m_suffixes, QDir::Files, QDirIterator::Subdirectories); + QDirIterator it(dirString, m_suffixes, QDir::Files, QDirIterator::Subdirectories); while (it.hasNext()) { emit foundFile(it.next()); } diff --git a/src/plugin/filesystemworker.h b/src/plugin/filesystemworker.h index cf775a8..8c1b1f9 100644 --- a/src/plugin/filesystemworker.h +++ b/src/plugin/filesystemworker.h @@ -22,13 +22,12 @@ #include -class FileSystemWorker : public QObject -{ +class FileSystemWorker : public QObject { Q_OBJECT public: - explicit FileSystemWorker(QStringList dirList, QStringList suffixes, QObject *parent = nullptr); + explicit FileSystemWorker(QStringList dirList, QStringList suffixes, QObject* parent = nullptr); - bool busy() {return m_busy;} + bool busy() { return m_busy; } void start(); void stop(); diff --git a/src/plugin/gallerymodel.cpp b/src/plugin/gallerymodel.cpp index c5f35a4..1b2c671 100644 --- a/src/plugin/gallerymodel.cpp +++ b/src/plugin/gallerymodel.cpp @@ -21,9 +21,9 @@ #include "filesystemworker.h" #include +#include #include #include -#include #include GalleryModel::GalleryModel(QObject* parent) @@ -185,7 +185,7 @@ void GalleryModel::formatFileList() void GalleryModel::appendFiles(QString path) { beginInsertRows(QModelIndex(), m_files.count(), m_files.count()); - if(!m_files.contains(path)) { + if (!m_files.contains(path)) { m_files.push_back(path); } endInsertRows(); @@ -258,7 +258,7 @@ bool GalleryModel::isVideo(int index) return false; } QString url = m_files.at(index); - if(url.isEmpty()) { + if (url.isEmpty()) { return false; } From 491709d573f8f2bdc2f70ef90fe1ba6fd61d1188 Mon Sep 17 00:00:00 2001 From: Sergey Chupligin Date: Fri, 3 Jan 2025 15:57:57 +0300 Subject: [PATCH 09/12] [FilesystemWorker] implement stop --- src/plugin/filesystemworker.cpp | 20 +++++++++++++++++--- src/plugin/filesystemworker.h | 2 ++ 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/plugin/filesystemworker.cpp b/src/plugin/filesystemworker.cpp index a759a1c..95b1394 100644 --- a/src/plugin/filesystemworker.cpp +++ b/src/plugin/filesystemworker.cpp @@ -32,12 +32,26 @@ FileSystemWorker::FileSystemWorker(QStringList dirList, QStringList suffixes, QO void FileSystemWorker::start() { + if(m_busy) { + qWarning() << "Stop before run again!"; + return; + } + m_busy = true; foreach (const QString& dirString, m_dirs) { - QDirIterator it(dirString, m_suffixes, QDir::Files, QDirIterator::Subdirectories); - while (it.hasNext()) { - emit foundFile(it.next()); + m_it = new QDirIterator(dirString, m_suffixes, QDir::Files, QDirIterator::Subdirectories); + while (m_it->hasNext()) { + emit foundFile(m_it->next()); } + delete m_it; + } + m_busy = false; +} + +void FileSystemWorker::stop() +{ + if(m_it != nullptr) { + delete m_it; } m_busy = false; } diff --git a/src/plugin/filesystemworker.h b/src/plugin/filesystemworker.h index 8c1b1f9..160a37b 100644 --- a/src/plugin/filesystemworker.h +++ b/src/plugin/filesystemworker.h @@ -20,6 +20,7 @@ #ifndef FILESYSTEMWORKER_H #define FILESYSTEMWORKER_H +#include #include class FileSystemWorker : public QObject { @@ -36,6 +37,7 @@ class FileSystemWorker : public QObject { void foundFile(QString path); private: + QDirIterator* m_it; QStringList m_dirs; QStringList m_suffixes; bool m_busy; From e45c2936c5bbb390310ae18fca29bc2c0d0ebfe2 Mon Sep 17 00:00:00 2001 From: Sergey Chupligin Date: Fri, 3 Jan 2025 17:11:15 +0300 Subject: [PATCH 10/12] Move to MediaFile struct --- .gitignore | 1 + src/plugin/filesystemworker.cpp | 46 +++++++++++++++++++++++++++------ src/plugin/filesystemworker.h | 17 ++++++++++-- src/plugin/gallerymodel.cpp | 34 +++++++++++++++++------- src/plugin/gallerymodel.h | 5 ++-- 5 files changed, 81 insertions(+), 22 deletions(-) diff --git a/.gitignore b/.gitignore index d1884aa..0383a5c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .directory +.commit *.o *.so *.qmlc diff --git a/src/plugin/filesystemworker.cpp b/src/plugin/filesystemworker.cpp index 95b1394..a730341 100644 --- a/src/plugin/filesystemworker.cpp +++ b/src/plugin/filesystemworker.cpp @@ -21,6 +21,8 @@ #include #include +#include +#include FileSystemWorker::FileSystemWorker(QStringList dirList, QStringList suffixes, QObject* parent) : QObject { parent } @@ -32,26 +34,54 @@ FileSystemWorker::FileSystemWorker(QStringList dirList, QStringList suffixes, QO void FileSystemWorker::start() { - if(m_busy) { + if (m_busy) { qWarning() << "Stop before run again!"; return; } + QMimeDatabase db; m_busy = true; + m_mutex.lock(); foreach (const QString& dirString, m_dirs) { - m_it = new QDirIterator(dirString, m_suffixes, QDir::Files, QDirIterator::Subdirectories); - while (m_it->hasNext()) { - emit foundFile(m_it->next()); + if (m_mutex.tryLock()) { + break; + } + QDirIterator it(dirString, m_suffixes, QDir::Files, QDirIterator::Subdirectories); + while (it.hasNext()) { + QString filePath = it.next(); + QFileInfo fInfo(filePath); + + MediaFile file; + file.path = filePath; + file.created = fInfo.birthTime(); + file.modified = fInfo.lastModified(); + file.mimeType = db.mimeTypeForFile(fInfo.absoluteFilePath()); + file.size = fInfo.size(); + + if (file.mimeType.name().startsWith("image/")) { + QImageReader reader(file.path); + QSize size = reader.size(); + if (size.isValid()) { + file.width = size.width(); + file.height = size.height(); + } else { + QImage image = reader.read(); + file.width = image.width(); + file.height = image.height(); + } + } else { + qWarning() << "Unsuported mime " << file.mimeType.name(); + } + file.isValid = file.height > 0 && file.width > 0; + emit foundFile(file); } - delete m_it; } + m_mutex.unlock(); m_busy = false; } void FileSystemWorker::stop() { - if(m_it != nullptr) { - delete m_it; - } + m_mutex.unlock(); m_busy = false; } diff --git a/src/plugin/filesystemworker.h b/src/plugin/filesystemworker.h index 160a37b..1f1d2e6 100644 --- a/src/plugin/filesystemworker.h +++ b/src/plugin/filesystemworker.h @@ -21,8 +21,21 @@ #define FILESYSTEMWORKER_H #include +#include +#include #include +struct MediaFile { + QString path = ""; + bool isValid = false; + QMimeType mimeType; + uint width = -1; + uint height = -1; + QDateTime modified; + QDateTime created; + uint size = -1; +}; + class FileSystemWorker : public QObject { Q_OBJECT public: @@ -34,10 +47,10 @@ class FileSystemWorker : public QObject { void stop(); signals: - void foundFile(QString path); + void foundFile(MediaFile file); private: - QDirIterator* m_it; + QMutex m_mutex; QStringList m_dirs; QStringList m_suffixes; bool m_busy; diff --git a/src/plugin/gallerymodel.cpp b/src/plugin/gallerymodel.cpp index 1b2c671..35f15fd 100644 --- a/src/plugin/gallerymodel.cpp +++ b/src/plugin/gallerymodel.cpp @@ -18,7 +18,6 @@ */ #include "gallerymodel.h" -#include "filesystemworker.h" #include #include @@ -35,6 +34,12 @@ GalleryModel::GalleryModel(QObject* parent) , m_fileSystemWatcher(new QFileSystemWatcher) { m_hash.insert(Qt::UserRole, QByteArray("url")); + m_hash.insert(Qt::UserRole + 1, QByteArray("mimeType")); + m_hash.insert(Qt::UserRole + 2, QByteArray("width")); + m_hash.insert(Qt::UserRole + 3, QByteArray("height")); + m_hash.insert(Qt::UserRole + 4, QByteArray("modified")); + m_hash.insert(Qt::UserRole + 5, QByteArray("created")); + m_hash.insert(Qt::UserRole + 6, QByteArray("fileSize")); formatMimeTypes(); connect(this, &GalleryModel::urlsChanged, this, &GalleryModel::onUrlsChanged); @@ -62,8 +67,9 @@ QVariant GalleryModel::data(const QModelIndex& index, int role) const return QVariant(); } + MediaFile file = m_files.at(index.row()); if (role == Qt::UserRole) { - return m_files.at(index.row()); + return file.path; } return QVariant(); @@ -182,12 +188,14 @@ void GalleryModel::formatFileList() endResetModel(); } -void GalleryModel::appendFiles(QString path) +void GalleryModel::appendFiles(MediaFile file) { - beginInsertRows(QModelIndex(), m_files.count(), m_files.count()); - if (!m_files.contains(path)) { - m_files.push_back(path); + if (!file.isValid) { + return; } + + beginInsertRows(QModelIndex(), m_files.count(), m_files.count()); + m_files.push_back(file); endInsertRows(); } @@ -257,7 +265,7 @@ bool GalleryModel::isVideo(int index) if (index < 0 || index >= m_files.count()) { return false; } - QString url = m_files.at(index); + QString url = m_files.at(index).path; if (url.isEmpty()) { return false; } @@ -276,9 +284,15 @@ QVariant GalleryModel::get(const int idx) } QMap itemData; - QString item = m_files.at(idx); - - itemData.insert("url", item); + MediaFile item = m_files.at(idx); + + itemData.insert("url", item.path); + itemData.insert("mimeType", item.mimeType.name()); + itemData.insert("width", item.width); + itemData.insert("height", item.height); + itemData.insert("modified", item.modified); + itemData.insert("created", item.created); + itemData.insert("fileSize", item.size); return QVariant(itemData); } diff --git a/src/plugin/gallerymodel.h b/src/plugin/gallerymodel.h index 433a7e6..5872115 100644 --- a/src/plugin/gallerymodel.h +++ b/src/plugin/gallerymodel.h @@ -20,6 +20,7 @@ #ifndef GALLERYMODEL_H #define GALLERYMODEL_H +#include "filesystemworker.h" #include #include #include @@ -86,7 +87,7 @@ private slots: void onUrlsChanged(); void onFileSystemChanged(QString path); void formatFileList(); - void appendFiles(QString path); + void appendFiles(MediaFile file); private: QHash m_hash; @@ -97,7 +98,7 @@ private slots: QList m_mimeTypes; QStringList m_urls; - QList m_files; + QList m_files; QFileSystemWatcher* m_fileSystemWatcher; From cc3b96cffd4ee18bfabd838ca87ecae3701708ed Mon Sep 17 00:00:00 2001 From: Sergey Chupligin Date: Sat, 4 Jan 2025 19:52:09 +0300 Subject: [PATCH 11/12] [GalleryDelegate] dont ask model about isVideo --- src/plugin/qml/GalleryDelegate.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugin/qml/GalleryDelegate.qml b/src/plugin/qml/GalleryDelegate.qml index 361336d..2a2180e 100644 --- a/src/plugin/qml/GalleryDelegate.qml +++ b/src/plugin/qml/GalleryDelegate.qml @@ -44,7 +44,7 @@ Image { //index is -1 when filters the model is reinitialized (e.g. filters change) so we have to treat that case too source: (url == undefined) ? "file:///usr/share/glacier-gallery/images/GridVideoThumbnail.jpg" : (index == -1) ? "" - : (GridView.view.model.isVideo(index) ? "file:///usr/share/glacier-gallery/images/GridVideoThumbnail.jpg" - : "image://nemoThumbnail/" + url) + : mimeType.startsWith("video/") ? "file:///usr/share/glacier-gallery/images/GridVideoThumbnail.jpg" + : "image://nemoThumbnail/" + url } From e3c1d794c11db7cee540c0083643629eaf5d1cc3 Mon Sep 17 00:00:00 2001 From: Sergey Chupligin Date: Sat, 4 Jan 2025 20:01:09 +0300 Subject: [PATCH 12/12] Rework model --- src/plugin/filesystemworker.cpp | 25 +++++--- src/plugin/filesystemworker.h | 9 +-- src/plugin/gallerymodel.cpp | 88 ++++++++++----------------- src/plugin/gallerymodel.h | 2 +- src/plugin/plugin.cpp | 1 + src/qml/components/ImageContainer.qml | 4 +- src/qml/pages/ImagePage.qml | 4 +- src/qml/pages/MainPage.qml | 17 +++++- 8 files changed, 76 insertions(+), 74 deletions(-) diff --git a/src/plugin/filesystemworker.cpp b/src/plugin/filesystemworker.cpp index a730341..34af8a2 100644 --- a/src/plugin/filesystemworker.cpp +++ b/src/plugin/filesystemworker.cpp @@ -24,26 +24,37 @@ #include #include -FileSystemWorker::FileSystemWorker(QStringList dirList, QStringList suffixes, QObject* parent) +FileSystemWorker::FileSystemWorker(QObject* parent) : QObject { parent } - , m_dirs(dirList) - , m_suffixes(suffixes) , m_busy(false) { } +void FileSystemWorker::setDirs(const QStringList dirs) +{ + stop(); + m_dirs = dirs; +} + +void FileSystemWorker::setSuffixes(const QStringList suff) +{ + stop(); + m_suffixes = suff; +} + void FileSystemWorker::start() { if (m_busy) { qWarning() << "Stop before run again!"; return; } + QMimeDatabase db; m_busy = true; - m_mutex.lock(); + emit busyChanged(); foreach (const QString& dirString, m_dirs) { - if (m_mutex.tryLock()) { + if (!m_busy) { //STOP break; } QDirIterator it(dirString, m_suffixes, QDir::Files, QDirIterator::Subdirectories); @@ -76,12 +87,12 @@ void FileSystemWorker::start() emit foundFile(file); } } - m_mutex.unlock(); m_busy = false; + emit busyChanged(); } void FileSystemWorker::stop() { - m_mutex.unlock(); m_busy = false; + emit busyChanged(); } diff --git a/src/plugin/filesystemworker.h b/src/plugin/filesystemworker.h index 1f1d2e6..cf34397 100644 --- a/src/plugin/filesystemworker.h +++ b/src/plugin/filesystemworker.h @@ -22,7 +22,6 @@ #include #include -#include #include struct MediaFile { @@ -38,19 +37,21 @@ struct MediaFile { class FileSystemWorker : public QObject { Q_OBJECT + Q_PROPERTY(bool busy READ busy NOTIFY busyChanged FINAL) public: - explicit FileSystemWorker(QStringList dirList, QStringList suffixes, QObject* parent = nullptr); + explicit FileSystemWorker(QObject* parent = nullptr); + void setDirs(QStringList dirs); + void setSuffixes(QStringList suff); bool busy() { return m_busy; } - void start(); void stop(); signals: void foundFile(MediaFile file); + void busyChanged(); private: - QMutex m_mutex; QStringList m_dirs; QStringList m_suffixes; bool m_busy; diff --git a/src/plugin/gallerymodel.cpp b/src/plugin/gallerymodel.cpp index 35f15fd..6186030 100644 --- a/src/plugin/gallerymodel.cpp +++ b/src/plugin/gallerymodel.cpp @@ -32,6 +32,7 @@ GalleryModel::GalleryModel(QObject* parent) , m_filter(FilterMode::AllFiles) , m_sortMode(SortMode::SortByTime) , m_fileSystemWatcher(new QFileSystemWatcher) + , m_work(new FileSystemWorker()) { m_hash.insert(Qt::UserRole, QByteArray("url")); m_hash.insert(Qt::UserRole + 1, QByteArray("mimeType")); @@ -46,6 +47,18 @@ GalleryModel::GalleryModel(QObject* parent) connect(m_fileSystemWatcher, &QFileSystemWatcher::fileChanged, this, &GalleryModel::onFileSystemChanged); connect(m_fileSystemWatcher, &QFileSystemWatcher::directoryChanged, this, &GalleryModel::onFileSystemChanged); + QThread* scanTread = new QThread; + connect(m_work, &FileSystemWorker::foundFile, this, &GalleryModel::appendFiles); + connect(m_work ,&FileSystemWorker::busyChanged, [=](){ + if(m_work->busy() != m_loading) { + m_loading = m_work->busy(); + emit loadingChanged(); + } + }); + + m_work->moveToThread(scanTread); + scanTread->start(); + formatFileList(); } @@ -70,8 +83,21 @@ QVariant GalleryModel::data(const QModelIndex& index, int role) const MediaFile file = m_files.at(index.row()); if (role == Qt::UserRole) { return file.path; + } else if (role == Qt::UserRole+1) { + return file.mimeType.name(); + } else if (role == Qt::UserRole+2) { + return file.width; + } else if (role == Qt::UserRole+3) { + return file.height; + } else if (role == Qt::UserRole+4) { + return file.modified; + } else if (role == Qt::UserRole+5) { + return file.created; + } else if (role == Qt::UserRole+6) { + return file.size; } + return QVariant(); } @@ -108,7 +134,7 @@ void GalleryModel::addPath(QString url) } if (url.isEmpty()) { - m_urls.append(QStandardPaths::standardLocations(QStandardPaths::HomeLocation)); + m_urls.append(QStandardPaths::standardLocations(QStandardPaths::PicturesLocation)); emit urlsChanged(); } } @@ -140,6 +166,9 @@ void GalleryModel::formatFileList() } m_files.clear(); + if(m_work != nullptr) { + m_work->stop(); + } QStringList suffixes; foreach (const QMimeType mType, m_mimeTypes) { @@ -148,43 +177,10 @@ void GalleryModel::formatFileList() } }; - FileSystemWorker* work = new FileSystemWorker(m_urls, suffixes); - QThread* scanTread = new QThread; - connect(scanTread, &QThread::started, work, &FileSystemWorker::start); - connect(work, &FileSystemWorker::foundFile, this, &GalleryModel::appendFiles); - - work->moveToThread(scanTread); - scanTread->start(); - - /*foreach (const QString& dirString, m_urls) { - QDir dir(dirString); - dir.setFilter(QDir::Files | QDir::NoDotAndDotDot | QDir::NoSymLinks); - switch (m_sortMode) { - case SortByName: - dir.setSorting(QDir::Name); - break; - case SortByTime: - dir.setSorting(QDir::Time); - break; - case SortBySize: - dir.setSorting(QDir::Size); - break; - case SortByType: - dir.setSorting(QDir::Type); - break; - default: - dir.setSorting(QDir::Unsorted); - break; - } - + m_work->setDirs(m_urls); + m_work->setSuffixes(suffixes); + m_work->start(); - - /*QDirIterator it(dirString, suffixes, QDir::Files, QDirIterator::Subdirectories); - while (it.hasNext()) { - qDebug() << it.next(); - } - - }*/ endResetModel(); } @@ -259,24 +255,6 @@ QString GalleryModel::sizeTotext(float size) return QString().setNum(size, 'f', 2) + " " + unit; } -bool GalleryModel::isVideo(int index) -{ - QMimeDatabase db; - if (index < 0 || index >= m_files.count()) { - return false; - } - QString url = m_files.at(index).path; - if (url.isEmpty()) { - return false; - } - - QFileInfo fileInfo(url); - if (db.mimeTypeForFile(fileInfo.absoluteFilePath()).name().startsWith("video/")) { - return true; - } - return false; -} - QVariant GalleryModel::get(const int idx) { if (idx >= m_files.size()) { diff --git a/src/plugin/gallerymodel.h b/src/plugin/gallerymodel.h index 5872115..dddef5a 100644 --- a/src/plugin/gallerymodel.h +++ b/src/plugin/gallerymodel.h @@ -70,7 +70,6 @@ class GalleryModel : public QAbstractListModel { void setSortMode(const GalleryModel::SortMode& newSort); Q_INVOKABLE QString sizeTotext(float size); - Q_INVOKABLE bool isVideo(int index); public slots: QVariant get(const int idx); @@ -101,6 +100,7 @@ private slots: QList m_files; QFileSystemWatcher* m_fileSystemWatcher; + FileSystemWorker* m_work; void formatMimeTypes(); }; diff --git a/src/plugin/plugin.cpp b/src/plugin/plugin.cpp index 665c3a0..ccce469 100644 --- a/src/plugin/plugin.cpp +++ b/src/plugin/plugin.cpp @@ -26,5 +26,6 @@ void QQuickNemoControlsExtensionPlugin::registerTypes(const char* uri) Q_ASSERT(uri == QLatin1String("Glacier.Gallery")); qmlRegisterModule(uri, 1, 0); //@uri Glacier.Gallery + qmlRegisterType(uri, 1, 0, "GalleryModel"); } diff --git a/src/qml/components/ImageContainer.qml b/src/qml/components/ImageContainer.qml index 9083087..d6ae3ed 100644 --- a/src/qml/components/ImageContainer.qml +++ b/src/qml/components/ImageContainer.qml @@ -39,7 +39,7 @@ Item { id: imgContainer property int index: -1 property variant pinchingController - property string source: "" + property string source readonly property bool isVideo: gallery.isVideo(source) === 1 property alias flickableArea: flickImg property int doubleClickInterval: 350 @@ -95,7 +95,7 @@ Item { height: imgContainer.height fillMode: Image.PreserveAspectFit - source: isVideo ? "file:///usr/share/glacier-gallery/images/DefaultVideoThumbnail.jpg" : "file://"+ imgContainer.source + source: imgContainer.source ? isVideo ? "file:///usr/share/glacier-gallery/images/DefaultVideoThumbnail.jpg" : "file://"+ imgContainer.source : undefined MouseArea { anchors.fill: parent diff --git a/src/qml/pages/ImagePage.qml b/src/qml/pages/ImagePage.qml index f0670b1..ceba13f 100644 --- a/src/qml/pages/ImagePage.qml +++ b/src/qml/pages/ImagePage.qml @@ -209,13 +209,13 @@ Page { if(parameterIndex > 1) { previosImage.source = galleryModel.get(parameterIndex-1).url; } else { - previosImage.source = ""; + previosImage.source = undefined; } if (parameterIndex+1 < galleryModel.count) { nextImage.source = galleryModel.get(parameterIndex+1).url; } else { - nextImage.source = ""; + nextImage.source = undefined; } currentImage.x = 0 diff --git a/src/qml/pages/MainPage.qml b/src/qml/pages/MainPage.qml index 4a0b3c4..3119ed0 100644 --- a/src/qml/pages/MainPage.qml +++ b/src/qml/pages/MainPage.qml @@ -39,15 +39,26 @@ import Nemo.Controls import Glacier.Gallery +import org.nemomobile.sortfiltermodel 1.0 + Page { id: mainPage headerTools: mainTools - GalleryView { - anchors.fill: parent - model: GalleryModel { + SortFilterModel{ + id: gallerySorted + sourceModel: GalleryModel { id: gallery } + sortRole: "modified" + sortOrder: Qt.DescendingOrder + property alias loading: gallery.loading + } + + + GalleryView { + anchors.fill: parent + model: gallerySorted delegate: GalleryDelegate { MouseArea {