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/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/gallery.cpp b/src/gallery.cpp index 7a8ae10..b68347e 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,7 @@ void Gallery::acquireVideoResources() m_resources->update(); m_resources->acquire(); } - -int Gallery::isVideo(QUrl fileUrl) +bool Gallery::isVideo(QString url) { - 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; + return false; } diff --git a/src/gallery.h b/src/gallery.h index a5d9ce4..14957dc 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 @@ -43,10 +43,10 @@ class Gallery : public QObject { public: explicit Gallery(QObject* parent = 0); + Q_INVOKABLE bool isVideo(QString url); public slots: void acquireVideoResources(); - int isVideo(QUrl fileName); QString fileToOpen() { return m_fileToOpen; } private: diff --git a/src/plugin/CMakeLists.txt b/src/plugin/CMakeLists.txt new file mode 100644 index 0000000..3724c62 --- /dev/null +++ b/src/plugin/CMakeLists.txt @@ -0,0 +1,62 @@ +### 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 + filesystemworker.cpp) + +set(HEADERS + plugin.h + gallerymodel.h + filesystemworker.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 ${SOURCES} +) + +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/filesystemworker.cpp b/src/plugin/filesystemworker.cpp new file mode 100644 index 0000000..34af8a2 --- /dev/null +++ b/src/plugin/filesystemworker.cpp @@ -0,0 +1,98 @@ +/* + * 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 +#include +#include + +FileSystemWorker::FileSystemWorker(QObject* parent) + : QObject { parent } + , 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; + emit busyChanged(); + foreach (const QString& dirString, m_dirs) { + if (!m_busy) { //STOP + 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); + } + } + m_busy = false; + emit busyChanged(); +} + +void FileSystemWorker::stop() +{ + m_busy = false; + emit busyChanged(); +} diff --git a/src/plugin/filesystemworker.h b/src/plugin/filesystemworker.h new file mode 100644 index 0000000..cf34397 --- /dev/null +++ b/src/plugin/filesystemworker.h @@ -0,0 +1,60 @@ +/* + * 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 +#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 + Q_PROPERTY(bool busy READ busy NOTIFY busyChanged FINAL) +public: + 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: + QStringList m_dirs; + QStringList m_suffixes; + bool m_busy; +}; + +#endif // FILESYSTEMWORKER_H diff --git a/src/plugin/gallerymodel.cpp b/src/plugin/gallerymodel.cpp new file mode 100644 index 0000000..6186030 --- /dev/null +++ b/src/plugin/gallerymodel.cpp @@ -0,0 +1,276 @@ +/* + * 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 + * 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 +#include +#include + +GalleryModel::GalleryModel(QObject* parent) + : QAbstractListModel { parent } + , m_loading(false) + , m_error(false) + , 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")); + 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); + 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(); +} + +GalleryModel::~GalleryModel() +{ + if (m_fileSystemWatcher != nullptr) { + delete m_fileSystemWatcher; + } +} + +int GalleryModel::rowCount(const QModelIndex& parent) const +{ + return m_files.count(); +} + +QVariant GalleryModel::data(const QModelIndex& index, int role) const +{ + if (!index.isValid()) { + return QVariant(); + } + + 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(); +} + +bool GalleryModel::loading() const +{ + return m_loading; +} + +bool GalleryModel::error() const +{ + return m_error; +} + +GalleryModel::FilterMode GalleryModel::filter() const +{ + return m_filter; +} + +void GalleryModel::setFilter(FilterMode newFilter) +{ + if (m_filter == newFilter) + return; + m_filter = newFilter; + + formatMimeTypes(); + emit filterChanged(); +} + +void GalleryModel::addPath(QString url) +{ + if (!m_urls.contains(url)) { + m_urls.append(url); + emit urlsChanged(); + } + + if (url.isEmpty()) { + m_urls.append(QStandardPaths::standardLocations(QStandardPaths::PicturesLocation)); + emit urlsChanged(); + } +} + +void GalleryModel::removePatch(QString url) +{ + 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() +{ + beginResetModel(); + QMimeDatabase db; + + if (m_urls.empty()) { + addPath(); + } + + m_files.clear(); + if(m_work != nullptr) { + m_work->stop(); + } + + QStringList suffixes; + foreach (const QMimeType mType, m_mimeTypes) { + foreach (QString suff, mType.suffixes()) { + suffixes << "*." + suff; + } + }; + + m_work->setDirs(m_urls); + m_work->setSuffixes(suffixes); + m_work->start(); + + endResetModel(); +} + +void GalleryModel::appendFiles(MediaFile file) +{ + if (!file.isValid) { + return; + } + + beginInsertRows(QModelIndex(), m_files.count(), m_files.count()); + m_files.push_back(file); + endInsertRows(); +} + +void GalleryModel::onFileSystemChanged(QString path) +{ + qDebug() << Q_FUNC_INFO << path; +} + +void GalleryModel::formatMimeTypes() +{ + QMimeDatabase db; + QList mimeList = db.allMimeTypes(); + + m_mimeTypes.clear(); + + 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; + } + } else if (m_filter == FilterMode::OnlyImages) { + if (mime.name().startsWith(QStringLiteral("image/"))) { + m_mimeTypes << mime; + } + } else if (m_filter == FilterMode::OnlyVideo) { + if (mime.name().startsWith(QStringLiteral("video/"))) { + m_mimeTypes << mime; + } + } + } + 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(); +} + +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; +} + +QVariant GalleryModel::get(const int idx) +{ + if (idx >= m_files.size()) { + return QVariant(); + } + + QMap itemData; + 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 new file mode 100644 index 0000000..dddef5a --- /dev/null +++ b/src/plugin/gallerymodel.h @@ -0,0 +1,108 @@ +/* + * 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 + * 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 "filesystemworker.h" +#include +#include +#include + +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 { + AllFiles, + OnlyImages, + OnlyVideo + }; + + enum SortMode { + SortByName = 0, + SortByTime, + SortBySize, + SortByType, + Unsorted = 255 + }; + Q_ENUMS(FilterMode) + Q_ENUMS(SortMode) + + 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; } + + bool loading() const; + bool error() const; + + FilterMode filter() const; + void setFilter(FilterMode newFilter); + + void addPath(QString url = ""); + void removePatch(QString url = ""); + + GalleryModel::SortMode sortMode() const; + void setSortMode(const GalleryModel::SortMode& newSort); + + Q_INVOKABLE QString sizeTotext(float size); + +public slots: + QVariant get(const int idx); + +signals: + void sortPropertiesChanged(); + void loadingChanged(); + void errorChanged(); + void filterChanged(); + void urlsChanged(); + void sortModeChanged(); + +private slots: + void onUrlsChanged(); + void onFileSystemChanged(QString path); + void formatFileList(); + void appendFiles(MediaFile file); + +private: + QHash m_hash; + bool m_loading; + bool m_error; + GalleryModel::FilterMode m_filter; + GalleryModel::SortMode m_sortMode; + + QList m_mimeTypes; + QStringList m_urls; + QList m_files; + + QFileSystemWatcher* m_fileSystemWatcher; + FileSystemWorker* m_work; + + void formatMimeTypes(); +}; + +#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..ccce469 --- /dev/null +++ b/src/plugin/plugin.cpp @@ -0,0 +1,31 @@ +/* + * 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..3e1ae28 --- /dev/null +++ b/src/plugin/plugin.h @@ -0,0 +1,33 @@ +/* + * 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 83% rename from src/qml/api/GalleryDelegate.qml rename to src/plugin/qml/GalleryDelegate.qml index ad45713..2a2180e 100644 --- a/src/qml/api/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,7 +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) ? "" + : mimeType.startsWith("video/") ? "file:///usr/share/glacier-gallery/images/GridVideoThumbnail.jpg" + : "image://nemoThumbnail/" + url } 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..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: * @@ -35,8 +35,6 @@ import QtQuick.Controls import Nemo import Nemo.Controls -import QtDocGallery 5.0 - Item{ id: downListView property alias model: littleImagesListView.model @@ -65,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 7f80831..d6ae3ed 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 @@ -35,13 +35,11 @@ import QtQuick.Controls import Nemo import Nemo.Controls -import QtDocGallery 5.0 - 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 @@ -97,7 +95,7 @@ Item { height: imgContainer.height fillMode: Image.PreserveAspectFit - source: isVideo ? "file:///usr/share/glacier-gallery/images/DefaultVideoThumbnail.jpg" : 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/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..ceba13f 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 { @@ -211,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/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 e54c1fe..3119ed0 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-2025 Chupligin Sergey * * You may use this file under the terms of the BSD license as follows: * @@ -37,21 +37,28 @@ import QtQuick.Layouts import Nemo import Nemo.Controls -import org.nemomobile.gallery 1.0 -import QtDocGallery 5.0 +import Glacier.Gallery +import org.nemomobile.sortfiltermodel 1.0 Page { id: mainPage - width: parent.width; - height: parent.height; 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 { @@ -85,9 +92,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 { @@ -121,18 +141,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 } } @@ -156,7 +171,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; + } } } } 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 {