197a0e9cb1
OBS-URL: https://build.opensuse.org/package/show/KDE:Extra/digikam?expand=0&rev=0a6bc8745f14567dd84651dbf4cfd5fb
13338 lines
386 KiB
Diff
13338 lines
386 KiB
Diff
From 7e00441c257e7e9e5dc5ab983fc06046fb72b0c5 Mon Sep 17 00:00:00 2001
|
|
From: Gilles Caulier <caulier.gilles@gmail.com>
|
|
Date: Sat, 22 Jul 2017 15:46:08 +0200
|
|
Subject: fix broken linking stage under MacOS with macports. move database
|
|
models into libdigikamdatabase. Let's others model in place to be included
|
|
into libdigikamcore
|
|
|
|
---
|
|
libs/database/CMakeLists.txt | 16 +-
|
|
libs/database/models/imagefiltermodel.cpp | 1116 ++++++++++++++++++
|
|
libs/database/models/imagefiltermodel.h | 299 +++++
|
|
libs/database/models/imagefiltermodelpriv.cpp | 258 ++++
|
|
libs/database/models/imagefiltermodelpriv.h | 159 +++
|
|
libs/database/models/imagefiltermodelthreads.cpp | 40 +
|
|
libs/database/models/imagefiltermodelthreads.h | 100 ++
|
|
libs/database/models/imagefiltersettings.cpp | 952 +++++++++++++++
|
|
libs/database/models/imagefiltersettings.h | 349 ++++++
|
|
libs/database/models/imagelistmodel.cpp | 70 ++
|
|
libs/database/models/imagelistmodel.h | 63 +
|
|
libs/database/models/imagemodel.cpp | 1368 ++++++++++++++++++++++
|
|
libs/database/models/imagemodel.h | 364 ++++++
|
|
libs/database/models/imagesortsettings.cpp | 400 +++++++
|
|
libs/database/models/imagesortsettings.h | 225 ++++
|
|
libs/database/models/imagethumbnailmodel.cpp | 323 +++++
|
|
libs/database/models/imagethumbnailmodel.h | 140 +++
|
|
libs/database/models/imageversionsmodel.cpp | 183 +++
|
|
libs/database/models/imageversionsmodel.h | 75 ++
|
|
libs/models/CMakeLists.txt | 15 +-
|
|
libs/models/imagefiltermodel.cpp | 1116 ------------------
|
|
libs/models/imagefiltermodel.h | 299 -----
|
|
libs/models/imagefiltermodelpriv.cpp | 258 ----
|
|
libs/models/imagefiltermodelpriv.h | 159 ---
|
|
libs/models/imagefiltermodelthreads.cpp | 40 -
|
|
libs/models/imagefiltermodelthreads.h | 100 --
|
|
libs/models/imagefiltersettings.cpp | 952 ---------------
|
|
libs/models/imagefiltersettings.h | 349 ------
|
|
libs/models/imagelistmodel.cpp | 70 --
|
|
libs/models/imagelistmodel.h | 63 -
|
|
libs/models/imagemodel.cpp | 1368 ----------------------
|
|
libs/models/imagemodel.h | 364 ------
|
|
libs/models/imagesortsettings.cpp | 400 -------
|
|
libs/models/imagesortsettings.h | 225 ----
|
|
libs/models/imagethumbnailmodel.cpp | 323 -----
|
|
libs/models/imagethumbnailmodel.h | 140 ---
|
|
libs/models/imageversionsmodel.cpp | 183 ---
|
|
libs/models/imageversionsmodel.h | 75 --
|
|
38 files changed, 6499 insertions(+), 6500 deletions(-)
|
|
create mode 100644 libs/database/models/imagefiltermodel.cpp
|
|
create mode 100644 libs/database/models/imagefiltermodel.h
|
|
create mode 100644 libs/database/models/imagefiltermodelpriv.cpp
|
|
create mode 100644 libs/database/models/imagefiltermodelpriv.h
|
|
create mode 100644 libs/database/models/imagefiltermodelthreads.cpp
|
|
create mode 100644 libs/database/models/imagefiltermodelthreads.h
|
|
create mode 100644 libs/database/models/imagefiltersettings.cpp
|
|
create mode 100644 libs/database/models/imagefiltersettings.h
|
|
create mode 100644 libs/database/models/imagelistmodel.cpp
|
|
create mode 100644 libs/database/models/imagelistmodel.h
|
|
create mode 100644 libs/database/models/imagemodel.cpp
|
|
create mode 100644 libs/database/models/imagemodel.h
|
|
create mode 100644 libs/database/models/imagesortsettings.cpp
|
|
create mode 100644 libs/database/models/imagesortsettings.h
|
|
create mode 100644 libs/database/models/imagethumbnailmodel.cpp
|
|
create mode 100644 libs/database/models/imagethumbnailmodel.h
|
|
create mode 100644 libs/database/models/imageversionsmodel.cpp
|
|
create mode 100644 libs/database/models/imageversionsmodel.h
|
|
delete mode 100644 libs/models/imagefiltermodel.cpp
|
|
delete mode 100644 libs/models/imagefiltermodel.h
|
|
delete mode 100644 libs/models/imagefiltermodelpriv.cpp
|
|
delete mode 100644 libs/models/imagefiltermodelpriv.h
|
|
delete mode 100644 libs/models/imagefiltermodelthreads.cpp
|
|
delete mode 100644 libs/models/imagefiltermodelthreads.h
|
|
delete mode 100644 libs/models/imagefiltersettings.cpp
|
|
delete mode 100644 libs/models/imagefiltersettings.h
|
|
delete mode 100644 libs/models/imagelistmodel.cpp
|
|
delete mode 100644 libs/models/imagelistmodel.h
|
|
delete mode 100644 libs/models/imagemodel.cpp
|
|
delete mode 100644 libs/models/imagemodel.h
|
|
delete mode 100644 libs/models/imagesortsettings.cpp
|
|
delete mode 100644 libs/models/imagesortsettings.h
|
|
delete mode 100644 libs/models/imagethumbnailmodel.cpp
|
|
delete mode 100644 libs/models/imagethumbnailmodel.h
|
|
delete mode 100644 libs/models/imageversionsmodel.cpp
|
|
delete mode 100644 libs/models/imageversionsmodel.h
|
|
|
|
diff --git a/libs/database/CMakeLists.txt b/libs/database/CMakeLists.txt
|
|
index 7d05536..a431a36 100644
|
|
--- a/libs/database/CMakeLists.txt
|
|
+++ b/libs/database/CMakeLists.txt
|
|
@@ -13,6 +13,18 @@ endif (POLICY CMP0063)
|
|
# Boost uses operator names (and, not, ...)
|
|
string(REPLACE "-fno-operator-names" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
|
|
|
|
+set(libdatabasemodels_SRCS
|
|
+ models/imagemodel.cpp
|
|
+ models/imagefiltermodel.cpp
|
|
+ models/imagefiltermodelpriv.cpp
|
|
+ models/imagefiltermodelthreads.cpp
|
|
+ models/imagefiltersettings.cpp
|
|
+ models/imagelistmodel.cpp
|
|
+ models/imagesortsettings.cpp
|
|
+ models/imagethumbnailmodel.cpp
|
|
+ models/imageversionsmodel.cpp
|
|
+)
|
|
+
|
|
set(libdatabasecore_SRCS
|
|
server/databaseserverstarter.cpp
|
|
server/databaseservererror.cpp
|
|
@@ -152,10 +164,10 @@ if(ENABLE_DBUS)
|
|
include_directories($<TARGET_PROPERTY:Qt5::DBus,INTERFACE_INCLUDE_DIRECTORIES>)
|
|
endif()
|
|
|
|
-add_library(digikamdatabase_src OBJECT ${digikamdatabase_LIB_SRCS})
|
|
+add_library(digikamdatabase_src OBJECT ${digikamdatabase_LIB_SRCS} ${libdatabasemodels_SRCS})
|
|
add_library(digikamdatabasemain_src OBJECT ${libdatabaseutils_SRCS} ${libimgqsort_SRCS})
|
|
add_library(digikamdatabasecore_src OBJECT ${libdatabasecore_SRCS})
|
|
-add_library(digikamdatabase SHARED $<TARGET_OBJECTS:digikamdatabase_src> $<TARGET_OBJECTS:digikamdatabasemodels_src>)
|
|
+add_library(digikamdatabase $<TARGET_OBJECTS:digikamdatabase_src>)
|
|
|
|
generate_export_header(digikamdatabase
|
|
BASE_NAME digikam_database
|
|
diff --git a/libs/database/models/imagefiltermodel.cpp b/libs/database/models/imagefiltermodel.cpp
|
|
new file mode 100644
|
|
index 0000000..3d57e05
|
|
--- /dev/null
|
|
+++ b/libs/database/models/imagefiltermodel.cpp
|
|
@@ -0,0 +1,1116 @@
|
|
+/* ============================================================
|
|
+ *
|
|
+ * This file is a part of digiKam project
|
|
+ * http://www.digikam.org
|
|
+ *
|
|
+ * Date : 2009-03-05
|
|
+ * Description : Qt item model for database entries
|
|
+ *
|
|
+ * Copyright (C) 2009-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
|
|
+ * Copyright (C) 2011-2017 by Gilles Caulier <caulier dot gilles at gmail dot com>
|
|
+ * Copyright (C) 2010 by Andi Clemens <andi dot clemens at gmail dot com>
|
|
+ * Copyright (C) 2011 by Michael G. Hansen <mike at mghansen dot de>
|
|
+ * Copyright (C) 2014 by Mohamed Anwer <m dot anwer at gmx dot com>
|
|
+ *
|
|
+ * This program is free software; you can redistribute it
|
|
+ * and/or modify it under the terms of the GNU General
|
|
+ * Public License as published by the Free Software Foundation;
|
|
+ * either version 2, or (at your option)
|
|
+ * any later version.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ *
|
|
+ * ============================================================ */
|
|
+
|
|
+#include "imagefiltermodel.h"
|
|
+#include "imagefiltermodelpriv.h"
|
|
+#include "imagefiltermodelthreads.h"
|
|
+
|
|
+// Local includes
|
|
+
|
|
+#include "digikam_debug.h"
|
|
+#include "coredbaccess.h"
|
|
+#include "coredbchangesets.h"
|
|
+#include "coredbwatch.h"
|
|
+#include "imageinfolist.h"
|
|
+#include "imagemodel.h"
|
|
+
|
|
+namespace Digikam
|
|
+{
|
|
+
|
|
+ImageSortFilterModel::ImageSortFilterModel(QObject* parent)
|
|
+ : DCategorizedSortFilterProxyModel(parent), m_chainedModel(0)
|
|
+{
|
|
+}
|
|
+
|
|
+void ImageSortFilterModel::setSourceImageModel(ImageModel* source)
|
|
+{
|
|
+ if (m_chainedModel)
|
|
+ {
|
|
+ m_chainedModel->setSourceImageModel(source);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ setDirectSourceImageModel(source);
|
|
+ }
|
|
+}
|
|
+
|
|
+void ImageSortFilterModel::setSourceFilterModel(ImageSortFilterModel* source)
|
|
+{
|
|
+ if (source)
|
|
+ {
|
|
+ ImageModel* const model = sourceImageModel();
|
|
+
|
|
+ if (model)
|
|
+ {
|
|
+ source->setSourceImageModel(model);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ m_chainedModel = source;
|
|
+ setSourceModel(source);
|
|
+}
|
|
+
|
|
+void ImageSortFilterModel::setDirectSourceImageModel(ImageModel* model)
|
|
+{
|
|
+ setSourceModel(model);
|
|
+}
|
|
+
|
|
+void ImageSortFilterModel::setSourceModel(QAbstractItemModel* model)
|
|
+{
|
|
+ // made it protected, only setSourceImageModel is public
|
|
+ DCategorizedSortFilterProxyModel::setSourceModel(model);
|
|
+}
|
|
+
|
|
+ImageModel* ImageSortFilterModel::sourceImageModel() const
|
|
+{
|
|
+ if (m_chainedModel)
|
|
+ {
|
|
+ return m_chainedModel->sourceImageModel();
|
|
+ }
|
|
+
|
|
+ return static_cast<ImageModel*>(sourceModel());
|
|
+}
|
|
+
|
|
+ImageSortFilterModel* ImageSortFilterModel::sourceFilterModel() const
|
|
+{
|
|
+ return m_chainedModel;
|
|
+}
|
|
+
|
|
+ImageFilterModel* ImageSortFilterModel::imageFilterModel() const
|
|
+{
|
|
+ // reimplemented in ImageFilterModel
|
|
+ if (m_chainedModel)
|
|
+ {
|
|
+ return m_chainedModel->imageFilterModel();
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+QModelIndex ImageSortFilterModel::mapToSourceImageModel(const QModelIndex& index) const
|
|
+{
|
|
+ if (m_chainedModel)
|
|
+ {
|
|
+ return m_chainedModel->mapToSourceImageModel(mapToSource(index));
|
|
+ }
|
|
+
|
|
+ return mapToSource(index);
|
|
+}
|
|
+
|
|
+QModelIndex ImageSortFilterModel::mapFromSourceImageModel(const QModelIndex& albummodel_index) const
|
|
+{
|
|
+ if (m_chainedModel)
|
|
+ {
|
|
+ return mapFromSource(m_chainedModel->mapFromSourceImageModel(albummodel_index));
|
|
+ }
|
|
+
|
|
+ return mapFromSource(albummodel_index);
|
|
+}
|
|
+
|
|
+
|
|
+QModelIndex ImageSortFilterModel::mapFromDirectSourceToSourceImageModel(const QModelIndex& sourceModel_index) const
|
|
+{
|
|
+ if (m_chainedModel)
|
|
+ {
|
|
+ return m_chainedModel->mapToSourceImageModel(sourceModel_index);
|
|
+ }
|
|
+ return sourceModel_index;
|
|
+}
|
|
+
|
|
+// -------------- Convenience mappers -------------------------------------------------------------------
|
|
+
|
|
+QList<QModelIndex> ImageSortFilterModel::mapListToSource(const QList<QModelIndex>& indexes) const
|
|
+{
|
|
+ QList<QModelIndex> sourceIndexes;
|
|
+ foreach(const QModelIndex& index, indexes)
|
|
+ {
|
|
+ sourceIndexes << mapToSourceImageModel(index);
|
|
+ }
|
|
+ return sourceIndexes;
|
|
+}
|
|
+
|
|
+QList<QModelIndex> ImageSortFilterModel::mapListFromSource(const QList<QModelIndex>& sourceIndexes) const
|
|
+{
|
|
+ QList<QModelIndex> indexes;
|
|
+ foreach(const QModelIndex& index, sourceIndexes)
|
|
+ {
|
|
+ indexes << mapFromSourceImageModel(index);
|
|
+ }
|
|
+ return indexes;
|
|
+}
|
|
+
|
|
+ImageInfo ImageSortFilterModel::imageInfo(const QModelIndex& index) const
|
|
+{
|
|
+ return sourceImageModel()->imageInfo(mapToSourceImageModel(index));
|
|
+}
|
|
+
|
|
+qlonglong ImageSortFilterModel::imageId(const QModelIndex& index) const
|
|
+{
|
|
+ return sourceImageModel()->imageId(mapToSourceImageModel(index));
|
|
+}
|
|
+
|
|
+QList<ImageInfo> ImageSortFilterModel::imageInfos(const QList<QModelIndex>& indexes) const
|
|
+{
|
|
+ QList<ImageInfo> infos;
|
|
+ ImageModel* const model = sourceImageModel();
|
|
+
|
|
+ foreach(const QModelIndex& index, indexes)
|
|
+ {
|
|
+ infos << model->imageInfo(mapToSourceImageModel(index));
|
|
+ }
|
|
+
|
|
+ return infos;
|
|
+}
|
|
+
|
|
+QList<qlonglong> ImageSortFilterModel::imageIds(const QList<QModelIndex>& indexes) const
|
|
+{
|
|
+ QList<qlonglong> ids;
|
|
+ ImageModel* const model = sourceImageModel();
|
|
+
|
|
+ foreach(const QModelIndex& index, indexes)
|
|
+ {
|
|
+ ids << model->imageId(mapToSourceImageModel(index));
|
|
+ }
|
|
+
|
|
+ return ids;
|
|
+}
|
|
+
|
|
+QModelIndex ImageSortFilterModel::indexForPath(const QString& filePath) const
|
|
+{
|
|
+ return mapFromSourceImageModel(sourceImageModel()->indexForPath(filePath));
|
|
+}
|
|
+
|
|
+QModelIndex ImageSortFilterModel::indexForImageInfo(const ImageInfo& info) const
|
|
+{
|
|
+ return mapFromSourceImageModel(sourceImageModel()->indexForImageInfo(info));
|
|
+}
|
|
+
|
|
+QModelIndex ImageSortFilterModel::indexForImageId(qlonglong id) const
|
|
+{
|
|
+ return mapFromSourceImageModel(sourceImageModel()->indexForImageId(id));
|
|
+}
|
|
+
|
|
+QList<ImageInfo> ImageSortFilterModel::imageInfosSorted() const
|
|
+{
|
|
+ QList<ImageInfo> infos;
|
|
+ const int size = rowCount();
|
|
+ ImageModel* const model = sourceImageModel();
|
|
+
|
|
+ for (int i=0; i<size; ++i)
|
|
+ {
|
|
+ infos << model->imageInfo(mapToSourceImageModel(index(i, 0)));
|
|
+ }
|
|
+
|
|
+ return infos;
|
|
+}
|
|
+
|
|
+// --------------------------------------------------------------------------------------------
|
|
+
|
|
+ImageFilterModel::ImageFilterModel(QObject* parent)
|
|
+ : ImageSortFilterModel(parent),
|
|
+ d_ptr(new ImageFilterModelPrivate)
|
|
+{
|
|
+ d_ptr->init(this);
|
|
+}
|
|
+
|
|
+ImageFilterModel::ImageFilterModel(ImageFilterModelPrivate& dd, QObject* parent)
|
|
+ : ImageSortFilterModel(parent),
|
|
+ d_ptr(&dd)
|
|
+{
|
|
+ d_ptr->init(this);
|
|
+}
|
|
+
|
|
+ImageFilterModel::~ImageFilterModel()
|
|
+{
|
|
+ Q_D(ImageFilterModel);
|
|
+ delete d;
|
|
+}
|
|
+
|
|
+void ImageFilterModel::setDirectSourceImageModel(ImageModel* sourceModel)
|
|
+{
|
|
+ Q_D(ImageFilterModel);
|
|
+
|
|
+ if (d->imageModel)
|
|
+ {
|
|
+ d->imageModel->unsetPreprocessor(d);
|
|
+ disconnect(d->imageModel, SIGNAL(modelReset()),
|
|
+ this, SLOT(slotModelReset()));
|
|
+ slotModelReset();
|
|
+ }
|
|
+
|
|
+ d->imageModel = sourceModel;
|
|
+
|
|
+ if (d->imageModel)
|
|
+ {
|
|
+ d->imageModel->setPreprocessor(d);
|
|
+
|
|
+ connect(d->imageModel, SIGNAL(preprocess(QList<ImageInfo>,QList<QVariant>)),
|
|
+ d, SLOT(preprocessInfos(QList<ImageInfo>,QList<QVariant>)));
|
|
+
|
|
+ connect(d->imageModel, SIGNAL(processAdded(QList<ImageInfo>,QList<QVariant>)),
|
|
+ d, SLOT(processAddedInfos(QList<ImageInfo>,QList<QVariant>)));
|
|
+
|
|
+ connect(d, SIGNAL(reAddImageInfos(QList<ImageInfo>,QList<QVariant>)),
|
|
+ d->imageModel, SLOT(reAddImageInfos(QList<ImageInfo>,QList<QVariant>)));
|
|
+
|
|
+ connect(d, SIGNAL(reAddingFinished()),
|
|
+ d->imageModel, SLOT(reAddingFinished()));
|
|
+
|
|
+ connect(d->imageModel, SIGNAL(modelReset()),
|
|
+ this, SLOT(slotModelReset()));
|
|
+
|
|
+ connect(d->imageModel, SIGNAL(imageChange(ImageChangeset,QItemSelection)),
|
|
+ this, SLOT(slotImageChange(ImageChangeset)));
|
|
+
|
|
+ connect(d->imageModel, SIGNAL(imageTagChange(ImageTagChangeset,QItemSelection)),
|
|
+ this, SLOT(slotImageTagChange(ImageTagChangeset)));
|
|
+ }
|
|
+
|
|
+ setSourceModel(d->imageModel);
|
|
+}
|
|
+
|
|
+QVariant ImageFilterModel::data(const QModelIndex& index, int role) const
|
|
+{
|
|
+ Q_D(const ImageFilterModel);
|
|
+
|
|
+ if (!index.isValid())
|
|
+ {
|
|
+ return QVariant();
|
|
+ }
|
|
+
|
|
+ switch (role)
|
|
+ {
|
|
+ // Attention: This breaks should there ever be another filter model between this and the ImageModel
|
|
+
|
|
+ case DCategorizedSortFilterProxyModel::CategoryDisplayRole:
|
|
+ return categoryIdentifier(d->imageModel->imageInfoRef(mapToSource(index)));
|
|
+ case CategorizationModeRole:
|
|
+ return d->sorter.categorizationMode;
|
|
+ case SortOrderRole:
|
|
+ return d->sorter.sortRole;
|
|
+ //case CategoryCountRole:
|
|
+ // return categoryCount(d->imageModel->imageInfoRef(mapToSource(index)));
|
|
+ case CategoryAlbumIdRole:
|
|
+ return d->imageModel->imageInfoRef(mapToSource(index)).albumId();
|
|
+ case CategoryFormatRole:
|
|
+ return d->imageModel->imageInfoRef(mapToSource(index)).format();
|
|
+ case GroupIsOpenRole:
|
|
+ return d->groupFilter.isAllOpen() ||
|
|
+ d->groupFilter.isOpen(d->imageModel->imageInfoRef(mapToSource(index)).id());
|
|
+ case ImageFilterModelPointerRole:
|
|
+ return QVariant::fromValue(const_cast<ImageFilterModel*>(this));
|
|
+ }
|
|
+
|
|
+ return DCategorizedSortFilterProxyModel::data(index, role);
|
|
+}
|
|
+
|
|
+ImageFilterModel* ImageFilterModel::imageFilterModel() const
|
|
+{
|
|
+ return const_cast<ImageFilterModel*>(this);
|
|
+}
|
|
+
|
|
+DatabaseFields::Set ImageFilterModel::suggestedWatchFlags() const
|
|
+{
|
|
+ DatabaseFields::Set watchFlags;
|
|
+ watchFlags |= DatabaseFields::Name | DatabaseFields::FileSize | DatabaseFields::ModificationDate;
|
|
+ watchFlags |= DatabaseFields::Rating | DatabaseFields::CreationDate | DatabaseFields::Orientation |
|
|
+ DatabaseFields::Width | DatabaseFields::Height;
|
|
+ watchFlags |= DatabaseFields::Comment;
|
|
+ watchFlags |= DatabaseFields::ImageRelations;
|
|
+ return watchFlags;
|
|
+}
|
|
+
|
|
+// -------------- Filter settings --------------
|
|
+
|
|
+void ImageFilterModel::setDayFilter(const QList<QDateTime>& days)
|
|
+{
|
|
+ Q_D(ImageFilterModel);
|
|
+ d->filter.setDayFilter(days);
|
|
+ setImageFilterSettings(d->filter);
|
|
+}
|
|
+
|
|
+void ImageFilterModel::setTagFilter(const QList<int>& includedTags, const QList<int>& excludedTags,
|
|
+ ImageFilterSettings::MatchingCondition matchingCond,
|
|
+ bool showUnTagged, const QList<int>& clTagIds, const QList<int>& plTagIds)
|
|
+{
|
|
+ Q_D(ImageFilterModel);
|
|
+ d->filter.setTagFilter(includedTags, excludedTags, matchingCond, showUnTagged, clTagIds, plTagIds);
|
|
+ setImageFilterSettings(d->filter);
|
|
+}
|
|
+
|
|
+void ImageFilterModel::setRatingFilter(int rating, ImageFilterSettings::RatingCondition ratingCond, bool isUnratedExcluded)
|
|
+{
|
|
+ Q_D(ImageFilterModel);
|
|
+ d->filter.setRatingFilter(rating, ratingCond, isUnratedExcluded);
|
|
+ setImageFilterSettings(d->filter);
|
|
+}
|
|
+
|
|
+void ImageFilterModel::setUrlWhitelist(const QList<QUrl> urlList, const QString& id)
|
|
+{
|
|
+ Q_D(ImageFilterModel);
|
|
+ d->filter.setUrlWhitelist(urlList, id);
|
|
+ setImageFilterSettings(d->filter);
|
|
+}
|
|
+
|
|
+void ImageFilterModel::setIdWhitelist(const QList<qlonglong>& idList, const QString& id)
|
|
+{
|
|
+ Q_D(ImageFilterModel);
|
|
+ d->filter.setIdWhitelist(idList, id);
|
|
+ setImageFilterSettings(d->filter);
|
|
+}
|
|
+
|
|
+void ImageFilterModel::setMimeTypeFilter(int mimeTypeFilter)
|
|
+{
|
|
+ Q_D(ImageFilterModel);
|
|
+ d->filter.setMimeTypeFilter(mimeTypeFilter);
|
|
+ setImageFilterSettings(d->filter);
|
|
+}
|
|
+
|
|
+void ImageFilterModel::setGeolocationFilter(const ImageFilterSettings::GeolocationCondition& condition)
|
|
+{
|
|
+ Q_D(ImageFilterModel);
|
|
+ d->filter.setGeolocationFilter(condition);
|
|
+ setImageFilterSettings(d->filter);
|
|
+}
|
|
+
|
|
+void ImageFilterModel::setTextFilter(const SearchTextFilterSettings& settings)
|
|
+{
|
|
+ Q_D(ImageFilterModel);
|
|
+ d->filter.setTextFilter(settings);
|
|
+ setImageFilterSettings(d->filter);
|
|
+}
|
|
+
|
|
+void ImageFilterModel::setImageFilterSettings(const ImageFilterSettings& settings)
|
|
+{
|
|
+ Q_D(ImageFilterModel);
|
|
+
|
|
+ {
|
|
+ QMutexLocker lock(&d->mutex);
|
|
+ d->version++;
|
|
+ d->filter = settings;
|
|
+ d->filterCopy = settings;
|
|
+ d->versionFilterCopy = d->versionFilter;
|
|
+ d->groupFilterCopy = d->groupFilter;
|
|
+
|
|
+ d->needPrepareComments = settings.isFilteringByText();
|
|
+ d->needPrepareTags = settings.isFilteringByTags();
|
|
+ d->needPrepareGroups = true;
|
|
+ d->needPrepare = d->needPrepareComments || d->needPrepareTags || d->needPrepareGroups;
|
|
+
|
|
+ d->hasOneMatch = false;
|
|
+ d->hasOneMatchForText = false;
|
|
+ }
|
|
+
|
|
+ d->filterResults.clear();
|
|
+
|
|
+ //d->categoryCountHashInt.clear();
|
|
+ //d->categoryCountHashString.clear();
|
|
+ if (d->imageModel)
|
|
+ {
|
|
+ d->infosToProcess(d->imageModel->imageInfos());
|
|
+ }
|
|
+
|
|
+ emit filterSettingsChanged(settings);
|
|
+}
|
|
+
|
|
+void ImageFilterModel::setVersionManagerSettings(const VersionManagerSettings& settings)
|
|
+{
|
|
+ Q_D(ImageFilterModel);
|
|
+ d->versionFilter.setVersionManagerSettings(settings);
|
|
+ setVersionImageFilterSettings(d->versionFilter);
|
|
+}
|
|
+
|
|
+void ImageFilterModel::setExceptionList(const QList<qlonglong>& idList, const QString& id)
|
|
+{
|
|
+ Q_D(ImageFilterModel);
|
|
+ d->versionFilter.setExceptionList(idList, id);
|
|
+ setVersionImageFilterSettings(d->versionFilter);
|
|
+}
|
|
+
|
|
+void ImageFilterModel::setVersionImageFilterSettings(const VersionImageFilterSettings& settings)
|
|
+{
|
|
+ Q_D(ImageFilterModel);
|
|
+ d->versionFilter = settings;
|
|
+ slotUpdateFilter();
|
|
+}
|
|
+
|
|
+bool ImageFilterModel::isGroupOpen(qlonglong group) const
|
|
+{
|
|
+ Q_D(const ImageFilterModel);
|
|
+ return d->groupFilter.isOpen(group);
|
|
+}
|
|
+
|
|
+bool ImageFilterModel::isAllGroupsOpen() const
|
|
+{
|
|
+ Q_D(const ImageFilterModel);
|
|
+ return d->groupFilter.isAllOpen();
|
|
+}
|
|
+
|
|
+void ImageFilterModel::setGroupOpen(qlonglong group, bool open)
|
|
+{
|
|
+ Q_D(ImageFilterModel);
|
|
+ d->groupFilter.setOpen(group, open);
|
|
+ setGroupImageFilterSettings(d->groupFilter);
|
|
+}
|
|
+
|
|
+void ImageFilterModel::toggleGroupOpen(qlonglong group)
|
|
+{
|
|
+ setGroupOpen(group, !isGroupOpen(group));
|
|
+}
|
|
+
|
|
+void ImageFilterModel::setAllGroupsOpen(bool open)
|
|
+{
|
|
+ Q_D(ImageFilterModel);
|
|
+ d->groupFilter.setAllOpen(open);
|
|
+ setGroupImageFilterSettings(d->groupFilter);
|
|
+}
|
|
+
|
|
+void ImageFilterModel::setGroupImageFilterSettings(const GroupImageFilterSettings& settings)
|
|
+{
|
|
+ Q_D(ImageFilterModel);
|
|
+ d->groupFilter = settings;
|
|
+ slotUpdateFilter();
|
|
+}
|
|
+
|
|
+void ImageFilterModel::slotUpdateFilter()
|
|
+{
|
|
+ Q_D(ImageFilterModel);
|
|
+ setImageFilterSettings(d->filter);
|
|
+}
|
|
+
|
|
+ImageFilterSettings ImageFilterModel::imageFilterSettings() const
|
|
+{
|
|
+ Q_D(const ImageFilterModel);
|
|
+ return d->filter;
|
|
+}
|
|
+
|
|
+ImageSortSettings ImageFilterModel::imageSortSettings() const
|
|
+{
|
|
+ Q_D(const ImageFilterModel);
|
|
+ return d->sorter;
|
|
+}
|
|
+
|
|
+VersionImageFilterSettings ImageFilterModel::versionImageFilterSettings() const
|
|
+{
|
|
+ Q_D(const ImageFilterModel);
|
|
+ return d->versionFilter;
|
|
+}
|
|
+
|
|
+GroupImageFilterSettings ImageFilterModel::groupImageFilterSettings() const
|
|
+{
|
|
+ Q_D(const ImageFilterModel);
|
|
+ return d->groupFilter;
|
|
+}
|
|
+
|
|
+void ImageFilterModel::slotModelReset()
|
|
+{
|
|
+ Q_D(ImageFilterModel);
|
|
+ {
|
|
+ QMutexLocker lock(&d->mutex);
|
|
+ // discard all packages on the way that are marked as send out for re-add
|
|
+ d->lastDiscardVersion = d->version;
|
|
+ d->sentOutForReAdd = 0;
|
|
+ // discard all packages on the way
|
|
+ d->version++;
|
|
+ d->sentOut = 0;
|
|
+
|
|
+ d->hasOneMatch = false;
|
|
+ d->hasOneMatchForText = false;
|
|
+ }
|
|
+ d->filterResults.clear();
|
|
+}
|
|
+
|
|
+bool ImageFilterModel::filterAcceptsRow(int source_row, const QModelIndex& source_parent) const
|
|
+{
|
|
+ Q_D(const ImageFilterModel);
|
|
+
|
|
+ if (source_parent.isValid())
|
|
+ {
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ qlonglong id = d->imageModel->imageId(source_row);
|
|
+ QHash<qlonglong, bool>::const_iterator it = d->filterResults.constFind(id);
|
|
+
|
|
+ if (it != d->filterResults.constEnd())
|
|
+ {
|
|
+ return it.value();
|
|
+ }
|
|
+
|
|
+ // usually done in thread and cache, unless source model changed
|
|
+ ImageInfo info = d->imageModel->imageInfo(source_row);
|
|
+ bool match = d->filter.matches(info);
|
|
+ match = match ? d->versionFilter.matches(info) : false;
|
|
+
|
|
+ return match ? d->groupFilter.matches(info) : false;
|
|
+}
|
|
+
|
|
+void ImageFilterModel::setSendImageInfoSignals(bool sendSignals)
|
|
+{
|
|
+ if (sendSignals)
|
|
+ {
|
|
+ connect(this, SIGNAL(rowsInserted(QModelIndex,int,int)),
|
|
+ this, SLOT(slotRowsInserted(QModelIndex,int,int)));
|
|
+
|
|
+ connect(this, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
|
|
+ this, SLOT(slotRowsAboutToBeRemoved(QModelIndex,int,int)));
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ disconnect(this, SIGNAL(rowsInserted(QModelIndex,int,int)),
|
|
+ this, SLOT(slotRowsInserted(QModelIndex,int,int)));
|
|
+
|
|
+ disconnect(this, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
|
|
+ this, SLOT(slotRowsAboutToBeRemoved(QModelIndex,int,int)));
|
|
+ }
|
|
+}
|
|
+
|
|
+void ImageFilterModel::slotRowsInserted(const QModelIndex& /*parent*/, int start, int end)
|
|
+{
|
|
+ QList<ImageInfo> infos;
|
|
+
|
|
+ for (int i=start; i<=end; ++i)
|
|
+ {
|
|
+ infos << imageInfo(index(i, 0));
|
|
+ }
|
|
+
|
|
+ emit imageInfosAdded(infos);
|
|
+}
|
|
+
|
|
+void ImageFilterModel::slotRowsAboutToBeRemoved(const QModelIndex& /*parent*/, int start, int end)
|
|
+{
|
|
+ QList<ImageInfo> infos;
|
|
+
|
|
+ for (int i=start; i<=end; ++i)
|
|
+ {
|
|
+ infos << imageInfo(index(i, 0));
|
|
+ }
|
|
+
|
|
+ emit imageInfosAboutToBeRemoved(infos);
|
|
+}
|
|
+
|
|
+// -------------- Threaded preparation & filtering --------------
|
|
+
|
|
+void ImageFilterModel::addPrepareHook(ImageFilterModelPrepareHook* hook)
|
|
+{
|
|
+ Q_D(ImageFilterModel);
|
|
+ QMutexLocker lock(&d->mutex);
|
|
+ d->prepareHooks << hook;
|
|
+}
|
|
+
|
|
+void ImageFilterModel::removePrepareHook(ImageFilterModelPrepareHook* hook)
|
|
+{
|
|
+ Q_D(ImageFilterModel);
|
|
+ QMutexLocker lock(&d->mutex);
|
|
+ d->prepareHooks.removeAll(hook);
|
|
+}
|
|
+
|
|
+void ImageFilterModelPreparer::process(ImageFilterModelTodoPackage package)
|
|
+{
|
|
+ if (!checkVersion(package))
|
|
+ {
|
|
+ emit discarded(package);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ // get thread-local copy
|
|
+ bool needPrepareTags, needPrepareComments, needPrepareGroups;
|
|
+ QList<ImageFilterModelPrepareHook*> prepareHooks;
|
|
+ {
|
|
+ QMutexLocker lock(&d->mutex);
|
|
+ needPrepareTags = d->needPrepareTags;
|
|
+ needPrepareComments = d->needPrepareComments;
|
|
+ needPrepareGroups = d->needPrepareGroups;
|
|
+ prepareHooks = d->prepareHooks;
|
|
+ }
|
|
+
|
|
+ //TODO: Make efficient!!
|
|
+ if (needPrepareComments)
|
|
+ {
|
|
+ foreach(const ImageInfo& info, package.infos)
|
|
+ {
|
|
+ info.comment();
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (!checkVersion(package))
|
|
+ {
|
|
+ emit discarded(package);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ // The downside of QVector: At some point, we may need a QList for an API.
|
|
+ // Nonetheless, QList and ImageInfo is fast. We could as well
|
|
+ // reimplement ImageInfoList to ImageInfoVector (internally with templates?)
|
|
+ ImageInfoList infoList;
|
|
+
|
|
+ if (needPrepareTags || needPrepareGroups)
|
|
+ {
|
|
+ infoList = package.infos.toList();
|
|
+ }
|
|
+
|
|
+ if (needPrepareTags)
|
|
+ {
|
|
+ infoList.loadTagIds();
|
|
+ }
|
|
+
|
|
+ if (needPrepareGroups)
|
|
+ {
|
|
+ infoList.loadGroupImageIds();
|
|
+ }
|
|
+
|
|
+ foreach(ImageFilterModelPrepareHook* hook, prepareHooks)
|
|
+ {
|
|
+ hook->prepare(package.infos);
|
|
+ }
|
|
+
|
|
+ emit processed(package);
|
|
+}
|
|
+
|
|
+void ImageFilterModelFilterer::process(ImageFilterModelTodoPackage package)
|
|
+{
|
|
+ if (!checkVersion(package))
|
|
+ {
|
|
+ emit discarded(package);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ // get thread-local copy
|
|
+ ImageFilterSettings localFilter;
|
|
+ VersionImageFilterSettings localVersionFilter;
|
|
+ GroupImageFilterSettings localGroupFilter;
|
|
+ bool hasOneMatch;
|
|
+ bool hasOneMatchForText;
|
|
+ {
|
|
+ QMutexLocker lock(&d->mutex);
|
|
+ localFilter = d->filterCopy;
|
|
+ localVersionFilter = d->versionFilterCopy;
|
|
+ localGroupFilter = d->groupFilterCopy;
|
|
+ hasOneMatch = d->hasOneMatch;
|
|
+ hasOneMatchForText = d->hasOneMatchForText;
|
|
+ }
|
|
+
|
|
+ // Actual filtering. The variants to spare checking hasOneMatch over and over again.
|
|
+ if (hasOneMatch && hasOneMatchForText)
|
|
+ {
|
|
+ foreach(const ImageInfo& info, package.infos)
|
|
+ {
|
|
+ package.filterResults[info.id()] = localFilter.matches(info) &&
|
|
+ localVersionFilter.matches(info) &&
|
|
+ localGroupFilter.matches(info);
|
|
+ }
|
|
+ }
|
|
+ else if (hasOneMatch)
|
|
+ {
|
|
+ bool matchForText;
|
|
+
|
|
+ foreach(const ImageInfo& info, package.infos)
|
|
+ {
|
|
+ package.filterResults[info.id()] = localFilter.matches(info, &matchForText) &&
|
|
+ localVersionFilter.matches(info) &&
|
|
+ localGroupFilter.matches(info);
|
|
+
|
|
+ if (matchForText)
|
|
+ {
|
|
+ hasOneMatchForText = true;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ bool result, matchForText;
|
|
+
|
|
+ foreach(const ImageInfo& info, package.infos)
|
|
+ {
|
|
+ result = localFilter.matches(info, &matchForText) &&
|
|
+ localVersionFilter.matches(info) &&
|
|
+ localGroupFilter.matches(info);
|
|
+ package.filterResults[info.id()] = result;
|
|
+
|
|
+ if (result)
|
|
+ {
|
|
+ hasOneMatch = true;
|
|
+ }
|
|
+
|
|
+ if (matchForText)
|
|
+ {
|
|
+ hasOneMatchForText = true;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (checkVersion(package))
|
|
+ {
|
|
+ QMutexLocker lock(&d->mutex);
|
|
+ d->hasOneMatch = hasOneMatch;
|
|
+ d->hasOneMatchForText = hasOneMatchForText;
|
|
+ }
|
|
+
|
|
+ emit processed(package);
|
|
+}
|
|
+
|
|
+// -------------- Sorting and Categorization -------------------------------------------------------
|
|
+
|
|
+void ImageFilterModel::setImageSortSettings(const ImageSortSettings& sorter)
|
|
+{
|
|
+ Q_D(ImageFilterModel);
|
|
+ d->sorter = sorter;
|
|
+ setCategorizedModel(d->sorter.categorizationMode != ImageSortSettings::NoCategories);
|
|
+ invalidate();
|
|
+}
|
|
+
|
|
+void ImageFilterModel::setCategorizationMode(ImageSortSettings::CategorizationMode mode)
|
|
+{
|
|
+ Q_D(ImageFilterModel);
|
|
+ d->sorter.setCategorizationMode(mode);
|
|
+ setImageSortSettings(d->sorter);
|
|
+}
|
|
+
|
|
+void ImageFilterModel::setCategorizationSortOrder(ImageSortSettings::SortOrder order)
|
|
+{
|
|
+ Q_D(ImageFilterModel);
|
|
+ d->sorter.setCategorizationSortOrder(order);
|
|
+ setImageSortSettings(d->sorter);
|
|
+}
|
|
+
|
|
+void ImageFilterModel::setSortRole(ImageSortSettings::SortRole role)
|
|
+{
|
|
+ Q_D(ImageFilterModel);
|
|
+ d->sorter.setSortRole(role);
|
|
+ setImageSortSettings(d->sorter);
|
|
+}
|
|
+
|
|
+void ImageFilterModel::setSortOrder(ImageSortSettings::SortOrder order)
|
|
+{
|
|
+ Q_D(ImageFilterModel);
|
|
+ d->sorter.setSortOrder(order);
|
|
+ setImageSortSettings(d->sorter);
|
|
+}
|
|
+
|
|
+void ImageFilterModel::setStringTypeNatural(bool natural)
|
|
+{
|
|
+ Q_D(ImageFilterModel);
|
|
+ d->sorter.setStringTypeNatural(natural);
|
|
+ setImageSortSettings(d->sorter);
|
|
+}
|
|
+
|
|
+int ImageFilterModel::compareCategories(const QModelIndex& left, const QModelIndex& right) const
|
|
+{
|
|
+ // source indexes
|
|
+ Q_D(const ImageFilterModel);
|
|
+
|
|
+ if (!d->sorter.isCategorized())
|
|
+ {
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ if (!left.isValid() || !right.isValid())
|
|
+ {
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ const ImageInfo& leftInfo = d->imageModel->imageInfoRef(left);
|
|
+ const ImageInfo& rightInfo = d->imageModel->imageInfoRef(right);
|
|
+
|
|
+ // Check grouping
|
|
+ qlonglong leftGroupImageId = leftInfo.groupImageId();
|
|
+ qlonglong rightGroupImageId = rightInfo.groupImageId();
|
|
+
|
|
+ return compareInfosCategories(leftGroupImageId == -1 ? leftInfo : ImageInfo(leftGroupImageId),
|
|
+ rightGroupImageId == -1 ? rightInfo : ImageInfo(rightGroupImageId));
|
|
+}
|
|
+
|
|
+bool ImageFilterModel::subSortLessThan(const QModelIndex& left, const QModelIndex& right) const
|
|
+{
|
|
+ // source indexes
|
|
+ Q_D(const ImageFilterModel);
|
|
+
|
|
+ if (!left.isValid() || !right.isValid())
|
|
+ {
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ if (left == right)
|
|
+ {
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ const ImageInfo& leftInfo = d->imageModel->imageInfoRef(left);
|
|
+ const ImageInfo& rightInfo = d->imageModel->imageInfoRef(right);
|
|
+
|
|
+ if (leftInfo == rightInfo)
|
|
+ {
|
|
+ return d->sorter.lessThan(left.data(ImageModel::ExtraDataRole), right.data(ImageModel::ExtraDataRole));
|
|
+ }
|
|
+
|
|
+ // Check grouping
|
|
+ qlonglong leftGroupImageId = leftInfo.groupImageId();
|
|
+ qlonglong rightGroupImageId = rightInfo.groupImageId();
|
|
+
|
|
+ // Either no grouping (-1), or same group image, or same image
|
|
+ if (leftGroupImageId == rightGroupImageId)
|
|
+ {
|
|
+ return infosLessThan(leftInfo, rightInfo);
|
|
+ }
|
|
+
|
|
+ // We have grouping to handle
|
|
+
|
|
+ // Is one grouped on the other? Sort behind leader.
|
|
+ if (leftGroupImageId == rightInfo.id())
|
|
+ {
|
|
+ return false;
|
|
+ }
|
|
+ if (rightGroupImageId == leftInfo.id())
|
|
+ {
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ // Use the group leader for sorting
|
|
+ return infosLessThan(leftGroupImageId == -1 ? leftInfo : ImageInfo(leftGroupImageId),
|
|
+ rightGroupImageId == -1 ? rightInfo : ImageInfo(rightGroupImageId));
|
|
+}
|
|
+
|
|
+int ImageFilterModel::compareInfosCategories(const ImageInfo& left, const ImageInfo& right) const
|
|
+{
|
|
+ // Note: reimplemented in ImageAlbumFilterModel
|
|
+ Q_D(const ImageFilterModel);
|
|
+ return d->sorter.compareCategories(left, right);
|
|
+}
|
|
+
|
|
+// Feel free to optimize. QString::number is 3x slower.
|
|
+static inline QString fastNumberToString(int id)
|
|
+{
|
|
+ const int size = sizeof(int) * 2;
|
|
+ char c[size+1];
|
|
+ c[size] = '\0';
|
|
+ char* p = c;
|
|
+ int number = id;
|
|
+
|
|
+ for (int i=0; i<size; ++i)
|
|
+ {
|
|
+ *p = 'a' + (number & 0xF);
|
|
+ number >>= 4;
|
|
+ ++p;
|
|
+ }
|
|
+
|
|
+ return QString::fromLatin1(c);
|
|
+}
|
|
+
|
|
+QString ImageFilterModel::categoryIdentifier(const ImageInfo& i) const
|
|
+{
|
|
+ Q_D(const ImageFilterModel);
|
|
+
|
|
+ if (!d->sorter.isCategorized())
|
|
+ {
|
|
+ return QString();
|
|
+ }
|
|
+
|
|
+ qlonglong groupedImageId = i.groupImageId();
|
|
+ ImageInfo info = groupedImageId == -1 ? i : ImageInfo(groupedImageId);
|
|
+
|
|
+ switch (d->sorter.categorizationMode)
|
|
+ {
|
|
+ case ImageSortSettings::NoCategories:
|
|
+ return QString();
|
|
+ case ImageSortSettings::OneCategory:
|
|
+ return QString();
|
|
+ case ImageSortSettings::CategoryByAlbum:
|
|
+ return fastNumberToString(info.albumId());
|
|
+ case ImageSortSettings::CategoryByFormat:
|
|
+ return info.format();
|
|
+ default:
|
|
+ return QString();
|
|
+ }
|
|
+}
|
|
+
|
|
+bool ImageFilterModel::infosLessThan(const ImageInfo& left, const ImageInfo& right) const
|
|
+{
|
|
+ Q_D(const ImageFilterModel);
|
|
+ return d->sorter.lessThan(left, right);
|
|
+}
|
|
+
|
|
+// -------------- Watching changes -----------------------------------------------------------------
|
|
+
|
|
+void ImageFilterModel::slotImageTagChange(const ImageTagChangeset& changeset)
|
|
+{
|
|
+ Q_D(ImageFilterModel);
|
|
+
|
|
+ if (!d->imageModel || d->imageModel->isEmpty())
|
|
+ {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ // already scheduled to re-filter?
|
|
+ if (d->updateFilterTimer->isActive())
|
|
+ {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ // do we filter at all?
|
|
+ if (!d->versionFilter.isFilteringByTags() &&
|
|
+ !d->filter.isFilteringByTags() &&
|
|
+ !d->filter.isFilteringByText())
|
|
+ {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ // is one of our images affected?
|
|
+ foreach(const qlonglong& id, changeset.ids())
|
|
+ {
|
|
+ // if one matching image id is found, trigger a refresh
|
|
+ if (d->imageModel->hasImage(id))
|
|
+ {
|
|
+ d->updateFilterTimer->start();
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+void ImageFilterModel::slotImageChange(const ImageChangeset& changeset)
|
|
+{
|
|
+ Q_D(ImageFilterModel);
|
|
+
|
|
+ if (!d->imageModel || d->imageModel->isEmpty())
|
|
+ {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ // already scheduled to re-filter?
|
|
+ if (d->updateFilterTimer->isActive())
|
|
+ {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ // is one of the values affected that we filter or sort by?
|
|
+ DatabaseFields::Set set = changeset.changes();
|
|
+ bool sortAffected = (set & d->sorter.watchFlags());
|
|
+ bool filterAffected = (set & d->filter.watchFlags()) || (set & d->groupFilter.watchFlags());
|
|
+
|
|
+ if (!sortAffected && !filterAffected)
|
|
+ {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ // is one of our images affected?
|
|
+ bool imageAffected = false;
|
|
+
|
|
+ foreach(const qlonglong& id, changeset.ids())
|
|
+ {
|
|
+ // if one matching image id is found, trigger a refresh
|
|
+ if (d->imageModel->hasImage(id))
|
|
+ {
|
|
+ imageAffected = true;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (!imageAffected)
|
|
+ {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (filterAffected)
|
|
+ {
|
|
+ d->updateFilterTimer->start();
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ invalidate(); // just resort, reuse filter results
|
|
+ }
|
|
+}
|
|
+
|
|
+// -------------------------------------------------------------------------------------------------------
|
|
+
|
|
+NoDuplicatesImageFilterModel::NoDuplicatesImageFilterModel(QObject* parent)
|
|
+ : ImageSortFilterModel(parent)
|
|
+{
|
|
+}
|
|
+
|
|
+bool NoDuplicatesImageFilterModel::filterAcceptsRow(int source_row, const QModelIndex& source_parent) const
|
|
+{
|
|
+ QModelIndex index = sourceModel()->index(source_row, 0, source_parent);
|
|
+
|
|
+ if (index.data(ImageModel::ExtraDataDuplicateCount).toInt() <= 1)
|
|
+ {
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ QModelIndex previousIndex = sourceModel()->index(source_row - 1, 0, source_parent);
|
|
+
|
|
+ if (!previousIndex.isValid())
|
|
+ {
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ if (sourceImageModel()->imageId(mapFromDirectSourceToSourceImageModel(index)) == sourceImageModel()->imageId(mapFromDirectSourceToSourceImageModel(previousIndex)))
|
|
+ {
|
|
+ return false;
|
|
+ }
|
|
+ return true;
|
|
+}
|
|
+
|
|
+/*
|
|
+void NoDuplicatesImageFilterModel::setSourceModel(QAbstractItemModel* model)
|
|
+{
|
|
+ if (sourceModel())
|
|
+ {
|
|
+ }
|
|
+
|
|
+ ImageSortFilterModel::setSourceModel(model);
|
|
+
|
|
+ if (sourceModel())
|
|
+ {
|
|
+ connect(sourceModel(), SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
|
|
+ this, SLOT(slotRowsAboutToBeRemoved(QModelIndex,int,int)));
|
|
+ }
|
|
+}
|
|
+
|
|
+void NoDuplicatesImageFilterModel::slotRowsAboutToBeRemoved(const QModelIndex& parent, int begin, int end)
|
|
+{
|
|
+ bool needInvalidate = false;
|
|
+
|
|
+ for (int i = begin; i<=end; ++i)
|
|
+ {
|
|
+ QModelIndex index = sourceModel()->index(i, 0, parent);
|
|
+
|
|
+ // filtered out by us?
|
|
+ if (!mapFromSource(index).isValid())
|
|
+ {
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ QModelIndex sourceIndex = mapFromDirectSourceToSourceImageModel(index);
|
|
+ qlonglong id = sourceImageModel()->imageId(sourceIndex);
|
|
+
|
|
+ if (sourceImageModel()->numberOfIndexesForImageId(id) > 1)
|
|
+ {
|
|
+ needInvalidate = true;
|
|
+ }
|
|
+ }
|
|
+}*/
|
|
+
|
|
+} // namespace Digikam
|
|
diff --git a/libs/database/models/imagefiltermodel.h b/libs/database/models/imagefiltermodel.h
|
|
new file mode 100644
|
|
index 0000000..d131b3e
|
|
--- /dev/null
|
|
+++ b/libs/database/models/imagefiltermodel.h
|
|
@@ -0,0 +1,299 @@
|
|
+/* ============================================================
|
|
+ *
|
|
+ * This file is a part of digiKam project
|
|
+ * http://www.digikam.org
|
|
+ *
|
|
+ * Date : 2009-03-05
|
|
+ * Description : Qt item model for database entries
|
|
+ *
|
|
+ * Copyright (C) 2009-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
|
|
+ * Copyright (C) 2011 by Gilles Caulier <caulier dot gilles at gmail dot com>
|
|
+ * Copyright (C) 2010 by Andi Clemens <andi dot clemens at gmail dot com>
|
|
+ * Copyright (C) 2011 by Michael G. Hansen <mike at mghansen dot de>
|
|
+ * Copyright (C) 2014 by Mohamed Anwer <m dot anwer at gmx dot com>
|
|
+ *
|
|
+ * This program is free software; you can redistribute it
|
|
+ * and/or modify it under the terms of the GNU General
|
|
+ * Public License as published by the Free Software Foundation;
|
|
+ * either version 2, or (at your option)
|
|
+ * any later version.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ *
|
|
+ * ============================================================ */
|
|
+
|
|
+#ifndef IMAGEFILTERMODEL_H
|
|
+#define IMAGEFILTERMODEL_H
|
|
+
|
|
+// Local includes
|
|
+
|
|
+#include "dcategorizedsortfilterproxymodel.h"
|
|
+#include "textfilter.h"
|
|
+#include "imagefiltersettings.h"
|
|
+#include "imagemodel.h"
|
|
+#include "imagesortsettings.h"
|
|
+#include "digikam_export.h"
|
|
+
|
|
+namespace Digikam
|
|
+{
|
|
+
|
|
+class ImageChangeset;
|
|
+class ImageFilterModel;
|
|
+class ImageTagChangeset;
|
|
+
|
|
+class DIGIKAM_DATABASE_EXPORT ImageFilterModelPrepareHook
|
|
+{
|
|
+public:
|
|
+
|
|
+ virtual ~ImageFilterModelPrepareHook() {};
|
|
+ virtual void prepare(const QVector<ImageInfo>& infos) = 0;
|
|
+};
|
|
+
|
|
+// -----------------------------------------------------------------------------------------------
|
|
+
|
|
+class DIGIKAM_DATABASE_EXPORT ImageSortFilterModel : public DCategorizedSortFilterProxyModel
|
|
+{
|
|
+ Q_OBJECT
|
|
+
|
|
+public:
|
|
+
|
|
+ explicit ImageSortFilterModel(QObject* parent = 0);
|
|
+
|
|
+ void setSourceImageModel(ImageModel* model);
|
|
+ ImageModel* sourceImageModel() const;
|
|
+
|
|
+ void setSourceFilterModel(ImageSortFilterModel* model);
|
|
+ ImageSortFilterModel* sourceFilterModel() const;
|
|
+
|
|
+ QModelIndex mapToSourceImageModel(const QModelIndex& index) const;
|
|
+ QModelIndex mapFromSourceImageModel(const QModelIndex& imagemodel_index) const;
|
|
+ QModelIndex mapFromDirectSourceToSourceImageModel(const QModelIndex& sourceModel_index) const;
|
|
+
|
|
+ /// Convenience methods mapped to ImageModel.
|
|
+ /// Mentioned indexes returned come from the source image model.
|
|
+ QList<QModelIndex> mapListToSource(const QList<QModelIndex>& indexes) const;
|
|
+ QList<QModelIndex> mapListFromSource(const QList<QModelIndex>& sourceIndexes) const;
|
|
+
|
|
+ ImageInfo imageInfo(const QModelIndex& index) const;
|
|
+ qlonglong imageId(const QModelIndex& index) const;
|
|
+ QList<ImageInfo> imageInfos(const QList<QModelIndex>& indexes) const;
|
|
+ QList<qlonglong> imageIds(const QList<QModelIndex>& indexes) const;
|
|
+
|
|
+ QModelIndex indexForPath(const QString& filePath) const;
|
|
+ QModelIndex indexForImageInfo(const ImageInfo& info) const;
|
|
+ QModelIndex indexForImageId(qlonglong id) const;
|
|
+
|
|
+ /** Returns a list of all image infos, sorted according to this model.
|
|
+ * If you do not need a sorted list, use ImageModel's imageInfos() method.
|
|
+ */
|
|
+ QList<ImageInfo> imageInfosSorted() const;
|
|
+
|
|
+ /// Returns this, any chained ImageFilterModel, or 0.
|
|
+ virtual ImageFilterModel* imageFilterModel() const;
|
|
+
|
|
+protected:
|
|
+
|
|
+ /// Reimplement if needed. Called only when model shall be set as (direct) sourceModel.
|
|
+ virtual void setDirectSourceImageModel(ImageModel* model);
|
|
+
|
|
+ // made protected
|
|
+ virtual void setSourceModel(QAbstractItemModel* model);
|
|
+
|
|
+protected:
|
|
+
|
|
+ ImageSortFilterModel* m_chainedModel;
|
|
+};
|
|
+
|
|
+// -----------------------------------------------------------------------------------------------
|
|
+
|
|
+class DIGIKAM_DATABASE_EXPORT ImageFilterModel : public ImageSortFilterModel
|
|
+{
|
|
+ Q_OBJECT
|
|
+
|
|
+public:
|
|
+
|
|
+ enum ImageFilterModelRoles
|
|
+ {
|
|
+ /// Returns the current categorization mode
|
|
+ CategorizationModeRole = ImageModel::FilterModelRoles + 1,
|
|
+ /// Returns the current sort order
|
|
+ SortOrderRole = ImageModel::FilterModelRoles + 2,
|
|
+ // / Returns the number of items in the index' category
|
|
+ //CategoryCountRole = ImageModel::FilterModelRoles + 3,
|
|
+ /// Returns the id of the PAlbum of the index which is used for category
|
|
+ CategoryAlbumIdRole = ImageModel::FilterModelRoles + 3,
|
|
+ /// Returns the format of the index which is used for category
|
|
+ CategoryFormatRole = ImageModel::FilterModelRoles + 4,
|
|
+ /// Returns true if the given image is a group leader, and the group is opened
|
|
+ GroupIsOpenRole = ImageModel::FilterModelRoles + 5,
|
|
+ ImageFilterModelPointerRole = ImageModel::FilterModelRoles + 50
|
|
+ };
|
|
+
|
|
+public:
|
|
+
|
|
+ explicit ImageFilterModel(QObject* parent = 0);
|
|
+ ~ImageFilterModel();
|
|
+
|
|
+ /** Add a hook to get added images for preparation tasks before they are added in the model */
|
|
+ void addPrepareHook(ImageFilterModelPrepareHook* hook);
|
|
+ void removePrepareHook(ImageFilterModelPrepareHook* hook);
|
|
+
|
|
+ /** Returns a set of DatabaseFields suggested to set as watch flags on the source ImageModel.
|
|
+ * The contained flags will be those that this model can sort or filter by. */
|
|
+ DatabaseFields::Set suggestedWatchFlags() const;
|
|
+
|
|
+ ImageFilterSettings imageFilterSettings() const;
|
|
+ VersionImageFilterSettings versionImageFilterSettings() const;
|
|
+ GroupImageFilterSettings groupImageFilterSettings() const;
|
|
+ ImageSortSettings imageSortSettings() const;
|
|
+
|
|
+ // group is identified by the id of its group leader
|
|
+ bool isGroupOpen(qlonglong group) const;
|
|
+ bool isAllGroupsOpen() const;
|
|
+
|
|
+ /// Enables sending imageInfosAdded and imageInfosAboutToBeRemoved
|
|
+ void setSendImageInfoSignals(bool sendSignals);
|
|
+
|
|
+ virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
|
|
+ virtual ImageFilterModel* imageFilterModel() const;
|
|
+
|
|
+public Q_SLOTS:
|
|
+
|
|
+ /** Changes the current version image filter settings and refilters. */
|
|
+ void setVersionImageFilterSettings(const VersionImageFilterSettings& settings);
|
|
+
|
|
+ /** Changes the current version image filter settings and refilters. */
|
|
+ void setGroupImageFilterSettings(const GroupImageFilterSettings& settings);
|
|
+
|
|
+ /** Adjust the current ImageFilterSettings.
|
|
+ * Equivalent to retrieving the current filter settings, adjusting the parameter
|
|
+ * and calling setImageFilterSettings.
|
|
+ * Provided for convenience.
|
|
+ * It is encouraged to use setImageFilterSettings if you change more than one
|
|
+ * parameter at a time.
|
|
+ */
|
|
+ void setDayFilter(const QList<QDateTime>& days);
|
|
+ void setTagFilter(const QList<int>& includedTags, const QList<int>& excludedTags,
|
|
+ ImageFilterSettings::MatchingCondition matchingCond, bool showUnTagged,
|
|
+ const QList<int>& clTagIds, const QList<int>& plTagIds);
|
|
+ void setRatingFilter(int rating, ImageFilterSettings::RatingCondition ratingCond, bool isUnratedExcluded);
|
|
+ void setMimeTypeFilter(int mimeTypeFilter);
|
|
+ void setGeolocationFilter(const ImageFilterSettings::GeolocationCondition& condition);
|
|
+ void setTextFilter(const SearchTextFilterSettings& settings);
|
|
+
|
|
+ void setCategorizationMode(ImageSortSettings::CategorizationMode mode);
|
|
+ void setCategorizationSortOrder(ImageSortSettings::SortOrder order);
|
|
+ void setSortRole(ImageSortSettings::SortRole role);
|
|
+ void setSortOrder(ImageSortSettings::SortOrder order);
|
|
+ void setStringTypeNatural(bool natural);
|
|
+ void setUrlWhitelist(const QList<QUrl> urlList, const QString& id);
|
|
+ void setIdWhitelist(const QList<qlonglong>& idList, const QString& id);
|
|
+
|
|
+ void setVersionManagerSettings(const VersionManagerSettings& settings);
|
|
+ void setExceptionList(const QList<qlonglong>& idlist, const QString& id);
|
|
+
|
|
+ void setGroupOpen(qlonglong group, bool open);
|
|
+ void toggleGroupOpen(qlonglong group);
|
|
+ void setAllGroupsOpen(bool open);
|
|
+
|
|
+ /** Changes the current image filter settings and refilters. */
|
|
+ virtual void setImageFilterSettings(const ImageFilterSettings& settings);
|
|
+
|
|
+ /** Changes the current image sort settings and resorts. */
|
|
+ virtual void setImageSortSettings(const ImageSortSettings& settings);
|
|
+
|
|
+Q_SIGNALS:
|
|
+
|
|
+ /// Signals that the set filter matches at least one index
|
|
+ void filterMatches(bool matches);
|
|
+
|
|
+ /** Signals that the set text filter matches at least one entry.
|
|
+ If no text filter is set, this signal is emitted
|
|
+ with 'false' when filterMatches() is emitted.
|
|
+ */
|
|
+ void filterMatchesForText(bool matchesByText);
|
|
+
|
|
+ /** Emitted when the filter settings have been changed
|
|
+ (the model may not yet have been updated)
|
|
+ */
|
|
+ void filterSettingsChanged(const ImageFilterSettings& settings);
|
|
+
|
|
+ /** These signals need to be explicitly enabled with setSendImageInfoSignals()
|
|
+ */
|
|
+ void imageInfosAdded(const QList<ImageInfo>& infos);
|
|
+ void imageInfosAboutToBeRemoved(const QList<ImageInfo>& infos);
|
|
+
|
|
+public:
|
|
+
|
|
+ // Declared as public because of use in sub-classes.
|
|
+ class ImageFilterModelPrivate;
|
|
+
|
|
+protected:
|
|
+
|
|
+ ImageFilterModelPrivate* const d_ptr;
|
|
+
|
|
+protected:
|
|
+
|
|
+ ImageFilterModel(ImageFilterModelPrivate& dd, QObject* parent);
|
|
+
|
|
+ virtual void setDirectSourceImageModel(ImageModel* model);
|
|
+
|
|
+ virtual bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const;
|
|
+
|
|
+ virtual int compareCategories(const QModelIndex& left, const QModelIndex& right) const;
|
|
+ virtual bool subSortLessThan(const QModelIndex& left, const QModelIndex& right) const;
|
|
+ //virtual int categoryCount(const ImageInfo& info) const;
|
|
+
|
|
+ /** Reimplement to customize category sorting,
|
|
+ * Return negative if category of left < category right,
|
|
+ * Return 0 if left and right are in the same category, else return positive.
|
|
+ */
|
|
+ virtual int compareInfosCategories(const ImageInfo& left, const ImageInfo& right) const;
|
|
+
|
|
+ /** Reimplement to customize sorting. Do not take categories into account here.
|
|
+ */
|
|
+ virtual bool infosLessThan(const ImageInfo& left, const ImageInfo& right) const;
|
|
+
|
|
+ /** Returns a unique identifier for the category if info. The string need not be for user display.
|
|
+ */
|
|
+ virtual QString categoryIdentifier(const ImageInfo& info) const;
|
|
+
|
|
+protected Q_SLOTS:
|
|
+
|
|
+ void slotModelReset();
|
|
+ void slotUpdateFilter();
|
|
+
|
|
+ void slotImageTagChange(const ImageTagChangeset& changeset);
|
|
+ void slotImageChange(const ImageChangeset& changeset);
|
|
+
|
|
+ void slotRowsInserted(const QModelIndex& parent, int start, int end);
|
|
+ void slotRowsAboutToBeRemoved(const QModelIndex& parent, int start, int end);
|
|
+
|
|
+private:
|
|
+
|
|
+ Q_DECLARE_PRIVATE(ImageFilterModel)
|
|
+};
|
|
+
|
|
+// -----------------------------------------------------------------------------------------------------
|
|
+
|
|
+class DIGIKAM_DATABASE_EXPORT NoDuplicatesImageFilterModel : public ImageSortFilterModel
|
|
+{
|
|
+ Q_OBJECT
|
|
+
|
|
+public:
|
|
+
|
|
+ explicit NoDuplicatesImageFilterModel(QObject* parent = 0);
|
|
+
|
|
+protected:
|
|
+
|
|
+ virtual bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const;
|
|
+};
|
|
+
|
|
+} // namespace Digikam
|
|
+
|
|
+Q_DECLARE_METATYPE(Digikam::ImageFilterModel*)
|
|
+
|
|
+#endif // IMAGEMODEL_H
|
|
diff --git a/libs/database/models/imagefiltermodelpriv.cpp b/libs/database/models/imagefiltermodelpriv.cpp
|
|
new file mode 100644
|
|
index 0000000..07d9e79
|
|
--- /dev/null
|
|
+++ b/libs/database/models/imagefiltermodelpriv.cpp
|
|
@@ -0,0 +1,258 @@
|
|
+/* ============================================================
|
|
+ *
|
|
+ * This file is a part of digiKam project
|
|
+ * http://www.digikam.org
|
|
+ *
|
|
+ * Date : 2009-03-05
|
|
+ * Description : Qt item model for database entries
|
|
+ *
|
|
+ * Copyright (C) 2009-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
|
|
+ * Copyright (C) 2011-2017 by Gilles Caulier <caulier dot gilles at gmail dot com>
|
|
+ * Copyright (C) 2010 by Andi Clemens <andi dot clemens at gmail dot com>
|
|
+ * Copyright (C) 2011 by Michael G. Hansen <mike at mghansen dot de>
|
|
+ * Copyright (C) 2014 by Mohamed Anwer <m dot anwer at gmx dot com>
|
|
+ *
|
|
+ * This program is free software; you can redistribute it
|
|
+ * and/or modify it under the terms of the GNU General
|
|
+ * Public License as published by the Free Software Foundation;
|
|
+ * either version 2, or (at your option)
|
|
+ * any later version.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ *
|
|
+ * ============================================================ */
|
|
+
|
|
+#include "imagefiltermodelpriv.h"
|
|
+
|
|
+// Local includes
|
|
+
|
|
+#include "digikam_debug.h"
|
|
+#include "imagefiltermodelthreads.h"
|
|
+
|
|
+namespace Digikam
|
|
+{
|
|
+
|
|
+ImageFilterModel::ImageFilterModelPrivate::ImageFilterModelPrivate()
|
|
+{
|
|
+ imageModel = 0;
|
|
+ version = 0;
|
|
+ lastDiscardVersion = 0;
|
|
+ sentOut = 0;
|
|
+ sentOutForReAdd = 0;
|
|
+ updateFilterTimer = 0;
|
|
+ needPrepare = false;
|
|
+ needPrepareComments = false;
|
|
+ needPrepareTags = false;
|
|
+ needPrepareGroups = false;
|
|
+ preparer = 0;
|
|
+ filterer = 0;
|
|
+ hasOneMatch = false;
|
|
+ hasOneMatchForText = false;
|
|
+
|
|
+ setupWorkers();
|
|
+}
|
|
+
|
|
+ImageFilterModel::ImageFilterModelPrivate::~ImageFilterModelPrivate()
|
|
+{
|
|
+ // facilitate thread stopping
|
|
+ ++version;
|
|
+ preparer->deactivate();
|
|
+ filterer->deactivate();
|
|
+ delete preparer;
|
|
+ delete filterer;
|
|
+}
|
|
+
|
|
+void ImageFilterModel::ImageFilterModelPrivate::init(ImageFilterModel* _q)
|
|
+{
|
|
+ q = _q;
|
|
+
|
|
+ updateFilterTimer = new QTimer(this);
|
|
+ updateFilterTimer->setSingleShot(true);
|
|
+ updateFilterTimer->setInterval(250);
|
|
+
|
|
+ connect(updateFilterTimer, SIGNAL(timeout()),
|
|
+ q, SLOT(slotUpdateFilter()));
|
|
+
|
|
+ // inter-thread redirection
|
|
+ qRegisterMetaType<ImageFilterModelTodoPackage>("ImageFilterModelTodoPackage");
|
|
+}
|
|
+
|
|
+void ImageFilterModel::ImageFilterModelPrivate::preprocessInfos(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues)
|
|
+{
|
|
+ infosToProcess(infos, extraValues, true);
|
|
+}
|
|
+
|
|
+void ImageFilterModel::ImageFilterModelPrivate::processAddedInfos(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues)
|
|
+{
|
|
+ // These have already been added, we just process them afterwards
|
|
+ infosToProcess(infos, extraValues, false);
|
|
+}
|
|
+
|
|
+void ImageFilterModel::ImageFilterModelPrivate::setupWorkers()
|
|
+{
|
|
+ preparer = new ImageFilterModelPreparer(this);
|
|
+ filterer = new ImageFilterModelFilterer(this);
|
|
+
|
|
+ // A package in constructed in infosToProcess.
|
|
+ // Normal flow is infosToProcess -> preparer::process -> filterer::process -> packageFinished.
|
|
+ // If no preparation is needed, the first step is skipped.
|
|
+ // If filter version changes, both will discard old package and send them to packageDiscarded.
|
|
+
|
|
+ connect(this, SIGNAL(packageToPrepare(ImageFilterModelTodoPackage)),
|
|
+ preparer, SLOT(process(ImageFilterModelTodoPackage)));
|
|
+
|
|
+ connect(this, SIGNAL(packageToFilter(ImageFilterModelTodoPackage)),
|
|
+ filterer, SLOT(process(ImageFilterModelTodoPackage)));
|
|
+
|
|
+ connect(preparer, SIGNAL(processed(ImageFilterModelTodoPackage)),
|
|
+ filterer, SLOT(process(ImageFilterModelTodoPackage)));
|
|
+
|
|
+ connect(filterer, SIGNAL(processed(ImageFilterModelTodoPackage)),
|
|
+ this, SLOT(packageFinished(ImageFilterModelTodoPackage)));
|
|
+
|
|
+ connect(preparer, SIGNAL(discarded(ImageFilterModelTodoPackage)),
|
|
+ this, SLOT(packageDiscarded(ImageFilterModelTodoPackage)));
|
|
+
|
|
+ connect(filterer, SIGNAL(discarded(ImageFilterModelTodoPackage)),
|
|
+ this, SLOT(packageDiscarded(ImageFilterModelTodoPackage)));
|
|
+}
|
|
+
|
|
+void ImageFilterModel::ImageFilterModelPrivate::infosToProcess(const QList<ImageInfo>& infos)
|
|
+{
|
|
+ infosToProcess(infos, QList<QVariant>(), false);
|
|
+}
|
|
+
|
|
+void ImageFilterModel::ImageFilterModelPrivate::infosToProcess(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues, bool forReAdd)
|
|
+{
|
|
+ if (infos.isEmpty())
|
|
+ {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ filterer->schedule();
|
|
+
|
|
+ if (needPrepare)
|
|
+ {
|
|
+ preparer->schedule();
|
|
+ }
|
|
+
|
|
+ Q_ASSERT(extraValues.isEmpty() || infos.size() == extraValues.size());
|
|
+
|
|
+ // prepare and filter in chunks
|
|
+ const int size = infos.size();
|
|
+ const int maxChunkSize = needPrepare ? PrepareChunkSize : FilterChunkSize;
|
|
+ const bool hasExtraValues = !extraValues.isEmpty();
|
|
+ QList<ImageInfo>::const_iterator it = infos.constBegin(), end;
|
|
+ QList<QVariant>::const_iterator xit = extraValues.constBegin(), xend;
|
|
+ int index = 0;
|
|
+ QVector<ImageInfo> infoVector;
|
|
+ QVector<QVariant> extraValueVector;
|
|
+
|
|
+ while (it != infos.constEnd())
|
|
+ {
|
|
+ const int chunkSize = qMin(maxChunkSize, size - index);
|
|
+ infoVector.resize(chunkSize);
|
|
+ end = it + chunkSize;
|
|
+ qCopy(it, end, infoVector.begin());
|
|
+
|
|
+ if (hasExtraValues)
|
|
+ {
|
|
+ extraValueVector.resize(chunkSize);
|
|
+ xend = xit + chunkSize;
|
|
+ qCopy(xit, xend, extraValueVector.begin());
|
|
+ xit = xend;
|
|
+ }
|
|
+
|
|
+ it = end;
|
|
+ index += chunkSize;
|
|
+
|
|
+ ++sentOut;
|
|
+
|
|
+ if (forReAdd)
|
|
+ {
|
|
+ ++sentOutForReAdd;
|
|
+ }
|
|
+
|
|
+ if (needPrepare)
|
|
+ {
|
|
+ emit packageToPrepare(ImageFilterModelTodoPackage(infoVector, extraValueVector, version, forReAdd));
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ emit packageToFilter(ImageFilterModelTodoPackage(infoVector, extraValueVector, version, forReAdd));
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+void ImageFilterModel::ImageFilterModelPrivate::packageFinished(const ImageFilterModelTodoPackage& package)
|
|
+{
|
|
+ // check if it got discarded on the journey
|
|
+ if (package.version != version)
|
|
+ {
|
|
+ packageDiscarded(package);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ // incorporate result
|
|
+ QHash<qlonglong, bool>::const_iterator it = package.filterResults.constBegin();
|
|
+
|
|
+ for (; it != package.filterResults.constEnd(); ++it)
|
|
+ {
|
|
+ filterResults.insert(it.key(), it.value());
|
|
+ }
|
|
+
|
|
+ // re-add if necessary
|
|
+ if (package.isForReAdd)
|
|
+ {
|
|
+ emit reAddImageInfos(package.infos.toList(), package.extraValues.toList());
|
|
+
|
|
+ if (sentOutForReAdd == 1) // last package
|
|
+ {
|
|
+ emit reAddingFinished();
|
|
+ }
|
|
+ }
|
|
+
|
|
+ // decrement counters
|
|
+ --sentOut;
|
|
+
|
|
+ if (package.isForReAdd)
|
|
+ {
|
|
+ --sentOutForReAdd;
|
|
+ }
|
|
+
|
|
+ // If all packages have returned, filtered and readded, and no more are expected,
|
|
+ // and there is need to tell the filter result to the view, do that
|
|
+ if (sentOut == 0 && sentOutForReAdd == 0 && !imageModel->isRefreshing())
|
|
+ {
|
|
+ q->invalidate(); // use invalidate, not invalidateFilter only. Sorting may have changed as well.
|
|
+ emit (q->filterMatches(hasOneMatch));
|
|
+ emit (q->filterMatchesForText(hasOneMatchForText));
|
|
+ filterer->deactivate();
|
|
+ preparer->deactivate();
|
|
+ }
|
|
+}
|
|
+
|
|
+void ImageFilterModel::ImageFilterModelPrivate::packageDiscarded(const ImageFilterModelTodoPackage& package)
|
|
+{
|
|
+ // Either, the model was reset, or the filter changed
|
|
+ // In the former case throw all away, in the latter case, recycle
|
|
+ if (package.version > lastDiscardVersion)
|
|
+ {
|
|
+ // Recycle packages: Send again with current version
|
|
+ // Do not increment sentOut or sentOutForReAdd here: it was not decremented!
|
|
+
|
|
+ if (needPrepare)
|
|
+ {
|
|
+ emit packageToPrepare(ImageFilterModelTodoPackage(package.infos, package.extraValues, version, package.isForReAdd));
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ emit packageToFilter(ImageFilterModelTodoPackage(package.infos, package.extraValues, version, package.isForReAdd));
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+} // namespace Digikam
|
|
diff --git a/libs/database/models/imagefiltermodelpriv.h b/libs/database/models/imagefiltermodelpriv.h
|
|
new file mode 100644
|
|
index 0000000..a9e3f22
|
|
--- /dev/null
|
|
+++ b/libs/database/models/imagefiltermodelpriv.h
|
|
@@ -0,0 +1,159 @@
|
|
+/* ============================================================
|
|
+ *
|
|
+ * This file is a part of digiKam project
|
|
+ * http://www.digikam.org
|
|
+ *
|
|
+ * Date : 2009-03-11
|
|
+ * Description : Qt item model for database entries - private shared header
|
|
+ *
|
|
+ * Copyright (C) 2009-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
|
|
+ *
|
|
+ * This program is free software; you can redistribute it
|
|
+ * and/or modify it under the terms of the GNU General
|
|
+ * Public License as published by the Free Software Foundation;
|
|
+ * either version 2, or (at your option)
|
|
+ * any later version.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ *
|
|
+ * ============================================================ */
|
|
+
|
|
+#ifndef IMAGEFILTERMODELPRIV_H
|
|
+#define IMAGEFILTERMODELPRIV_H
|
|
+
|
|
+// Qt includes
|
|
+
|
|
+#include <QHash>
|
|
+#include <QMutex>
|
|
+#include <QMutexLocker>
|
|
+#include <QSet>
|
|
+#include <QThread>
|
|
+#include <QTimer>
|
|
+#include <QWaitCondition>
|
|
+
|
|
+// Local includes
|
|
+
|
|
+#include "imageinfo.h"
|
|
+#include "imagefiltermodel.h"
|
|
+
|
|
+#include "digikam_export.h"
|
|
+// Yes, we need the EXPORT macro in a private header because
|
|
+// this private header is shared across binary objects.
|
|
+// This does NOT make this classes here any more public!
|
|
+
|
|
+namespace Digikam
|
|
+{
|
|
+
|
|
+const int PrepareChunkSize = 101;
|
|
+const int FilterChunkSize = 2001;
|
|
+
|
|
+class ImageFilterModelTodoPackage
|
|
+{
|
|
+public:
|
|
+
|
|
+ ImageFilterModelTodoPackage()
|
|
+ : version(0), isForReAdd(false)
|
|
+ {
|
|
+ }
|
|
+
|
|
+ ImageFilterModelTodoPackage(const QVector<ImageInfo>& infos, const QVector<QVariant>& extraValues, int version, bool isForReAdd)
|
|
+ : infos(infos), extraValues(extraValues), version(version), isForReAdd(isForReAdd)
|
|
+ {
|
|
+ }
|
|
+
|
|
+ QVector<ImageInfo> infos;
|
|
+ QVector<QVariant> extraValues;
|
|
+ unsigned int version;
|
|
+ bool isForReAdd;
|
|
+ QHash<qlonglong, bool> filterResults;
|
|
+};
|
|
+
|
|
+// ------------------------------------------------------------------------------------------------
|
|
+
|
|
+class ImageFilterModelPreparer;
|
|
+class ImageFilterModelFilterer;
|
|
+
|
|
+class DIGIKAM_DATABASE_EXPORT ImageFilterModel::ImageFilterModelPrivate : public QObject
|
|
+{
|
|
+ Q_OBJECT
|
|
+
|
|
+public:
|
|
+
|
|
+ ImageFilterModelPrivate();
|
|
+ ~ImageFilterModelPrivate();
|
|
+
|
|
+ void init(ImageFilterModel* q);
|
|
+ void setupWorkers();
|
|
+ void infosToProcess(const QList<ImageInfo>& infos);
|
|
+ void infosToProcess(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues, bool forReAdd = true);
|
|
+
|
|
+public:
|
|
+
|
|
+ ImageFilterModel* q;
|
|
+
|
|
+ ImageModel* imageModel;
|
|
+
|
|
+ ImageFilterSettings filter;
|
|
+ ImageSortSettings sorter;
|
|
+ VersionImageFilterSettings versionFilter;
|
|
+ GroupImageFilterSettings groupFilter;
|
|
+
|
|
+ volatile unsigned int version;
|
|
+ unsigned int lastDiscardVersion;
|
|
+ unsigned int lastFilteredVersion;
|
|
+ int sentOut;
|
|
+ int sentOutForReAdd;
|
|
+
|
|
+ QTimer* updateFilterTimer;
|
|
+
|
|
+ bool needPrepare;
|
|
+ bool needPrepareComments;
|
|
+ bool needPrepareTags;
|
|
+ bool needPrepareGroups;
|
|
+
|
|
+ QMutex mutex;
|
|
+ ImageFilterSettings filterCopy;
|
|
+ VersionImageFilterSettings versionFilterCopy;
|
|
+ GroupImageFilterSettings groupFilterCopy;
|
|
+ ImageFilterModelPreparer* preparer;
|
|
+ ImageFilterModelFilterer* filterer;
|
|
+
|
|
+ QHash<qlonglong, bool> filterResults;
|
|
+ bool hasOneMatch;
|
|
+ bool hasOneMatchForText;
|
|
+
|
|
+ QList<ImageFilterModelPrepareHook*> prepareHooks;
|
|
+
|
|
+/*
|
|
+ QHash<int, QSet<qlonglong> > categoryCountHashInt;
|
|
+ QHash<QString, QSet<qlonglong> > categoryCountHashString;
|
|
+
|
|
+public:
|
|
+
|
|
+ void cacheCategoryCount(int id, qlonglong imageid) const
|
|
+ { const_cast<ImageFilterModelPrivate*>(this)->categoryCountHashInt[id].insert(imageid); }
|
|
+ void cacheCategoryCount(const QString& id, qlonglong imageid) const
|
|
+ { const_cast<ImageFilterModelPrivate*>(this)->categoryCountHashString[id].insert(imageid); }
|
|
+*/
|
|
+
|
|
+public Q_SLOTS:
|
|
+
|
|
+ void preprocessInfos(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues);
|
|
+ void processAddedInfos(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues);
|
|
+ void packageFinished(const ImageFilterModelTodoPackage& package);
|
|
+ void packageDiscarded(const ImageFilterModelTodoPackage& package);
|
|
+
|
|
+Q_SIGNALS:
|
|
+
|
|
+ void packageToPrepare(const ImageFilterModelTodoPackage& package);
|
|
+ void packageToFilter(const ImageFilterModelTodoPackage& package);
|
|
+ void reAddImageInfos(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues);
|
|
+ void reAddingFinished();
|
|
+};
|
|
+
|
|
+} // namespace Digikam
|
|
+
|
|
+#endif // IMAGEFILTERMODELPRIV_H
|
|
diff --git a/libs/database/models/imagefiltermodelthreads.cpp b/libs/database/models/imagefiltermodelthreads.cpp
|
|
new file mode 100644
|
|
index 0000000..aa5c462
|
|
--- /dev/null
|
|
+++ b/libs/database/models/imagefiltermodelthreads.cpp
|
|
@@ -0,0 +1,40 @@
|
|
+/* ============================================================
|
|
+ *
|
|
+ * This file is a part of digiKam project
|
|
+ * http://www.digikam.org
|
|
+ *
|
|
+ * Date : 2009-03-05
|
|
+ * Description : Qt item model for database entries
|
|
+ *
|
|
+ * Copyright (C) 2009-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
|
|
+ * Copyright (C) 2011-2017 by Gilles Caulier <caulier dot gilles at gmail dot com>
|
|
+ * Copyright (C) 2010 by Andi Clemens <andi dot clemens at gmail dot com>
|
|
+ * Copyright (C) 2011 by Michael G. Hansen <mike at mghansen dot de>
|
|
+ * Copyright (C) 2014 by Mohamed Anwer <m dot anwer at gmx dot com>
|
|
+ *
|
|
+ * This program is free software; you can redistribute it
|
|
+ * and/or modify it under the terms of the GNU General
|
|
+ * Public License as published by the Free Software Foundation;
|
|
+ * either version 2, or (at your option)
|
|
+ * any later version.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ *
|
|
+ * ============================================================ */
|
|
+
|
|
+#include "imagefiltermodel.h"
|
|
+#include "imagefiltermodelpriv.h"
|
|
+#include "imagefiltermodelthreads.h"
|
|
+
|
|
+namespace Digikam
|
|
+{
|
|
+
|
|
+ImageFilterModelWorker::ImageFilterModelWorker(ImageFilterModel::ImageFilterModelPrivate* const d)
|
|
+ : d(d)
|
|
+{
|
|
+}
|
|
+
|
|
+} // namespace Digikam
|
|
diff --git a/libs/database/models/imagefiltermodelthreads.h b/libs/database/models/imagefiltermodelthreads.h
|
|
new file mode 100644
|
|
index 0000000..83fa987
|
|
--- /dev/null
|
|
+++ b/libs/database/models/imagefiltermodelthreads.h
|
|
@@ -0,0 +1,100 @@
|
|
+/* ============================================================
|
|
+ *
|
|
+ * This file is a part of digiKam project
|
|
+ * http://www.digikam.org
|
|
+ *
|
|
+ * Date : 2009-03-11
|
|
+ * Description : Qt item model for database entries - private header
|
|
+ *
|
|
+ * Copyright (C) 2009-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
|
|
+ *
|
|
+ * This program is free software; you can redistribute it
|
|
+ * and/or modify it under the terms of the GNU General
|
|
+ * Public License as published by the Free Software Foundation;
|
|
+ * either version 2, or (at your option)
|
|
+ * any later version.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ *
|
|
+ * ============================================================ */
|
|
+
|
|
+#ifndef IMAGEFILTERMODELTHREADS_H
|
|
+#define IMAGEFILTERMODELTHREADS_H
|
|
+
|
|
+// Qt includes
|
|
+
|
|
+#include <QThread>
|
|
+
|
|
+// Local includes
|
|
+
|
|
+#include "digikam_export.h"
|
|
+#include "workerobject.h"
|
|
+
|
|
+namespace Digikam
|
|
+{
|
|
+
|
|
+class DIGIKAM_DATABASE_EXPORT ImageFilterModelWorker : public WorkerObject
|
|
+{
|
|
+ Q_OBJECT
|
|
+
|
|
+public:
|
|
+
|
|
+ explicit ImageFilterModelWorker(ImageFilterModel::ImageFilterModelPrivate* const d);
|
|
+
|
|
+ bool checkVersion(const ImageFilterModelTodoPackage& package)
|
|
+ {
|
|
+ return d->version == package.version;
|
|
+ }
|
|
+
|
|
+public Q_SLOTS:
|
|
+
|
|
+ virtual void process(ImageFilterModelTodoPackage package) = 0;
|
|
+
|
|
+Q_SIGNALS:
|
|
+
|
|
+ void processed(const ImageFilterModelTodoPackage& package);
|
|
+ void discarded(const ImageFilterModelTodoPackage& package);
|
|
+
|
|
+protected:
|
|
+
|
|
+ ImageFilterModel::ImageFilterModelPrivate* d;
|
|
+};
|
|
+
|
|
+// -----------------------------------------------------------------------------------------
|
|
+
|
|
+class DIGIKAM_DATABASE_EXPORT ImageFilterModelPreparer : public ImageFilterModelWorker
|
|
+{
|
|
+ Q_OBJECT
|
|
+
|
|
+public:
|
|
+
|
|
+ explicit ImageFilterModelPreparer(ImageFilterModel::ImageFilterModelPrivate* const d)
|
|
+ : ImageFilterModelWorker(d)
|
|
+ {
|
|
+ }
|
|
+
|
|
+ void process(ImageFilterModelTodoPackage package);
|
|
+};
|
|
+
|
|
+// ----------------------------------------------------------------------------------------
|
|
+
|
|
+class DIGIKAM_DATABASE_EXPORT ImageFilterModelFilterer : public ImageFilterModelWorker
|
|
+{
|
|
+ Q_OBJECT
|
|
+
|
|
+public:
|
|
+
|
|
+ explicit ImageFilterModelFilterer(ImageFilterModel::ImageFilterModelPrivate* const d)
|
|
+ : ImageFilterModelWorker(d)
|
|
+ {
|
|
+ }
|
|
+
|
|
+ void process(ImageFilterModelTodoPackage package);
|
|
+};
|
|
+
|
|
+} // namespace Digikam
|
|
+
|
|
+#endif // IMAGEFILTERMODELTHREADS_H
|
|
diff --git a/libs/database/models/imagefiltersettings.cpp b/libs/database/models/imagefiltersettings.cpp
|
|
new file mode 100644
|
|
index 0000000..b61e7f9
|
|
--- /dev/null
|
|
+++ b/libs/database/models/imagefiltersettings.cpp
|
|
@@ -0,0 +1,952 @@
|
|
+/* ============================================================
|
|
+ *
|
|
+ * This file is a part of digiKam project
|
|
+ * http://www.digikam.org
|
|
+ *
|
|
+ * Date : 2009-03-05
|
|
+ * Description : Filter values for use with ImageFilterModel
|
|
+ *
|
|
+ * Copyright (C) 2009-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
|
|
+ * Copyright (C) 2011-2017 by Gilles Caulier <caulier dot gilles at gmail dot com>
|
|
+ * Copyright (C) 2010 by Andi Clemens <andi dot clemens at gmail dot com>
|
|
+ * Copyright (C) 2011 by Michael G. Hansen <mike at mghansen dot de>
|
|
+ * Copyright (C) 2014 by Mohamed Anwer <m dot anwer at gmx dot com>
|
|
+ *
|
|
+ * This program is free software; you can redistribute it
|
|
+ * and/or modify it under the terms of the GNU General
|
|
+ * Public License as published by the Free Software Foundation;
|
|
+ * either version 2, or (at your option)
|
|
+ * any later version.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ *
|
|
+ * ============================================================ */
|
|
+
|
|
+#include "imagefiltersettings.h"
|
|
+
|
|
+// C++ includes
|
|
+
|
|
+#include <cmath>
|
|
+
|
|
+// Qt includes
|
|
+
|
|
+#include <QDateTime>
|
|
+
|
|
+// Local includes
|
|
+
|
|
+#include "digikam_debug.h"
|
|
+#include "coredbfields.h"
|
|
+#include "digikam_globals.h"
|
|
+#include "imageinfo.h"
|
|
+#include "tagscache.h"
|
|
+#include "versionmanagersettings.h"
|
|
+
|
|
+namespace Digikam
|
|
+{
|
|
+
|
|
+ImageFilterSettings::ImageFilterSettings()
|
|
+{
|
|
+ m_untaggedFilter = false;
|
|
+ m_isUnratedExcluded = false;
|
|
+ m_ratingFilter = 0;
|
|
+ m_mimeTypeFilter = MimeFilter::AllFiles;
|
|
+ m_ratingCond = GreaterEqualCondition;
|
|
+ m_matchingCond = OrCondition;
|
|
+ m_geolocationCondition = GeolocationNoFilter;
|
|
+}
|
|
+
|
|
+DatabaseFields::Set ImageFilterSettings::watchFlags() const
|
|
+{
|
|
+ DatabaseFields::Set set;
|
|
+
|
|
+ if (isFilteringByDay())
|
|
+ {
|
|
+ set |= DatabaseFields::CreationDate;
|
|
+ }
|
|
+
|
|
+ if (isFilteringByText())
|
|
+ {
|
|
+ set |= DatabaseFields::Name;
|
|
+ set |= DatabaseFields::Comment;
|
|
+ }
|
|
+
|
|
+ if (isFilteringByRating())
|
|
+ {
|
|
+ set |= DatabaseFields::Rating;
|
|
+ }
|
|
+
|
|
+ if (isFilteringByTypeMime())
|
|
+ {
|
|
+ set |= DatabaseFields::Category;
|
|
+ set |= DatabaseFields::Format;
|
|
+ }
|
|
+
|
|
+ if (isFilteringByGeolocation())
|
|
+ {
|
|
+ set |= DatabaseFields::ImagePositionsAll;
|
|
+ }
|
|
+
|
|
+ if (isFilteringByColorLabels())
|
|
+ {
|
|
+ set |= DatabaseFields::ColorLabel;
|
|
+ }
|
|
+
|
|
+ if (isFilteringByPickLabels())
|
|
+ {
|
|
+ set |= DatabaseFields::PickLabel;
|
|
+ }
|
|
+
|
|
+ return set;
|
|
+}
|
|
+
|
|
+bool ImageFilterSettings::isFilteringByDay() const
|
|
+{
|
|
+ if (!m_dayFilter.isEmpty())
|
|
+ {
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ return false;
|
|
+}
|
|
+
|
|
+bool ImageFilterSettings::isFilteringByTags() const
|
|
+{
|
|
+ if (!m_includeTagFilter.isEmpty() || !m_excludeTagFilter.isEmpty() || m_untaggedFilter)
|
|
+ {
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ return false;
|
|
+}
|
|
+
|
|
+bool ImageFilterSettings::isFilteringByColorLabels() const
|
|
+{
|
|
+ if (!m_colorLabelTagFilter.isEmpty())
|
|
+ {
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ return false;
|
|
+}
|
|
+
|
|
+bool ImageFilterSettings::isFilteringByPickLabels() const
|
|
+{
|
|
+ if (!m_pickLabelTagFilter.isEmpty())
|
|
+ {
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ return false;
|
|
+}
|
|
+
|
|
+bool ImageFilterSettings::isFilteringByText() const
|
|
+{
|
|
+ if (!m_textFilterSettings.text.isEmpty())
|
|
+ {
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ return false;
|
|
+}
|
|
+
|
|
+bool ImageFilterSettings::isFilteringByTypeMime() const
|
|
+{
|
|
+ if (m_mimeTypeFilter != MimeFilter::AllFiles)
|
|
+ {
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ return false;
|
|
+}
|
|
+
|
|
+bool ImageFilterSettings::isFilteringByGeolocation() const
|
|
+{
|
|
+ return (m_geolocationCondition != GeolocationNoFilter);
|
|
+}
|
|
+
|
|
+bool ImageFilterSettings::isFilteringByRating() const
|
|
+{
|
|
+ if (m_ratingFilter != 0 || m_ratingCond != GreaterEqualCondition || m_isUnratedExcluded)
|
|
+ {
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ return false;
|
|
+}
|
|
+
|
|
+bool ImageFilterSettings::isFilteringInternally() const
|
|
+{
|
|
+ return (isFiltering() || !m_urlWhitelists.isEmpty() || !m_idWhitelists.isEmpty());
|
|
+}
|
|
+
|
|
+bool ImageFilterSettings::isFiltering() const
|
|
+{
|
|
+ return isFilteringByDay() ||
|
|
+ isFilteringByTags() ||
|
|
+ isFilteringByText() ||
|
|
+ isFilteringByRating() ||
|
|
+ isFilteringByTypeMime() ||
|
|
+ isFilteringByColorLabels() ||
|
|
+ isFilteringByPickLabels() ||
|
|
+ isFilteringByGeolocation();
|
|
+}
|
|
+
|
|
+void ImageFilterSettings::setDayFilter(const QList<QDateTime>& days)
|
|
+{
|
|
+ m_dayFilter.clear();
|
|
+
|
|
+ for (QList<QDateTime>::const_iterator it = days.constBegin(); it != days.constEnd(); ++it)
|
|
+ {
|
|
+ m_dayFilter.insert(*it, true);
|
|
+ }
|
|
+}
|
|
+
|
|
+void ImageFilterSettings::setTagFilter(const QList<int>& includedTags,
|
|
+ const QList<int>& excludedTags,
|
|
+ MatchingCondition matchingCondition,
|
|
+ bool showUnTagged,
|
|
+ const QList<int>& clTagIds,
|
|
+ const QList<int>& plTagIds)
|
|
+{
|
|
+ m_includeTagFilter = includedTags;
|
|
+ m_excludeTagFilter = excludedTags;
|
|
+ m_matchingCond = matchingCondition;
|
|
+ m_untaggedFilter = showUnTagged;
|
|
+ m_colorLabelTagFilter = clTagIds;
|
|
+ m_pickLabelTagFilter = plTagIds;
|
|
+}
|
|
+
|
|
+void ImageFilterSettings::setRatingFilter(int rating, RatingCondition ratingCondition, bool isUnratedExcluded)
|
|
+{
|
|
+ m_ratingFilter = rating;
|
|
+ m_ratingCond = ratingCondition;
|
|
+ m_isUnratedExcluded = isUnratedExcluded;
|
|
+}
|
|
+
|
|
+void ImageFilterSettings::setMimeTypeFilter(int mime)
|
|
+{
|
|
+ m_mimeTypeFilter = (MimeFilter::TypeMimeFilter)mime;
|
|
+}
|
|
+
|
|
+void ImageFilterSettings::setGeolocationFilter(const GeolocationCondition& condition)
|
|
+{
|
|
+ m_geolocationCondition = condition;
|
|
+}
|
|
+
|
|
+void ImageFilterSettings::setTextFilter(const SearchTextFilterSettings& settings)
|
|
+{
|
|
+ m_textFilterSettings = settings;
|
|
+}
|
|
+
|
|
+void ImageFilterSettings::setTagNames(const QHash<int, QString>& hash)
|
|
+{
|
|
+ m_tagNameHash = hash;
|
|
+}
|
|
+
|
|
+void ImageFilterSettings::setAlbumNames(const QHash<int, QString>& hash)
|
|
+{
|
|
+ m_albumNameHash = hash;
|
|
+}
|
|
+
|
|
+void ImageFilterSettings::setUrlWhitelist(const QList<QUrl>& urlList, const QString& id)
|
|
+{
|
|
+ if (urlList.isEmpty())
|
|
+ {
|
|
+ m_urlWhitelists.remove(id);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ m_urlWhitelists.insert(id, urlList);
|
|
+ }
|
|
+}
|
|
+
|
|
+void ImageFilterSettings::setIdWhitelist(const QList<qlonglong>& idList, const QString& id)
|
|
+{
|
|
+ if (idList.isEmpty())
|
|
+ {
|
|
+ m_idWhitelists.remove(id);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ m_idWhitelists.insert(id, idList);
|
|
+ }
|
|
+}
|
|
+
|
|
+template <class ContainerA, class ContainerB>
|
|
+bool containsAnyOf(const ContainerA& listA, const ContainerB& listB)
|
|
+{
|
|
+ foreach (const typename ContainerA::value_type& a, listA)
|
|
+ {
|
|
+ if (listB.contains(a))
|
|
+ {
|
|
+ return true;
|
|
+ }
|
|
+ }
|
|
+ return false;
|
|
+}
|
|
+
|
|
+template <class ContainerA, typename Value, class ContainerB>
|
|
+bool containsNoneOfExcept(const ContainerA& list, const ContainerB& noneOfList, const Value& exception)
|
|
+{
|
|
+ foreach (const typename ContainerB::value_type& n, noneOfList)
|
|
+ {
|
|
+ if (n != exception && list.contains(n))
|
|
+ {
|
|
+ return false;
|
|
+ }
|
|
+ }
|
|
+ return true;
|
|
+}
|
|
+
|
|
+bool ImageFilterSettings::matches(const ImageInfo& info, bool* const foundText) const
|
|
+{
|
|
+ if (foundText)
|
|
+ {
|
|
+ *foundText = false;
|
|
+ }
|
|
+
|
|
+ if (!isFilteringInternally())
|
|
+ {
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ bool match = false;
|
|
+
|
|
+ if (!m_includeTagFilter.isEmpty() || !m_excludeTagFilter.isEmpty())
|
|
+ {
|
|
+ QList<int> tagIds = info.tagIds();
|
|
+ QList<int>::const_iterator it;
|
|
+
|
|
+ match = m_includeTagFilter.isEmpty();
|
|
+
|
|
+ if (m_matchingCond == OrCondition)
|
|
+ {
|
|
+ for (it = m_includeTagFilter.begin(); it != m_includeTagFilter.end(); ++it)
|
|
+ {
|
|
+ if (tagIds.contains(*it))
|
|
+ {
|
|
+ match = true;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ match |= (m_untaggedFilter && tagIds.isEmpty());
|
|
+ }
|
|
+ else // AND matching condition...
|
|
+ {
|
|
+ // m_untaggedFilter and non-empty tag filter, combined with AND, is logically no match
|
|
+ if (!m_untaggedFilter)
|
|
+ {
|
|
+ for (it = m_includeTagFilter.begin(); it != m_includeTagFilter.end(); ++it)
|
|
+ {
|
|
+ if (!tagIds.contains(*it))
|
|
+ {
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (it == m_includeTagFilter.end())
|
|
+ {
|
|
+ match = true;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ for (it = m_excludeTagFilter.begin(); it != m_excludeTagFilter.end(); ++it)
|
|
+ {
|
|
+ if (tagIds.contains(*it))
|
|
+ {
|
|
+ match = false;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ else if (m_untaggedFilter)
|
|
+ {
|
|
+ match = !TagsCache::instance()->containsPublicTags(info.tagIds());
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ match = true;
|
|
+ }
|
|
+
|
|
+ //-- Filter by pick labels ------------------------------------------------
|
|
+
|
|
+ if (!m_pickLabelTagFilter.isEmpty())
|
|
+ {
|
|
+ QList<int> tagIds = info.tagIds();
|
|
+ bool matchPL = false;
|
|
+
|
|
+ if (containsAnyOf(m_pickLabelTagFilter, tagIds))
|
|
+ {
|
|
+ matchPL = true;
|
|
+ }
|
|
+ else if (!matchPL)
|
|
+ {
|
|
+ int noPickLabelTagId = TagsCache::instance()->tagForPickLabel(NoPickLabel);
|
|
+
|
|
+ if (m_pickLabelTagFilter.contains(noPickLabelTagId))
|
|
+ {
|
|
+ // Searching for "has no ColorLabel" requires special handling:
|
|
+ // Scan that the tag ids contains none of the ColorLabel tags, except maybe the NoColorLabel tag
|
|
+ matchPL = containsNoneOfExcept(tagIds, TagsCache::instance()->pickLabelTags(), noPickLabelTagId);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ match &= matchPL;
|
|
+ }
|
|
+
|
|
+ //-- Filter by color labels ------------------------------------------------
|
|
+
|
|
+ if (!m_colorLabelTagFilter.isEmpty())
|
|
+ {
|
|
+ QList<int> tagIds = info.tagIds();
|
|
+ bool matchCL = false;
|
|
+
|
|
+ if (containsAnyOf(m_colorLabelTagFilter, tagIds))
|
|
+ {
|
|
+ matchCL = true;
|
|
+ }
|
|
+ else if (!matchCL)
|
|
+ {
|
|
+ int noColorLabelTagId = TagsCache::instance()->tagForColorLabel(NoColorLabel);
|
|
+
|
|
+ if (m_colorLabelTagFilter.contains(noColorLabelTagId))
|
|
+ {
|
|
+ // Searching for "has no ColorLabel" requires special handling:
|
|
+ // Scan that the tag ids contains none of the ColorLabel tags, except maybe the NoColorLabel tag
|
|
+ matchCL = containsNoneOfExcept(tagIds, TagsCache::instance()->colorLabelTags(), noColorLabelTagId);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ match &= matchCL;
|
|
+ }
|
|
+
|
|
+ //-- Filter by date -----------------------------------------------------------
|
|
+
|
|
+ if (!m_dayFilter.isEmpty())
|
|
+ {
|
|
+ match &= m_dayFilter.contains(QDateTime(info.dateTime().date(), QTime()));
|
|
+ }
|
|
+
|
|
+ //-- Filter by rating ---------------------------------------------------------
|
|
+
|
|
+ if (m_ratingFilter >= 0)
|
|
+ {
|
|
+ // for now we treat -1 (no rating) just like a rating of 0.
|
|
+ int rating = info.rating();
|
|
+
|
|
+ if (rating == -1)
|
|
+ {
|
|
+ rating = 0;
|
|
+ }
|
|
+
|
|
+ if(m_isUnratedExcluded && rating == 0)
|
|
+ {
|
|
+ match = false;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ if (m_ratingCond == GreaterEqualCondition)
|
|
+ {
|
|
+ // If the rating is not >=, i.e it is <, then it does not match.
|
|
+ if (rating < m_ratingFilter)
|
|
+ {
|
|
+ match = false;
|
|
+ }
|
|
+ }
|
|
+ else if (m_ratingCond == EqualCondition)
|
|
+ {
|
|
+ // If the rating is not =, i.e it is !=, then it does not match.
|
|
+ if (rating != m_ratingFilter)
|
|
+ {
|
|
+ match = false;
|
|
+ }
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ // If the rating is not <=, i.e it is >, then it does not match.
|
|
+ if (rating > m_ratingFilter)
|
|
+ {
|
|
+ match = false;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ // -- Filter by mime type -----------------------------------------------------
|
|
+
|
|
+ switch (m_mimeTypeFilter)
|
|
+ {
|
|
+ // info.format is a standardized string: Only one possibility per mime type
|
|
+ case MimeFilter::ImageFiles:
|
|
+ {
|
|
+ if (info.category() != DatabaseItem::Image)
|
|
+ {
|
|
+ match = false;
|
|
+ }
|
|
+
|
|
+ break;
|
|
+ }
|
|
+ case MimeFilter::JPGFiles:
|
|
+ {
|
|
+ if (info.format() != QLatin1String("JPG"))
|
|
+ {
|
|
+ match = false;
|
|
+ }
|
|
+
|
|
+ break;
|
|
+ }
|
|
+ case MimeFilter::PNGFiles:
|
|
+ {
|
|
+ if (info.format() != QLatin1String("PNG"))
|
|
+ {
|
|
+ match = false;
|
|
+ }
|
|
+
|
|
+ break;
|
|
+ }
|
|
+ case MimeFilter::TIFFiles:
|
|
+ {
|
|
+ if (info.format() != QLatin1String("TIFF"))
|
|
+ {
|
|
+ match = false;
|
|
+ }
|
|
+
|
|
+ break;
|
|
+ }
|
|
+ case MimeFilter::DNGFiles:
|
|
+ {
|
|
+ if (info.format() != QLatin1String("RAW-DNG"))
|
|
+ {
|
|
+ match = false;
|
|
+ }
|
|
+
|
|
+ break;
|
|
+ }
|
|
+ case MimeFilter::NoRAWFiles:
|
|
+ {
|
|
+ if (info.format().startsWith(QLatin1String("RAW")))
|
|
+ {
|
|
+ match = false;
|
|
+ }
|
|
+
|
|
+ break;
|
|
+ }
|
|
+ case MimeFilter::RAWFiles:
|
|
+ {
|
|
+ if (!info.format().startsWith(QLatin1String("RAW")))
|
|
+ {
|
|
+ match = false;
|
|
+ }
|
|
+
|
|
+ break;
|
|
+ }
|
|
+ case MimeFilter::MoviesFiles:
|
|
+ {
|
|
+ if (info.category() != DatabaseItem::Video)
|
|
+ {
|
|
+ match = false;
|
|
+ }
|
|
+
|
|
+ break;
|
|
+ }
|
|
+ case MimeFilter::AudioFiles:
|
|
+ {
|
|
+ if (info.category() != DatabaseItem::Audio)
|
|
+ {
|
|
+ match = false;
|
|
+ }
|
|
+
|
|
+ break;
|
|
+ }
|
|
+ case MimeFilter::RasterFiles:
|
|
+ {
|
|
+ if (info.format() != QLatin1String("PSD") && // Adobe Photoshop Document
|
|
+ info.format() != QLatin1String("PSB") && // Adobe Photoshop Big
|
|
+ info.format() != QLatin1String("XCF") && // Gimp
|
|
+ info.format() != QLatin1String("KRA") && // Krita
|
|
+ info.format() != QLatin1String("ORA") // Open Raster
|
|
+ )
|
|
+ {
|
|
+ match = false;
|
|
+ }
|
|
+
|
|
+ break;
|
|
+ }
|
|
+ default:
|
|
+ {
|
|
+ // All Files: do nothing...
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ //-- Filter by geolocation ----------------------------------------------------
|
|
+
|
|
+ if (m_geolocationCondition!=GeolocationNoFilter)
|
|
+ {
|
|
+ if (m_geolocationCondition==GeolocationNoCoordinates)
|
|
+ {
|
|
+ if (info.hasCoordinates())
|
|
+ {
|
|
+ match = false;
|
|
+ }
|
|
+ }
|
|
+ else if (m_geolocationCondition==GeolocationHasCoordinates)
|
|
+ {
|
|
+ if (!info.hasCoordinates())
|
|
+ {
|
|
+ match = false;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ //-- Filter by text -----------------------------------------------------------
|
|
+
|
|
+ if (!m_textFilterSettings.text.isEmpty())
|
|
+ {
|
|
+ bool textMatch = false;
|
|
+
|
|
+ // Image name
|
|
+ if (m_textFilterSettings.textFields & SearchTextFilterSettings::ImageName &&
|
|
+ info.name().contains(m_textFilterSettings.text, m_textFilterSettings.caseSensitive))
|
|
+ {
|
|
+ textMatch = true;
|
|
+ }
|
|
+
|
|
+ // Image title
|
|
+ if (m_textFilterSettings.textFields & SearchTextFilterSettings::ImageTitle &&
|
|
+ info.title().contains(m_textFilterSettings.text, m_textFilterSettings.caseSensitive))
|
|
+ {
|
|
+ textMatch = true;
|
|
+ }
|
|
+
|
|
+ // Image comment
|
|
+ if (m_textFilterSettings.textFields & SearchTextFilterSettings::ImageComment &&
|
|
+ info.comment().contains(m_textFilterSettings.text, m_textFilterSettings.caseSensitive))
|
|
+ {
|
|
+ textMatch = true;
|
|
+ }
|
|
+
|
|
+ // Tag names
|
|
+ foreach(int id, info.tagIds())
|
|
+ {
|
|
+ if (m_textFilterSettings.textFields & SearchTextFilterSettings::TagName &&
|
|
+ m_tagNameHash.value(id).contains(m_textFilterSettings.text, m_textFilterSettings.caseSensitive))
|
|
+ {
|
|
+ textMatch = true;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ // Album names
|
|
+ if (m_textFilterSettings.textFields & SearchTextFilterSettings::AlbumName &&
|
|
+ m_albumNameHash.value(info.albumId()).contains(m_textFilterSettings.text, m_textFilterSettings.caseSensitive))
|
|
+ {
|
|
+ textMatch = true;
|
|
+ }
|
|
+
|
|
+ // Image Aspect Ratio
|
|
+ if (m_textFilterSettings.textFields & SearchTextFilterSettings::ImageAspectRatio)
|
|
+ {
|
|
+ QRegExp expRatio (QLatin1String("^\\d+:\\d+$"));
|
|
+ QRegExp expFloat (QLatin1String("^\\d+(.\\d+)?$"));
|
|
+
|
|
+ if (expRatio.indexIn(m_textFilterSettings.text) > -1 && m_textFilterSettings.text.contains(QRegExp(QLatin1String(":\\d+"))))
|
|
+ {
|
|
+ QString trimmedTextFilterSettingsText = m_textFilterSettings.text;
|
|
+ QStringList numberStringList = trimmedTextFilterSettingsText.split(QLatin1String(":"), QString::SkipEmptyParts);
|
|
+
|
|
+ if (numberStringList.length() == 2)
|
|
+ {
|
|
+ QString numString = (QString)numberStringList.at(0), denomString = (QString)numberStringList.at(1);
|
|
+ bool canConverseNum = false;
|
|
+ bool canConverseDenom = false;
|
|
+ int num = numString.toInt(&canConverseNum, 10), denom = denomString.toInt(&canConverseDenom, 10);
|
|
+
|
|
+ if (canConverseNum && canConverseDenom)
|
|
+ {
|
|
+ if (fabs(info.aspectRatio() - (double)num / denom) < 0.1)
|
|
+ textMatch = true;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ else if (expFloat.indexIn(m_textFilterSettings.text) > -1)
|
|
+ {
|
|
+ QString trimmedTextFilterSettingsText = m_textFilterSettings.text;
|
|
+ bool canConverse = false;
|
|
+ double ratio = trimmedTextFilterSettingsText.toDouble(&canConverse);
|
|
+
|
|
+ if (canConverse)
|
|
+ {
|
|
+ if (fabs(info.aspectRatio() - ratio) < 0.1)
|
|
+ textMatch = true;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ // Image Pixel Size
|
|
+ // See bug #341053 for details.
|
|
+
|
|
+ if (m_textFilterSettings.textFields & SearchTextFilterSettings::ImagePixelSize)
|
|
+ {
|
|
+ QSize size = info.dimensions();
|
|
+ int pixelSize = size.height()*size.width();
|
|
+ QString text = m_textFilterSettings.text;
|
|
+
|
|
+ if(text.contains(QRegExp(QLatin1String("^>\\d{1,15}$"))) && pixelSize > (text.remove(0,1)).toInt())
|
|
+ {
|
|
+ textMatch = true;
|
|
+ }
|
|
+ else if(text.contains(QRegExp(QLatin1String("^<\\d{1,15}$"))) && pixelSize < (text.remove(0,1)).toInt())
|
|
+ {
|
|
+ textMatch = true;
|
|
+ }
|
|
+ else if(text.contains(QRegExp(QLatin1String("^\\d+$"))) && pixelSize == text.toInt())
|
|
+ {
|
|
+ textMatch = true;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ match &= textMatch;
|
|
+
|
|
+ if (foundText)
|
|
+ {
|
|
+ *foundText = textMatch;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ // -- filter by URL-whitelists ------------------------------------------------
|
|
+ // NOTE: whitelists are always AND for now.
|
|
+
|
|
+ if (match)
|
|
+ {
|
|
+ const QUrl url = info.fileUrl();
|
|
+
|
|
+ for (QHash<QString, QList<QUrl>>::const_iterator it = m_urlWhitelists.constBegin();
|
|
+ it!=m_urlWhitelists.constEnd(); ++it)
|
|
+ {
|
|
+ match = it->contains(url);
|
|
+
|
|
+ if (!match)
|
|
+ {
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (match)
|
|
+ {
|
|
+ const qlonglong id = info.id();
|
|
+
|
|
+ for (QHash<QString, QList<qlonglong> >::const_iterator it = m_idWhitelists.constBegin();
|
|
+ it!=m_idWhitelists.constEnd(); ++it)
|
|
+ {
|
|
+ match = it->contains(id);
|
|
+
|
|
+ if (!match)
|
|
+ {
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return match;
|
|
+}
|
|
+
|
|
+// -------------------------------------------------------------------------------------------------
|
|
+
|
|
+VersionImageFilterSettings::VersionImageFilterSettings()
|
|
+{
|
|
+ m_includeTagFilter = 0;
|
|
+ m_exceptionTagFilter = 0;
|
|
+}
|
|
+
|
|
+VersionImageFilterSettings::VersionImageFilterSettings(const VersionManagerSettings& settings)
|
|
+{
|
|
+ setVersionManagerSettings(settings);
|
|
+}
|
|
+
|
|
+bool VersionImageFilterSettings::operator==(const VersionImageFilterSettings& other) const
|
|
+{
|
|
+ return m_excludeTagFilter == other.m_excludeTagFilter &&
|
|
+ m_exceptionLists == other.m_exceptionLists;
|
|
+}
|
|
+
|
|
+bool VersionImageFilterSettings::matches(const ImageInfo& info) const
|
|
+{
|
|
+ if (!isFiltering())
|
|
+ {
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ const qlonglong id = info.id();
|
|
+
|
|
+ for (QHash<QString, QList<qlonglong> >::const_iterator it = m_exceptionLists.constBegin();
|
|
+ it != m_exceptionLists.constEnd(); ++it)
|
|
+ {
|
|
+ if (it->contains(id))
|
|
+ {
|
|
+ return true;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ bool match = true;
|
|
+ QList<int> tagIds = info.tagIds();
|
|
+
|
|
+ if (!tagIds.contains(m_includeTagFilter))
|
|
+ {
|
|
+ for (QList<int>::const_iterator it = m_excludeTagFilter.begin();
|
|
+ it != m_excludeTagFilter.end(); ++it)
|
|
+ {
|
|
+ if (tagIds.contains(*it))
|
|
+ {
|
|
+ match = false;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (!match)
|
|
+ {
|
|
+ if (tagIds.contains(m_exceptionTagFilter))
|
|
+ {
|
|
+ match = true;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return match;
|
|
+}
|
|
+
|
|
+bool VersionImageFilterSettings::isHiddenBySettings(const ImageInfo& info) const
|
|
+{
|
|
+ QList<int> tagIds = info.tagIds();
|
|
+
|
|
+ foreach(int tagId, m_excludeTagFilter)
|
|
+ {
|
|
+ if (tagIds.contains(tagId))
|
|
+ {
|
|
+ return true;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return false;
|
|
+}
|
|
+
|
|
+bool VersionImageFilterSettings::isExemptedBySettings(const ImageInfo& info) const
|
|
+{
|
|
+ return info.tagIds().contains(m_exceptionTagFilter);
|
|
+}
|
|
+
|
|
+void VersionImageFilterSettings::setVersionManagerSettings(const VersionManagerSettings& settings)
|
|
+{
|
|
+ m_excludeTagFilter.clear();
|
|
+
|
|
+ if (!settings.enabled)
|
|
+ {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (!(settings.showInViewFlags & VersionManagerSettings::ShowOriginal))
|
|
+ {
|
|
+ m_excludeTagFilter << TagsCache::instance()->getOrCreateInternalTag(InternalTagName::originalVersion());
|
|
+ }
|
|
+
|
|
+ if (!(settings.showInViewFlags & VersionManagerSettings::ShowIntermediates))
|
|
+ {
|
|
+ m_excludeTagFilter << TagsCache::instance()->getOrCreateInternalTag(InternalTagName::intermediateVersion());
|
|
+ }
|
|
+
|
|
+ m_includeTagFilter = TagsCache::instance()->getOrCreateInternalTag(InternalTagName::currentVersion());
|
|
+ m_exceptionTagFilter = TagsCache::instance()->getOrCreateInternalTag(InternalTagName::versionAlwaysVisible());
|
|
+}
|
|
+
|
|
+void VersionImageFilterSettings::setExceptionList(const QList<qlonglong>& idList, const QString& id)
|
|
+{
|
|
+ if (idList.isEmpty())
|
|
+ {
|
|
+ m_exceptionLists.remove(id);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ m_exceptionLists.insert(id, idList);
|
|
+ }
|
|
+}
|
|
+
|
|
+bool VersionImageFilterSettings::isFiltering() const
|
|
+{
|
|
+ return !m_excludeTagFilter.isEmpty();
|
|
+}
|
|
+
|
|
+bool VersionImageFilterSettings::isFilteringByTags() const
|
|
+{
|
|
+ return isFiltering();
|
|
+}
|
|
+
|
|
+// -------------------------------------------------------------------------------------------------
|
|
+
|
|
+GroupImageFilterSettings::GroupImageFilterSettings()
|
|
+ : m_allOpen(false)
|
|
+{
|
|
+}
|
|
+
|
|
+bool GroupImageFilterSettings::operator==(const GroupImageFilterSettings& other) const
|
|
+{
|
|
+ return (m_allOpen == other.m_allOpen &&
|
|
+ m_openGroups == other.m_openGroups);
|
|
+}
|
|
+
|
|
+bool GroupImageFilterSettings::matches(const ImageInfo& info) const
|
|
+{
|
|
+ if (m_allOpen)
|
|
+ {
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ if (info.isGrouped())
|
|
+ {
|
|
+ return m_openGroups.contains(info.groupImage().id());
|
|
+ }
|
|
+ return true;
|
|
+}
|
|
+
|
|
+void GroupImageFilterSettings::setOpen(qlonglong group, bool open)
|
|
+{
|
|
+ if (open)
|
|
+ {
|
|
+ m_openGroups << group;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ m_openGroups.remove(group);
|
|
+ }
|
|
+}
|
|
+
|
|
+bool GroupImageFilterSettings::isOpen(qlonglong group) const
|
|
+{
|
|
+ return m_openGroups.contains(group);
|
|
+}
|
|
+
|
|
+void GroupImageFilterSettings::setAllOpen(bool open)
|
|
+{
|
|
+ m_allOpen = open;
|
|
+}
|
|
+
|
|
+bool GroupImageFilterSettings::isAllOpen() const
|
|
+{
|
|
+ return m_allOpen;
|
|
+}
|
|
+
|
|
+bool GroupImageFilterSettings::isFiltering() const
|
|
+{
|
|
+ return !m_allOpen;
|
|
+}
|
|
+
|
|
+DatabaseFields::Set GroupImageFilterSettings::watchFlags() const
|
|
+{
|
|
+ return DatabaseFields::ImageRelations;
|
|
+}
|
|
+
|
|
+} // namespace Digikam
|
|
diff --git a/libs/database/models/imagefiltersettings.h b/libs/database/models/imagefiltersettings.h
|
|
new file mode 100644
|
|
index 0000000..0e7beae
|
|
--- /dev/null
|
|
+++ b/libs/database/models/imagefiltersettings.h
|
|
@@ -0,0 +1,349 @@
|
|
+/* ============================================================
|
|
+ *
|
|
+ * This file is a part of digiKam project
|
|
+ * http://www.digikam.org
|
|
+ *
|
|
+ * Date : 2009-03-05
|
|
+ * Description : Filter values for use with ImageFilterModel
|
|
+ *
|
|
+ * Copyright (C) 2009-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
|
|
+ * Copyright (C) 2011-2017 by Gilles Caulier <caulier dot gilles at gmail dot com>
|
|
+ * Copyright (C) 2010 by Andi Clemens <andi dot clemens at gmail dot com>
|
|
+ * Copyright (C) 2011 by Michael G. Hansen <mike at mghansen dot de>
|
|
+ * Copyright (C) 2014 by Mohamed Anwer <m dot anwer at gmx dot com>
|
|
+ *
|
|
+ * This program is free software; you can redistribute it
|
|
+ * and/or modify it under the terms of the GNU General
|
|
+ * Public License as published by the Free Software Foundation;
|
|
+ * either version 2, or (at your option)
|
|
+ * any later version.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ *
|
|
+ * ============================================================ */
|
|
+
|
|
+#ifndef IMAGEFILTERSETTINGS_H
|
|
+#define IMAGEFILTERSETTINGS_H
|
|
+
|
|
+// Qt includes
|
|
+
|
|
+#include <QHash>
|
|
+#include <QList>
|
|
+#include <QMap>
|
|
+#include <QString>
|
|
+#include <QSet>
|
|
+#include <QUrl>
|
|
+
|
|
+// Local includes
|
|
+
|
|
+#include "searchtextbar.h"
|
|
+#include "mimefilter.h"
|
|
+#include "digikam_export.h"
|
|
+
|
|
+namespace Digikam
|
|
+{
|
|
+
|
|
+class ImageInfo;
|
|
+class VersionManagerSettings;
|
|
+
|
|
+namespace DatabaseFields
|
|
+{
|
|
+ class Set;
|
|
+}
|
|
+
|
|
+// ---------------------------------------------------------------------------------------
|
|
+
|
|
+class DIGIKAM_DATABASE_EXPORT SearchTextFilterSettings : public SearchTextSettings
|
|
+{
|
|
+
|
|
+public:
|
|
+
|
|
+ enum TextFilterFields
|
|
+ {
|
|
+ None = 0x00,
|
|
+ ImageName = 0x01,
|
|
+ ImageTitle = 0x02,
|
|
+ ImageComment = 0x04,
|
|
+ TagName = 0x08,
|
|
+ AlbumName = 0x10,
|
|
+ ImageAspectRatio = 0x20,
|
|
+ ImagePixelSize = 0x40,
|
|
+ All = ImageName | ImageTitle | ImageComment | TagName | AlbumName | ImageAspectRatio | ImagePixelSize
|
|
+ };
|
|
+
|
|
+public:
|
|
+
|
|
+ SearchTextFilterSettings()
|
|
+ {
|
|
+ textFields = None;
|
|
+ }
|
|
+
|
|
+ explicit SearchTextFilterSettings(const SearchTextSettings& settings)
|
|
+ {
|
|
+ caseSensitive = settings.caseSensitive;
|
|
+ text = settings.text;
|
|
+ textFields = None;
|
|
+ }
|
|
+
|
|
+ TextFilterFields textFields;
|
|
+};
|
|
+
|
|
+// ---------------------------------------------------------------------------------------
|
|
+
|
|
+class DIGIKAM_DATABASE_EXPORT ImageFilterSettings
|
|
+{
|
|
+public:
|
|
+
|
|
+ ImageFilterSettings();
|
|
+
|
|
+ /**
|
|
+ * Returns true if the given ImageInfo matches the filter criteria.
|
|
+ * Optionally, foundText is set to true if it matched by text search.
|
|
+ */
|
|
+ bool matches(const ImageInfo& info, bool* const foundText = 0) const;
|
|
+
|
|
+public:
|
|
+
|
|
+ /// --- Tags filter ---
|
|
+
|
|
+ /// Possible logical matching condition used to sort tags id.
|
|
+ enum MatchingCondition
|
|
+ {
|
|
+ OrCondition,
|
|
+ AndCondition
|
|
+ };
|
|
+
|
|
+ void setTagFilter(const QList<int>& includedTags,
|
|
+ const QList<int>& excludedTags,
|
|
+ MatchingCondition matchingCond,
|
|
+ bool showUnTagged,
|
|
+ const QList<int>& clTagIds,
|
|
+ const QList<int>& plTagIds);
|
|
+
|
|
+public:
|
|
+
|
|
+ /// --- Rating filter ---
|
|
+
|
|
+ /// Possible conditions used to filter rating: >=, =, <=
|
|
+ enum RatingCondition
|
|
+ {
|
|
+ GreaterEqualCondition,
|
|
+ EqualCondition,
|
|
+ LessEqualCondition
|
|
+ };
|
|
+
|
|
+ void setRatingFilter(int rating, RatingCondition ratingCond, bool isUnratedExcluded);
|
|
+
|
|
+public:
|
|
+
|
|
+ /// --- Date filter ---
|
|
+ void setDayFilter(const QList<QDateTime>& days);
|
|
+
|
|
+public:
|
|
+
|
|
+ /// --- Text filter ---
|
|
+ void setTextFilter(const SearchTextFilterSettings& settings);
|
|
+ void setTagNames(const QHash<int, QString>& tagNameHash);
|
|
+ void setAlbumNames(const QHash<int, QString>& albumNameHash);
|
|
+
|
|
+public:
|
|
+
|
|
+ /// --- Mime filter ---
|
|
+ void setMimeTypeFilter(int mimeTypeFilter);
|
|
+
|
|
+public:
|
|
+
|
|
+ /// --- Geolocation filter
|
|
+ enum GeolocationCondition
|
|
+ {
|
|
+ GeolocationNoFilter = 0,
|
|
+ GeolocationNoCoordinates = 1 << 1,
|
|
+ GeolocationHasCoordinates = 1 << 2
|
|
+ };
|
|
+
|
|
+ void setGeolocationFilter(const GeolocationCondition& condition);
|
|
+
|
|
+public:
|
|
+
|
|
+ /// Returns if the day is a filter criteria
|
|
+ bool isFilteringByDay() const;
|
|
+
|
|
+ /// Returns if the type mime is a filter criteria
|
|
+ bool isFilteringByTypeMime() const;
|
|
+
|
|
+ /// Returns whether geolocation is a filter criteria
|
|
+ bool isFilteringByGeolocation() const;
|
|
+
|
|
+ /// Returns if the rating is a filter criteria
|
|
+ bool isFilteringByRating() const;
|
|
+
|
|
+ /// Returns if the pick labels is a filter criteria
|
|
+ bool isFilteringByPickLabels() const;
|
|
+
|
|
+ /// Returns if the color labels is a filter criteria
|
|
+ bool isFilteringByColorLabels() const;
|
|
+
|
|
+ /// Returns if the tag is a filter criteria
|
|
+ bool isFilteringByTags() const;
|
|
+
|
|
+ /// Returns if the text (including comment) is a filter criteria
|
|
+ bool isFilteringByText() const;
|
|
+
|
|
+ /// Returns if images will be filtered by these criteria at all
|
|
+ bool isFiltering() const;
|
|
+
|
|
+public:
|
|
+
|
|
+ /// --- URL whitelist filter
|
|
+ void setUrlWhitelist(const QList<QUrl>& urlList, const QString& id);
|
|
+
|
|
+public:
|
|
+
|
|
+ /// --- ID whitelist filter
|
|
+ void setIdWhitelist(const QList<qlonglong>& idList, const QString& id);
|
|
+
|
|
+public:
|
|
+
|
|
+ /// --- Change notification ---
|
|
+
|
|
+ /** Returns database fields a change in which would affect the current filtering.
|
|
+ * To find out if an image tag change affects filtering, test isFilteringByTags().
|
|
+ * The text filter will also be affected by changes in tags and album names.
|
|
+ */
|
|
+ DatabaseFields::Set watchFlags() const;
|
|
+
|
|
+private:
|
|
+
|
|
+ /**
|
|
+ * @brief Returns whether some internal filtering (whitelist by id or URL) or normal filtering is going on
|
|
+ */
|
|
+ bool isFilteringInternally() const;
|
|
+
|
|
+private:
|
|
+
|
|
+ /// --- Tags filter ---
|
|
+ bool m_untaggedFilter;
|
|
+ QList<int> m_includeTagFilter;
|
|
+ QList<int> m_excludeTagFilter;
|
|
+ MatchingCondition m_matchingCond;
|
|
+ QList<int> m_colorLabelTagFilter;
|
|
+ QList<int> m_pickLabelTagFilter;
|
|
+
|
|
+ /// --- Rating filter ---
|
|
+ int m_ratingFilter;
|
|
+ RatingCondition m_ratingCond;
|
|
+ bool m_isUnratedExcluded;
|
|
+
|
|
+ /// --- Date filter ---
|
|
+ QMap<QDateTime, bool> m_dayFilter;
|
|
+
|
|
+ /// --- Text filter ---
|
|
+ SearchTextFilterSettings m_textFilterSettings;
|
|
+
|
|
+ /// Helpers for text search: Set these if you want to search album or tag names with text search
|
|
+ QHash<int, QString> m_tagNameHash;
|
|
+ QHash<int, QString> m_albumNameHash;
|
|
+
|
|
+ /// --- Mime filter ---
|
|
+ MimeFilter::TypeMimeFilter m_mimeTypeFilter;
|
|
+
|
|
+ /// --- Geolocation filter
|
|
+ GeolocationCondition m_geolocationCondition;
|
|
+
|
|
+ /// --- URL whitelist filter
|
|
+ QHash<QString,QList<QUrl>> m_urlWhitelists;
|
|
+
|
|
+ /// --- ID whitelist filter
|
|
+ QHash<QString,QList<qlonglong> > m_idWhitelists;
|
|
+};
|
|
+
|
|
+// ---------------------------------------------------------------------------------------
|
|
+
|
|
+class DIGIKAM_DATABASE_EXPORT VersionImageFilterSettings
|
|
+{
|
|
+public:
|
|
+
|
|
+ VersionImageFilterSettings();
|
|
+ explicit VersionImageFilterSettings(const VersionManagerSettings& settings);
|
|
+
|
|
+ bool operator==(const VersionImageFilterSettings& other) const;
|
|
+
|
|
+ /**
|
|
+ * Returns true if the given ImageInfo matches the filter criteria.
|
|
+ */
|
|
+ bool matches(const ImageInfo& info) const;
|
|
+
|
|
+ bool isHiddenBySettings(const ImageInfo& info) const;
|
|
+ bool isExemptedBySettings(const ImageInfo& info) const;
|
|
+
|
|
+ /// --- Tags filter ---
|
|
+
|
|
+ void setVersionManagerSettings(const VersionManagerSettings& settings);
|
|
+
|
|
+ /**
|
|
+ * Add list with exceptions: These images will be exempted from filtering by this filter
|
|
+ */
|
|
+ void setExceptionList(const QList<qlonglong>& idlist, const QString& id);
|
|
+
|
|
+ /// Returns if images will be filtered by these criteria at all
|
|
+ bool isFiltering() const;
|
|
+
|
|
+ /// Returns if the tag is a filter criteria
|
|
+ bool isFilteringByTags() const;
|
|
+
|
|
+ /// DatabaseFields::Set watchFlags() const: Would return 0
|
|
+
|
|
+protected:
|
|
+
|
|
+ QList<int> m_excludeTagFilter;
|
|
+ int m_includeTagFilter;
|
|
+ int m_exceptionTagFilter;
|
|
+ QHash<QString,QList<qlonglong> > m_exceptionLists;
|
|
+};
|
|
+
|
|
+// ---------------------------------------------------------------------------------------
|
|
+
|
|
+class DIGIKAM_DATABASE_EXPORT GroupImageFilterSettings
|
|
+{
|
|
+public:
|
|
+
|
|
+ GroupImageFilterSettings();
|
|
+
|
|
+ bool operator==(const GroupImageFilterSettings& other) const;
|
|
+
|
|
+ /**
|
|
+ * Returns true if the given ImageInfo matches the filter criteria.
|
|
+ */
|
|
+ bool matches(const ImageInfo& info) const;
|
|
+
|
|
+ /**
|
|
+ * Open or close a group.
|
|
+ */
|
|
+ void setOpen(qlonglong group, bool open);
|
|
+ bool isOpen(qlonglong group) const;
|
|
+
|
|
+ /**
|
|
+ * Open all groups
|
|
+ */
|
|
+ void setAllOpen(bool open);
|
|
+ bool isAllOpen() const;
|
|
+
|
|
+ /// Returns if images will be filtered by these criteria at all
|
|
+ bool isFiltering() const;
|
|
+
|
|
+ DatabaseFields::Set watchFlags() const;
|
|
+
|
|
+protected:
|
|
+
|
|
+ bool m_allOpen;
|
|
+ QSet<qlonglong> m_openGroups;
|
|
+};
|
|
+
|
|
+} // namespace Digikam
|
|
+
|
|
+Q_DECLARE_METATYPE(Digikam::ImageFilterSettings::GeolocationCondition)
|
|
+
|
|
+#endif // IMAGEFILTERSETTINGS_H
|
|
diff --git a/libs/database/models/imagelistmodel.cpp b/libs/database/models/imagelistmodel.cpp
|
|
new file mode 100644
|
|
index 0000000..fafce34
|
|
--- /dev/null
|
|
+++ b/libs/database/models/imagelistmodel.cpp
|
|
@@ -0,0 +1,70 @@
|
|
+/* ============================================================
|
|
+ *
|
|
+ * This file is a part of digiKam project
|
|
+ * http://www.digikam.org
|
|
+ *
|
|
+ * Date : 2010-12-06
|
|
+ * Description : An image model based on a static list
|
|
+ *
|
|
+ * Copyright (C) 2010-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
|
|
+ *
|
|
+ * This program is free software; you can redistribute it
|
|
+ * and/or modify it under the terms of the GNU General
|
|
+ * Public License as published by the Free Software Foundation;
|
|
+ * either version 2, or (at your option)
|
|
+ * any later version.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ *
|
|
+ * ============================================================ */
|
|
+
|
|
+#include "imagelistmodel.h"
|
|
+
|
|
+// Local includes
|
|
+
|
|
+#include "digikam_debug.h"
|
|
+#include "coredbaccess.h"
|
|
+#include "coredbchangesets.h"
|
|
+#include "coredbwatch.h"
|
|
+#include "imageinfo.h"
|
|
+#include "imageinfolist.h"
|
|
+
|
|
+namespace Digikam
|
|
+{
|
|
+
|
|
+ImageListModel::ImageListModel(QObject* parent)
|
|
+ : ImageThumbnailModel(parent)
|
|
+{
|
|
+ connect(CoreDbAccess::databaseWatch(), SIGNAL(collectionImageChange(CollectionImageChangeset)),
|
|
+ this, SLOT(slotCollectionImageChange(CollectionImageChangeset)));
|
|
+}
|
|
+
|
|
+ImageListModel::~ImageListModel()
|
|
+{
|
|
+}
|
|
+
|
|
+void ImageListModel::slotCollectionImageChange(const CollectionImageChangeset& changeset)
|
|
+{
|
|
+ if (isEmpty())
|
|
+ {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ switch (changeset.operation())
|
|
+ {
|
|
+ case CollectionImageChangeset::Added:
|
|
+ break;
|
|
+ case CollectionImageChangeset::Removed:
|
|
+ case CollectionImageChangeset::RemovedAll:
|
|
+ removeImageInfos(ImageInfoList(changeset.ids()));
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+}
|
|
+
|
|
+} // namespace Digikam
|
|
diff --git a/libs/database/models/imagelistmodel.h b/libs/database/models/imagelistmodel.h
|
|
new file mode 100644
|
|
index 0000000..a225b1b
|
|
--- /dev/null
|
|
+++ b/libs/database/models/imagelistmodel.h
|
|
@@ -0,0 +1,63 @@
|
|
+/* ============================================================
|
|
+ *
|
|
+ * This file is a part of digiKam project
|
|
+ * http://www.digikam.org
|
|
+ *
|
|
+ * Date : 2010-12-06
|
|
+ * Description : An image model based on a static list
|
|
+ *
|
|
+ * Copyright (C) 2010-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
|
|
+ *
|
|
+ * This program is free software; you can redistribute it
|
|
+ * and/or modify it under the terms of the GNU General
|
|
+ * Public License as published by the Free Software Foundation;
|
|
+ * either version 2, or (at your option)
|
|
+ * any later version.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ *
|
|
+ * ============================================================ */
|
|
+
|
|
+#ifndef IMAGELISTMODEL_H
|
|
+#define IMAGELISTMODEL_H
|
|
+
|
|
+// Local includes
|
|
+
|
|
+#include "imagethumbnailmodel.h"
|
|
+#include "digikam_export.h"
|
|
+
|
|
+namespace Digikam
|
|
+{
|
|
+
|
|
+class ImageChangeset;
|
|
+class CollectionImageChangeset;
|
|
+
|
|
+class DIGIKAM_DATABASE_EXPORT ImageListModel : public ImageThumbnailModel
|
|
+{
|
|
+ Q_OBJECT
|
|
+
|
|
+public:
|
|
+
|
|
+ explicit ImageListModel(QObject* parent = 0);
|
|
+ ~ImageListModel();
|
|
+
|
|
+ // NOTE: necessary methods to add and remove ImageInfos to the model are inherited from ImageModel
|
|
+
|
|
+Q_SIGNALS:
|
|
+
|
|
+ /**
|
|
+ * Emitted when images are removed from the model because they are removed in the database
|
|
+ */
|
|
+ void imageInfosRemoved(const QList<ImageInfo>& infos);
|
|
+
|
|
+protected Q_SLOTS:
|
|
+
|
|
+ void slotCollectionImageChange(const CollectionImageChangeset& changeset);
|
|
+};
|
|
+
|
|
+} // namespace Digikam
|
|
+
|
|
+#endif // IMAGELISTMODEL_H
|
|
diff --git a/libs/database/models/imagemodel.cpp b/libs/database/models/imagemodel.cpp
|
|
new file mode 100644
|
|
index 0000000..41b43cf
|
|
--- /dev/null
|
|
+++ b/libs/database/models/imagemodel.cpp
|
|
@@ -0,0 +1,1368 @@
|
|
+/* ============================================================
|
|
+ *
|
|
+ * This file is a part of digiKam project
|
|
+ * http://www.digikam.org
|
|
+ *
|
|
+ * Date : 2009-03-05
|
|
+ * Description : Qt item model for database entries
|
|
+ *
|
|
+ * Copyright (C) 2009-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
|
|
+ *
|
|
+ * This program is free software; you can redistribute it
|
|
+ * and/or modify it under the terms of the GNU General
|
|
+ * Public License as published by the Free Software Foundation;
|
|
+ * either version 2, or (at your option)
|
|
+ * any later version.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ *
|
|
+ * ============================================================ */
|
|
+
|
|
+#include "imagemodel.h"
|
|
+
|
|
+// Qt includes
|
|
+
|
|
+#include <QHash>
|
|
+#include <QItemSelection>
|
|
+
|
|
+// Local includes
|
|
+
|
|
+#include "digikam_debug.h"
|
|
+#include "coredbchangesets.h"
|
|
+#include "coredbfields.h"
|
|
+#include "coredbwatch.h"
|
|
+#include "imageinfo.h"
|
|
+#include "imageinfolist.h"
|
|
+#include "abstractitemdragdrophandler.h"
|
|
+
|
|
+namespace Digikam
|
|
+{
|
|
+
|
|
+class ImageModel::Private
|
|
+{
|
|
+public:
|
|
+
|
|
+ Private()
|
|
+ {
|
|
+ preprocessor = 0;
|
|
+ keepFilePathCache = false;
|
|
+ sendRemovalSignals = false;
|
|
+ incrementalUpdater = 0;
|
|
+ refreshing = false;
|
|
+ reAdding = false;
|
|
+ incrementalRefreshRequested = false;
|
|
+ }
|
|
+
|
|
+ ImageInfoList infos;
|
|
+ QList<QVariant> extraValues;
|
|
+ QHash<qlonglong, int> idHash;
|
|
+
|
|
+ bool keepFilePathCache;
|
|
+ QHash<QString, qlonglong> filePathHash;
|
|
+
|
|
+ bool sendRemovalSignals;
|
|
+
|
|
+ QObject* preprocessor;
|
|
+ bool refreshing;
|
|
+ bool reAdding;
|
|
+ bool incrementalRefreshRequested;
|
|
+
|
|
+ DatabaseFields::Set watchFlags;
|
|
+
|
|
+ class ImageModelIncrementalUpdater* incrementalUpdater;
|
|
+
|
|
+ ImageInfoList pendingInfos;
|
|
+ QList<QVariant> pendingExtraValues;
|
|
+
|
|
+ inline bool isValid(const QModelIndex& index)
|
|
+ {
|
|
+ if (!index.isValid())
|
|
+ {
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ if (index.row() < 0 || index.row() >= infos.size())
|
|
+ {
|
|
+ qCDebug(DIGIKAM_GENERAL_LOG) << "Invalid index" << index;
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ return true;
|
|
+ }
|
|
+ inline bool extraValueValid(const QModelIndex& index)
|
|
+ {
|
|
+ // we assume isValid() being called before, no duplicate checks
|
|
+ if (index.row() >= extraValues.size())
|
|
+ {
|
|
+ qCDebug(DIGIKAM_GENERAL_LOG) << "Invalid index for extraData" << index;
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ return true;
|
|
+ }
|
|
+};
|
|
+
|
|
+typedef QPair<int, int> IntPair; // to make foreach macro happy
|
|
+typedef QList<IntPair> IntPairList;
|
|
+
|
|
+class ImageModelIncrementalUpdater
|
|
+{
|
|
+public:
|
|
+
|
|
+ explicit ImageModelIncrementalUpdater(ImageModel::Private* d);
|
|
+
|
|
+ void appendInfos(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues);
|
|
+ void aboutToBeRemovedInModel(const IntPairList& aboutToBeRemoved);
|
|
+ QList<IntPair> oldIndexes();
|
|
+
|
|
+ static QList<IntPair> toContiguousPairs(const QList<int>& ids);
|
|
+
|
|
+public:
|
|
+
|
|
+ QHash<qlonglong, int> oldIds;
|
|
+ QList<QVariant> oldExtraValues;
|
|
+ QList<ImageInfo> newInfos;
|
|
+ QList<QVariant> newExtraValues;
|
|
+ QList<IntPairList> modelRemovals;
|
|
+};
|
|
+
|
|
+ImageModel::ImageModel(QObject* parent)
|
|
+ : QAbstractListModel(parent),
|
|
+ d(new Private)
|
|
+{
|
|
+ connect(CoreDbAccess::databaseWatch(), SIGNAL(imageChange(ImageChangeset)),
|
|
+ this, SLOT(slotImageChange(ImageChangeset)));
|
|
+
|
|
+ connect(CoreDbAccess::databaseWatch(), SIGNAL(imageTagChange(ImageTagChangeset)),
|
|
+ this, SLOT(slotImageTagChange(ImageTagChangeset)));
|
|
+}
|
|
+
|
|
+ImageModel::~ImageModel()
|
|
+{
|
|
+ delete d->incrementalUpdater;
|
|
+ delete d;
|
|
+}
|
|
+
|
|
+// ------------ Access methods -------------
|
|
+
|
|
+void ImageModel::setKeepsFilePathCache(bool keepCache)
|
|
+{
|
|
+ d->keepFilePathCache = keepCache;
|
|
+}
|
|
+
|
|
+bool ImageModel::keepsFilePathCache() const
|
|
+{
|
|
+ return d->keepFilePathCache;
|
|
+}
|
|
+
|
|
+bool ImageModel::isEmpty() const
|
|
+{
|
|
+ return d->infos.isEmpty();
|
|
+}
|
|
+
|
|
+void ImageModel::setWatchFlags(const DatabaseFields::Set& set)
|
|
+{
|
|
+ d->watchFlags = set;
|
|
+}
|
|
+
|
|
+ImageInfo ImageModel::imageInfo(const QModelIndex& index) const
|
|
+{
|
|
+ if (!d->isValid(index))
|
|
+ {
|
|
+ return ImageInfo();
|
|
+ }
|
|
+
|
|
+ return d->infos.at(index.row());
|
|
+}
|
|
+
|
|
+ImageInfo& ImageModel::imageInfoRef(const QModelIndex& index) const
|
|
+{
|
|
+ return d->infos[index.row()];
|
|
+}
|
|
+
|
|
+qlonglong ImageModel::imageId(const QModelIndex& index) const
|
|
+{
|
|
+ if (!d->isValid(index))
|
|
+ {
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ return d->infos.at(index.row()).id();
|
|
+}
|
|
+
|
|
+QList<ImageInfo> ImageModel::imageInfos(const QList<QModelIndex>& indexes) const
|
|
+{
|
|
+ QList<ImageInfo> infos;
|
|
+
|
|
+ foreach(const QModelIndex& index, indexes)
|
|
+ {
|
|
+ infos << imageInfo(index);
|
|
+ }
|
|
+
|
|
+ return infos;
|
|
+}
|
|
+
|
|
+QList<qlonglong> ImageModel::imageIds(const QList<QModelIndex>& indexes) const
|
|
+{
|
|
+ QList<qlonglong> ids;
|
|
+
|
|
+ foreach(const QModelIndex& index, indexes)
|
|
+ {
|
|
+ ids << imageId(index);
|
|
+ }
|
|
+
|
|
+ return ids;
|
|
+}
|
|
+
|
|
+ImageInfo ImageModel::imageInfo(int row) const
|
|
+{
|
|
+ if (row >= d->infos.size())
|
|
+ {
|
|
+ return ImageInfo();
|
|
+ }
|
|
+
|
|
+ return d->infos.at(row);
|
|
+}
|
|
+
|
|
+ImageInfo& ImageModel::imageInfoRef(int row) const
|
|
+{
|
|
+ return d->infos[row];
|
|
+}
|
|
+
|
|
+qlonglong ImageModel::imageId(int row) const
|
|
+{
|
|
+ if (row < 0 || row >= d->infos.size())
|
|
+ {
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ return d->infos.at(row).id();
|
|
+}
|
|
+
|
|
+QModelIndex ImageModel::indexForImageInfo(const ImageInfo& info) const
|
|
+{
|
|
+ return indexForImageId(info.id());
|
|
+}
|
|
+
|
|
+QModelIndex ImageModel::indexForImageInfo(const ImageInfo& info, const QVariant& extraValue) const
|
|
+{
|
|
+ return indexForImageId(info.id(), extraValue);
|
|
+}
|
|
+
|
|
+QList<QModelIndex> ImageModel::indexesForImageInfo(const ImageInfo& info) const
|
|
+{
|
|
+ return indexesForImageId(info.id());
|
|
+}
|
|
+
|
|
+QModelIndex ImageModel::indexForImageId(qlonglong id) const
|
|
+{
|
|
+ int index = d->idHash.value(id, -1);
|
|
+
|
|
+ if (index != -1)
|
|
+ {
|
|
+ return createIndex(index, 0);
|
|
+ }
|
|
+
|
|
+ return QModelIndex();
|
|
+}
|
|
+
|
|
+QModelIndex ImageModel::indexForImageId(qlonglong id, const QVariant& extraValue) const
|
|
+{
|
|
+ if (d->extraValues.isEmpty())
|
|
+ return indexForImageId(id);
|
|
+
|
|
+ QHash<qlonglong, int>::const_iterator it;
|
|
+
|
|
+ for (it = d->idHash.constFind(id); it != d->idHash.constEnd() && it.key() == id; ++it)
|
|
+ {
|
|
+ if (d->extraValues.at(it.value()) == extraValue)
|
|
+ return createIndex(it.value(), 0);
|
|
+ }
|
|
+
|
|
+ return QModelIndex();
|
|
+}
|
|
+
|
|
+QList<QModelIndex> ImageModel::indexesForImageId(qlonglong id) const
|
|
+{
|
|
+ QList<QModelIndex> indexes;
|
|
+ QHash<qlonglong, int>::const_iterator it;
|
|
+
|
|
+ for (it = d->idHash.constFind(id); it != d->idHash.constEnd() && it.key() == id; ++it)
|
|
+ {
|
|
+ indexes << createIndex(it.value(), 0);
|
|
+ }
|
|
+
|
|
+ return indexes;
|
|
+}
|
|
+
|
|
+int ImageModel::numberOfIndexesForImageInfo(const ImageInfo& info) const
|
|
+{
|
|
+ return numberOfIndexesForImageId(info.id());
|
|
+}
|
|
+
|
|
+int ImageModel::numberOfIndexesForImageId(qlonglong id) const
|
|
+{
|
|
+ if (d->extraValues.isEmpty())
|
|
+ {
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ int count = 0;
|
|
+ QHash<qlonglong,int>::const_iterator it;
|
|
+
|
|
+ for (it = d->idHash.constFind(id); it != d->idHash.constEnd() && it.key() == id; ++it)
|
|
+ {
|
|
+ ++count;
|
|
+ }
|
|
+
|
|
+ return count;
|
|
+}
|
|
+
|
|
+// static method
|
|
+ImageInfo ImageModel::retrieveImageInfo(const QModelIndex& index)
|
|
+{
|
|
+ if (!index.isValid())
|
|
+ {
|
|
+ return ImageInfo();
|
|
+ }
|
|
+
|
|
+ ImageModel* const model = index.data(ImageModelPointerRole).value<ImageModel*>();
|
|
+ int row = index.data(ImageModelInternalId).toInt();
|
|
+
|
|
+ if (!model)
|
|
+ {
|
|
+ return ImageInfo();
|
|
+ }
|
|
+
|
|
+ return model->imageInfo(row);
|
|
+}
|
|
+
|
|
+// static method
|
|
+qlonglong ImageModel::retrieveImageId(const QModelIndex& index)
|
|
+{
|
|
+ if (!index.isValid())
|
|
+ {
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ ImageModel* const model = index.data(ImageModelPointerRole).value<ImageModel*>();
|
|
+ int row = index.data(ImageModelInternalId).toInt();
|
|
+
|
|
+ if (!model)
|
|
+ {
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ return model->imageId(row);
|
|
+}
|
|
+
|
|
+QModelIndex ImageModel::indexForPath(const QString& filePath) const
|
|
+{
|
|
+ if (d->keepFilePathCache)
|
|
+ {
|
|
+ return indexForImageId(d->filePathHash.value(filePath));
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ const int size = d->infos.size();
|
|
+
|
|
+ for (int i=0; i<size; ++i)
|
|
+ {
|
|
+ if (d->infos.at(i).filePath() == filePath)
|
|
+ {
|
|
+ return createIndex(i, 0);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return QModelIndex();
|
|
+}
|
|
+
|
|
+QList<QModelIndex> ImageModel::indexesForPath(const QString& filePath) const
|
|
+{
|
|
+ if (d->keepFilePathCache)
|
|
+ {
|
|
+ return indexesForImageId(d->filePathHash.value(filePath));
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ QList<QModelIndex> indexes;
|
|
+ const int size = d->infos.size();
|
|
+
|
|
+ for (int i=0; i<size; ++i)
|
|
+ {
|
|
+ if (d->infos.at(i).filePath() == filePath)
|
|
+ {
|
|
+ indexes << createIndex(i, 0);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return indexes;
|
|
+ }
|
|
+}
|
|
+
|
|
+ImageInfo ImageModel::imageInfo(const QString& filePath) const
|
|
+{
|
|
+ if (d->keepFilePathCache)
|
|
+ {
|
|
+ qlonglong id = d->filePathHash.value(filePath);
|
|
+
|
|
+ if (id)
|
|
+ {
|
|
+ int index = d->idHash.value(id, -1);
|
|
+
|
|
+ if (index != -1)
|
|
+ {
|
|
+ return d->infos.at(index);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ foreach(const ImageInfo& info, d->infos)
|
|
+ {
|
|
+ if (info.filePath() == filePath)
|
|
+ {
|
|
+ return info;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return ImageInfo();
|
|
+}
|
|
+
|
|
+QList<ImageInfo> ImageModel::imageInfos(const QString& filePath) const
|
|
+{
|
|
+ QList<ImageInfo> infos;
|
|
+
|
|
+ if (d->keepFilePathCache)
|
|
+ {
|
|
+ qlonglong id = d->filePathHash.value(filePath);
|
|
+
|
|
+ if (id)
|
|
+ {
|
|
+ foreach(int index, d->idHash.values(id))
|
|
+ {
|
|
+ infos << d->infos.at(index);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ foreach(const ImageInfo& info, d->infos)
|
|
+ {
|
|
+ if (info.filePath() == filePath)
|
|
+ {
|
|
+ infos << info;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return infos;
|
|
+}
|
|
+
|
|
+void ImageModel::addImageInfo(const ImageInfo& info)
|
|
+{
|
|
+ addImageInfos(QList<ImageInfo>() << info, QList<QVariant>());
|
|
+}
|
|
+
|
|
+void ImageModel::addImageInfos(const QList<ImageInfo>& infos)
|
|
+{
|
|
+ addImageInfos(infos, QList<QVariant>());
|
|
+}
|
|
+
|
|
+void ImageModel::addImageInfos(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues)
|
|
+{
|
|
+ if (infos.isEmpty())
|
|
+ {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (d->incrementalUpdater)
|
|
+ {
|
|
+ d->incrementalUpdater->appendInfos(infos, extraValues);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ appendInfos(infos, extraValues);
|
|
+ }
|
|
+}
|
|
+
|
|
+void ImageModel::addImageInfoSynchronously(const ImageInfo& info)
|
|
+{
|
|
+ addImageInfosSynchronously(QList<ImageInfo>() << info, QList<QVariant>());
|
|
+}
|
|
+
|
|
+void ImageModel::addImageInfosSynchronously(const QList<ImageInfo>& infos)
|
|
+{
|
|
+ addImageInfos(infos, QList<QVariant>());
|
|
+}
|
|
+
|
|
+void ImageModel::addImageInfosSynchronously(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues)
|
|
+{
|
|
+ if (infos.isEmpty())
|
|
+ {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ publiciseInfos(infos, extraValues);
|
|
+ emit processAdded(infos, extraValues);
|
|
+}
|
|
+
|
|
+void ImageModel::ensureHasImageInfo(const ImageInfo& info)
|
|
+{
|
|
+ ensureHasImageInfos(QList<ImageInfo>() << info, QList<QVariant>());
|
|
+}
|
|
+
|
|
+void ImageModel::ensureHasImageInfos(const QList<ImageInfo>& infos)
|
|
+{
|
|
+ ensureHasImageInfos(infos, QList<QVariant>());
|
|
+}
|
|
+
|
|
+void ImageModel::ensureHasImageInfos(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues)
|
|
+{
|
|
+ if (extraValues.isEmpty())
|
|
+ {
|
|
+ if (!d->pendingExtraValues.isEmpty())
|
|
+ {
|
|
+ qCDebug(DIGIKAM_GENERAL_LOG) << "ExtraValue / No Extra Value mismatch. Ignoring added infos.";
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ if (d->pendingInfos.size() != d->pendingExtraValues.size())
|
|
+ {
|
|
+ qCDebug(DIGIKAM_GENERAL_LOG) << "ExtraValue / No Extra Value mismatch. Ignoring added infos.";
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ d->pendingInfos << infos;
|
|
+ d->pendingExtraValues << extraValues;
|
|
+ cleanSituationChecks();
|
|
+}
|
|
+
|
|
+void ImageModel::clearImageInfos()
|
|
+{
|
|
+ d->infos.clear();
|
|
+ d->extraValues.clear();
|
|
+ d->idHash.clear();
|
|
+ d->filePathHash.clear();
|
|
+ delete d->incrementalUpdater;
|
|
+ d->incrementalUpdater = 0;
|
|
+ d->pendingInfos.clear();
|
|
+ d->pendingExtraValues.clear();
|
|
+ d->refreshing = false;
|
|
+ d->reAdding = false;
|
|
+ d->incrementalRefreshRequested = false;
|
|
+
|
|
+ beginResetModel();
|
|
+ endResetModel();
|
|
+
|
|
+ imageInfosCleared();
|
|
+}
|
|
+
|
|
+void ImageModel::setImageInfos(const QList<ImageInfo>& infos)
|
|
+{
|
|
+ clearImageInfos();
|
|
+ addImageInfos(infos);
|
|
+}
|
|
+
|
|
+QList<ImageInfo> ImageModel::imageInfos() const
|
|
+{
|
|
+ return d->infos;
|
|
+}
|
|
+
|
|
+QList<qlonglong> ImageModel::imageIds() const
|
|
+{
|
|
+ return d->idHash.keys();
|
|
+}
|
|
+
|
|
+bool ImageModel::hasImage(qlonglong id) const
|
|
+{
|
|
+ return d->idHash.contains(id);
|
|
+}
|
|
+
|
|
+bool ImageModel::hasImage(const ImageInfo& info) const
|
|
+{
|
|
+ return d->idHash.contains(info.id());
|
|
+}
|
|
+
|
|
+bool ImageModel::hasImage(const ImageInfo& info, const QVariant& extraValue) const
|
|
+{
|
|
+ return hasImage(info.id(), extraValue);
|
|
+}
|
|
+
|
|
+bool ImageModel::hasImage(qlonglong id, const QVariant& extraValue) const
|
|
+{
|
|
+ if (d->extraValues.isEmpty())
|
|
+ return hasImage(id);
|
|
+
|
|
+ QHash<qlonglong, int>::const_iterator it;
|
|
+
|
|
+ for (it = d->idHash.constFind(id); it != d->idHash.constEnd() && it.key() == id; ++it)
|
|
+ {
|
|
+ if (d->extraValues.at(it.value()) == extraValue)
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ return false;;
|
|
+}
|
|
+
|
|
+QList<ImageInfo> ImageModel::uniqueImageInfos() const
|
|
+{
|
|
+ if (d->extraValues.isEmpty())
|
|
+ {
|
|
+ return d->infos;
|
|
+ }
|
|
+
|
|
+ QList<ImageInfo> uniqueInfos;
|
|
+ const int size = d->infos.size();
|
|
+
|
|
+ for (int i=0; i<size; ++i)
|
|
+ {
|
|
+ const ImageInfo& info = d->infos.at(i);
|
|
+
|
|
+ if (d->idHash.value(info.id()) == i)
|
|
+ {
|
|
+ uniqueInfos << info;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return uniqueInfos;
|
|
+}
|
|
+
|
|
+void ImageModel::emitDataChangedForAll()
|
|
+{
|
|
+ if (d->infos.isEmpty())
|
|
+ {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ QModelIndex first = createIndex(0, 0);
|
|
+ QModelIndex last = createIndex(d->infos.size() - 1, 0);
|
|
+ emit dataChanged(first, last);
|
|
+}
|
|
+
|
|
+void ImageModel::emitDataChangedForSelection(const QItemSelection& selection)
|
|
+{
|
|
+ if (!selection.isEmpty())
|
|
+ {
|
|
+ foreach(const QItemSelectionRange& range, selection)
|
|
+ {
|
|
+ emit dataChanged(range.topLeft(), range.bottomRight());
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+void ImageModel::ensureHasGroupedImages(const ImageInfo& groupLeader)
|
|
+{
|
|
+ ensureHasImageInfos(groupLeader.groupedImages());
|
|
+}
|
|
+
|
|
+// ------------ Preprocessing -------------
|
|
+
|
|
+void ImageModel::setPreprocessor(QObject* preprocessor)
|
|
+{
|
|
+ unsetPreprocessor(d->preprocessor);
|
|
+ d->preprocessor = preprocessor;
|
|
+}
|
|
+
|
|
+void ImageModel::unsetPreprocessor(QObject* preprocessor)
|
|
+{
|
|
+ if (preprocessor && d->preprocessor == preprocessor)
|
|
+ {
|
|
+ disconnect(this, SIGNAL(preprocess(QList<ImageInfo>,QList<QVariant>)), 0, 0);
|
|
+ disconnect(d->preprocessor, 0, this, SLOT(reAddImageInfos(QList<ImageInfo>,QList<QVariant>)));
|
|
+ disconnect(d->preprocessor, 0, this, SLOT(reAddingFinished()));
|
|
+ }
|
|
+}
|
|
+
|
|
+void ImageModel::appendInfos(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues)
|
|
+{
|
|
+ if (infos.isEmpty())
|
|
+ {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (d->preprocessor)
|
|
+ {
|
|
+ d->reAdding = true;
|
|
+ emit preprocess(infos, extraValues);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ publiciseInfos(infos, extraValues);
|
|
+ }
|
|
+}
|
|
+
|
|
+void ImageModel::appendInfosChecked(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues)
|
|
+{
|
|
+ // This method does deduplication. It is private because in context of readding or refreshing it is of no use.
|
|
+
|
|
+ if (extraValues.isEmpty())
|
|
+ {
|
|
+ QList<ImageInfo> checkedInfos;
|
|
+
|
|
+ foreach (const ImageInfo& info, infos)
|
|
+ {
|
|
+ if (!hasImage(info))
|
|
+ {
|
|
+ checkedInfos << info;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ appendInfos(checkedInfos, QList<QVariant>());
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ QList<ImageInfo> checkedInfos;
|
|
+ QList<QVariant> checkedExtraValues;
|
|
+ const int size = infos.size();
|
|
+
|
|
+ for (int i=0; i<size; i++)
|
|
+ {
|
|
+ if (!hasImage(infos[i], extraValues[i]))
|
|
+ {
|
|
+ checkedInfos << infos[i];
|
|
+ checkedExtraValues << extraValues[i];
|
|
+ }
|
|
+ }
|
|
+
|
|
+ appendInfos(checkedInfos, checkedExtraValues);
|
|
+ }
|
|
+}
|
|
+
|
|
+void ImageModel::reAddImageInfos(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues)
|
|
+{
|
|
+ // addImageInfos -> appendInfos -> preprocessor -> reAddImageInfos
|
|
+ publiciseInfos(infos, extraValues);
|
|
+}
|
|
+
|
|
+void ImageModel::reAddingFinished()
|
|
+{
|
|
+ d->reAdding = false;
|
|
+ cleanSituationChecks();
|
|
+}
|
|
+
|
|
+void ImageModel::startRefresh()
|
|
+{
|
|
+ d->refreshing = true;
|
|
+}
|
|
+
|
|
+void ImageModel::finishRefresh()
|
|
+{
|
|
+ d->refreshing = false;
|
|
+ cleanSituationChecks();
|
|
+}
|
|
+
|
|
+bool ImageModel::isRefreshing() const
|
|
+{
|
|
+ return d->refreshing;
|
|
+}
|
|
+
|
|
+void ImageModel::cleanSituationChecks()
|
|
+{
|
|
+ // For starting an incremental refresh we want a clear situation:
|
|
+ // Any remaining batches from non-incremental refreshing subclasses have been received in appendInfos(),
|
|
+ // any batches sent to preprocessor for re-adding have been re-added.
|
|
+ if (d->refreshing || d->reAdding)
|
|
+ {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (!d->pendingInfos.isEmpty())
|
|
+ {
|
|
+ appendInfosChecked(d->pendingInfos, d->pendingExtraValues);
|
|
+ d->pendingInfos.clear();
|
|
+ d->pendingExtraValues.clear();
|
|
+ cleanSituationChecks();
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (d->incrementalRefreshRequested)
|
|
+ {
|
|
+ d->incrementalRefreshRequested = false;
|
|
+ emit readyForIncrementalRefresh();
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ emit allRefreshingFinished();
|
|
+ }
|
|
+}
|
|
+
|
|
+void ImageModel::publiciseInfos(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues)
|
|
+{
|
|
+ if (infos.isEmpty())
|
|
+ {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ Q_ASSERT(infos.size() == extraValues.size() || (extraValues.isEmpty() && d->extraValues.isEmpty()));
|
|
+
|
|
+ emit imageInfosAboutToBeAdded(infos);
|
|
+ const int firstNewIndex = d->infos.size();
|
|
+ const int lastNewIndex = d->infos.size() + infos.size() - 1;
|
|
+ beginInsertRows(QModelIndex(), firstNewIndex, lastNewIndex);
|
|
+ d->infos << infos;
|
|
+ d->extraValues << extraValues;
|
|
+
|
|
+ for (int i=firstNewIndex; i<=lastNewIndex; ++i)
|
|
+ {
|
|
+ const ImageInfo& info = d->infos.at(i);
|
|
+ qlonglong id = info.id();
|
|
+ d->idHash.insertMulti(id, i);
|
|
+
|
|
+ if (d->keepFilePathCache)
|
|
+ {
|
|
+ d->filePathHash[info.filePath()] = id;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ endInsertRows();
|
|
+ emit imageInfosAdded(infos);
|
|
+}
|
|
+
|
|
+void ImageModel::requestIncrementalRefresh()
|
|
+{
|
|
+ if (d->reAdding)
|
|
+ {
|
|
+ d->incrementalRefreshRequested = true;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ emit readyForIncrementalRefresh();
|
|
+ }
|
|
+}
|
|
+
|
|
+bool ImageModel::hasIncrementalRefreshPending() const
|
|
+{
|
|
+ return d->incrementalRefreshRequested;
|
|
+}
|
|
+
|
|
+void ImageModel::startIncrementalRefresh()
|
|
+{
|
|
+ delete d->incrementalUpdater;
|
|
+
|
|
+ d->incrementalUpdater = new ImageModelIncrementalUpdater(d);
|
|
+}
|
|
+
|
|
+void ImageModel::finishIncrementalRefresh()
|
|
+{
|
|
+ if (!d->incrementalUpdater)
|
|
+ {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ // remove old entries
|
|
+ QList<QPair<int, int> > pairs = d->incrementalUpdater->oldIndexes();
|
|
+ removeRowPairs(pairs);
|
|
+
|
|
+ // add new indexes
|
|
+ appendInfos(d->incrementalUpdater->newInfos, d->incrementalUpdater->newExtraValues);
|
|
+
|
|
+ delete d->incrementalUpdater;
|
|
+ d->incrementalUpdater = 0;
|
|
+}
|
|
+
|
|
+void ImageModel::removeIndex(const QModelIndex& index)
|
|
+{
|
|
+ removeIndexes(QList<QModelIndex>() << index);
|
|
+}
|
|
+
|
|
+void ImageModel::removeIndexes(const QList<QModelIndex>& indexes)
|
|
+{
|
|
+ QList<int> listIndexes;
|
|
+
|
|
+ foreach(const QModelIndex& index, indexes)
|
|
+ {
|
|
+ if (d->isValid(index))
|
|
+ {
|
|
+ listIndexes << index.row();
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (listIndexes.isEmpty())
|
|
+ {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ removeRowPairsWithCheck(ImageModelIncrementalUpdater::toContiguousPairs(listIndexes));
|
|
+}
|
|
+
|
|
+void ImageModel::removeImageInfo(const ImageInfo& info)
|
|
+{
|
|
+ removeImageInfos(QList<ImageInfo>() << info);
|
|
+}
|
|
+
|
|
+void ImageModel::removeImageInfos(const QList<ImageInfo>& infos)
|
|
+{
|
|
+ QList<int> listIndexes;
|
|
+
|
|
+ foreach(const ImageInfo& info, infos)
|
|
+ {
|
|
+ QModelIndex index = indexForImageId(info.id());
|
|
+
|
|
+ if (index.isValid())
|
|
+ {
|
|
+ listIndexes << index.row();
|
|
+ }
|
|
+ }
|
|
+ removeRowPairsWithCheck(ImageModelIncrementalUpdater::toContiguousPairs(listIndexes));
|
|
+}
|
|
+
|
|
+void ImageModel::removeImageInfos(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues)
|
|
+{
|
|
+ if (extraValues.isEmpty())
|
|
+ {
|
|
+ removeImageInfos(infos);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ QList<int> listIndexes;
|
|
+
|
|
+ for (int i=0; i<infos.size(); ++i)
|
|
+ {
|
|
+ QModelIndex index = indexForImageId(infos.at(i).id(), extraValues.at(i));
|
|
+
|
|
+ if (index.isValid())
|
|
+ {
|
|
+ listIndexes << index.row();
|
|
+ }
|
|
+ }
|
|
+
|
|
+ removeRowPairsWithCheck(ImageModelIncrementalUpdater::toContiguousPairs(listIndexes));
|
|
+}
|
|
+
|
|
+void ImageModel::setSendRemovalSignals(bool send)
|
|
+{
|
|
+ d->sendRemovalSignals = send;
|
|
+}
|
|
+
|
|
+template <class List, typename T>
|
|
+static bool pairsContain(const List& list, T value)
|
|
+{
|
|
+ typename List::const_iterator middle;
|
|
+ typename List::const_iterator begin = list.begin();
|
|
+ typename List::const_iterator end = list.end();
|
|
+ int n = int(end - begin);
|
|
+ int half;
|
|
+
|
|
+ while (n > 0)
|
|
+ {
|
|
+ half = n >> 1;
|
|
+ middle = begin + half;
|
|
+
|
|
+ if (middle->first <= value && middle->second >= value)
|
|
+ {
|
|
+ return true;
|
|
+ }
|
|
+ else if (middle->second < value)
|
|
+ {
|
|
+ begin = middle + 1;
|
|
+ n -= half + 1;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ n = half;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return false;
|
|
+}
|
|
+
|
|
+void ImageModel::removeRowPairsWithCheck(const QList<QPair<int, int> >& toRemove)
|
|
+{
|
|
+ if (d->incrementalUpdater)
|
|
+ {
|
|
+ d->incrementalUpdater->aboutToBeRemovedInModel(toRemove);
|
|
+ }
|
|
+
|
|
+ removeRowPairs(toRemove);
|
|
+}
|
|
+
|
|
+void ImageModel::removeRowPairs(const QList<QPair<int, int> >& toRemove)
|
|
+{
|
|
+ if (toRemove.isEmpty())
|
|
+ {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ // Remove old indexes
|
|
+ // Keep in mind that when calling beginRemoveRows all structures announced to be removed
|
|
+ // must still be valid, and this includes our hashes as well, which limits what we can optimize
|
|
+
|
|
+ int removedRows = 0, offset = 0;
|
|
+ typedef QPair<int, int> IntPair; // to make foreach macro happy
|
|
+
|
|
+ foreach(const IntPair& pair, toRemove)
|
|
+ {
|
|
+ const int begin = pair.first - offset;
|
|
+ const int end = pair.second - offset; // inclusive
|
|
+ removedRows = end - begin + 1;
|
|
+
|
|
+ // when removing from the list, all subsequent indexes are affected
|
|
+ offset += removedRows;
|
|
+
|
|
+ QList<ImageInfo> removedInfos;
|
|
+
|
|
+ if (d->sendRemovalSignals)
|
|
+ {
|
|
+ qCopy(d->infos.begin() + begin, d->infos.begin() + end, removedInfos.begin());
|
|
+ emit imageInfosAboutToBeRemoved(removedInfos);
|
|
+ }
|
|
+
|
|
+ imageInfosAboutToBeRemoved(begin, end);
|
|
+ beginRemoveRows(QModelIndex(), begin, end);
|
|
+
|
|
+ // update idHash - which points to indexes of d->infos, and these change now!
|
|
+ QHash<qlonglong, int>::iterator it;
|
|
+
|
|
+ for (it = d->idHash.begin(); it != d->idHash.end(); )
|
|
+ {
|
|
+ if (it.value() >= begin)
|
|
+ {
|
|
+ if (it.value() > end)
|
|
+ {
|
|
+ // after the removed interval: adjust index
|
|
+ it.value() -= removedRows;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ // in the removed interval
|
|
+ it = d->idHash.erase(it);
|
|
+ continue;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ ++it;
|
|
+ }
|
|
+
|
|
+ // remove from list
|
|
+ d->infos.erase(d->infos.begin() + begin, d->infos.begin() + (end + 1));
|
|
+
|
|
+ if (!d->extraValues.isEmpty())
|
|
+ {
|
|
+ d->extraValues.erase(d->extraValues.begin() + begin, d->extraValues.begin() + (end + 1));
|
|
+ }
|
|
+
|
|
+ endRemoveRows();
|
|
+
|
|
+ if (d->sendRemovalSignals)
|
|
+ {
|
|
+ emit imageInfosRemoved(removedInfos);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ // tidy up: remove old indexes from file path hash now
|
|
+ if (d->keepFilePathCache)
|
|
+ {
|
|
+ QHash<QString, qlonglong>::iterator it;
|
|
+
|
|
+ for (it = d->filePathHash.begin(); it != d->filePathHash.end(); )
|
|
+ {
|
|
+ if (pairsContain(toRemove, it.value()))
|
|
+ {
|
|
+ it = d->filePathHash.erase(it);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ ++it;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+ImageModelIncrementalUpdater::ImageModelIncrementalUpdater(ImageModel::Private* d)
|
|
+{
|
|
+ oldIds = d->idHash;
|
|
+ oldExtraValues = d->extraValues;
|
|
+}
|
|
+
|
|
+void ImageModelIncrementalUpdater::appendInfos(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues)
|
|
+{
|
|
+ if (extraValues.isEmpty())
|
|
+ {
|
|
+ foreach(const ImageInfo& info, infos)
|
|
+ {
|
|
+ QHash<qlonglong,int>::iterator it = oldIds.find(info.id());
|
|
+
|
|
+ if (it != oldIds.end())
|
|
+ {
|
|
+ oldIds.erase(it);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ newInfos << info;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ for (int i=0; i<infos.size(); ++i)
|
|
+ {
|
|
+ const ImageInfo& info = infos.at(i);
|
|
+ bool found = false;
|
|
+ QHash<qlonglong,int>::iterator it;
|
|
+
|
|
+ for (it = oldIds.find(info.id()); it != oldIds.end() && it.key() == info.id(); ++it)
|
|
+ {
|
|
+ // first check is for bug #262596. Not sure if needed.
|
|
+ if (it.value() < oldExtraValues.size() && extraValues.at(i) == oldExtraValues.at(it.value()))
|
|
+ {
|
|
+ found = true;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (found)
|
|
+ {
|
|
+ oldIds.erase(it);
|
|
+ // do not erase from oldExtraValues - oldIds is a hash id -> index.
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ newInfos << info;
|
|
+ newExtraValues << extraValues.at(i);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+void ImageModelIncrementalUpdater::aboutToBeRemovedInModel(const IntPairList& toRemove)
|
|
+{
|
|
+ modelRemovals << toRemove;
|
|
+}
|
|
+
|
|
+QList<QPair<int, int> > ImageModelIncrementalUpdater::oldIndexes()
|
|
+{
|
|
+ // first, apply all changes to indexes by direct removal in model
|
|
+ // while the updater was active
|
|
+ foreach(const IntPairList& list, modelRemovals)
|
|
+ {
|
|
+ int removedRows = 0, offset = 0;
|
|
+
|
|
+ foreach(const IntPair& pair, list)
|
|
+ {
|
|
+ const int begin = pair.first - offset;
|
|
+ const int end = pair.second - offset; // inclusive
|
|
+ removedRows = end - begin + 1;
|
|
+
|
|
+ // when removing from the list, all subsequent indexes are affected
|
|
+ offset += removedRows;
|
|
+
|
|
+ // update idHash - which points to indexes of d->infos, and these change now!
|
|
+ QHash<qlonglong, int>::iterator it;
|
|
+
|
|
+ for (it = oldIds.begin(); it != oldIds.end(); )
|
|
+ {
|
|
+ if (it.value() >= begin)
|
|
+ {
|
|
+ if (it.value() > end)
|
|
+ {
|
|
+ // after the removed interval: adjust index
|
|
+ it.value() -= removedRows;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ // in the removed interval
|
|
+ it = oldIds.erase(it);
|
|
+ continue;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ ++it;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ modelRemovals.clear();
|
|
+
|
|
+ return toContiguousPairs(oldIds.values());
|
|
+}
|
|
+
|
|
+QList<QPair<int, int> > ImageModelIncrementalUpdater::toContiguousPairs(const QList<int>& unsorted)
|
|
+{
|
|
+ // Take the given indices and return them as contiguous pairs [begin, end]
|
|
+
|
|
+ QList<QPair<int, int> > pairs;
|
|
+
|
|
+ if (unsorted.isEmpty())
|
|
+ {
|
|
+ return pairs;
|
|
+ }
|
|
+
|
|
+ QList<int> indices(unsorted);
|
|
+ qSort(indices);
|
|
+
|
|
+ QPair<int, int> pair(indices.first(), indices.first());
|
|
+
|
|
+ for (int i=1; i<indices.size(); ++i)
|
|
+ {
|
|
+ const int &index = indices.at(i);
|
|
+
|
|
+ if (index == pair.second + 1)
|
|
+ {
|
|
+ pair.second = index;
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ pairs << pair; // insert last pair
|
|
+ pair.first = index;
|
|
+ pair.second = index;
|
|
+ }
|
|
+
|
|
+ pairs << pair;
|
|
+
|
|
+ return pairs;
|
|
+}
|
|
+
|
|
+// ------------ QAbstractItemModel implementation -------------
|
|
+
|
|
+QVariant ImageModel::data(const QModelIndex& index, int role) const
|
|
+{
|
|
+ if (!d->isValid(index))
|
|
+ {
|
|
+ return QVariant();
|
|
+ }
|
|
+
|
|
+ switch (role)
|
|
+ {
|
|
+ case Qt::DisplayRole:
|
|
+ case Qt::ToolTipRole:
|
|
+ return d->infos.at(index.row()).name();
|
|
+
|
|
+ case ImageModelPointerRole:
|
|
+ return QVariant::fromValue(const_cast<ImageModel*>(this));
|
|
+
|
|
+ case ImageModelInternalId:
|
|
+ return index.row();
|
|
+
|
|
+ case CreationDateRole:
|
|
+ return d->infos.at(index.row()).dateTime();
|
|
+
|
|
+ case ExtraDataRole:
|
|
+
|
|
+ if (d->extraValueValid(index))
|
|
+ {
|
|
+ return d->extraValues.at(index.row());
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ return QVariant();
|
|
+ }
|
|
+
|
|
+ case ExtraDataDuplicateCount:
|
|
+ {
|
|
+ qlonglong id = d->infos.at(index.row()).id();
|
|
+ return numberOfIndexesForImageId(id);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return QVariant();
|
|
+}
|
|
+
|
|
+QVariant ImageModel::headerData(int section, Qt::Orientation orientation, int role) const
|
|
+{
|
|
+ Q_UNUSED(section)
|
|
+ Q_UNUSED(orientation)
|
|
+ Q_UNUSED(role)
|
|
+ return QVariant();
|
|
+}
|
|
+
|
|
+int ImageModel::rowCount(const QModelIndex& parent) const
|
|
+{
|
|
+ if (parent.isValid())
|
|
+ {
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ return d->infos.size();
|
|
+}
|
|
+
|
|
+Qt::ItemFlags ImageModel::flags(const QModelIndex& index) const
|
|
+{
|
|
+ if (!d->isValid(index))
|
|
+ {
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ Qt::ItemFlags f = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
|
|
+
|
|
+ f |= dragDropFlags(index);
|
|
+
|
|
+ return f;
|
|
+}
|
|
+
|
|
+QModelIndex ImageModel::index(int row, int column, const QModelIndex& parent) const
|
|
+{
|
|
+ if (column != 0 || row < 0 || parent.isValid() || row >= d->infos.size())
|
|
+ {
|
|
+ return QModelIndex();
|
|
+ }
|
|
+
|
|
+ return createIndex(row, 0);
|
|
+}
|
|
+
|
|
+// ------------ Database watch -------------
|
|
+
|
|
+void ImageModel::slotImageChange(const ImageChangeset& changeset)
|
|
+{
|
|
+ if (d->infos.isEmpty())
|
|
+ {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (d->watchFlags & changeset.changes())
|
|
+ {
|
|
+ QItemSelection items;
|
|
+
|
|
+ foreach(const qlonglong& id, changeset.ids())
|
|
+ {
|
|
+ QModelIndex index = indexForImageId(id);
|
|
+
|
|
+ if (index.isValid())
|
|
+ {
|
|
+ items.select(index, index);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (!items.isEmpty())
|
|
+ {
|
|
+ emitDataChangedForSelection(items);
|
|
+ emit imageChange(changeset, items);
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+void ImageModel::slotImageTagChange(const ImageTagChangeset& changeset)
|
|
+{
|
|
+ if (d->infos.isEmpty())
|
|
+ {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ QItemSelection items;
|
|
+
|
|
+ foreach(const qlonglong& id, changeset.ids())
|
|
+ {
|
|
+ QModelIndex index = indexForImageId(id);
|
|
+
|
|
+ if (index.isValid())
|
|
+ {
|
|
+ items.select(index, index);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (!items.isEmpty())
|
|
+ {
|
|
+ emitDataChangedForSelection(items);
|
|
+ emit imageTagChange(changeset, items);
|
|
+ }
|
|
+}
|
|
+
|
|
+} // namespace Digikam
|
|
diff --git a/libs/database/models/imagemodel.h b/libs/database/models/imagemodel.h
|
|
new file mode 100644
|
|
index 0000000..dcf94c2
|
|
--- /dev/null
|
|
+++ b/libs/database/models/imagemodel.h
|
|
@@ -0,0 +1,364 @@
|
|
+/* ============================================================
|
|
+ *
|
|
+ * This file is a part of digiKam project
|
|
+ * http://www.digikam.org
|
|
+ *
|
|
+ * Date : 2009-03-05
|
|
+ * Description : Qt item model for database entries
|
|
+ *
|
|
+ * Copyright (C) 2009-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
|
|
+ *
|
|
+ * This program is free software; you can redistribute it
|
|
+ * and/or modify it under the terms of the GNU General
|
|
+ * Public License as published by the Free Software Foundation;
|
|
+ * either version 2, or (at your option)
|
|
+ * any later version.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ *
|
|
+ * ============================================================ */
|
|
+
|
|
+#ifndef IMAGEMODEL_H
|
|
+#define IMAGEMODEL_H
|
|
+
|
|
+// Qt includes
|
|
+
|
|
+#include <QAbstractListModel>
|
|
+
|
|
+// Local includes
|
|
+
|
|
+#include "dragdropimplementations.h"
|
|
+#include "imageinfo.h"
|
|
+#include "digikam_export.h"
|
|
+
|
|
+class QItemSelection;
|
|
+
|
|
+namespace Digikam
|
|
+{
|
|
+
|
|
+class ImageChangeset;
|
|
+class ImageTagChangeset;
|
|
+
|
|
+namespace DatabaseFields
|
|
+{
|
|
+class Set;
|
|
+}
|
|
+
|
|
+class DIGIKAM_DATABASE_EXPORT ImageModel : public QAbstractListModel, public DragDropModelImplementation
|
|
+{
|
|
+ Q_OBJECT
|
|
+
|
|
+public:
|
|
+
|
|
+ enum ImageModelRoles
|
|
+ {
|
|
+ /// An ImageModel* pointer to this model
|
|
+ ImageModelPointerRole = Qt::UserRole,
|
|
+ ImageModelInternalId = Qt::UserRole + 1,
|
|
+ /// Returns a thumbnail pixmap. May be implemented by subclasses.
|
|
+ /// Returns either a valid pixmap or a null QVariant.
|
|
+ ThumbnailRole = Qt::UserRole + 2,
|
|
+ /// Returns a QDateTime with the creation date
|
|
+ CreationDateRole = Qt::UserRole + 3,
|
|
+ /// Return (optional) extraData field
|
|
+ ExtraDataRole = Qt::UserRole + 5,
|
|
+ /// Returns the number of duplicate indexes for the same image id
|
|
+ ExtraDataDuplicateCount = Qt::UserRole + 6,
|
|
+
|
|
+ // Roles which are defined here but not implemented by ImageModel
|
|
+ /// Returns position of item in Left Light Table preview.
|
|
+ LTLeftPanelRole = Qt::UserRole + 50,
|
|
+ /// Returns position of item in Right Light Table preview.
|
|
+ LTRightPanelRole = Qt::UserRole + 51,
|
|
+
|
|
+ // For use by subclasses
|
|
+ SubclassRoles = Qt::UserRole + 100,
|
|
+ // For use by filter models
|
|
+ FilterModelRoles = Qt::UserRole + 500
|
|
+ };
|
|
+
|
|
+public:
|
|
+
|
|
+ explicit ImageModel(QObject* parent = 0);
|
|
+ ~ImageModel();
|
|
+
|
|
+ /** If a cache is kept, lookup by file path is fast,
|
|
+ * without a cache it is O(n). Default is false.
|
|
+ */
|
|
+ void setKeepsFilePathCache(bool keepCache);
|
|
+ bool keepsFilePathCache() const;
|
|
+
|
|
+ /** Set a set of database fields to watch.
|
|
+ * If either of these is changed, dataChanged() will be emitted.
|
|
+ * Default is no flag (no signal will be emitted).
|
|
+ */
|
|
+ void setWatchFlags(const DatabaseFields::Set& set);
|
|
+
|
|
+ /** Returns the ImageInfo object, reference or image id from the underlying data
|
|
+ * pointed to by the index.
|
|
+ * If the index is not valid, imageInfo will return a null ImageInfo, imageId will
|
|
+ * return 0, imageInfoRef must not be called with an invalid index.
|
|
+ */
|
|
+ ImageInfo imageInfo(const QModelIndex& index) const;
|
|
+ ImageInfo& imageInfoRef(const QModelIndex& index) const;
|
|
+ qlonglong imageId(const QModelIndex& index) const;
|
|
+ QList<ImageInfo> imageInfos(const QList<QModelIndex>& indexes) const;
|
|
+ QList<qlonglong> imageIds(const QList<QModelIndex>& indexes) const;
|
|
+
|
|
+ /** Returns the ImageInfo object, reference or image id from the underlying data
|
|
+ * of the given row (parent is the invalid QModelIndex, column is 0).
|
|
+ * Note that imageInfoRef will crash if index is invalid.
|
|
+ */
|
|
+ ImageInfo imageInfo(int row) const;
|
|
+ ImageInfo& imageInfoRef(int row) const;
|
|
+ qlonglong imageId(int row) const;
|
|
+
|
|
+ /** Return the index for the given ImageInfo or id, if contained in this model.
|
|
+ */
|
|
+ QModelIndex indexForImageInfo(const ImageInfo& info) const;
|
|
+ QModelIndex indexForImageInfo(const ImageInfo& info, const QVariant& extraValue) const;
|
|
+ QModelIndex indexForImageId(qlonglong id) const;
|
|
+ QModelIndex indexForImageId(qlonglong id, const QVariant& extraValue) const;
|
|
+ QList<QModelIndex> indexesForImageInfo(const ImageInfo& info) const;
|
|
+ QList<QModelIndex> indexesForImageId(qlonglong id) const;
|
|
+
|
|
+ int numberOfIndexesForImageInfo(const ImageInfo& info) const;
|
|
+ int numberOfIndexesForImageId(qlonglong id) const;
|
|
+
|
|
+ /** Returns the index or ImageInfo object from the underlying data
|
|
+ * for the given file path. This is fast if keepsFilePathCache is enabled.
|
|
+ * The file path is as returned by ImageInfo.filePath().
|
|
+ * In case of multiple occurrences of the same file, the simpler variants return
|
|
+ * any one found first, use the QList methods to retrieve all occurrences.
|
|
+ */
|
|
+ QModelIndex indexForPath(const QString& filePath) const;
|
|
+ ImageInfo imageInfo(const QString& filePath) const;
|
|
+ QList<QModelIndex> indexesForPath(const QString& filePath) const;
|
|
+ QList<ImageInfo> imageInfos(const QString& filePath) const;
|
|
+
|
|
+ /** Main entry point for subclasses adding image infos to the model.
|
|
+ * If you list entries not unique per image id, you must add an extraValue
|
|
+ * so that every entry is unique by imageId and extraValues.
|
|
+ * Please note that these methods do not prevent addition of duplicate entries.
|
|
+ */
|
|
+ void addImageInfo(const ImageInfo& info);
|
|
+ void addImageInfos(const QList<ImageInfo>& infos);
|
|
+ void addImageInfos(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues);
|
|
+
|
|
+ /** Clears image infos and resets model.
|
|
+ */
|
|
+ void clearImageInfos();
|
|
+
|
|
+ /** Clears and adds the infos.
|
|
+ */
|
|
+ void setImageInfos(const QList<ImageInfo>& infos);
|
|
+
|
|
+ /**
|
|
+ * Directly remove the given indexes or infos from the model.
|
|
+ */
|
|
+ void removeIndex(const QModelIndex& indexes);
|
|
+ void removeIndexes(const QList<QModelIndex>& indexes);
|
|
+ void removeImageInfo(const ImageInfo& info);
|
|
+ void removeImageInfos(const QList<ImageInfo>& infos);
|
|
+ void removeImageInfos(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues);
|
|
+
|
|
+ /**
|
|
+ * addImageInfo() is asynchronous if a prepocessor is set.
|
|
+ * This method first adds the info, synchronously.
|
|
+ * Only afterwards, the preprocessor will have the opportunity to process it.
|
|
+ * This method also bypasses any incremental updates.
|
|
+ * Please note that these methods do not prevent addition of duplicate entries.
|
|
+ */
|
|
+ void addImageInfoSynchronously(const ImageInfo& info);
|
|
+ void addImageInfosSynchronously(const QList<ImageInfo>& infos);
|
|
+ void addImageInfosSynchronously(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues);
|
|
+
|
|
+ /**
|
|
+ * Add the given entries. Method returns immediately, the
|
|
+ * addition may happen later asynchronously.
|
|
+ * These methods prevent the addition of duplicate entries.
|
|
+ */
|
|
+ void ensureHasImageInfo(const ImageInfo& info);
|
|
+ void ensureHasImageInfos(const QList<ImageInfo>& infos);
|
|
+ void ensureHasImageInfos(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues);
|
|
+
|
|
+ /**
|
|
+ * Ensure that all images grouped on the given leader are contained in the model.
|
|
+ */
|
|
+ void ensureHasGroupedImages(const ImageInfo& groupLeader);
|
|
+
|
|
+ QList<ImageInfo> imageInfos() const;
|
|
+ QList<qlonglong> imageIds() const;
|
|
+ QList<ImageInfo> uniqueImageInfos() const;
|
|
+
|
|
+ bool hasImage(qlonglong id) const;
|
|
+ bool hasImage(const ImageInfo& info) const;
|
|
+ bool hasImage(const ImageInfo& info, const QVariant& extraValue) const;
|
|
+ bool hasImage(qlonglong id, const QVariant& extraValue) const;
|
|
+
|
|
+ bool isEmpty() const;
|
|
+
|
|
+ // Drag and Drop
|
|
+ DECLARE_MODEL_DRAG_DROP_METHODS
|
|
+
|
|
+ /**
|
|
+ * Install an object as a preprocessor for ImageInfos added to this model.
|
|
+ * For every QList of ImageInfos added to addImageInfo, the signal preprocess()
|
|
+ * will be emitted. The preprocessor may process the items and shall then readd
|
|
+ * them by calling reAddImageInfos(). It may take some time to process.
|
|
+ * It shall discard any held infos when the modelReset() signal is sent.
|
|
+ * It shall call readdFinished() when no reset occurred and all infos on the way have been readded.
|
|
+ * This means that only after calling this method, you shall make three connections
|
|
+ * (preprocess -> your slot, your signal -> reAddImageInfos, your signal -> reAddingFinished)
|
|
+ * and make or already hold a connection modelReset() -> your slot.
|
|
+ * There is only one preprocessor at a time, a previously set object will be disconnected.
|
|
+ */
|
|
+ void setPreprocessor(QObject* processor);
|
|
+ void unsetPreprocessor(QObject* processor);
|
|
+
|
|
+ /**
|
|
+ * Returns true if this model is currently refreshing.
|
|
+ * For a preprocessor this means that, although the preprocessor may currently have
|
|
+ * processed all it got, more batches are to be expected.
|
|
+ */
|
|
+ bool isRefreshing() const;
|
|
+
|
|
+ /**
|
|
+ * Enable sending of imageInfosAboutToBeRemoved and imageInfosRemoved signals.
|
|
+ * Default: false
|
|
+ */
|
|
+ void setSendRemovalSignals(bool send);
|
|
+
|
|
+ virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
|
|
+ virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
|
|
+ virtual int rowCount(const QModelIndex& parent = QModelIndex()) const;
|
|
+ virtual Qt::ItemFlags flags(const QModelIndex& index) const;
|
|
+ virtual QModelIndex index(int row, int column = 0, const QModelIndex& parent = QModelIndex()) const;
|
|
+
|
|
+ /** Retrieves the imageInfo object from the data() method of the given index.
|
|
+ * The index may be from a QSortFilterProxyModel as long as an ImageModel is at the end. */
|
|
+ static ImageInfo retrieveImageInfo(const QModelIndex& index);
|
|
+ static qlonglong retrieveImageId(const QModelIndex& index);
|
|
+
|
|
+Q_SIGNALS:
|
|
+
|
|
+ /** Informs that ImageInfos will be added to the model.
|
|
+ * This signal is sent before the model data is changed and views are informed.
|
|
+ */
|
|
+ void imageInfosAboutToBeAdded(const QList<ImageInfo>& infos);
|
|
+
|
|
+ /** Informs that ImageInfos have been added to the model.
|
|
+ * This signal is sent after the model data is changed and views are informed.
|
|
+ */
|
|
+ void imageInfosAdded(const QList<ImageInfo>& infos);
|
|
+
|
|
+ /** Informs that ImageInfos will be removed from the model.
|
|
+ * This signal is sent before the model data is changed and views are informed.
|
|
+ * Note: You need to explicitly enable sending of this signal. It is not sent
|
|
+ * in clearImageInfos().
|
|
+ */
|
|
+ void imageInfosAboutToBeRemoved(const QList<ImageInfo>& infos);
|
|
+
|
|
+ /** Informs that ImageInfos have been removed from the model.
|
|
+ * This signal is sent after the model data is changed and views are informed. *
|
|
+ * Note: You need to explicitly enable sending of this signal. It is not sent
|
|
+ * in clearImageInfos().
|
|
+ */
|
|
+ void imageInfosRemoved(const QList<ImageInfo>& infos);
|
|
+
|
|
+ /** Connect to this signal only if you are the current preprocessor.
|
|
+ */
|
|
+ void preprocess(const QList<ImageInfo>& infos, const QList<QVariant>&);
|
|
+ void processAdded(const QList<ImageInfo>& infos, const QList<QVariant>&);
|
|
+
|
|
+ /** If an ImageChangeset affected indexes of this model with changes as set in watchFlags(),
|
|
+ * this signal contains the changeset and the affected indexes.
|
|
+ */
|
|
+ void imageChange(const ImageChangeset&, const QItemSelection&);
|
|
+
|
|
+ /** If an ImageTagChangeset affected indexes of this model,
|
|
+ * this signal contains the changeset and the affected indexes.
|
|
+ */
|
|
+ void imageTagChange(const ImageTagChangeset&, const QItemSelection&);
|
|
+
|
|
+ /** Signals that the model is right now ready to start an incremental refresh.
|
|
+ * This is guaranteed only for the scope of emitting this signal.
|
|
+ */
|
|
+ void readyForIncrementalRefresh();
|
|
+
|
|
+ /** Signals that the model has finished currently with all scheduled
|
|
+ * refreshing, full or incremental, and all preprocessing.
|
|
+ * The model is in polished, clean situation right now.
|
|
+ */
|
|
+ void allRefreshingFinished();
|
|
+
|
|
+public Q_SLOTS:
|
|
+
|
|
+ void reAddImageInfos(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues);
|
|
+ void reAddingFinished();
|
|
+
|
|
+protected:
|
|
+
|
|
+ /** Subclasses that add ImageInfos in batches shall call startRefresh()
|
|
+ * when they start sending batches and finishRefresh() when they have finished.
|
|
+ * No incremental refreshes will be started while listing.
|
|
+ * A clearImageInfos() always stops listing, calling finishRefresh() is then not necessary.
|
|
+ */
|
|
+ void startRefresh();
|
|
+ void finishRefresh();
|
|
+
|
|
+ /** As soon as the model is ready to start an incremental refresh, the signal
|
|
+ * readyForIncrementalRefresh() will be emitted. The signal will be emitted inline
|
|
+ * if the model is ready right now.
|
|
+ */
|
|
+ void requestIncrementalRefresh();
|
|
+ bool hasIncrementalRefreshPending() const;
|
|
+
|
|
+ /** Starts an incremental refresh operation. You shall only call this method from a slot
|
|
+ * connected to readyForIncrementalRefresh(). To initiate an incremental refresh,
|
|
+ * call requestIncrementalRefresh().
|
|
+ */
|
|
+ void startIncrementalRefresh();
|
|
+ void finishIncrementalRefresh();
|
|
+
|
|
+ void emitDataChangedForAll();
|
|
+ void emitDataChangedForSelection(const QItemSelection& selection);
|
|
+
|
|
+ // Called when the internal storage is cleared
|
|
+ virtual void imageInfosCleared() {};
|
|
+
|
|
+ // Called before rowsAboutToBeRemoved
|
|
+ virtual void imageInfosAboutToBeRemoved(int /*begin*/, int /*end*/) {};
|
|
+
|
|
+protected Q_SLOTS:
|
|
+
|
|
+ virtual void slotImageChange(const ImageChangeset& changeset);
|
|
+ virtual void slotImageTagChange(const ImageTagChangeset& changeset);
|
|
+
|
|
+private:
|
|
+
|
|
+ void appendInfos(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues);
|
|
+ void appendInfosChecked(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues);
|
|
+ void publiciseInfos(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues);
|
|
+ void cleanSituationChecks();
|
|
+ void removeRowPairsWithCheck(const QList<QPair<int, int> >& toRemove);
|
|
+ void removeRowPairs(const QList<QPair<int, int> >& toRemove);
|
|
+
|
|
+public:
|
|
+
|
|
+ // Declared public because it's used in ImageModelIncrementalUpdater class
|
|
+ class Private;
|
|
+
|
|
+private:
|
|
+
|
|
+ Private* const d;
|
|
+};
|
|
+
|
|
+} // namespace Digikam
|
|
+
|
|
+Q_DECLARE_METATYPE(Digikam::ImageModel*)
|
|
+
|
|
+#endif // IMAGEMODEL_H
|
|
diff --git a/libs/database/models/imagesortsettings.cpp b/libs/database/models/imagesortsettings.cpp
|
|
new file mode 100644
|
|
index 0000000..39ee6e1
|
|
--- /dev/null
|
|
+++ b/libs/database/models/imagesortsettings.cpp
|
|
@@ -0,0 +1,400 @@
|
|
+/* ============================================================
|
|
+ *
|
|
+ * This file is a part of digiKam project
|
|
+ * http://www.digikam.org
|
|
+ *
|
|
+ * Date : 2009-03-05
|
|
+ * Description : Filter values for use with ImageFilterModel
|
|
+ *
|
|
+ * Copyright (C) 2009 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
|
|
+ * Copyright (C) 2014 by Mohamed Anwer <m dot anwer at gmx dot com>
|
|
+ *
|
|
+ * This program is free software; you can redistribute it
|
|
+ * and/or modify it under the terms of the GNU General
|
|
+ * Public License as published by the Free Software Foundation;
|
|
+ * either version 2, or (at your option)
|
|
+ * any later version.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ *
|
|
+ * ============================================================ */
|
|
+
|
|
+#include "imagesortsettings.h"
|
|
+
|
|
+// Qt includes
|
|
+
|
|
+#include <QDateTime>
|
|
+#include <QRectF>
|
|
+
|
|
+// Local includes
|
|
+
|
|
+#include "coredbfields.h"
|
|
+#include "imageinfo.h"
|
|
+
|
|
+namespace Digikam
|
|
+{
|
|
+
|
|
+ImageSortSettings::ImageSortSettings()
|
|
+{
|
|
+ categorizationMode = NoCategories;
|
|
+ categorizationSortOrder = DefaultOrder;
|
|
+ categorizationCaseSensitivity = Qt::CaseSensitive;
|
|
+ sortRole = SortByFileName;
|
|
+ sortOrder = DefaultOrder;
|
|
+ strTypeNatural = true;
|
|
+ sortCaseSensitivity = Qt::CaseSensitive;
|
|
+ currentCategorizationSortOrder = Qt::AscendingOrder;
|
|
+ currentSortOrder = Qt::AscendingOrder;
|
|
+}
|
|
+
|
|
+bool ImageSortSettings::operator==(const ImageSortSettings& other) const
|
|
+{
|
|
+ return
|
|
+ categorizationMode == other.categorizationMode &&
|
|
+ categorizationSortOrder == other.categorizationSortOrder &&
|
|
+ categorizationCaseSensitivity == other.categorizationCaseSensitivity &&
|
|
+ sortRole == other.sortRole &&
|
|
+ sortOrder == other.sortOrder &&
|
|
+ sortCaseSensitivity == other.sortCaseSensitivity;
|
|
+}
|
|
+
|
|
+void ImageSortSettings::setCategorizationMode(CategorizationMode mode)
|
|
+{
|
|
+ categorizationMode = mode;
|
|
+
|
|
+ if (categorizationSortOrder == DefaultOrder)
|
|
+ {
|
|
+ currentCategorizationSortOrder = defaultSortOrderForCategorizationMode(categorizationMode);
|
|
+ }
|
|
+}
|
|
+
|
|
+void ImageSortSettings::setCategorizationSortOrder(SortOrder order)
|
|
+{
|
|
+ categorizationSortOrder = order;
|
|
+
|
|
+ if (categorizationSortOrder == DefaultOrder)
|
|
+ {
|
|
+ currentCategorizationSortOrder = defaultSortOrderForCategorizationMode(categorizationMode);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ currentCategorizationSortOrder = (Qt::SortOrder)categorizationSortOrder;
|
|
+ }
|
|
+}
|
|
+
|
|
+void ImageSortSettings::setSortRole(SortRole role)
|
|
+{
|
|
+ sortRole = role;
|
|
+
|
|
+ if (sortOrder == DefaultOrder)
|
|
+ {
|
|
+ currentSortOrder = defaultSortOrderForSortRole(sortRole);
|
|
+ }
|
|
+}
|
|
+
|
|
+void ImageSortSettings::setSortOrder(SortOrder order)
|
|
+{
|
|
+ sortOrder = order;
|
|
+
|
|
+ if (sortOrder == DefaultOrder)
|
|
+ {
|
|
+ currentSortOrder = defaultSortOrderForSortRole(sortRole);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ currentSortOrder = (Qt::SortOrder)order;
|
|
+ }
|
|
+}
|
|
+
|
|
+void ImageSortSettings::setStringTypeNatural(bool natural)
|
|
+{
|
|
+ strTypeNatural = natural;
|
|
+}
|
|
+
|
|
+Qt::SortOrder ImageSortSettings::defaultSortOrderForCategorizationMode(CategorizationMode mode)
|
|
+{
|
|
+ switch (mode)
|
|
+ {
|
|
+ case NoCategories:
|
|
+ case OneCategory:
|
|
+ case CategoryByAlbum:
|
|
+ case CategoryByFormat:
|
|
+ default:
|
|
+ return Qt::AscendingOrder;
|
|
+ }
|
|
+}
|
|
+
|
|
+Qt::SortOrder ImageSortSettings::defaultSortOrderForSortRole(SortRole role)
|
|
+{
|
|
+ switch (role)
|
|
+ {
|
|
+ case SortByFileName:
|
|
+ case SortByFilePath:
|
|
+ return Qt::AscendingOrder;
|
|
+ case SortByFileSize:
|
|
+ return Qt::DescendingOrder;
|
|
+ case SortByModificationDate:
|
|
+ case SortByCreationDate:
|
|
+ return Qt::AscendingOrder;
|
|
+ case SortByRating:
|
|
+ case SortByImageSize:
|
|
+ return Qt::DescendingOrder;
|
|
+ case SortByAspectRatio:
|
|
+ return Qt::DescendingOrder;
|
|
+ case SortBySimilarity:
|
|
+ return Qt::DescendingOrder;
|
|
+ default:
|
|
+ return Qt::AscendingOrder;
|
|
+ }
|
|
+}
|
|
+
|
|
+int ImageSortSettings::compareCategories(const ImageInfo& left, const ImageInfo& right) const
|
|
+{
|
|
+ switch (categorizationMode)
|
|
+ {
|
|
+ case NoCategories:
|
|
+ case OneCategory:
|
|
+ return 0;
|
|
+ case CategoryByAlbum:
|
|
+ {
|
|
+ int leftAlbum = left.albumId();
|
|
+ int rightAlbum = right.albumId();
|
|
+
|
|
+ // return comparation result
|
|
+ if (leftAlbum == rightAlbum)
|
|
+ {
|
|
+ return 0;
|
|
+ }
|
|
+ else if (lessThanByOrder(leftAlbum, rightAlbum, currentCategorizationSortOrder))
|
|
+ {
|
|
+ return -1;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ return 1;
|
|
+ }
|
|
+ }
|
|
+ case CategoryByFormat:
|
|
+ {
|
|
+ return naturalCompare(left.format(), right.format(),
|
|
+ currentCategorizationSortOrder, categorizationCaseSensitivity, strTypeNatural);
|
|
+ }
|
|
+ default:
|
|
+ return 0;
|
|
+ }
|
|
+}
|
|
+
|
|
+bool ImageSortSettings::lessThan(const ImageInfo& left, const ImageInfo& right) const
|
|
+{
|
|
+ int result = compare(left, right, sortRole);
|
|
+
|
|
+ if (result != 0)
|
|
+ {
|
|
+ return result < 0;
|
|
+ }
|
|
+
|
|
+ // are they identical?
|
|
+ if (left == right)
|
|
+ {
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ // If left and right equal for first sort order, use a hierarchy of all sort orders
|
|
+ if ( (result = compare(left, right, SortByFileName)) != 0)
|
|
+ {
|
|
+ return result < 0;
|
|
+ }
|
|
+
|
|
+ if ( (result = compare(left, right, SortByCreationDate)) != 0)
|
|
+ {
|
|
+ return result < 0;
|
|
+ }
|
|
+
|
|
+ if ( (result = compare(left, right, SortByModificationDate)) != 0)
|
|
+ {
|
|
+ return result < 0;
|
|
+ }
|
|
+
|
|
+ if ( (result = compare(left, right, SortByFilePath)) != 0)
|
|
+ {
|
|
+ return result < 0;
|
|
+ }
|
|
+
|
|
+ if ( (result = compare(left, right, SortByFileSize)) != 0)
|
|
+ {
|
|
+ return result < 0;
|
|
+ }
|
|
+
|
|
+ if ( (result = compare(left, right, SortBySimilarity)) != 0)
|
|
+ {
|
|
+ return result < 0;
|
|
+ }
|
|
+
|
|
+ return false;
|
|
+}
|
|
+
|
|
+int ImageSortSettings::compare(const ImageInfo& left, const ImageInfo& right) const
|
|
+{
|
|
+ return compare(left, right, sortRole);
|
|
+}
|
|
+
|
|
+int ImageSortSettings::compare(const ImageInfo& left, const ImageInfo& right, SortRole role) const
|
|
+{
|
|
+ switch (role)
|
|
+ {
|
|
+ case SortByFileName:
|
|
+ {
|
|
+ bool versioning = (left.name().contains(QLatin1String("_v"), Qt::CaseInsensitive) ||
|
|
+ right.name().contains(QLatin1String("_v"), Qt::CaseInsensitive));
|
|
+ return naturalCompare(left.name(), right.name(), currentSortOrder, sortCaseSensitivity, strTypeNatural, versioning);
|
|
+ }
|
|
+ case SortByFilePath:
|
|
+ return naturalCompare(left.filePath(), right.filePath(), currentSortOrder, sortCaseSensitivity, strTypeNatural);
|
|
+ case SortByFileSize:
|
|
+ return compareByOrder(left.fileSize(), right.fileSize(), currentSortOrder);
|
|
+ case SortByModificationDate:
|
|
+ return compareByOrder(left.modDateTime(), right.modDateTime(), currentSortOrder);
|
|
+ case SortByCreationDate:
|
|
+ return compareByOrder(left.dateTime(), right.dateTime(), currentSortOrder);
|
|
+ case SortByRating:
|
|
+ // I have the feeling that inverting the sort order for rating is the natural order
|
|
+ return - compareByOrder(left.rating(), right.rating(), currentSortOrder);
|
|
+ case SortByImageSize:
|
|
+ {
|
|
+ QSize leftSize = left.dimensions();
|
|
+ QSize rightSize = right.dimensions();
|
|
+ int leftPixels = leftSize.width() * leftSize.height();
|
|
+ int rightPixels = rightSize.width() * rightSize.height();
|
|
+ return compareByOrder(leftPixels, rightPixels, currentSortOrder);
|
|
+ }
|
|
+ case SortByAspectRatio:
|
|
+ {
|
|
+ QSize leftSize = left.dimensions();
|
|
+ QSize rightSize = right.dimensions();
|
|
+ int leftAR = (double(leftSize.width()) / double(leftSize.height())) * 1000000;
|
|
+ int rightAR = (double(rightSize.width()) / double(rightSize.height())) * 1000000;
|
|
+ return compareByOrder(leftAR, rightAR, currentSortOrder);
|
|
+ }
|
|
+ case SortBySimilarity:
|
|
+ {
|
|
+ qlonglong leftReferenceImageId = left.currentReferenceImage();
|
|
+ qlonglong rightReferenceImageId = right.currentReferenceImage();
|
|
+ // make sure that the original image has always the highest similarity.
|
|
+ double leftSimilarity = left.id() == leftReferenceImageId ? 1.1 : left.currentSimilarity();
|
|
+ double rightSimilarity = right.id() == rightReferenceImageId ? 1.1 : right.currentSimilarity();
|
|
+ return compareByOrder(leftSimilarity, rightSimilarity, currentSortOrder);
|
|
+ }
|
|
+ default:
|
|
+ return 1;
|
|
+ }
|
|
+}
|
|
+
|
|
+bool ImageSortSettings::lessThan(const QVariant& left, const QVariant& right) const
|
|
+{
|
|
+ if (left.type() != right.type())
|
|
+ {
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ switch (left.type())
|
|
+ {
|
|
+ case QVariant::Int:
|
|
+ return compareByOrder(left.toInt(), right.toInt(), currentSortOrder);
|
|
+ case QVariant::UInt:
|
|
+ return compareByOrder(left.toUInt(), right.toUInt(), currentSortOrder);
|
|
+ case QVariant::LongLong:
|
|
+ return compareByOrder(left.toLongLong(), right.toLongLong(), currentSortOrder);
|
|
+ case QVariant::ULongLong:
|
|
+ return compareByOrder(left.toULongLong(), right.toULongLong(), currentSortOrder);
|
|
+ case QVariant::Double:
|
|
+ return compareByOrder(left.toDouble(), right.toDouble(), currentSortOrder);
|
|
+ case QVariant::Date:
|
|
+ return compareByOrder(left.toDate(), right.toDate(), currentSortOrder);
|
|
+ case QVariant::DateTime:
|
|
+ return compareByOrder(left.toDateTime(), right.toDateTime(), currentSortOrder);
|
|
+ case QVariant::Time:
|
|
+ return compareByOrder(left.toTime(), right.toTime(), currentSortOrder);
|
|
+ case QVariant::Rect:
|
|
+ case QVariant::RectF:
|
|
+ {
|
|
+ QRectF rectLeft = left.toRectF();
|
|
+ QRectF rectRight = right.toRectF();
|
|
+ int result;
|
|
+
|
|
+ if ((result = compareByOrder(rectLeft.top(), rectRight.top(), currentSortOrder)) != 0)
|
|
+ {
|
|
+ return result < 0;
|
|
+ }
|
|
+
|
|
+ if ((result = compareByOrder(rectLeft.left(), rectRight.left(), currentSortOrder)) != 0)
|
|
+ {
|
|
+ return result < 0;
|
|
+ }
|
|
+
|
|
+ QSizeF sizeLeft = rectLeft.size(), sizeRight = rectRight.size();
|
|
+
|
|
+ if ((result = compareByOrder(sizeLeft.width()*sizeLeft.height(), sizeRight.width()*sizeRight.height(), currentSortOrder)) != 0)
|
|
+ {
|
|
+ return result < 0;
|
|
+ }
|
|
+ // FIXME: fall through?? If not, add "break" here
|
|
+ }
|
|
+ default:
|
|
+ return naturalCompare(left.toString(), right.toString(), currentSortOrder, sortCaseSensitivity, strTypeNatural);
|
|
+ }
|
|
+}
|
|
+
|
|
+DatabaseFields::Set ImageSortSettings::watchFlags() const
|
|
+{
|
|
+ DatabaseFields::Set set;
|
|
+
|
|
+ switch (sortRole)
|
|
+ {
|
|
+ case SortByFileName:
|
|
+ set |= DatabaseFields::Name;
|
|
+ break;
|
|
+ case SortByFilePath:
|
|
+ set |= DatabaseFields::Name;
|
|
+ break;
|
|
+ case SortByFileSize:
|
|
+ set |= DatabaseFields::FileSize;
|
|
+ break;
|
|
+ case SortByModificationDate:
|
|
+ set |= DatabaseFields::ModificationDate;
|
|
+ break;
|
|
+ case SortByCreationDate:
|
|
+ set |= DatabaseFields::CreationDate;
|
|
+ break;
|
|
+ case SortByRating:
|
|
+ set |= DatabaseFields::Rating;
|
|
+ break;
|
|
+ case SortByImageSize:
|
|
+ set |= DatabaseFields::Width | DatabaseFields::Height;
|
|
+ break;
|
|
+ case SortByAspectRatio:
|
|
+ set |= DatabaseFields::Width | DatabaseFields::Height;
|
|
+ break;
|
|
+ case SortBySimilarity:
|
|
+ // TODO: Not sure what to do here....
|
|
+ set |= DatabaseFields::Name;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ switch (categorizationMode)
|
|
+ {
|
|
+ case NoCategories:
|
|
+ case OneCategory:
|
|
+ case CategoryByAlbum:
|
|
+ break;
|
|
+ case CategoryByFormat:
|
|
+ set |= DatabaseFields::Format;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ return set;
|
|
+}
|
|
+
|
|
+} // namespace Digikam
|
|
diff --git a/libs/database/models/imagesortsettings.h b/libs/database/models/imagesortsettings.h
|
|
new file mode 100644
|
|
index 0000000..2a5fd8c
|
|
--- /dev/null
|
|
+++ b/libs/database/models/imagesortsettings.h
|
|
@@ -0,0 +1,225 @@
|
|
+/* ============================================================
|
|
+ *
|
|
+ * This file is a part of digiKam project
|
|
+ * http://www.digikam.org
|
|
+ *
|
|
+ * Date : 2009-05-31
|
|
+ * Description : Sort settings for use with ImageFilterModel
|
|
+ *
|
|
+ * Copyright (C) 2009 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
|
|
+ *
|
|
+ * This program is free software; you can redistribute it
|
|
+ * and/or modify it under the terms of the GNU General
|
|
+ * Public License as published by the Free Software Foundation;
|
|
+ * either version 2, or (at your option)
|
|
+ * any later version.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ *
|
|
+ * ============================================================ */
|
|
+
|
|
+#ifndef IMAGESORTSETTINGS_H
|
|
+#define IMAGESORTSETTINGS_H
|
|
+
|
|
+// Qt includes
|
|
+
|
|
+#include <QHash>
|
|
+#include <QList>
|
|
+#include <QMap>
|
|
+#include <QString>
|
|
+#include <QCollator>
|
|
+
|
|
+// Local includes
|
|
+
|
|
+#include "digikam_export.h"
|
|
+
|
|
+namespace Digikam
|
|
+{
|
|
+
|
|
+class ImageInfo;
|
|
+
|
|
+namespace DatabaseFields
|
|
+{
|
|
+ class Set;
|
|
+}
|
|
+
|
|
+class DIGIKAM_DATABASE_EXPORT ImageSortSettings
|
|
+{
|
|
+public:
|
|
+
|
|
+ ImageSortSettings();
|
|
+
|
|
+ bool operator==(const ImageSortSettings& other) const;
|
|
+
|
|
+ /** Compares the categories of left and right.
|
|
+ * Return -1 if left is less than right, 0 if both fall in the same category,
|
|
+ * and 1 if left is greater than right.
|
|
+ * Adheres to set categorization mode and current category sort order.
|
|
+ */
|
|
+ int compareCategories(const ImageInfo& left, const ImageInfo& right) const;
|
|
+
|
|
+ /** Returns true if left is less than right.
|
|
+ * Adheres to current sort role and sort order.
|
|
+ */
|
|
+ bool lessThan(const ImageInfo& left, const ImageInfo& right) const;
|
|
+
|
|
+ /** Compares the ImageInfos left and right.
|
|
+ * Return -1 if left is less than right, 1 if left is greater than right,
|
|
+ * and 0 if left equals right comparing the current sort role's value.
|
|
+ * Adheres to set sort role and sort order.
|
|
+ */
|
|
+ int compare(const ImageInfo& left, const ImageInfo& right) const;
|
|
+
|
|
+ /** Returns true if left QVariant is less than right.
|
|
+ * Adheres to current sort role and sort order.
|
|
+ * Use for extraValue, if necessary.
|
|
+ */
|
|
+ bool lessThan(const QVariant& left, const QVariant& right) const;
|
|
+
|
|
+ enum SortOrder
|
|
+ {
|
|
+ AscendingOrder = Qt::AscendingOrder,
|
|
+ DescendingOrder = Qt::DescendingOrder,
|
|
+ DefaultOrder /// sort order depends on the chosen sort role
|
|
+ };
|
|
+
|
|
+ /// --- Categories ---
|
|
+
|
|
+ enum CategorizationMode
|
|
+ {
|
|
+ NoCategories, /// categorization switched off
|
|
+ OneCategory, /// all items in one global category
|
|
+ CategoryByAlbum,
|
|
+ CategoryByFormat
|
|
+ };
|
|
+
|
|
+ CategorizationMode categorizationMode;
|
|
+ SortOrder categorizationSortOrder;
|
|
+
|
|
+ void setCategorizationMode(CategorizationMode mode);
|
|
+ void setCategorizationSortOrder(SortOrder order);
|
|
+
|
|
+ /// Only Ascending or Descending, never DefaultOrder
|
|
+ Qt::SortOrder currentCategorizationSortOrder;
|
|
+ Qt::CaseSensitivity categorizationCaseSensitivity;
|
|
+
|
|
+ bool isCategorized() const { return categorizationMode >= CategoryByAlbum; }
|
|
+
|
|
+ /// --- Image Sorting ---
|
|
+
|
|
+ enum SortRole
|
|
+ {
|
|
+ // Note: For legacy reasons, the order of the first five entries must remain unchanged
|
|
+ SortByFileName,
|
|
+ SortByFilePath,
|
|
+ SortByCreationDate,
|
|
+ SortByFileSize,
|
|
+ SortByRating,
|
|
+ SortByModificationDate,
|
|
+ SortByImageSize, // pixel number
|
|
+ SortByAspectRatio, // width / height * 100000
|
|
+ SortBySimilarity
|
|
+ };
|
|
+
|
|
+ SortRole sortRole;
|
|
+ SortOrder sortOrder;
|
|
+ bool strTypeNatural;
|
|
+
|
|
+ void setSortRole(SortRole role);
|
|
+ void setSortOrder(SortOrder order);
|
|
+ void setStringTypeNatural(bool natural);
|
|
+
|
|
+ Qt::SortOrder currentSortOrder;
|
|
+ Qt::CaseSensitivity sortCaseSensitivity;
|
|
+
|
|
+ int compare(const ImageInfo& left, const ImageInfo& right, SortRole sortRole) const;
|
|
+
|
|
+ // --- ---
|
|
+
|
|
+ static Qt::SortOrder defaultSortOrderForCategorizationMode(CategorizationMode mode);
|
|
+ static Qt::SortOrder defaultSortOrderForSortRole(SortRole role);
|
|
+
|
|
+ /// --- Change notification ---
|
|
+
|
|
+ /** Returns database fields a change in which would affect the current sorting.
|
|
+ */
|
|
+ DatabaseFields::Set watchFlags() const;
|
|
+
|
|
+ /// --- Utilities ---
|
|
+
|
|
+ /** Returns a < b if sortOrder is Ascending, or b < a if order is descending.
|
|
+ */
|
|
+ template <typename T>
|
|
+ static inline bool lessThanByOrder(const T& a, const T& b, Qt::SortOrder sortOrder)
|
|
+ {
|
|
+ if (sortOrder == Qt::AscendingOrder)
|
|
+ {
|
|
+ return a < b;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ return b < a;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /** Returns the usual compare result of -1, 0, or 1 for lessThan, equals and greaterThan.
|
|
+ */
|
|
+ template <typename T>
|
|
+ static inline int compareValue(const T& a, const T& b)
|
|
+ {
|
|
+ if (a == b)
|
|
+ {
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ if (a < b)
|
|
+ {
|
|
+ return -1;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ return 1;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /** Takes a typical result from a compare method (0 is equal, -1 is less than, 1 is greater than)
|
|
+ * and applies the given sort order to it.
|
|
+ */
|
|
+ static inline int compareByOrder(int compareResult, Qt::SortOrder sortOrder)
|
|
+ {
|
|
+ if (sortOrder == Qt::AscendingOrder)
|
|
+ {
|
|
+ return compareResult;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ return - compareResult;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ template <typename T>
|
|
+ static inline int compareByOrder(const T& a, const T& b, Qt::SortOrder sortOrder)
|
|
+ {
|
|
+ return compareByOrder(compareValue(a, b), sortOrder);
|
|
+ }
|
|
+
|
|
+ /** Compares the two string by natural comparison and adheres to given sort order
|
|
+ */
|
|
+ static inline int naturalCompare(const QString& a, const QString& b, Qt::SortOrder sortOrder,
|
|
+ Qt::CaseSensitivity caseSensitive = Qt::CaseSensitive,
|
|
+ bool natural = true, bool versioning = false)
|
|
+ {
|
|
+ QCollator collator;
|
|
+ collator.setNumericMode(natural);
|
|
+ collator.setIgnorePunctuation(versioning);
|
|
+ collator.setCaseSensitivity(caseSensitive);
|
|
+ return (compareByOrder(collator.compare(a, b), sortOrder));
|
|
+ }
|
|
+};
|
|
+
|
|
+} // namespace Digikam
|
|
+
|
|
+#endif // IMAGESORTSETTINGS_H
|
|
diff --git a/libs/database/models/imagethumbnailmodel.cpp b/libs/database/models/imagethumbnailmodel.cpp
|
|
new file mode 100644
|
|
index 0000000..b7f5661
|
|
--- /dev/null
|
|
+++ b/libs/database/models/imagethumbnailmodel.cpp
|
|
@@ -0,0 +1,323 @@
|
|
+/* ============================================================
|
|
+ *
|
|
+ * This file is a part of digiKam project
|
|
+ * http://www.digikam.org
|
|
+ *
|
|
+ * Date : 2009-03-05
|
|
+ * Description : Qt item model for database entries with support for thumbnail loading
|
|
+ *
|
|
+ * Copyright (C) 2009-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
|
|
+ * Copyright (C) 2011-2017 by Gilles Caulier <caulier dot gilles at gmail dot com>
|
|
+ *
|
|
+ * This program is free software; you can redistribute it
|
|
+ * and/or modify it under the terms of the GNU General
|
|
+ * Public License as published by the Free Software Foundation;
|
|
+ * either version 2, or (at your option)
|
|
+ * any later version.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ *
|
|
+ * ============================================================ */
|
|
+
|
|
+#include "imagethumbnailmodel.h"
|
|
+
|
|
+// Qt includes
|
|
+
|
|
+#include <QHash>
|
|
+
|
|
+// Local includes
|
|
+
|
|
+#include "digikam_debug.h"
|
|
+#include "thumbnailloadthread.h"
|
|
+#include "digikam_export.h"
|
|
+#include "digikam_globals.h"
|
|
+
|
|
+namespace Digikam
|
|
+{
|
|
+
|
|
+class ImageThumbnailModel::ImageThumbnailModelPriv
|
|
+{
|
|
+public:
|
|
+
|
|
+ ImageThumbnailModelPriv() :
|
|
+ thread(0),
|
|
+ preloadThread(0),
|
|
+ thumbSize(0),
|
|
+ lastGlobalThumbSize(0),
|
|
+ preloadThumbSize(0),
|
|
+ emitDataChanged(true)
|
|
+ {
|
|
+ staticListContainingThumbnailRole << ImageModel::ThumbnailRole;
|
|
+ }
|
|
+
|
|
+ ThumbnailLoadThread* thread;
|
|
+ ThumbnailLoadThread* preloadThread;
|
|
+ ThumbnailSize thumbSize;
|
|
+ ThumbnailSize lastGlobalThumbSize;
|
|
+ ThumbnailSize preloadThumbSize;
|
|
+ QRect detailRect;
|
|
+ QVector<int> staticListContainingThumbnailRole;
|
|
+
|
|
+ bool emitDataChanged;
|
|
+
|
|
+ int preloadThumbnailSize() const
|
|
+ {
|
|
+ if (preloadThumbSize.size())
|
|
+ {
|
|
+ return preloadThumbSize.size();
|
|
+ }
|
|
+
|
|
+ return thumbSize.size();
|
|
+ }
|
|
+};
|
|
+
|
|
+ImageThumbnailModel::ImageThumbnailModel(QObject* parent)
|
|
+ : ImageModel(parent), d(new ImageThumbnailModelPriv)
|
|
+{
|
|
+ setKeepsFilePathCache(true);
|
|
+}
|
|
+
|
|
+ImageThumbnailModel::~ImageThumbnailModel()
|
|
+{
|
|
+ delete d->preloadThread;
|
|
+ delete d;
|
|
+}
|
|
+
|
|
+void ImageThumbnailModel::setThumbnailLoadThread(ThumbnailLoadThread* thread)
|
|
+{
|
|
+ d->thread = thread;
|
|
+
|
|
+ connect(d->thread, SIGNAL(signalThumbnailLoaded(LoadingDescription,QPixmap)),
|
|
+ this, SLOT(slotThumbnailLoaded(LoadingDescription,QPixmap)));
|
|
+}
|
|
+
|
|
+ThumbnailLoadThread* ImageThumbnailModel::thumbnailLoadThread() const
|
|
+{
|
|
+ return d->thread;
|
|
+}
|
|
+
|
|
+ThumbnailSize ImageThumbnailModel::thumbnailSize() const
|
|
+{
|
|
+ return d->thumbSize;
|
|
+}
|
|
+
|
|
+void ImageThumbnailModel::setThumbnailSize(const ThumbnailSize& size)
|
|
+{
|
|
+ d->lastGlobalThumbSize = size;
|
|
+ d->thumbSize = size;
|
|
+}
|
|
+
|
|
+void ImageThumbnailModel::setPreloadThumbnailSize(const ThumbnailSize& size)
|
|
+{
|
|
+ d->preloadThumbSize = size;
|
|
+}
|
|
+
|
|
+void ImageThumbnailModel::setEmitDataChanged(bool emitSignal)
|
|
+{
|
|
+ d->emitDataChanged = emitSignal;
|
|
+}
|
|
+
|
|
+void ImageThumbnailModel::setPreloadThumbnails(bool preload)
|
|
+{
|
|
+ if (preload)
|
|
+ {
|
|
+ if (!d->preloadThread)
|
|
+ {
|
|
+ d->preloadThread = new ThumbnailLoadThread;
|
|
+ d->preloadThread->setPixmapRequested(false);
|
|
+ d->preloadThread->setPriority(QThread::LowestPriority);
|
|
+ }
|
|
+
|
|
+ connect(this, SIGNAL(allRefreshingFinished()),
|
|
+ this, SLOT(preloadAllThumbnails()));
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ delete d->preloadThread;
|
|
+ d->preloadThread = 0;
|
|
+ disconnect(this, SIGNAL(allRefreshingFinished()),
|
|
+ this, SLOT(preloadAllThumbnails()));
|
|
+ }
|
|
+}
|
|
+
|
|
+void ImageThumbnailModel::prepareThumbnails(const QList<QModelIndex>& indexesToPrepare)
|
|
+{
|
|
+ prepareThumbnails(indexesToPrepare, d->thumbSize);
|
|
+}
|
|
+
|
|
+void ImageThumbnailModel::prepareThumbnails(const QList<QModelIndex>& indexesToPrepare, const ThumbnailSize& thumbSize)
|
|
+{
|
|
+ if (!d->thread)
|
|
+ {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ QList<ThumbnailIdentifier> ids;
|
|
+ foreach(const QModelIndex& index, indexesToPrepare)
|
|
+ {
|
|
+ ids << imageInfoRef(index).thumbnailIdentifier();
|
|
+ }
|
|
+ d->thread->findGroup(ids, thumbSize.size());
|
|
+}
|
|
+
|
|
+void ImageThumbnailModel::preloadThumbnails(const QList<ImageInfo>& infos)
|
|
+{
|
|
+ if (!d->preloadThread)
|
|
+ {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ QList<ThumbnailIdentifier> ids;
|
|
+ foreach(const ImageInfo& info, infos)
|
|
+ {
|
|
+ ids << info.thumbnailIdentifier();
|
|
+ }
|
|
+ d->preloadThread->pregenerateGroup(ids, d->preloadThumbnailSize());
|
|
+}
|
|
+
|
|
+void ImageThumbnailModel::preloadThumbnails(const QList<QModelIndex>& indexesToPreload)
|
|
+{
|
|
+ if (!d->preloadThread)
|
|
+ {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ QList<ThumbnailIdentifier> ids;
|
|
+ foreach(const QModelIndex& index, indexesToPreload)
|
|
+ {
|
|
+ ids << imageInfoRef(index).thumbnailIdentifier();
|
|
+ }
|
|
+ d->preloadThread->stopAllTasks();
|
|
+ d->preloadThread->pregenerateGroup(ids, d->preloadThumbnailSize());
|
|
+}
|
|
+
|
|
+void ImageThumbnailModel::preloadAllThumbnails()
|
|
+{
|
|
+ preloadThumbnails(imageInfos());
|
|
+}
|
|
+
|
|
+void ImageThumbnailModel::imageInfosCleared()
|
|
+{
|
|
+ if (d->preloadThread)
|
|
+ {
|
|
+ d->preloadThread->stopAllTasks();
|
|
+ }
|
|
+}
|
|
+
|
|
+QVariant ImageThumbnailModel::data(const QModelIndex& index, int role) const
|
|
+{
|
|
+ if (role == ThumbnailRole && d->thread && index.isValid())
|
|
+ {
|
|
+ QPixmap thumbnail;
|
|
+ ImageInfo info = imageInfo(index);
|
|
+ QString path = info.filePath();
|
|
+
|
|
+ if (info.isNull())
|
|
+ {
|
|
+ return QVariant(QVariant::Pixmap);
|
|
+ }
|
|
+
|
|
+ if (!d->detailRect.isNull())
|
|
+ {
|
|
+ if (d->thread->find(info.thumbnailIdentifier(), d->detailRect, thumbnail, d->thumbSize.size()))
|
|
+ {
|
|
+ return thumbnail;
|
|
+ }
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ if (d->thread->find(info.thumbnailIdentifier(), thumbnail, d->thumbSize.size()))
|
|
+ {
|
|
+ return thumbnail;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return QVariant(QVariant::Pixmap);
|
|
+ }
|
|
+
|
|
+ return ImageModel::data(index, role);
|
|
+}
|
|
+
|
|
+bool ImageThumbnailModel::setData(const QModelIndex& index, const QVariant& value, int role)
|
|
+{
|
|
+ if (role == ThumbnailRole)
|
|
+ {
|
|
+ switch (value.type())
|
|
+ {
|
|
+ case QVariant::Invalid:
|
|
+ d->thumbSize = d->lastGlobalThumbSize;
|
|
+ d->detailRect = QRect();
|
|
+ break;
|
|
+
|
|
+ case QVariant::Int:
|
|
+
|
|
+ if (value.isNull())
|
|
+ {
|
|
+ d->thumbSize = d->lastGlobalThumbSize;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ d->thumbSize = value.toInt();
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ case QVariant::Rect:
|
|
+
|
|
+ if (value.isNull())
|
|
+ {
|
|
+ d->detailRect = QRect();
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ d->detailRect = value.toRect();
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return ImageModel::setData(index, value, role);
|
|
+}
|
|
+
|
|
+void ImageThumbnailModel::slotThumbnailLoaded(const LoadingDescription& loadingDescription, const QPixmap& thumb)
|
|
+{
|
|
+ if (thumb.isNull())
|
|
+ {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ // In case of multiple occurrence, we currently do not know which thumbnail is this. Signal change on all.
|
|
+ QModelIndexList indexes;
|
|
+ ThumbnailIdentifier thumbId = loadingDescription.thumbnailIdentifier();
|
|
+ if (thumbId.filePath.isEmpty())
|
|
+ {
|
|
+ indexes = indexesForImageId(thumbId.id);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ indexes = indexesForPath(thumbId.filePath);
|
|
+ }
|
|
+ foreach(const QModelIndex& index, indexes)
|
|
+ {
|
|
+ if (thumb.isNull())
|
|
+ {
|
|
+ emit thumbnailFailed(index, loadingDescription.previewParameters.size);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ emit thumbnailAvailable(index, loadingDescription.previewParameters.size);
|
|
+
|
|
+ if (d->emitDataChanged)
|
|
+ {
|
|
+ emit dataChanged(index, index, d->staticListContainingThumbnailRole);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+} // namespace Digikam
|
|
diff --git a/libs/database/models/imagethumbnailmodel.h b/libs/database/models/imagethumbnailmodel.h
|
|
new file mode 100644
|
|
index 0000000..366ca65
|
|
--- /dev/null
|
|
+++ b/libs/database/models/imagethumbnailmodel.h
|
|
@@ -0,0 +1,140 @@
|
|
+/* ============================================================
|
|
+ *
|
|
+ * This file is a part of digiKam project
|
|
+ * http://www.digikam.org
|
|
+ *
|
|
+ * Date : 2009-03-05
|
|
+ * Description : Qt item model for database entries with support for thumbnail loading
|
|
+ *
|
|
+ * Copyright (C) 2009-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
|
|
+ * Copyright (C) 2011 by Gilles Caulier <caulier dot gilles at gmail dot com>
|
|
+ *
|
|
+ * This program is free software; you can redistribute it
|
|
+ * and/or modify it under the terms of the GNU General
|
|
+ * Public License as published by the Free Software Foundation;
|
|
+ * either version 2, or (at your option)
|
|
+ * any later version.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ *
|
|
+ * ============================================================ */
|
|
+
|
|
+#ifndef IMAGETHUMBNAILMODEL_H
|
|
+#define IMAGETHUMBNAILMODEL_H
|
|
+
|
|
+// Local includes
|
|
+
|
|
+#include "imagemodel.h"
|
|
+#include "thumbnailsize.h"
|
|
+#include "digikam_export.h"
|
|
+
|
|
+namespace Digikam
|
|
+{
|
|
+
|
|
+class LoadingDescription;
|
|
+class ThumbnailLoadThread;
|
|
+
|
|
+class DIGIKAM_DATABASE_EXPORT ImageThumbnailModel : public ImageModel
|
|
+{
|
|
+ Q_OBJECT
|
|
+
|
|
+public:
|
|
+
|
|
+ /**
|
|
+ * An ImageModel that supports thumbnail loading.
|
|
+ * You need to set a ThumbnailLoadThread to enable thumbnail loading.
|
|
+ * Adjust the thumbnail size to your needs.
|
|
+ * Note that setKeepsFilePathCache is enabled per default.
|
|
+ */
|
|
+ explicit ImageThumbnailModel(QObject* parent);
|
|
+ ~ImageThumbnailModel();
|
|
+
|
|
+ /** Enable thumbnail loading and set the thread that shall be used.
|
|
+ * The thumbnail size of this thread will be adjusted.
|
|
+ */
|
|
+ void setThumbnailLoadThread(ThumbnailLoadThread* thread);
|
|
+ ThumbnailLoadThread* thumbnailLoadThread() const;
|
|
+
|
|
+ /// Set the thumbnail size to use
|
|
+ void setThumbnailSize(const ThumbnailSize& thumbSize);
|
|
+
|
|
+ /// If you want to fix a size for preloading, do it here.
|
|
+ void setPreloadThumbnailSize(const ThumbnailSize& thumbSize);
|
|
+
|
|
+ void setExifRotate(bool rotate);
|
|
+
|
|
+ /**
|
|
+ * Enable emitting dataChanged() when a thumbnail becomes available.
|
|
+ * The thumbnailAvailable() signal will be emitted in any case.
|
|
+ * Default is true.
|
|
+ */
|
|
+ void setEmitDataChanged(bool emitSignal);
|
|
+
|
|
+ /**
|
|
+ * Enable preloading of thumbnails:
|
|
+ * If preloading is enabled, for every entry in the model a thumbnail generation is started.
|
|
+ * Default: false.
|
|
+ */
|
|
+ void setPreloadThumbnails(bool preload);
|
|
+
|
|
+ ThumbnailSize thumbnailSize() const;
|
|
+
|
|
+ /**
|
|
+ * Handles the ThumbnailRole.
|
|
+ * If the pixmap is available, returns it in the QVariant.
|
|
+ * If it still needs to be loaded, returns a null QVariant and emits
|
|
+ * thumbnailAvailable() as soon as it is available.
|
|
+ */
|
|
+ virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
|
|
+
|
|
+ /**
|
|
+ * You can override the current thumbnail size by giving an integer value for ThumbnailRole.
|
|
+ * Set a null QVariant to use the thumbnail size set by setThumbnailSize() again.
|
|
+ * The index given here is ignored for this purpose.
|
|
+ */
|
|
+ virtual bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::DisplayRole);
|
|
+
|
|
+public Q_SLOTS:
|
|
+
|
|
+ /** Prepare the thumbnail loading for the given indexes
|
|
+ */
|
|
+ void prepareThumbnails(const QList<QModelIndex>& indexesToPrepare);
|
|
+ void prepareThumbnails(const QList<QModelIndex>& indexesToPrepare, const ThumbnailSize& thumbSize);
|
|
+
|
|
+ /**
|
|
+ * Preload thumbnail for the given infos resp. indexes.
|
|
+ * Note: Use setPreloadThumbnails to automatically preload all entries in the model.
|
|
+ * Note: This only ensures thumbnail generation. It is not guaranteed that pixmaps
|
|
+ * are stored in the cache. For thumbnails that are expect to be drawn immediately,
|
|
+ * include them in prepareThumbnails().
|
|
+ * Note: Stops preloading of previously added thumbnails.
|
|
+ */
|
|
+ void preloadThumbnails(const QList<ImageInfo>&);
|
|
+ void preloadThumbnails(const QList<QModelIndex>&);
|
|
+ void preloadAllThumbnails();
|
|
+
|
|
+Q_SIGNALS:
|
|
+
|
|
+ void thumbnailAvailable(const QModelIndex& index, int requestedSize);
|
|
+ void thumbnailFailed(const QModelIndex& index, int requestedSize);
|
|
+
|
|
+protected:
|
|
+
|
|
+ virtual void imageInfosCleared();
|
|
+
|
|
+protected Q_SLOTS:
|
|
+
|
|
+ void slotThumbnailLoaded(const LoadingDescription& loadingDescription, const QPixmap& thumb);
|
|
+
|
|
+private:
|
|
+
|
|
+ class ImageThumbnailModelPriv;
|
|
+ ImageThumbnailModelPriv* const d;
|
|
+};
|
|
+
|
|
+} // namespace Digikam
|
|
+
|
|
+#endif /* IMAGETHUMBNAILMODEL_H */
|
|
diff --git a/libs/database/models/imageversionsmodel.cpp b/libs/database/models/imageversionsmodel.cpp
|
|
new file mode 100644
|
|
index 0000000..e6ba582
|
|
--- /dev/null
|
|
+++ b/libs/database/models/imageversionsmodel.cpp
|
|
@@ -0,0 +1,183 @@
|
|
+/* ============================================================
|
|
+ *
|
|
+ * This file is a part of digiKam project
|
|
+ * http://www.digikam.org
|
|
+ *
|
|
+ * Date : 2010-07-13
|
|
+ * Description : Model for image versions
|
|
+ *
|
|
+ * Copyright (C) 2010 by Martin Klapetek <martin dot klapetek at gmail dot com>
|
|
+ *
|
|
+ * This program is free software; you can redistribute it
|
|
+ * and/or modify it under the terms of the GNU General
|
|
+ * Public License as published by the Free Software Foundation;
|
|
+ * either version 2, or (at your option)
|
|
+ * any later version.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ *
|
|
+ * ============================================================ */
|
|
+
|
|
+#include "imageversionsmodel.h"
|
|
+
|
|
+// KDE includes
|
|
+
|
|
+#include <klocalizedstring.h>
|
|
+
|
|
+// Local includes
|
|
+
|
|
+#include "digikam_debug.h"
|
|
+#include "workingwidget.h"
|
|
+
|
|
+namespace Digikam
|
|
+{
|
|
+
|
|
+class ImageVersionsModel::Private
|
|
+{
|
|
+public:
|
|
+
|
|
+ Private()
|
|
+ {
|
|
+ data = 0;
|
|
+ paintTree = false;
|
|
+ }
|
|
+
|
|
+ ///Complete paths with filenames and tree level
|
|
+ QList<QPair<QString, int> >* data;
|
|
+ ///This is for delegate to paint it as selected
|
|
+ QString currentSelectedImage;
|
|
+ ///If true, the delegate will paint items as a tree
|
|
+ ///if false, it will be painted as a list
|
|
+ bool paintTree;
|
|
+};
|
|
+
|
|
+ImageVersionsModel::ImageVersionsModel(QObject* parent)
|
|
+ : QAbstractListModel(parent),
|
|
+ d(new Private)
|
|
+{
|
|
+ d->data = new QList<QPair<QString, int> >;
|
|
+}
|
|
+
|
|
+ImageVersionsModel::~ImageVersionsModel()
|
|
+{
|
|
+ //qDeleteAll(d->data);
|
|
+ delete d;
|
|
+}
|
|
+
|
|
+Qt::ItemFlags ImageVersionsModel::flags(const QModelIndex& index) const
|
|
+{
|
|
+ if (!index.isValid())
|
|
+ {
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
|
|
+}
|
|
+
|
|
+QVariant ImageVersionsModel::data(const QModelIndex& index, int role) const
|
|
+{
|
|
+ if (!index.isValid())
|
|
+ {
|
|
+ return QVariant();
|
|
+ }
|
|
+
|
|
+ if (role == Qt::DisplayRole && !d->data->isEmpty())
|
|
+ {
|
|
+ return d->data->at(index.row()).first;
|
|
+ }
|
|
+ else if (role == Qt::UserRole && !d->data->isEmpty())
|
|
+ {
|
|
+ return d->data->at(index.row()).second;
|
|
+ }
|
|
+ else if (role == Qt::DisplayRole && d->data->isEmpty())
|
|
+ {
|
|
+ //TODO: make this text Italic
|
|
+ return QVariant(QString(i18n("No image selected")));
|
|
+ }
|
|
+
|
|
+ return QVariant();
|
|
+}
|
|
+
|
|
+int ImageVersionsModel::rowCount(const QModelIndex& parent) const
|
|
+{
|
|
+ Q_UNUSED(parent)
|
|
+ return d->data->count();
|
|
+}
|
|
+
|
|
+void ImageVersionsModel::setupModelData(QList<QPair<QString, int> >& data)
|
|
+{
|
|
+ beginResetModel();
|
|
+
|
|
+ d->data->clear();
|
|
+
|
|
+ if (!data.isEmpty())
|
|
+ {
|
|
+ d->data->append(data);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ d->data->append(qMakePair(QString(i18n("This is the original image")), 0));
|
|
+ }
|
|
+
|
|
+ endResetModel();
|
|
+}
|
|
+
|
|
+void ImageVersionsModel::clearModelData()
|
|
+{
|
|
+ beginResetModel();
|
|
+
|
|
+ if (!d->data->isEmpty())
|
|
+ {
|
|
+ d->data->clear();
|
|
+ }
|
|
+
|
|
+ endResetModel();
|
|
+}
|
|
+
|
|
+void ImageVersionsModel::slotAnimationStep()
|
|
+{
|
|
+ emit dataChanged(createIndex(0, 0), createIndex(rowCount()-1, 1));
|
|
+}
|
|
+
|
|
+QString ImageVersionsModel::currentSelectedImage() const
|
|
+{
|
|
+ return d->currentSelectedImage;
|
|
+}
|
|
+
|
|
+void ImageVersionsModel::setCurrentSelectedImage(const QString& path)
|
|
+{
|
|
+ d->currentSelectedImage = path;
|
|
+}
|
|
+
|
|
+QModelIndex ImageVersionsModel::currentSelectedImageIndex() const
|
|
+{
|
|
+ return index(listIndexOf(d->currentSelectedImage), 0);
|
|
+}
|
|
+
|
|
+bool ImageVersionsModel::paintTree() const
|
|
+{
|
|
+ return d->paintTree;
|
|
+}
|
|
+
|
|
+void ImageVersionsModel::setPaintTree(bool paint)
|
|
+{
|
|
+ d->paintTree = paint;
|
|
+}
|
|
+
|
|
+int ImageVersionsModel::listIndexOf(const QString& item) const
|
|
+{
|
|
+ for (int i = 0; i < d->data->size(); ++i)
|
|
+ {
|
|
+ if (d->data->at(i).first == item)
|
|
+ {
|
|
+ return i;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return -1;
|
|
+}
|
|
+
|
|
+} // namespace Digikam
|
|
diff --git a/libs/database/models/imageversionsmodel.h b/libs/database/models/imageversionsmodel.h
|
|
new file mode 100644
|
|
index 0000000..ed08529
|
|
--- /dev/null
|
|
+++ b/libs/database/models/imageversionsmodel.h
|
|
@@ -0,0 +1,75 @@
|
|
+/* ============================================================
|
|
+ *
|
|
+ * This file is a part of digiKam project
|
|
+ * http://www.digikam.org
|
|
+ *
|
|
+ * Date : 2010-07-13
|
|
+ * Description : Model for image versions
|
|
+ *
|
|
+ * Copyright (C) 2010 by Martin Klapetek <martin dot klapetek at gmail dot com>
|
|
+ *
|
|
+ * This program is free software; you can redistribute it
|
|
+ * and/or modify it under the terms of the GNU General
|
|
+ * Public License as published by the Free Software Foundation;
|
|
+ * either version 2, or (at your option)
|
|
+ * any later version.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ *
|
|
+ * ============================================================ */
|
|
+
|
|
+#ifndef IMAGEVERSIONSMODEL_H
|
|
+#define IMAGEVERSIONSMODEL_H
|
|
+
|
|
+// Qt includes
|
|
+
|
|
+#include <QModelIndex>
|
|
+#include <QPixmap>
|
|
+
|
|
+// Local includes
|
|
+
|
|
+#include "digikam_export.h"
|
|
+
|
|
+namespace Digikam
|
|
+{
|
|
+
|
|
+class DIGIKAM_DATABASE_EXPORT ImageVersionsModel : public QAbstractListModel
|
|
+{
|
|
+ Q_OBJECT
|
|
+
|
|
+public:
|
|
+
|
|
+ explicit ImageVersionsModel(QObject* parent = 0);
|
|
+ ~ImageVersionsModel();
|
|
+
|
|
+ Qt::ItemFlags flags(const QModelIndex& index) const;
|
|
+ QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
|
|
+ int rowCount(const QModelIndex& parent = QModelIndex()) const;
|
|
+
|
|
+ void setupModelData(QList<QPair<QString, int> >& data);
|
|
+ void clearModelData();
|
|
+
|
|
+ QString currentSelectedImage() const;
|
|
+ void setCurrentSelectedImage(const QString& path);
|
|
+ QModelIndex currentSelectedImageIndex() const;
|
|
+
|
|
+ bool paintTree() const;
|
|
+ int listIndexOf(const QString& item) const;
|
|
+
|
|
+public Q_SLOTS:
|
|
+
|
|
+ void slotAnimationStep();
|
|
+ void setPaintTree(bool paint);
|
|
+
|
|
+private:
|
|
+
|
|
+ class Private;
|
|
+ Private* const d;
|
|
+};
|
|
+
|
|
+} // namespace Digikam
|
|
+
|
|
+#endif // IMAGEVERSIONSMODEL_H
|
|
diff --git a/libs/models/CMakeLists.txt b/libs/models/CMakeLists.txt
|
|
index cbabfaa..804456b 100644
|
|
--- a/libs/models/CMakeLists.txt
|
|
+++ b/libs/models/CMakeLists.txt
|
|
@@ -9,18 +9,6 @@ if (POLICY CMP0063)
|
|
cmake_policy(SET CMP0063 NEW)
|
|
endif (POLICY CMP0063)
|
|
|
|
-set(libdatabasemodels_SRCS
|
|
- imagemodel.cpp
|
|
- imagefiltermodel.cpp
|
|
- imagefiltermodelpriv.cpp
|
|
- imagefiltermodelthreads.cpp
|
|
- imagefiltersettings.cpp
|
|
- imagelistmodel.cpp
|
|
- imagesortsettings.cpp
|
|
- imagethumbnailmodel.cpp
|
|
- imageversionsmodel.cpp
|
|
-)
|
|
-
|
|
set(libalbummodels_SRCS
|
|
imagealbummodel.cpp
|
|
imagealbumfiltermodel.cpp
|
|
@@ -52,5 +40,4 @@ endif()
|
|
#for digikam core lib
|
|
add_library(digikamgenericmodels_src OBJECT ${libgenericmodels_SRCS})
|
|
|
|
-add_library(digikamdatabasemodels_src OBJECT ${libdatabasemodels_SRCS})
|
|
-add_library(digikammodels_src OBJECT ${libalbummodels_SRCS} ${libgenericmodels_SRCS})
|
|
+add_library(digikammodels_src OBJECT ${libalbummodels_SRCS} ${libgenericmodels_SRCS})
|
|
diff --git a/libs/models/imagefiltermodel.cpp b/libs/models/imagefiltermodel.cpp
|
|
deleted file mode 100644
|
|
index 3d57e05..0000000
|
|
--- a/libs/models/imagefiltermodel.cpp
|
|
+++ /dev/null
|
|
@@ -1,1116 +0,0 @@
|
|
-/* ============================================================
|
|
- *
|
|
- * This file is a part of digiKam project
|
|
- * http://www.digikam.org
|
|
- *
|
|
- * Date : 2009-03-05
|
|
- * Description : Qt item model for database entries
|
|
- *
|
|
- * Copyright (C) 2009-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
|
|
- * Copyright (C) 2011-2017 by Gilles Caulier <caulier dot gilles at gmail dot com>
|
|
- * Copyright (C) 2010 by Andi Clemens <andi dot clemens at gmail dot com>
|
|
- * Copyright (C) 2011 by Michael G. Hansen <mike at mghansen dot de>
|
|
- * Copyright (C) 2014 by Mohamed Anwer <m dot anwer at gmx dot com>
|
|
- *
|
|
- * This program is free software; you can redistribute it
|
|
- * and/or modify it under the terms of the GNU General
|
|
- * Public License as published by the Free Software Foundation;
|
|
- * either version 2, or (at your option)
|
|
- * any later version.
|
|
- *
|
|
- * This program is distributed in the hope that it will be useful,
|
|
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
- * GNU General Public License for more details.
|
|
- *
|
|
- * ============================================================ */
|
|
-
|
|
-#include "imagefiltermodel.h"
|
|
-#include "imagefiltermodelpriv.h"
|
|
-#include "imagefiltermodelthreads.h"
|
|
-
|
|
-// Local includes
|
|
-
|
|
-#include "digikam_debug.h"
|
|
-#include "coredbaccess.h"
|
|
-#include "coredbchangesets.h"
|
|
-#include "coredbwatch.h"
|
|
-#include "imageinfolist.h"
|
|
-#include "imagemodel.h"
|
|
-
|
|
-namespace Digikam
|
|
-{
|
|
-
|
|
-ImageSortFilterModel::ImageSortFilterModel(QObject* parent)
|
|
- : DCategorizedSortFilterProxyModel(parent), m_chainedModel(0)
|
|
-{
|
|
-}
|
|
-
|
|
-void ImageSortFilterModel::setSourceImageModel(ImageModel* source)
|
|
-{
|
|
- if (m_chainedModel)
|
|
- {
|
|
- m_chainedModel->setSourceImageModel(source);
|
|
- }
|
|
- else
|
|
- {
|
|
- setDirectSourceImageModel(source);
|
|
- }
|
|
-}
|
|
-
|
|
-void ImageSortFilterModel::setSourceFilterModel(ImageSortFilterModel* source)
|
|
-{
|
|
- if (source)
|
|
- {
|
|
- ImageModel* const model = sourceImageModel();
|
|
-
|
|
- if (model)
|
|
- {
|
|
- source->setSourceImageModel(model);
|
|
- }
|
|
- }
|
|
-
|
|
- m_chainedModel = source;
|
|
- setSourceModel(source);
|
|
-}
|
|
-
|
|
-void ImageSortFilterModel::setDirectSourceImageModel(ImageModel* model)
|
|
-{
|
|
- setSourceModel(model);
|
|
-}
|
|
-
|
|
-void ImageSortFilterModel::setSourceModel(QAbstractItemModel* model)
|
|
-{
|
|
- // made it protected, only setSourceImageModel is public
|
|
- DCategorizedSortFilterProxyModel::setSourceModel(model);
|
|
-}
|
|
-
|
|
-ImageModel* ImageSortFilterModel::sourceImageModel() const
|
|
-{
|
|
- if (m_chainedModel)
|
|
- {
|
|
- return m_chainedModel->sourceImageModel();
|
|
- }
|
|
-
|
|
- return static_cast<ImageModel*>(sourceModel());
|
|
-}
|
|
-
|
|
-ImageSortFilterModel* ImageSortFilterModel::sourceFilterModel() const
|
|
-{
|
|
- return m_chainedModel;
|
|
-}
|
|
-
|
|
-ImageFilterModel* ImageSortFilterModel::imageFilterModel() const
|
|
-{
|
|
- // reimplemented in ImageFilterModel
|
|
- if (m_chainedModel)
|
|
- {
|
|
- return m_chainedModel->imageFilterModel();
|
|
- }
|
|
-
|
|
- return 0;
|
|
-}
|
|
-
|
|
-QModelIndex ImageSortFilterModel::mapToSourceImageModel(const QModelIndex& index) const
|
|
-{
|
|
- if (m_chainedModel)
|
|
- {
|
|
- return m_chainedModel->mapToSourceImageModel(mapToSource(index));
|
|
- }
|
|
-
|
|
- return mapToSource(index);
|
|
-}
|
|
-
|
|
-QModelIndex ImageSortFilterModel::mapFromSourceImageModel(const QModelIndex& albummodel_index) const
|
|
-{
|
|
- if (m_chainedModel)
|
|
- {
|
|
- return mapFromSource(m_chainedModel->mapFromSourceImageModel(albummodel_index));
|
|
- }
|
|
-
|
|
- return mapFromSource(albummodel_index);
|
|
-}
|
|
-
|
|
-
|
|
-QModelIndex ImageSortFilterModel::mapFromDirectSourceToSourceImageModel(const QModelIndex& sourceModel_index) const
|
|
-{
|
|
- if (m_chainedModel)
|
|
- {
|
|
- return m_chainedModel->mapToSourceImageModel(sourceModel_index);
|
|
- }
|
|
- return sourceModel_index;
|
|
-}
|
|
-
|
|
-// -------------- Convenience mappers -------------------------------------------------------------------
|
|
-
|
|
-QList<QModelIndex> ImageSortFilterModel::mapListToSource(const QList<QModelIndex>& indexes) const
|
|
-{
|
|
- QList<QModelIndex> sourceIndexes;
|
|
- foreach(const QModelIndex& index, indexes)
|
|
- {
|
|
- sourceIndexes << mapToSourceImageModel(index);
|
|
- }
|
|
- return sourceIndexes;
|
|
-}
|
|
-
|
|
-QList<QModelIndex> ImageSortFilterModel::mapListFromSource(const QList<QModelIndex>& sourceIndexes) const
|
|
-{
|
|
- QList<QModelIndex> indexes;
|
|
- foreach(const QModelIndex& index, sourceIndexes)
|
|
- {
|
|
- indexes << mapFromSourceImageModel(index);
|
|
- }
|
|
- return indexes;
|
|
-}
|
|
-
|
|
-ImageInfo ImageSortFilterModel::imageInfo(const QModelIndex& index) const
|
|
-{
|
|
- return sourceImageModel()->imageInfo(mapToSourceImageModel(index));
|
|
-}
|
|
-
|
|
-qlonglong ImageSortFilterModel::imageId(const QModelIndex& index) const
|
|
-{
|
|
- return sourceImageModel()->imageId(mapToSourceImageModel(index));
|
|
-}
|
|
-
|
|
-QList<ImageInfo> ImageSortFilterModel::imageInfos(const QList<QModelIndex>& indexes) const
|
|
-{
|
|
- QList<ImageInfo> infos;
|
|
- ImageModel* const model = sourceImageModel();
|
|
-
|
|
- foreach(const QModelIndex& index, indexes)
|
|
- {
|
|
- infos << model->imageInfo(mapToSourceImageModel(index));
|
|
- }
|
|
-
|
|
- return infos;
|
|
-}
|
|
-
|
|
-QList<qlonglong> ImageSortFilterModel::imageIds(const QList<QModelIndex>& indexes) const
|
|
-{
|
|
- QList<qlonglong> ids;
|
|
- ImageModel* const model = sourceImageModel();
|
|
-
|
|
- foreach(const QModelIndex& index, indexes)
|
|
- {
|
|
- ids << model->imageId(mapToSourceImageModel(index));
|
|
- }
|
|
-
|
|
- return ids;
|
|
-}
|
|
-
|
|
-QModelIndex ImageSortFilterModel::indexForPath(const QString& filePath) const
|
|
-{
|
|
- return mapFromSourceImageModel(sourceImageModel()->indexForPath(filePath));
|
|
-}
|
|
-
|
|
-QModelIndex ImageSortFilterModel::indexForImageInfo(const ImageInfo& info) const
|
|
-{
|
|
- return mapFromSourceImageModel(sourceImageModel()->indexForImageInfo(info));
|
|
-}
|
|
-
|
|
-QModelIndex ImageSortFilterModel::indexForImageId(qlonglong id) const
|
|
-{
|
|
- return mapFromSourceImageModel(sourceImageModel()->indexForImageId(id));
|
|
-}
|
|
-
|
|
-QList<ImageInfo> ImageSortFilterModel::imageInfosSorted() const
|
|
-{
|
|
- QList<ImageInfo> infos;
|
|
- const int size = rowCount();
|
|
- ImageModel* const model = sourceImageModel();
|
|
-
|
|
- for (int i=0; i<size; ++i)
|
|
- {
|
|
- infos << model->imageInfo(mapToSourceImageModel(index(i, 0)));
|
|
- }
|
|
-
|
|
- return infos;
|
|
-}
|
|
-
|
|
-// --------------------------------------------------------------------------------------------
|
|
-
|
|
-ImageFilterModel::ImageFilterModel(QObject* parent)
|
|
- : ImageSortFilterModel(parent),
|
|
- d_ptr(new ImageFilterModelPrivate)
|
|
-{
|
|
- d_ptr->init(this);
|
|
-}
|
|
-
|
|
-ImageFilterModel::ImageFilterModel(ImageFilterModelPrivate& dd, QObject* parent)
|
|
- : ImageSortFilterModel(parent),
|
|
- d_ptr(&dd)
|
|
-{
|
|
- d_ptr->init(this);
|
|
-}
|
|
-
|
|
-ImageFilterModel::~ImageFilterModel()
|
|
-{
|
|
- Q_D(ImageFilterModel);
|
|
- delete d;
|
|
-}
|
|
-
|
|
-void ImageFilterModel::setDirectSourceImageModel(ImageModel* sourceModel)
|
|
-{
|
|
- Q_D(ImageFilterModel);
|
|
-
|
|
- if (d->imageModel)
|
|
- {
|
|
- d->imageModel->unsetPreprocessor(d);
|
|
- disconnect(d->imageModel, SIGNAL(modelReset()),
|
|
- this, SLOT(slotModelReset()));
|
|
- slotModelReset();
|
|
- }
|
|
-
|
|
- d->imageModel = sourceModel;
|
|
-
|
|
- if (d->imageModel)
|
|
- {
|
|
- d->imageModel->setPreprocessor(d);
|
|
-
|
|
- connect(d->imageModel, SIGNAL(preprocess(QList<ImageInfo>,QList<QVariant>)),
|
|
- d, SLOT(preprocessInfos(QList<ImageInfo>,QList<QVariant>)));
|
|
-
|
|
- connect(d->imageModel, SIGNAL(processAdded(QList<ImageInfo>,QList<QVariant>)),
|
|
- d, SLOT(processAddedInfos(QList<ImageInfo>,QList<QVariant>)));
|
|
-
|
|
- connect(d, SIGNAL(reAddImageInfos(QList<ImageInfo>,QList<QVariant>)),
|
|
- d->imageModel, SLOT(reAddImageInfos(QList<ImageInfo>,QList<QVariant>)));
|
|
-
|
|
- connect(d, SIGNAL(reAddingFinished()),
|
|
- d->imageModel, SLOT(reAddingFinished()));
|
|
-
|
|
- connect(d->imageModel, SIGNAL(modelReset()),
|
|
- this, SLOT(slotModelReset()));
|
|
-
|
|
- connect(d->imageModel, SIGNAL(imageChange(ImageChangeset,QItemSelection)),
|
|
- this, SLOT(slotImageChange(ImageChangeset)));
|
|
-
|
|
- connect(d->imageModel, SIGNAL(imageTagChange(ImageTagChangeset,QItemSelection)),
|
|
- this, SLOT(slotImageTagChange(ImageTagChangeset)));
|
|
- }
|
|
-
|
|
- setSourceModel(d->imageModel);
|
|
-}
|
|
-
|
|
-QVariant ImageFilterModel::data(const QModelIndex& index, int role) const
|
|
-{
|
|
- Q_D(const ImageFilterModel);
|
|
-
|
|
- if (!index.isValid())
|
|
- {
|
|
- return QVariant();
|
|
- }
|
|
-
|
|
- switch (role)
|
|
- {
|
|
- // Attention: This breaks should there ever be another filter model between this and the ImageModel
|
|
-
|
|
- case DCategorizedSortFilterProxyModel::CategoryDisplayRole:
|
|
- return categoryIdentifier(d->imageModel->imageInfoRef(mapToSource(index)));
|
|
- case CategorizationModeRole:
|
|
- return d->sorter.categorizationMode;
|
|
- case SortOrderRole:
|
|
- return d->sorter.sortRole;
|
|
- //case CategoryCountRole:
|
|
- // return categoryCount(d->imageModel->imageInfoRef(mapToSource(index)));
|
|
- case CategoryAlbumIdRole:
|
|
- return d->imageModel->imageInfoRef(mapToSource(index)).albumId();
|
|
- case CategoryFormatRole:
|
|
- return d->imageModel->imageInfoRef(mapToSource(index)).format();
|
|
- case GroupIsOpenRole:
|
|
- return d->groupFilter.isAllOpen() ||
|
|
- d->groupFilter.isOpen(d->imageModel->imageInfoRef(mapToSource(index)).id());
|
|
- case ImageFilterModelPointerRole:
|
|
- return QVariant::fromValue(const_cast<ImageFilterModel*>(this));
|
|
- }
|
|
-
|
|
- return DCategorizedSortFilterProxyModel::data(index, role);
|
|
-}
|
|
-
|
|
-ImageFilterModel* ImageFilterModel::imageFilterModel() const
|
|
-{
|
|
- return const_cast<ImageFilterModel*>(this);
|
|
-}
|
|
-
|
|
-DatabaseFields::Set ImageFilterModel::suggestedWatchFlags() const
|
|
-{
|
|
- DatabaseFields::Set watchFlags;
|
|
- watchFlags |= DatabaseFields::Name | DatabaseFields::FileSize | DatabaseFields::ModificationDate;
|
|
- watchFlags |= DatabaseFields::Rating | DatabaseFields::CreationDate | DatabaseFields::Orientation |
|
|
- DatabaseFields::Width | DatabaseFields::Height;
|
|
- watchFlags |= DatabaseFields::Comment;
|
|
- watchFlags |= DatabaseFields::ImageRelations;
|
|
- return watchFlags;
|
|
-}
|
|
-
|
|
-// -------------- Filter settings --------------
|
|
-
|
|
-void ImageFilterModel::setDayFilter(const QList<QDateTime>& days)
|
|
-{
|
|
- Q_D(ImageFilterModel);
|
|
- d->filter.setDayFilter(days);
|
|
- setImageFilterSettings(d->filter);
|
|
-}
|
|
-
|
|
-void ImageFilterModel::setTagFilter(const QList<int>& includedTags, const QList<int>& excludedTags,
|
|
- ImageFilterSettings::MatchingCondition matchingCond,
|
|
- bool showUnTagged, const QList<int>& clTagIds, const QList<int>& plTagIds)
|
|
-{
|
|
- Q_D(ImageFilterModel);
|
|
- d->filter.setTagFilter(includedTags, excludedTags, matchingCond, showUnTagged, clTagIds, plTagIds);
|
|
- setImageFilterSettings(d->filter);
|
|
-}
|
|
-
|
|
-void ImageFilterModel::setRatingFilter(int rating, ImageFilterSettings::RatingCondition ratingCond, bool isUnratedExcluded)
|
|
-{
|
|
- Q_D(ImageFilterModel);
|
|
- d->filter.setRatingFilter(rating, ratingCond, isUnratedExcluded);
|
|
- setImageFilterSettings(d->filter);
|
|
-}
|
|
-
|
|
-void ImageFilterModel::setUrlWhitelist(const QList<QUrl> urlList, const QString& id)
|
|
-{
|
|
- Q_D(ImageFilterModel);
|
|
- d->filter.setUrlWhitelist(urlList, id);
|
|
- setImageFilterSettings(d->filter);
|
|
-}
|
|
-
|
|
-void ImageFilterModel::setIdWhitelist(const QList<qlonglong>& idList, const QString& id)
|
|
-{
|
|
- Q_D(ImageFilterModel);
|
|
- d->filter.setIdWhitelist(idList, id);
|
|
- setImageFilterSettings(d->filter);
|
|
-}
|
|
-
|
|
-void ImageFilterModel::setMimeTypeFilter(int mimeTypeFilter)
|
|
-{
|
|
- Q_D(ImageFilterModel);
|
|
- d->filter.setMimeTypeFilter(mimeTypeFilter);
|
|
- setImageFilterSettings(d->filter);
|
|
-}
|
|
-
|
|
-void ImageFilterModel::setGeolocationFilter(const ImageFilterSettings::GeolocationCondition& condition)
|
|
-{
|
|
- Q_D(ImageFilterModel);
|
|
- d->filter.setGeolocationFilter(condition);
|
|
- setImageFilterSettings(d->filter);
|
|
-}
|
|
-
|
|
-void ImageFilterModel::setTextFilter(const SearchTextFilterSettings& settings)
|
|
-{
|
|
- Q_D(ImageFilterModel);
|
|
- d->filter.setTextFilter(settings);
|
|
- setImageFilterSettings(d->filter);
|
|
-}
|
|
-
|
|
-void ImageFilterModel::setImageFilterSettings(const ImageFilterSettings& settings)
|
|
-{
|
|
- Q_D(ImageFilterModel);
|
|
-
|
|
- {
|
|
- QMutexLocker lock(&d->mutex);
|
|
- d->version++;
|
|
- d->filter = settings;
|
|
- d->filterCopy = settings;
|
|
- d->versionFilterCopy = d->versionFilter;
|
|
- d->groupFilterCopy = d->groupFilter;
|
|
-
|
|
- d->needPrepareComments = settings.isFilteringByText();
|
|
- d->needPrepareTags = settings.isFilteringByTags();
|
|
- d->needPrepareGroups = true;
|
|
- d->needPrepare = d->needPrepareComments || d->needPrepareTags || d->needPrepareGroups;
|
|
-
|
|
- d->hasOneMatch = false;
|
|
- d->hasOneMatchForText = false;
|
|
- }
|
|
-
|
|
- d->filterResults.clear();
|
|
-
|
|
- //d->categoryCountHashInt.clear();
|
|
- //d->categoryCountHashString.clear();
|
|
- if (d->imageModel)
|
|
- {
|
|
- d->infosToProcess(d->imageModel->imageInfos());
|
|
- }
|
|
-
|
|
- emit filterSettingsChanged(settings);
|
|
-}
|
|
-
|
|
-void ImageFilterModel::setVersionManagerSettings(const VersionManagerSettings& settings)
|
|
-{
|
|
- Q_D(ImageFilterModel);
|
|
- d->versionFilter.setVersionManagerSettings(settings);
|
|
- setVersionImageFilterSettings(d->versionFilter);
|
|
-}
|
|
-
|
|
-void ImageFilterModel::setExceptionList(const QList<qlonglong>& idList, const QString& id)
|
|
-{
|
|
- Q_D(ImageFilterModel);
|
|
- d->versionFilter.setExceptionList(idList, id);
|
|
- setVersionImageFilterSettings(d->versionFilter);
|
|
-}
|
|
-
|
|
-void ImageFilterModel::setVersionImageFilterSettings(const VersionImageFilterSettings& settings)
|
|
-{
|
|
- Q_D(ImageFilterModel);
|
|
- d->versionFilter = settings;
|
|
- slotUpdateFilter();
|
|
-}
|
|
-
|
|
-bool ImageFilterModel::isGroupOpen(qlonglong group) const
|
|
-{
|
|
- Q_D(const ImageFilterModel);
|
|
- return d->groupFilter.isOpen(group);
|
|
-}
|
|
-
|
|
-bool ImageFilterModel::isAllGroupsOpen() const
|
|
-{
|
|
- Q_D(const ImageFilterModel);
|
|
- return d->groupFilter.isAllOpen();
|
|
-}
|
|
-
|
|
-void ImageFilterModel::setGroupOpen(qlonglong group, bool open)
|
|
-{
|
|
- Q_D(ImageFilterModel);
|
|
- d->groupFilter.setOpen(group, open);
|
|
- setGroupImageFilterSettings(d->groupFilter);
|
|
-}
|
|
-
|
|
-void ImageFilterModel::toggleGroupOpen(qlonglong group)
|
|
-{
|
|
- setGroupOpen(group, !isGroupOpen(group));
|
|
-}
|
|
-
|
|
-void ImageFilterModel::setAllGroupsOpen(bool open)
|
|
-{
|
|
- Q_D(ImageFilterModel);
|
|
- d->groupFilter.setAllOpen(open);
|
|
- setGroupImageFilterSettings(d->groupFilter);
|
|
-}
|
|
-
|
|
-void ImageFilterModel::setGroupImageFilterSettings(const GroupImageFilterSettings& settings)
|
|
-{
|
|
- Q_D(ImageFilterModel);
|
|
- d->groupFilter = settings;
|
|
- slotUpdateFilter();
|
|
-}
|
|
-
|
|
-void ImageFilterModel::slotUpdateFilter()
|
|
-{
|
|
- Q_D(ImageFilterModel);
|
|
- setImageFilterSettings(d->filter);
|
|
-}
|
|
-
|
|
-ImageFilterSettings ImageFilterModel::imageFilterSettings() const
|
|
-{
|
|
- Q_D(const ImageFilterModel);
|
|
- return d->filter;
|
|
-}
|
|
-
|
|
-ImageSortSettings ImageFilterModel::imageSortSettings() const
|
|
-{
|
|
- Q_D(const ImageFilterModel);
|
|
- return d->sorter;
|
|
-}
|
|
-
|
|
-VersionImageFilterSettings ImageFilterModel::versionImageFilterSettings() const
|
|
-{
|
|
- Q_D(const ImageFilterModel);
|
|
- return d->versionFilter;
|
|
-}
|
|
-
|
|
-GroupImageFilterSettings ImageFilterModel::groupImageFilterSettings() const
|
|
-{
|
|
- Q_D(const ImageFilterModel);
|
|
- return d->groupFilter;
|
|
-}
|
|
-
|
|
-void ImageFilterModel::slotModelReset()
|
|
-{
|
|
- Q_D(ImageFilterModel);
|
|
- {
|
|
- QMutexLocker lock(&d->mutex);
|
|
- // discard all packages on the way that are marked as send out for re-add
|
|
- d->lastDiscardVersion = d->version;
|
|
- d->sentOutForReAdd = 0;
|
|
- // discard all packages on the way
|
|
- d->version++;
|
|
- d->sentOut = 0;
|
|
-
|
|
- d->hasOneMatch = false;
|
|
- d->hasOneMatchForText = false;
|
|
- }
|
|
- d->filterResults.clear();
|
|
-}
|
|
-
|
|
-bool ImageFilterModel::filterAcceptsRow(int source_row, const QModelIndex& source_parent) const
|
|
-{
|
|
- Q_D(const ImageFilterModel);
|
|
-
|
|
- if (source_parent.isValid())
|
|
- {
|
|
- return false;
|
|
- }
|
|
-
|
|
- qlonglong id = d->imageModel->imageId(source_row);
|
|
- QHash<qlonglong, bool>::const_iterator it = d->filterResults.constFind(id);
|
|
-
|
|
- if (it != d->filterResults.constEnd())
|
|
- {
|
|
- return it.value();
|
|
- }
|
|
-
|
|
- // usually done in thread and cache, unless source model changed
|
|
- ImageInfo info = d->imageModel->imageInfo(source_row);
|
|
- bool match = d->filter.matches(info);
|
|
- match = match ? d->versionFilter.matches(info) : false;
|
|
-
|
|
- return match ? d->groupFilter.matches(info) : false;
|
|
-}
|
|
-
|
|
-void ImageFilterModel::setSendImageInfoSignals(bool sendSignals)
|
|
-{
|
|
- if (sendSignals)
|
|
- {
|
|
- connect(this, SIGNAL(rowsInserted(QModelIndex,int,int)),
|
|
- this, SLOT(slotRowsInserted(QModelIndex,int,int)));
|
|
-
|
|
- connect(this, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
|
|
- this, SLOT(slotRowsAboutToBeRemoved(QModelIndex,int,int)));
|
|
- }
|
|
- else
|
|
- {
|
|
- disconnect(this, SIGNAL(rowsInserted(QModelIndex,int,int)),
|
|
- this, SLOT(slotRowsInserted(QModelIndex,int,int)));
|
|
-
|
|
- disconnect(this, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
|
|
- this, SLOT(slotRowsAboutToBeRemoved(QModelIndex,int,int)));
|
|
- }
|
|
-}
|
|
-
|
|
-void ImageFilterModel::slotRowsInserted(const QModelIndex& /*parent*/, int start, int end)
|
|
-{
|
|
- QList<ImageInfo> infos;
|
|
-
|
|
- for (int i=start; i<=end; ++i)
|
|
- {
|
|
- infos << imageInfo(index(i, 0));
|
|
- }
|
|
-
|
|
- emit imageInfosAdded(infos);
|
|
-}
|
|
-
|
|
-void ImageFilterModel::slotRowsAboutToBeRemoved(const QModelIndex& /*parent*/, int start, int end)
|
|
-{
|
|
- QList<ImageInfo> infos;
|
|
-
|
|
- for (int i=start; i<=end; ++i)
|
|
- {
|
|
- infos << imageInfo(index(i, 0));
|
|
- }
|
|
-
|
|
- emit imageInfosAboutToBeRemoved(infos);
|
|
-}
|
|
-
|
|
-// -------------- Threaded preparation & filtering --------------
|
|
-
|
|
-void ImageFilterModel::addPrepareHook(ImageFilterModelPrepareHook* hook)
|
|
-{
|
|
- Q_D(ImageFilterModel);
|
|
- QMutexLocker lock(&d->mutex);
|
|
- d->prepareHooks << hook;
|
|
-}
|
|
-
|
|
-void ImageFilterModel::removePrepareHook(ImageFilterModelPrepareHook* hook)
|
|
-{
|
|
- Q_D(ImageFilterModel);
|
|
- QMutexLocker lock(&d->mutex);
|
|
- d->prepareHooks.removeAll(hook);
|
|
-}
|
|
-
|
|
-void ImageFilterModelPreparer::process(ImageFilterModelTodoPackage package)
|
|
-{
|
|
- if (!checkVersion(package))
|
|
- {
|
|
- emit discarded(package);
|
|
- return;
|
|
- }
|
|
-
|
|
- // get thread-local copy
|
|
- bool needPrepareTags, needPrepareComments, needPrepareGroups;
|
|
- QList<ImageFilterModelPrepareHook*> prepareHooks;
|
|
- {
|
|
- QMutexLocker lock(&d->mutex);
|
|
- needPrepareTags = d->needPrepareTags;
|
|
- needPrepareComments = d->needPrepareComments;
|
|
- needPrepareGroups = d->needPrepareGroups;
|
|
- prepareHooks = d->prepareHooks;
|
|
- }
|
|
-
|
|
- //TODO: Make efficient!!
|
|
- if (needPrepareComments)
|
|
- {
|
|
- foreach(const ImageInfo& info, package.infos)
|
|
- {
|
|
- info.comment();
|
|
- }
|
|
- }
|
|
-
|
|
- if (!checkVersion(package))
|
|
- {
|
|
- emit discarded(package);
|
|
- return;
|
|
- }
|
|
-
|
|
- // The downside of QVector: At some point, we may need a QList for an API.
|
|
- // Nonetheless, QList and ImageInfo is fast. We could as well
|
|
- // reimplement ImageInfoList to ImageInfoVector (internally with templates?)
|
|
- ImageInfoList infoList;
|
|
-
|
|
- if (needPrepareTags || needPrepareGroups)
|
|
- {
|
|
- infoList = package.infos.toList();
|
|
- }
|
|
-
|
|
- if (needPrepareTags)
|
|
- {
|
|
- infoList.loadTagIds();
|
|
- }
|
|
-
|
|
- if (needPrepareGroups)
|
|
- {
|
|
- infoList.loadGroupImageIds();
|
|
- }
|
|
-
|
|
- foreach(ImageFilterModelPrepareHook* hook, prepareHooks)
|
|
- {
|
|
- hook->prepare(package.infos);
|
|
- }
|
|
-
|
|
- emit processed(package);
|
|
-}
|
|
-
|
|
-void ImageFilterModelFilterer::process(ImageFilterModelTodoPackage package)
|
|
-{
|
|
- if (!checkVersion(package))
|
|
- {
|
|
- emit discarded(package);
|
|
- return;
|
|
- }
|
|
-
|
|
- // get thread-local copy
|
|
- ImageFilterSettings localFilter;
|
|
- VersionImageFilterSettings localVersionFilter;
|
|
- GroupImageFilterSettings localGroupFilter;
|
|
- bool hasOneMatch;
|
|
- bool hasOneMatchForText;
|
|
- {
|
|
- QMutexLocker lock(&d->mutex);
|
|
- localFilter = d->filterCopy;
|
|
- localVersionFilter = d->versionFilterCopy;
|
|
- localGroupFilter = d->groupFilterCopy;
|
|
- hasOneMatch = d->hasOneMatch;
|
|
- hasOneMatchForText = d->hasOneMatchForText;
|
|
- }
|
|
-
|
|
- // Actual filtering. The variants to spare checking hasOneMatch over and over again.
|
|
- if (hasOneMatch && hasOneMatchForText)
|
|
- {
|
|
- foreach(const ImageInfo& info, package.infos)
|
|
- {
|
|
- package.filterResults[info.id()] = localFilter.matches(info) &&
|
|
- localVersionFilter.matches(info) &&
|
|
- localGroupFilter.matches(info);
|
|
- }
|
|
- }
|
|
- else if (hasOneMatch)
|
|
- {
|
|
- bool matchForText;
|
|
-
|
|
- foreach(const ImageInfo& info, package.infos)
|
|
- {
|
|
- package.filterResults[info.id()] = localFilter.matches(info, &matchForText) &&
|
|
- localVersionFilter.matches(info) &&
|
|
- localGroupFilter.matches(info);
|
|
-
|
|
- if (matchForText)
|
|
- {
|
|
- hasOneMatchForText = true;
|
|
- }
|
|
- }
|
|
- }
|
|
- else
|
|
- {
|
|
- bool result, matchForText;
|
|
-
|
|
- foreach(const ImageInfo& info, package.infos)
|
|
- {
|
|
- result = localFilter.matches(info, &matchForText) &&
|
|
- localVersionFilter.matches(info) &&
|
|
- localGroupFilter.matches(info);
|
|
- package.filterResults[info.id()] = result;
|
|
-
|
|
- if (result)
|
|
- {
|
|
- hasOneMatch = true;
|
|
- }
|
|
-
|
|
- if (matchForText)
|
|
- {
|
|
- hasOneMatchForText = true;
|
|
- }
|
|
- }
|
|
- }
|
|
-
|
|
- if (checkVersion(package))
|
|
- {
|
|
- QMutexLocker lock(&d->mutex);
|
|
- d->hasOneMatch = hasOneMatch;
|
|
- d->hasOneMatchForText = hasOneMatchForText;
|
|
- }
|
|
-
|
|
- emit processed(package);
|
|
-}
|
|
-
|
|
-// -------------- Sorting and Categorization -------------------------------------------------------
|
|
-
|
|
-void ImageFilterModel::setImageSortSettings(const ImageSortSettings& sorter)
|
|
-{
|
|
- Q_D(ImageFilterModel);
|
|
- d->sorter = sorter;
|
|
- setCategorizedModel(d->sorter.categorizationMode != ImageSortSettings::NoCategories);
|
|
- invalidate();
|
|
-}
|
|
-
|
|
-void ImageFilterModel::setCategorizationMode(ImageSortSettings::CategorizationMode mode)
|
|
-{
|
|
- Q_D(ImageFilterModel);
|
|
- d->sorter.setCategorizationMode(mode);
|
|
- setImageSortSettings(d->sorter);
|
|
-}
|
|
-
|
|
-void ImageFilterModel::setCategorizationSortOrder(ImageSortSettings::SortOrder order)
|
|
-{
|
|
- Q_D(ImageFilterModel);
|
|
- d->sorter.setCategorizationSortOrder(order);
|
|
- setImageSortSettings(d->sorter);
|
|
-}
|
|
-
|
|
-void ImageFilterModel::setSortRole(ImageSortSettings::SortRole role)
|
|
-{
|
|
- Q_D(ImageFilterModel);
|
|
- d->sorter.setSortRole(role);
|
|
- setImageSortSettings(d->sorter);
|
|
-}
|
|
-
|
|
-void ImageFilterModel::setSortOrder(ImageSortSettings::SortOrder order)
|
|
-{
|
|
- Q_D(ImageFilterModel);
|
|
- d->sorter.setSortOrder(order);
|
|
- setImageSortSettings(d->sorter);
|
|
-}
|
|
-
|
|
-void ImageFilterModel::setStringTypeNatural(bool natural)
|
|
-{
|
|
- Q_D(ImageFilterModel);
|
|
- d->sorter.setStringTypeNatural(natural);
|
|
- setImageSortSettings(d->sorter);
|
|
-}
|
|
-
|
|
-int ImageFilterModel::compareCategories(const QModelIndex& left, const QModelIndex& right) const
|
|
-{
|
|
- // source indexes
|
|
- Q_D(const ImageFilterModel);
|
|
-
|
|
- if (!d->sorter.isCategorized())
|
|
- {
|
|
- return 0;
|
|
- }
|
|
-
|
|
- if (!left.isValid() || !right.isValid())
|
|
- {
|
|
- return -1;
|
|
- }
|
|
-
|
|
- const ImageInfo& leftInfo = d->imageModel->imageInfoRef(left);
|
|
- const ImageInfo& rightInfo = d->imageModel->imageInfoRef(right);
|
|
-
|
|
- // Check grouping
|
|
- qlonglong leftGroupImageId = leftInfo.groupImageId();
|
|
- qlonglong rightGroupImageId = rightInfo.groupImageId();
|
|
-
|
|
- return compareInfosCategories(leftGroupImageId == -1 ? leftInfo : ImageInfo(leftGroupImageId),
|
|
- rightGroupImageId == -1 ? rightInfo : ImageInfo(rightGroupImageId));
|
|
-}
|
|
-
|
|
-bool ImageFilterModel::subSortLessThan(const QModelIndex& left, const QModelIndex& right) const
|
|
-{
|
|
- // source indexes
|
|
- Q_D(const ImageFilterModel);
|
|
-
|
|
- if (!left.isValid() || !right.isValid())
|
|
- {
|
|
- return true;
|
|
- }
|
|
-
|
|
- if (left == right)
|
|
- {
|
|
- return false;
|
|
- }
|
|
-
|
|
- const ImageInfo& leftInfo = d->imageModel->imageInfoRef(left);
|
|
- const ImageInfo& rightInfo = d->imageModel->imageInfoRef(right);
|
|
-
|
|
- if (leftInfo == rightInfo)
|
|
- {
|
|
- return d->sorter.lessThan(left.data(ImageModel::ExtraDataRole), right.data(ImageModel::ExtraDataRole));
|
|
- }
|
|
-
|
|
- // Check grouping
|
|
- qlonglong leftGroupImageId = leftInfo.groupImageId();
|
|
- qlonglong rightGroupImageId = rightInfo.groupImageId();
|
|
-
|
|
- // Either no grouping (-1), or same group image, or same image
|
|
- if (leftGroupImageId == rightGroupImageId)
|
|
- {
|
|
- return infosLessThan(leftInfo, rightInfo);
|
|
- }
|
|
-
|
|
- // We have grouping to handle
|
|
-
|
|
- // Is one grouped on the other? Sort behind leader.
|
|
- if (leftGroupImageId == rightInfo.id())
|
|
- {
|
|
- return false;
|
|
- }
|
|
- if (rightGroupImageId == leftInfo.id())
|
|
- {
|
|
- return true;
|
|
- }
|
|
-
|
|
- // Use the group leader for sorting
|
|
- return infosLessThan(leftGroupImageId == -1 ? leftInfo : ImageInfo(leftGroupImageId),
|
|
- rightGroupImageId == -1 ? rightInfo : ImageInfo(rightGroupImageId));
|
|
-}
|
|
-
|
|
-int ImageFilterModel::compareInfosCategories(const ImageInfo& left, const ImageInfo& right) const
|
|
-{
|
|
- // Note: reimplemented in ImageAlbumFilterModel
|
|
- Q_D(const ImageFilterModel);
|
|
- return d->sorter.compareCategories(left, right);
|
|
-}
|
|
-
|
|
-// Feel free to optimize. QString::number is 3x slower.
|
|
-static inline QString fastNumberToString(int id)
|
|
-{
|
|
- const int size = sizeof(int) * 2;
|
|
- char c[size+1];
|
|
- c[size] = '\0';
|
|
- char* p = c;
|
|
- int number = id;
|
|
-
|
|
- for (int i=0; i<size; ++i)
|
|
- {
|
|
- *p = 'a' + (number & 0xF);
|
|
- number >>= 4;
|
|
- ++p;
|
|
- }
|
|
-
|
|
- return QString::fromLatin1(c);
|
|
-}
|
|
-
|
|
-QString ImageFilterModel::categoryIdentifier(const ImageInfo& i) const
|
|
-{
|
|
- Q_D(const ImageFilterModel);
|
|
-
|
|
- if (!d->sorter.isCategorized())
|
|
- {
|
|
- return QString();
|
|
- }
|
|
-
|
|
- qlonglong groupedImageId = i.groupImageId();
|
|
- ImageInfo info = groupedImageId == -1 ? i : ImageInfo(groupedImageId);
|
|
-
|
|
- switch (d->sorter.categorizationMode)
|
|
- {
|
|
- case ImageSortSettings::NoCategories:
|
|
- return QString();
|
|
- case ImageSortSettings::OneCategory:
|
|
- return QString();
|
|
- case ImageSortSettings::CategoryByAlbum:
|
|
- return fastNumberToString(info.albumId());
|
|
- case ImageSortSettings::CategoryByFormat:
|
|
- return info.format();
|
|
- default:
|
|
- return QString();
|
|
- }
|
|
-}
|
|
-
|
|
-bool ImageFilterModel::infosLessThan(const ImageInfo& left, const ImageInfo& right) const
|
|
-{
|
|
- Q_D(const ImageFilterModel);
|
|
- return d->sorter.lessThan(left, right);
|
|
-}
|
|
-
|
|
-// -------------- Watching changes -----------------------------------------------------------------
|
|
-
|
|
-void ImageFilterModel::slotImageTagChange(const ImageTagChangeset& changeset)
|
|
-{
|
|
- Q_D(ImageFilterModel);
|
|
-
|
|
- if (!d->imageModel || d->imageModel->isEmpty())
|
|
- {
|
|
- return;
|
|
- }
|
|
-
|
|
- // already scheduled to re-filter?
|
|
- if (d->updateFilterTimer->isActive())
|
|
- {
|
|
- return;
|
|
- }
|
|
-
|
|
- // do we filter at all?
|
|
- if (!d->versionFilter.isFilteringByTags() &&
|
|
- !d->filter.isFilteringByTags() &&
|
|
- !d->filter.isFilteringByText())
|
|
- {
|
|
- return;
|
|
- }
|
|
-
|
|
- // is one of our images affected?
|
|
- foreach(const qlonglong& id, changeset.ids())
|
|
- {
|
|
- // if one matching image id is found, trigger a refresh
|
|
- if (d->imageModel->hasImage(id))
|
|
- {
|
|
- d->updateFilterTimer->start();
|
|
- return;
|
|
- }
|
|
- }
|
|
-}
|
|
-
|
|
-void ImageFilterModel::slotImageChange(const ImageChangeset& changeset)
|
|
-{
|
|
- Q_D(ImageFilterModel);
|
|
-
|
|
- if (!d->imageModel || d->imageModel->isEmpty())
|
|
- {
|
|
- return;
|
|
- }
|
|
-
|
|
- // already scheduled to re-filter?
|
|
- if (d->updateFilterTimer->isActive())
|
|
- {
|
|
- return;
|
|
- }
|
|
-
|
|
- // is one of the values affected that we filter or sort by?
|
|
- DatabaseFields::Set set = changeset.changes();
|
|
- bool sortAffected = (set & d->sorter.watchFlags());
|
|
- bool filterAffected = (set & d->filter.watchFlags()) || (set & d->groupFilter.watchFlags());
|
|
-
|
|
- if (!sortAffected && !filterAffected)
|
|
- {
|
|
- return;
|
|
- }
|
|
-
|
|
- // is one of our images affected?
|
|
- bool imageAffected = false;
|
|
-
|
|
- foreach(const qlonglong& id, changeset.ids())
|
|
- {
|
|
- // if one matching image id is found, trigger a refresh
|
|
- if (d->imageModel->hasImage(id))
|
|
- {
|
|
- imageAffected = true;
|
|
- break;
|
|
- }
|
|
- }
|
|
-
|
|
- if (!imageAffected)
|
|
- {
|
|
- return;
|
|
- }
|
|
-
|
|
- if (filterAffected)
|
|
- {
|
|
- d->updateFilterTimer->start();
|
|
- }
|
|
- else
|
|
- {
|
|
- invalidate(); // just resort, reuse filter results
|
|
- }
|
|
-}
|
|
-
|
|
-// -------------------------------------------------------------------------------------------------------
|
|
-
|
|
-NoDuplicatesImageFilterModel::NoDuplicatesImageFilterModel(QObject* parent)
|
|
- : ImageSortFilterModel(parent)
|
|
-{
|
|
-}
|
|
-
|
|
-bool NoDuplicatesImageFilterModel::filterAcceptsRow(int source_row, const QModelIndex& source_parent) const
|
|
-{
|
|
- QModelIndex index = sourceModel()->index(source_row, 0, source_parent);
|
|
-
|
|
- if (index.data(ImageModel::ExtraDataDuplicateCount).toInt() <= 1)
|
|
- {
|
|
- return true;
|
|
- }
|
|
-
|
|
- QModelIndex previousIndex = sourceModel()->index(source_row - 1, 0, source_parent);
|
|
-
|
|
- if (!previousIndex.isValid())
|
|
- {
|
|
- return true;
|
|
- }
|
|
-
|
|
- if (sourceImageModel()->imageId(mapFromDirectSourceToSourceImageModel(index)) == sourceImageModel()->imageId(mapFromDirectSourceToSourceImageModel(previousIndex)))
|
|
- {
|
|
- return false;
|
|
- }
|
|
- return true;
|
|
-}
|
|
-
|
|
-/*
|
|
-void NoDuplicatesImageFilterModel::setSourceModel(QAbstractItemModel* model)
|
|
-{
|
|
- if (sourceModel())
|
|
- {
|
|
- }
|
|
-
|
|
- ImageSortFilterModel::setSourceModel(model);
|
|
-
|
|
- if (sourceModel())
|
|
- {
|
|
- connect(sourceModel(), SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
|
|
- this, SLOT(slotRowsAboutToBeRemoved(QModelIndex,int,int)));
|
|
- }
|
|
-}
|
|
-
|
|
-void NoDuplicatesImageFilterModel::slotRowsAboutToBeRemoved(const QModelIndex& parent, int begin, int end)
|
|
-{
|
|
- bool needInvalidate = false;
|
|
-
|
|
- for (int i = begin; i<=end; ++i)
|
|
- {
|
|
- QModelIndex index = sourceModel()->index(i, 0, parent);
|
|
-
|
|
- // filtered out by us?
|
|
- if (!mapFromSource(index).isValid())
|
|
- {
|
|
- continue;
|
|
- }
|
|
-
|
|
- QModelIndex sourceIndex = mapFromDirectSourceToSourceImageModel(index);
|
|
- qlonglong id = sourceImageModel()->imageId(sourceIndex);
|
|
-
|
|
- if (sourceImageModel()->numberOfIndexesForImageId(id) > 1)
|
|
- {
|
|
- needInvalidate = true;
|
|
- }
|
|
- }
|
|
-}*/
|
|
-
|
|
-} // namespace Digikam
|
|
diff --git a/libs/models/imagefiltermodel.h b/libs/models/imagefiltermodel.h
|
|
deleted file mode 100644
|
|
index d131b3e..0000000
|
|
--- a/libs/models/imagefiltermodel.h
|
|
+++ /dev/null
|
|
@@ -1,299 +0,0 @@
|
|
-/* ============================================================
|
|
- *
|
|
- * This file is a part of digiKam project
|
|
- * http://www.digikam.org
|
|
- *
|
|
- * Date : 2009-03-05
|
|
- * Description : Qt item model for database entries
|
|
- *
|
|
- * Copyright (C) 2009-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
|
|
- * Copyright (C) 2011 by Gilles Caulier <caulier dot gilles at gmail dot com>
|
|
- * Copyright (C) 2010 by Andi Clemens <andi dot clemens at gmail dot com>
|
|
- * Copyright (C) 2011 by Michael G. Hansen <mike at mghansen dot de>
|
|
- * Copyright (C) 2014 by Mohamed Anwer <m dot anwer at gmx dot com>
|
|
- *
|
|
- * This program is free software; you can redistribute it
|
|
- * and/or modify it under the terms of the GNU General
|
|
- * Public License as published by the Free Software Foundation;
|
|
- * either version 2, or (at your option)
|
|
- * any later version.
|
|
- *
|
|
- * This program is distributed in the hope that it will be useful,
|
|
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
- * GNU General Public License for more details.
|
|
- *
|
|
- * ============================================================ */
|
|
-
|
|
-#ifndef IMAGEFILTERMODEL_H
|
|
-#define IMAGEFILTERMODEL_H
|
|
-
|
|
-// Local includes
|
|
-
|
|
-#include "dcategorizedsortfilterproxymodel.h"
|
|
-#include "textfilter.h"
|
|
-#include "imagefiltersettings.h"
|
|
-#include "imagemodel.h"
|
|
-#include "imagesortsettings.h"
|
|
-#include "digikam_export.h"
|
|
-
|
|
-namespace Digikam
|
|
-{
|
|
-
|
|
-class ImageChangeset;
|
|
-class ImageFilterModel;
|
|
-class ImageTagChangeset;
|
|
-
|
|
-class DIGIKAM_DATABASE_EXPORT ImageFilterModelPrepareHook
|
|
-{
|
|
-public:
|
|
-
|
|
- virtual ~ImageFilterModelPrepareHook() {};
|
|
- virtual void prepare(const QVector<ImageInfo>& infos) = 0;
|
|
-};
|
|
-
|
|
-// -----------------------------------------------------------------------------------------------
|
|
-
|
|
-class DIGIKAM_DATABASE_EXPORT ImageSortFilterModel : public DCategorizedSortFilterProxyModel
|
|
-{
|
|
- Q_OBJECT
|
|
-
|
|
-public:
|
|
-
|
|
- explicit ImageSortFilterModel(QObject* parent = 0);
|
|
-
|
|
- void setSourceImageModel(ImageModel* model);
|
|
- ImageModel* sourceImageModel() const;
|
|
-
|
|
- void setSourceFilterModel(ImageSortFilterModel* model);
|
|
- ImageSortFilterModel* sourceFilterModel() const;
|
|
-
|
|
- QModelIndex mapToSourceImageModel(const QModelIndex& index) const;
|
|
- QModelIndex mapFromSourceImageModel(const QModelIndex& imagemodel_index) const;
|
|
- QModelIndex mapFromDirectSourceToSourceImageModel(const QModelIndex& sourceModel_index) const;
|
|
-
|
|
- /// Convenience methods mapped to ImageModel.
|
|
- /// Mentioned indexes returned come from the source image model.
|
|
- QList<QModelIndex> mapListToSource(const QList<QModelIndex>& indexes) const;
|
|
- QList<QModelIndex> mapListFromSource(const QList<QModelIndex>& sourceIndexes) const;
|
|
-
|
|
- ImageInfo imageInfo(const QModelIndex& index) const;
|
|
- qlonglong imageId(const QModelIndex& index) const;
|
|
- QList<ImageInfo> imageInfos(const QList<QModelIndex>& indexes) const;
|
|
- QList<qlonglong> imageIds(const QList<QModelIndex>& indexes) const;
|
|
-
|
|
- QModelIndex indexForPath(const QString& filePath) const;
|
|
- QModelIndex indexForImageInfo(const ImageInfo& info) const;
|
|
- QModelIndex indexForImageId(qlonglong id) const;
|
|
-
|
|
- /** Returns a list of all image infos, sorted according to this model.
|
|
- * If you do not need a sorted list, use ImageModel's imageInfos() method.
|
|
- */
|
|
- QList<ImageInfo> imageInfosSorted() const;
|
|
-
|
|
- /// Returns this, any chained ImageFilterModel, or 0.
|
|
- virtual ImageFilterModel* imageFilterModel() const;
|
|
-
|
|
-protected:
|
|
-
|
|
- /// Reimplement if needed. Called only when model shall be set as (direct) sourceModel.
|
|
- virtual void setDirectSourceImageModel(ImageModel* model);
|
|
-
|
|
- // made protected
|
|
- virtual void setSourceModel(QAbstractItemModel* model);
|
|
-
|
|
-protected:
|
|
-
|
|
- ImageSortFilterModel* m_chainedModel;
|
|
-};
|
|
-
|
|
-// -----------------------------------------------------------------------------------------------
|
|
-
|
|
-class DIGIKAM_DATABASE_EXPORT ImageFilterModel : public ImageSortFilterModel
|
|
-{
|
|
- Q_OBJECT
|
|
-
|
|
-public:
|
|
-
|
|
- enum ImageFilterModelRoles
|
|
- {
|
|
- /// Returns the current categorization mode
|
|
- CategorizationModeRole = ImageModel::FilterModelRoles + 1,
|
|
- /// Returns the current sort order
|
|
- SortOrderRole = ImageModel::FilterModelRoles + 2,
|
|
- // / Returns the number of items in the index' category
|
|
- //CategoryCountRole = ImageModel::FilterModelRoles + 3,
|
|
- /// Returns the id of the PAlbum of the index which is used for category
|
|
- CategoryAlbumIdRole = ImageModel::FilterModelRoles + 3,
|
|
- /// Returns the format of the index which is used for category
|
|
- CategoryFormatRole = ImageModel::FilterModelRoles + 4,
|
|
- /// Returns true if the given image is a group leader, and the group is opened
|
|
- GroupIsOpenRole = ImageModel::FilterModelRoles + 5,
|
|
- ImageFilterModelPointerRole = ImageModel::FilterModelRoles + 50
|
|
- };
|
|
-
|
|
-public:
|
|
-
|
|
- explicit ImageFilterModel(QObject* parent = 0);
|
|
- ~ImageFilterModel();
|
|
-
|
|
- /** Add a hook to get added images for preparation tasks before they are added in the model */
|
|
- void addPrepareHook(ImageFilterModelPrepareHook* hook);
|
|
- void removePrepareHook(ImageFilterModelPrepareHook* hook);
|
|
-
|
|
- /** Returns a set of DatabaseFields suggested to set as watch flags on the source ImageModel.
|
|
- * The contained flags will be those that this model can sort or filter by. */
|
|
- DatabaseFields::Set suggestedWatchFlags() const;
|
|
-
|
|
- ImageFilterSettings imageFilterSettings() const;
|
|
- VersionImageFilterSettings versionImageFilterSettings() const;
|
|
- GroupImageFilterSettings groupImageFilterSettings() const;
|
|
- ImageSortSettings imageSortSettings() const;
|
|
-
|
|
- // group is identified by the id of its group leader
|
|
- bool isGroupOpen(qlonglong group) const;
|
|
- bool isAllGroupsOpen() const;
|
|
-
|
|
- /// Enables sending imageInfosAdded and imageInfosAboutToBeRemoved
|
|
- void setSendImageInfoSignals(bool sendSignals);
|
|
-
|
|
- virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
|
|
- virtual ImageFilterModel* imageFilterModel() const;
|
|
-
|
|
-public Q_SLOTS:
|
|
-
|
|
- /** Changes the current version image filter settings and refilters. */
|
|
- void setVersionImageFilterSettings(const VersionImageFilterSettings& settings);
|
|
-
|
|
- /** Changes the current version image filter settings and refilters. */
|
|
- void setGroupImageFilterSettings(const GroupImageFilterSettings& settings);
|
|
-
|
|
- /** Adjust the current ImageFilterSettings.
|
|
- * Equivalent to retrieving the current filter settings, adjusting the parameter
|
|
- * and calling setImageFilterSettings.
|
|
- * Provided for convenience.
|
|
- * It is encouraged to use setImageFilterSettings if you change more than one
|
|
- * parameter at a time.
|
|
- */
|
|
- void setDayFilter(const QList<QDateTime>& days);
|
|
- void setTagFilter(const QList<int>& includedTags, const QList<int>& excludedTags,
|
|
- ImageFilterSettings::MatchingCondition matchingCond, bool showUnTagged,
|
|
- const QList<int>& clTagIds, const QList<int>& plTagIds);
|
|
- void setRatingFilter(int rating, ImageFilterSettings::RatingCondition ratingCond, bool isUnratedExcluded);
|
|
- void setMimeTypeFilter(int mimeTypeFilter);
|
|
- void setGeolocationFilter(const ImageFilterSettings::GeolocationCondition& condition);
|
|
- void setTextFilter(const SearchTextFilterSettings& settings);
|
|
-
|
|
- void setCategorizationMode(ImageSortSettings::CategorizationMode mode);
|
|
- void setCategorizationSortOrder(ImageSortSettings::SortOrder order);
|
|
- void setSortRole(ImageSortSettings::SortRole role);
|
|
- void setSortOrder(ImageSortSettings::SortOrder order);
|
|
- void setStringTypeNatural(bool natural);
|
|
- void setUrlWhitelist(const QList<QUrl> urlList, const QString& id);
|
|
- void setIdWhitelist(const QList<qlonglong>& idList, const QString& id);
|
|
-
|
|
- void setVersionManagerSettings(const VersionManagerSettings& settings);
|
|
- void setExceptionList(const QList<qlonglong>& idlist, const QString& id);
|
|
-
|
|
- void setGroupOpen(qlonglong group, bool open);
|
|
- void toggleGroupOpen(qlonglong group);
|
|
- void setAllGroupsOpen(bool open);
|
|
-
|
|
- /** Changes the current image filter settings and refilters. */
|
|
- virtual void setImageFilterSettings(const ImageFilterSettings& settings);
|
|
-
|
|
- /** Changes the current image sort settings and resorts. */
|
|
- virtual void setImageSortSettings(const ImageSortSettings& settings);
|
|
-
|
|
-Q_SIGNALS:
|
|
-
|
|
- /// Signals that the set filter matches at least one index
|
|
- void filterMatches(bool matches);
|
|
-
|
|
- /** Signals that the set text filter matches at least one entry.
|
|
- If no text filter is set, this signal is emitted
|
|
- with 'false' when filterMatches() is emitted.
|
|
- */
|
|
- void filterMatchesForText(bool matchesByText);
|
|
-
|
|
- /** Emitted when the filter settings have been changed
|
|
- (the model may not yet have been updated)
|
|
- */
|
|
- void filterSettingsChanged(const ImageFilterSettings& settings);
|
|
-
|
|
- /** These signals need to be explicitly enabled with setSendImageInfoSignals()
|
|
- */
|
|
- void imageInfosAdded(const QList<ImageInfo>& infos);
|
|
- void imageInfosAboutToBeRemoved(const QList<ImageInfo>& infos);
|
|
-
|
|
-public:
|
|
-
|
|
- // Declared as public because of use in sub-classes.
|
|
- class ImageFilterModelPrivate;
|
|
-
|
|
-protected:
|
|
-
|
|
- ImageFilterModelPrivate* const d_ptr;
|
|
-
|
|
-protected:
|
|
-
|
|
- ImageFilterModel(ImageFilterModelPrivate& dd, QObject* parent);
|
|
-
|
|
- virtual void setDirectSourceImageModel(ImageModel* model);
|
|
-
|
|
- virtual bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const;
|
|
-
|
|
- virtual int compareCategories(const QModelIndex& left, const QModelIndex& right) const;
|
|
- virtual bool subSortLessThan(const QModelIndex& left, const QModelIndex& right) const;
|
|
- //virtual int categoryCount(const ImageInfo& info) const;
|
|
-
|
|
- /** Reimplement to customize category sorting,
|
|
- * Return negative if category of left < category right,
|
|
- * Return 0 if left and right are in the same category, else return positive.
|
|
- */
|
|
- virtual int compareInfosCategories(const ImageInfo& left, const ImageInfo& right) const;
|
|
-
|
|
- /** Reimplement to customize sorting. Do not take categories into account here.
|
|
- */
|
|
- virtual bool infosLessThan(const ImageInfo& left, const ImageInfo& right) const;
|
|
-
|
|
- /** Returns a unique identifier for the category if info. The string need not be for user display.
|
|
- */
|
|
- virtual QString categoryIdentifier(const ImageInfo& info) const;
|
|
-
|
|
-protected Q_SLOTS:
|
|
-
|
|
- void slotModelReset();
|
|
- void slotUpdateFilter();
|
|
-
|
|
- void slotImageTagChange(const ImageTagChangeset& changeset);
|
|
- void slotImageChange(const ImageChangeset& changeset);
|
|
-
|
|
- void slotRowsInserted(const QModelIndex& parent, int start, int end);
|
|
- void slotRowsAboutToBeRemoved(const QModelIndex& parent, int start, int end);
|
|
-
|
|
-private:
|
|
-
|
|
- Q_DECLARE_PRIVATE(ImageFilterModel)
|
|
-};
|
|
-
|
|
-// -----------------------------------------------------------------------------------------------------
|
|
-
|
|
-class DIGIKAM_DATABASE_EXPORT NoDuplicatesImageFilterModel : public ImageSortFilterModel
|
|
-{
|
|
- Q_OBJECT
|
|
-
|
|
-public:
|
|
-
|
|
- explicit NoDuplicatesImageFilterModel(QObject* parent = 0);
|
|
-
|
|
-protected:
|
|
-
|
|
- virtual bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const;
|
|
-};
|
|
-
|
|
-} // namespace Digikam
|
|
-
|
|
-Q_DECLARE_METATYPE(Digikam::ImageFilterModel*)
|
|
-
|
|
-#endif // IMAGEMODEL_H
|
|
diff --git a/libs/models/imagefiltermodelpriv.cpp b/libs/models/imagefiltermodelpriv.cpp
|
|
deleted file mode 100644
|
|
index 07d9e79..0000000
|
|
--- a/libs/models/imagefiltermodelpriv.cpp
|
|
+++ /dev/null
|
|
@@ -1,258 +0,0 @@
|
|
-/* ============================================================
|
|
- *
|
|
- * This file is a part of digiKam project
|
|
- * http://www.digikam.org
|
|
- *
|
|
- * Date : 2009-03-05
|
|
- * Description : Qt item model for database entries
|
|
- *
|
|
- * Copyright (C) 2009-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
|
|
- * Copyright (C) 2011-2017 by Gilles Caulier <caulier dot gilles at gmail dot com>
|
|
- * Copyright (C) 2010 by Andi Clemens <andi dot clemens at gmail dot com>
|
|
- * Copyright (C) 2011 by Michael G. Hansen <mike at mghansen dot de>
|
|
- * Copyright (C) 2014 by Mohamed Anwer <m dot anwer at gmx dot com>
|
|
- *
|
|
- * This program is free software; you can redistribute it
|
|
- * and/or modify it under the terms of the GNU General
|
|
- * Public License as published by the Free Software Foundation;
|
|
- * either version 2, or (at your option)
|
|
- * any later version.
|
|
- *
|
|
- * This program is distributed in the hope that it will be useful,
|
|
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
- * GNU General Public License for more details.
|
|
- *
|
|
- * ============================================================ */
|
|
-
|
|
-#include "imagefiltermodelpriv.h"
|
|
-
|
|
-// Local includes
|
|
-
|
|
-#include "digikam_debug.h"
|
|
-#include "imagefiltermodelthreads.h"
|
|
-
|
|
-namespace Digikam
|
|
-{
|
|
-
|
|
-ImageFilterModel::ImageFilterModelPrivate::ImageFilterModelPrivate()
|
|
-{
|
|
- imageModel = 0;
|
|
- version = 0;
|
|
- lastDiscardVersion = 0;
|
|
- sentOut = 0;
|
|
- sentOutForReAdd = 0;
|
|
- updateFilterTimer = 0;
|
|
- needPrepare = false;
|
|
- needPrepareComments = false;
|
|
- needPrepareTags = false;
|
|
- needPrepareGroups = false;
|
|
- preparer = 0;
|
|
- filterer = 0;
|
|
- hasOneMatch = false;
|
|
- hasOneMatchForText = false;
|
|
-
|
|
- setupWorkers();
|
|
-}
|
|
-
|
|
-ImageFilterModel::ImageFilterModelPrivate::~ImageFilterModelPrivate()
|
|
-{
|
|
- // facilitate thread stopping
|
|
- ++version;
|
|
- preparer->deactivate();
|
|
- filterer->deactivate();
|
|
- delete preparer;
|
|
- delete filterer;
|
|
-}
|
|
-
|
|
-void ImageFilterModel::ImageFilterModelPrivate::init(ImageFilterModel* _q)
|
|
-{
|
|
- q = _q;
|
|
-
|
|
- updateFilterTimer = new QTimer(this);
|
|
- updateFilterTimer->setSingleShot(true);
|
|
- updateFilterTimer->setInterval(250);
|
|
-
|
|
- connect(updateFilterTimer, SIGNAL(timeout()),
|
|
- q, SLOT(slotUpdateFilter()));
|
|
-
|
|
- // inter-thread redirection
|
|
- qRegisterMetaType<ImageFilterModelTodoPackage>("ImageFilterModelTodoPackage");
|
|
-}
|
|
-
|
|
-void ImageFilterModel::ImageFilterModelPrivate::preprocessInfos(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues)
|
|
-{
|
|
- infosToProcess(infos, extraValues, true);
|
|
-}
|
|
-
|
|
-void ImageFilterModel::ImageFilterModelPrivate::processAddedInfos(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues)
|
|
-{
|
|
- // These have already been added, we just process them afterwards
|
|
- infosToProcess(infos, extraValues, false);
|
|
-}
|
|
-
|
|
-void ImageFilterModel::ImageFilterModelPrivate::setupWorkers()
|
|
-{
|
|
- preparer = new ImageFilterModelPreparer(this);
|
|
- filterer = new ImageFilterModelFilterer(this);
|
|
-
|
|
- // A package in constructed in infosToProcess.
|
|
- // Normal flow is infosToProcess -> preparer::process -> filterer::process -> packageFinished.
|
|
- // If no preparation is needed, the first step is skipped.
|
|
- // If filter version changes, both will discard old package and send them to packageDiscarded.
|
|
-
|
|
- connect(this, SIGNAL(packageToPrepare(ImageFilterModelTodoPackage)),
|
|
- preparer, SLOT(process(ImageFilterModelTodoPackage)));
|
|
-
|
|
- connect(this, SIGNAL(packageToFilter(ImageFilterModelTodoPackage)),
|
|
- filterer, SLOT(process(ImageFilterModelTodoPackage)));
|
|
-
|
|
- connect(preparer, SIGNAL(processed(ImageFilterModelTodoPackage)),
|
|
- filterer, SLOT(process(ImageFilterModelTodoPackage)));
|
|
-
|
|
- connect(filterer, SIGNAL(processed(ImageFilterModelTodoPackage)),
|
|
- this, SLOT(packageFinished(ImageFilterModelTodoPackage)));
|
|
-
|
|
- connect(preparer, SIGNAL(discarded(ImageFilterModelTodoPackage)),
|
|
- this, SLOT(packageDiscarded(ImageFilterModelTodoPackage)));
|
|
-
|
|
- connect(filterer, SIGNAL(discarded(ImageFilterModelTodoPackage)),
|
|
- this, SLOT(packageDiscarded(ImageFilterModelTodoPackage)));
|
|
-}
|
|
-
|
|
-void ImageFilterModel::ImageFilterModelPrivate::infosToProcess(const QList<ImageInfo>& infos)
|
|
-{
|
|
- infosToProcess(infos, QList<QVariant>(), false);
|
|
-}
|
|
-
|
|
-void ImageFilterModel::ImageFilterModelPrivate::infosToProcess(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues, bool forReAdd)
|
|
-{
|
|
- if (infos.isEmpty())
|
|
- {
|
|
- return;
|
|
- }
|
|
-
|
|
- filterer->schedule();
|
|
-
|
|
- if (needPrepare)
|
|
- {
|
|
- preparer->schedule();
|
|
- }
|
|
-
|
|
- Q_ASSERT(extraValues.isEmpty() || infos.size() == extraValues.size());
|
|
-
|
|
- // prepare and filter in chunks
|
|
- const int size = infos.size();
|
|
- const int maxChunkSize = needPrepare ? PrepareChunkSize : FilterChunkSize;
|
|
- const bool hasExtraValues = !extraValues.isEmpty();
|
|
- QList<ImageInfo>::const_iterator it = infos.constBegin(), end;
|
|
- QList<QVariant>::const_iterator xit = extraValues.constBegin(), xend;
|
|
- int index = 0;
|
|
- QVector<ImageInfo> infoVector;
|
|
- QVector<QVariant> extraValueVector;
|
|
-
|
|
- while (it != infos.constEnd())
|
|
- {
|
|
- const int chunkSize = qMin(maxChunkSize, size - index);
|
|
- infoVector.resize(chunkSize);
|
|
- end = it + chunkSize;
|
|
- qCopy(it, end, infoVector.begin());
|
|
-
|
|
- if (hasExtraValues)
|
|
- {
|
|
- extraValueVector.resize(chunkSize);
|
|
- xend = xit + chunkSize;
|
|
- qCopy(xit, xend, extraValueVector.begin());
|
|
- xit = xend;
|
|
- }
|
|
-
|
|
- it = end;
|
|
- index += chunkSize;
|
|
-
|
|
- ++sentOut;
|
|
-
|
|
- if (forReAdd)
|
|
- {
|
|
- ++sentOutForReAdd;
|
|
- }
|
|
-
|
|
- if (needPrepare)
|
|
- {
|
|
- emit packageToPrepare(ImageFilterModelTodoPackage(infoVector, extraValueVector, version, forReAdd));
|
|
- }
|
|
- else
|
|
- {
|
|
- emit packageToFilter(ImageFilterModelTodoPackage(infoVector, extraValueVector, version, forReAdd));
|
|
- }
|
|
- }
|
|
-}
|
|
-
|
|
-void ImageFilterModel::ImageFilterModelPrivate::packageFinished(const ImageFilterModelTodoPackage& package)
|
|
-{
|
|
- // check if it got discarded on the journey
|
|
- if (package.version != version)
|
|
- {
|
|
- packageDiscarded(package);
|
|
- return;
|
|
- }
|
|
-
|
|
- // incorporate result
|
|
- QHash<qlonglong, bool>::const_iterator it = package.filterResults.constBegin();
|
|
-
|
|
- for (; it != package.filterResults.constEnd(); ++it)
|
|
- {
|
|
- filterResults.insert(it.key(), it.value());
|
|
- }
|
|
-
|
|
- // re-add if necessary
|
|
- if (package.isForReAdd)
|
|
- {
|
|
- emit reAddImageInfos(package.infos.toList(), package.extraValues.toList());
|
|
-
|
|
- if (sentOutForReAdd == 1) // last package
|
|
- {
|
|
- emit reAddingFinished();
|
|
- }
|
|
- }
|
|
-
|
|
- // decrement counters
|
|
- --sentOut;
|
|
-
|
|
- if (package.isForReAdd)
|
|
- {
|
|
- --sentOutForReAdd;
|
|
- }
|
|
-
|
|
- // If all packages have returned, filtered and readded, and no more are expected,
|
|
- // and there is need to tell the filter result to the view, do that
|
|
- if (sentOut == 0 && sentOutForReAdd == 0 && !imageModel->isRefreshing())
|
|
- {
|
|
- q->invalidate(); // use invalidate, not invalidateFilter only. Sorting may have changed as well.
|
|
- emit (q->filterMatches(hasOneMatch));
|
|
- emit (q->filterMatchesForText(hasOneMatchForText));
|
|
- filterer->deactivate();
|
|
- preparer->deactivate();
|
|
- }
|
|
-}
|
|
-
|
|
-void ImageFilterModel::ImageFilterModelPrivate::packageDiscarded(const ImageFilterModelTodoPackage& package)
|
|
-{
|
|
- // Either, the model was reset, or the filter changed
|
|
- // In the former case throw all away, in the latter case, recycle
|
|
- if (package.version > lastDiscardVersion)
|
|
- {
|
|
- // Recycle packages: Send again with current version
|
|
- // Do not increment sentOut or sentOutForReAdd here: it was not decremented!
|
|
-
|
|
- if (needPrepare)
|
|
- {
|
|
- emit packageToPrepare(ImageFilterModelTodoPackage(package.infos, package.extraValues, version, package.isForReAdd));
|
|
- }
|
|
- else
|
|
- {
|
|
- emit packageToFilter(ImageFilterModelTodoPackage(package.infos, package.extraValues, version, package.isForReAdd));
|
|
- }
|
|
- }
|
|
-}
|
|
-
|
|
-} // namespace Digikam
|
|
diff --git a/libs/models/imagefiltermodelpriv.h b/libs/models/imagefiltermodelpriv.h
|
|
deleted file mode 100644
|
|
index a9e3f22..0000000
|
|
--- a/libs/models/imagefiltermodelpriv.h
|
|
+++ /dev/null
|
|
@@ -1,159 +0,0 @@
|
|
-/* ============================================================
|
|
- *
|
|
- * This file is a part of digiKam project
|
|
- * http://www.digikam.org
|
|
- *
|
|
- * Date : 2009-03-11
|
|
- * Description : Qt item model for database entries - private shared header
|
|
- *
|
|
- * Copyright (C) 2009-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
|
|
- *
|
|
- * This program is free software; you can redistribute it
|
|
- * and/or modify it under the terms of the GNU General
|
|
- * Public License as published by the Free Software Foundation;
|
|
- * either version 2, or (at your option)
|
|
- * any later version.
|
|
- *
|
|
- * This program is distributed in the hope that it will be useful,
|
|
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
- * GNU General Public License for more details.
|
|
- *
|
|
- * ============================================================ */
|
|
-
|
|
-#ifndef IMAGEFILTERMODELPRIV_H
|
|
-#define IMAGEFILTERMODELPRIV_H
|
|
-
|
|
-// Qt includes
|
|
-
|
|
-#include <QHash>
|
|
-#include <QMutex>
|
|
-#include <QMutexLocker>
|
|
-#include <QSet>
|
|
-#include <QThread>
|
|
-#include <QTimer>
|
|
-#include <QWaitCondition>
|
|
-
|
|
-// Local includes
|
|
-
|
|
-#include "imageinfo.h"
|
|
-#include "imagefiltermodel.h"
|
|
-
|
|
-#include "digikam_export.h"
|
|
-// Yes, we need the EXPORT macro in a private header because
|
|
-// this private header is shared across binary objects.
|
|
-// This does NOT make this classes here any more public!
|
|
-
|
|
-namespace Digikam
|
|
-{
|
|
-
|
|
-const int PrepareChunkSize = 101;
|
|
-const int FilterChunkSize = 2001;
|
|
-
|
|
-class ImageFilterModelTodoPackage
|
|
-{
|
|
-public:
|
|
-
|
|
- ImageFilterModelTodoPackage()
|
|
- : version(0), isForReAdd(false)
|
|
- {
|
|
- }
|
|
-
|
|
- ImageFilterModelTodoPackage(const QVector<ImageInfo>& infos, const QVector<QVariant>& extraValues, int version, bool isForReAdd)
|
|
- : infos(infos), extraValues(extraValues), version(version), isForReAdd(isForReAdd)
|
|
- {
|
|
- }
|
|
-
|
|
- QVector<ImageInfo> infos;
|
|
- QVector<QVariant> extraValues;
|
|
- unsigned int version;
|
|
- bool isForReAdd;
|
|
- QHash<qlonglong, bool> filterResults;
|
|
-};
|
|
-
|
|
-// ------------------------------------------------------------------------------------------------
|
|
-
|
|
-class ImageFilterModelPreparer;
|
|
-class ImageFilterModelFilterer;
|
|
-
|
|
-class DIGIKAM_DATABASE_EXPORT ImageFilterModel::ImageFilterModelPrivate : public QObject
|
|
-{
|
|
- Q_OBJECT
|
|
-
|
|
-public:
|
|
-
|
|
- ImageFilterModelPrivate();
|
|
- ~ImageFilterModelPrivate();
|
|
-
|
|
- void init(ImageFilterModel* q);
|
|
- void setupWorkers();
|
|
- void infosToProcess(const QList<ImageInfo>& infos);
|
|
- void infosToProcess(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues, bool forReAdd = true);
|
|
-
|
|
-public:
|
|
-
|
|
- ImageFilterModel* q;
|
|
-
|
|
- ImageModel* imageModel;
|
|
-
|
|
- ImageFilterSettings filter;
|
|
- ImageSortSettings sorter;
|
|
- VersionImageFilterSettings versionFilter;
|
|
- GroupImageFilterSettings groupFilter;
|
|
-
|
|
- volatile unsigned int version;
|
|
- unsigned int lastDiscardVersion;
|
|
- unsigned int lastFilteredVersion;
|
|
- int sentOut;
|
|
- int sentOutForReAdd;
|
|
-
|
|
- QTimer* updateFilterTimer;
|
|
-
|
|
- bool needPrepare;
|
|
- bool needPrepareComments;
|
|
- bool needPrepareTags;
|
|
- bool needPrepareGroups;
|
|
-
|
|
- QMutex mutex;
|
|
- ImageFilterSettings filterCopy;
|
|
- VersionImageFilterSettings versionFilterCopy;
|
|
- GroupImageFilterSettings groupFilterCopy;
|
|
- ImageFilterModelPreparer* preparer;
|
|
- ImageFilterModelFilterer* filterer;
|
|
-
|
|
- QHash<qlonglong, bool> filterResults;
|
|
- bool hasOneMatch;
|
|
- bool hasOneMatchForText;
|
|
-
|
|
- QList<ImageFilterModelPrepareHook*> prepareHooks;
|
|
-
|
|
-/*
|
|
- QHash<int, QSet<qlonglong> > categoryCountHashInt;
|
|
- QHash<QString, QSet<qlonglong> > categoryCountHashString;
|
|
-
|
|
-public:
|
|
-
|
|
- void cacheCategoryCount(int id, qlonglong imageid) const
|
|
- { const_cast<ImageFilterModelPrivate*>(this)->categoryCountHashInt[id].insert(imageid); }
|
|
- void cacheCategoryCount(const QString& id, qlonglong imageid) const
|
|
- { const_cast<ImageFilterModelPrivate*>(this)->categoryCountHashString[id].insert(imageid); }
|
|
-*/
|
|
-
|
|
-public Q_SLOTS:
|
|
-
|
|
- void preprocessInfos(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues);
|
|
- void processAddedInfos(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues);
|
|
- void packageFinished(const ImageFilterModelTodoPackage& package);
|
|
- void packageDiscarded(const ImageFilterModelTodoPackage& package);
|
|
-
|
|
-Q_SIGNALS:
|
|
-
|
|
- void packageToPrepare(const ImageFilterModelTodoPackage& package);
|
|
- void packageToFilter(const ImageFilterModelTodoPackage& package);
|
|
- void reAddImageInfos(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues);
|
|
- void reAddingFinished();
|
|
-};
|
|
-
|
|
-} // namespace Digikam
|
|
-
|
|
-#endif // IMAGEFILTERMODELPRIV_H
|
|
diff --git a/libs/models/imagefiltermodelthreads.cpp b/libs/models/imagefiltermodelthreads.cpp
|
|
deleted file mode 100644
|
|
index aa5c462..0000000
|
|
--- a/libs/models/imagefiltermodelthreads.cpp
|
|
+++ /dev/null
|
|
@@ -1,40 +0,0 @@
|
|
-/* ============================================================
|
|
- *
|
|
- * This file is a part of digiKam project
|
|
- * http://www.digikam.org
|
|
- *
|
|
- * Date : 2009-03-05
|
|
- * Description : Qt item model for database entries
|
|
- *
|
|
- * Copyright (C) 2009-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
|
|
- * Copyright (C) 2011-2017 by Gilles Caulier <caulier dot gilles at gmail dot com>
|
|
- * Copyright (C) 2010 by Andi Clemens <andi dot clemens at gmail dot com>
|
|
- * Copyright (C) 2011 by Michael G. Hansen <mike at mghansen dot de>
|
|
- * Copyright (C) 2014 by Mohamed Anwer <m dot anwer at gmx dot com>
|
|
- *
|
|
- * This program is free software; you can redistribute it
|
|
- * and/or modify it under the terms of the GNU General
|
|
- * Public License as published by the Free Software Foundation;
|
|
- * either version 2, or (at your option)
|
|
- * any later version.
|
|
- *
|
|
- * This program is distributed in the hope that it will be useful,
|
|
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
- * GNU General Public License for more details.
|
|
- *
|
|
- * ============================================================ */
|
|
-
|
|
-#include "imagefiltermodel.h"
|
|
-#include "imagefiltermodelpriv.h"
|
|
-#include "imagefiltermodelthreads.h"
|
|
-
|
|
-namespace Digikam
|
|
-{
|
|
-
|
|
-ImageFilterModelWorker::ImageFilterModelWorker(ImageFilterModel::ImageFilterModelPrivate* const d)
|
|
- : d(d)
|
|
-{
|
|
-}
|
|
-
|
|
-} // namespace Digikam
|
|
diff --git a/libs/models/imagefiltermodelthreads.h b/libs/models/imagefiltermodelthreads.h
|
|
deleted file mode 100644
|
|
index 83fa987..0000000
|
|
--- a/libs/models/imagefiltermodelthreads.h
|
|
+++ /dev/null
|
|
@@ -1,100 +0,0 @@
|
|
-/* ============================================================
|
|
- *
|
|
- * This file is a part of digiKam project
|
|
- * http://www.digikam.org
|
|
- *
|
|
- * Date : 2009-03-11
|
|
- * Description : Qt item model for database entries - private header
|
|
- *
|
|
- * Copyright (C) 2009-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
|
|
- *
|
|
- * This program is free software; you can redistribute it
|
|
- * and/or modify it under the terms of the GNU General
|
|
- * Public License as published by the Free Software Foundation;
|
|
- * either version 2, or (at your option)
|
|
- * any later version.
|
|
- *
|
|
- * This program is distributed in the hope that it will be useful,
|
|
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
- * GNU General Public License for more details.
|
|
- *
|
|
- * ============================================================ */
|
|
-
|
|
-#ifndef IMAGEFILTERMODELTHREADS_H
|
|
-#define IMAGEFILTERMODELTHREADS_H
|
|
-
|
|
-// Qt includes
|
|
-
|
|
-#include <QThread>
|
|
-
|
|
-// Local includes
|
|
-
|
|
-#include "digikam_export.h"
|
|
-#include "workerobject.h"
|
|
-
|
|
-namespace Digikam
|
|
-{
|
|
-
|
|
-class DIGIKAM_DATABASE_EXPORT ImageFilterModelWorker : public WorkerObject
|
|
-{
|
|
- Q_OBJECT
|
|
-
|
|
-public:
|
|
-
|
|
- explicit ImageFilterModelWorker(ImageFilterModel::ImageFilterModelPrivate* const d);
|
|
-
|
|
- bool checkVersion(const ImageFilterModelTodoPackage& package)
|
|
- {
|
|
- return d->version == package.version;
|
|
- }
|
|
-
|
|
-public Q_SLOTS:
|
|
-
|
|
- virtual void process(ImageFilterModelTodoPackage package) = 0;
|
|
-
|
|
-Q_SIGNALS:
|
|
-
|
|
- void processed(const ImageFilterModelTodoPackage& package);
|
|
- void discarded(const ImageFilterModelTodoPackage& package);
|
|
-
|
|
-protected:
|
|
-
|
|
- ImageFilterModel::ImageFilterModelPrivate* d;
|
|
-};
|
|
-
|
|
-// -----------------------------------------------------------------------------------------
|
|
-
|
|
-class DIGIKAM_DATABASE_EXPORT ImageFilterModelPreparer : public ImageFilterModelWorker
|
|
-{
|
|
- Q_OBJECT
|
|
-
|
|
-public:
|
|
-
|
|
- explicit ImageFilterModelPreparer(ImageFilterModel::ImageFilterModelPrivate* const d)
|
|
- : ImageFilterModelWorker(d)
|
|
- {
|
|
- }
|
|
-
|
|
- void process(ImageFilterModelTodoPackage package);
|
|
-};
|
|
-
|
|
-// ----------------------------------------------------------------------------------------
|
|
-
|
|
-class DIGIKAM_DATABASE_EXPORT ImageFilterModelFilterer : public ImageFilterModelWorker
|
|
-{
|
|
- Q_OBJECT
|
|
-
|
|
-public:
|
|
-
|
|
- explicit ImageFilterModelFilterer(ImageFilterModel::ImageFilterModelPrivate* const d)
|
|
- : ImageFilterModelWorker(d)
|
|
- {
|
|
- }
|
|
-
|
|
- void process(ImageFilterModelTodoPackage package);
|
|
-};
|
|
-
|
|
-} // namespace Digikam
|
|
-
|
|
-#endif // IMAGEFILTERMODELTHREADS_H
|
|
diff --git a/libs/models/imagefiltersettings.cpp b/libs/models/imagefiltersettings.cpp
|
|
deleted file mode 100644
|
|
index b61e7f9..0000000
|
|
--- a/libs/models/imagefiltersettings.cpp
|
|
+++ /dev/null
|
|
@@ -1,952 +0,0 @@
|
|
-/* ============================================================
|
|
- *
|
|
- * This file is a part of digiKam project
|
|
- * http://www.digikam.org
|
|
- *
|
|
- * Date : 2009-03-05
|
|
- * Description : Filter values for use with ImageFilterModel
|
|
- *
|
|
- * Copyright (C) 2009-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
|
|
- * Copyright (C) 2011-2017 by Gilles Caulier <caulier dot gilles at gmail dot com>
|
|
- * Copyright (C) 2010 by Andi Clemens <andi dot clemens at gmail dot com>
|
|
- * Copyright (C) 2011 by Michael G. Hansen <mike at mghansen dot de>
|
|
- * Copyright (C) 2014 by Mohamed Anwer <m dot anwer at gmx dot com>
|
|
- *
|
|
- * This program is free software; you can redistribute it
|
|
- * and/or modify it under the terms of the GNU General
|
|
- * Public License as published by the Free Software Foundation;
|
|
- * either version 2, or (at your option)
|
|
- * any later version.
|
|
- *
|
|
- * This program is distributed in the hope that it will be useful,
|
|
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
- * GNU General Public License for more details.
|
|
- *
|
|
- * ============================================================ */
|
|
-
|
|
-#include "imagefiltersettings.h"
|
|
-
|
|
-// C++ includes
|
|
-
|
|
-#include <cmath>
|
|
-
|
|
-// Qt includes
|
|
-
|
|
-#include <QDateTime>
|
|
-
|
|
-// Local includes
|
|
-
|
|
-#include "digikam_debug.h"
|
|
-#include "coredbfields.h"
|
|
-#include "digikam_globals.h"
|
|
-#include "imageinfo.h"
|
|
-#include "tagscache.h"
|
|
-#include "versionmanagersettings.h"
|
|
-
|
|
-namespace Digikam
|
|
-{
|
|
-
|
|
-ImageFilterSettings::ImageFilterSettings()
|
|
-{
|
|
- m_untaggedFilter = false;
|
|
- m_isUnratedExcluded = false;
|
|
- m_ratingFilter = 0;
|
|
- m_mimeTypeFilter = MimeFilter::AllFiles;
|
|
- m_ratingCond = GreaterEqualCondition;
|
|
- m_matchingCond = OrCondition;
|
|
- m_geolocationCondition = GeolocationNoFilter;
|
|
-}
|
|
-
|
|
-DatabaseFields::Set ImageFilterSettings::watchFlags() const
|
|
-{
|
|
- DatabaseFields::Set set;
|
|
-
|
|
- if (isFilteringByDay())
|
|
- {
|
|
- set |= DatabaseFields::CreationDate;
|
|
- }
|
|
-
|
|
- if (isFilteringByText())
|
|
- {
|
|
- set |= DatabaseFields::Name;
|
|
- set |= DatabaseFields::Comment;
|
|
- }
|
|
-
|
|
- if (isFilteringByRating())
|
|
- {
|
|
- set |= DatabaseFields::Rating;
|
|
- }
|
|
-
|
|
- if (isFilteringByTypeMime())
|
|
- {
|
|
- set |= DatabaseFields::Category;
|
|
- set |= DatabaseFields::Format;
|
|
- }
|
|
-
|
|
- if (isFilteringByGeolocation())
|
|
- {
|
|
- set |= DatabaseFields::ImagePositionsAll;
|
|
- }
|
|
-
|
|
- if (isFilteringByColorLabels())
|
|
- {
|
|
- set |= DatabaseFields::ColorLabel;
|
|
- }
|
|
-
|
|
- if (isFilteringByPickLabels())
|
|
- {
|
|
- set |= DatabaseFields::PickLabel;
|
|
- }
|
|
-
|
|
- return set;
|
|
-}
|
|
-
|
|
-bool ImageFilterSettings::isFilteringByDay() const
|
|
-{
|
|
- if (!m_dayFilter.isEmpty())
|
|
- {
|
|
- return true;
|
|
- }
|
|
-
|
|
- return false;
|
|
-}
|
|
-
|
|
-bool ImageFilterSettings::isFilteringByTags() const
|
|
-{
|
|
- if (!m_includeTagFilter.isEmpty() || !m_excludeTagFilter.isEmpty() || m_untaggedFilter)
|
|
- {
|
|
- return true;
|
|
- }
|
|
-
|
|
- return false;
|
|
-}
|
|
-
|
|
-bool ImageFilterSettings::isFilteringByColorLabels() const
|
|
-{
|
|
- if (!m_colorLabelTagFilter.isEmpty())
|
|
- {
|
|
- return true;
|
|
- }
|
|
-
|
|
- return false;
|
|
-}
|
|
-
|
|
-bool ImageFilterSettings::isFilteringByPickLabels() const
|
|
-{
|
|
- if (!m_pickLabelTagFilter.isEmpty())
|
|
- {
|
|
- return true;
|
|
- }
|
|
-
|
|
- return false;
|
|
-}
|
|
-
|
|
-bool ImageFilterSettings::isFilteringByText() const
|
|
-{
|
|
- if (!m_textFilterSettings.text.isEmpty())
|
|
- {
|
|
- return true;
|
|
- }
|
|
-
|
|
- return false;
|
|
-}
|
|
-
|
|
-bool ImageFilterSettings::isFilteringByTypeMime() const
|
|
-{
|
|
- if (m_mimeTypeFilter != MimeFilter::AllFiles)
|
|
- {
|
|
- return true;
|
|
- }
|
|
-
|
|
- return false;
|
|
-}
|
|
-
|
|
-bool ImageFilterSettings::isFilteringByGeolocation() const
|
|
-{
|
|
- return (m_geolocationCondition != GeolocationNoFilter);
|
|
-}
|
|
-
|
|
-bool ImageFilterSettings::isFilteringByRating() const
|
|
-{
|
|
- if (m_ratingFilter != 0 || m_ratingCond != GreaterEqualCondition || m_isUnratedExcluded)
|
|
- {
|
|
- return true;
|
|
- }
|
|
-
|
|
- return false;
|
|
-}
|
|
-
|
|
-bool ImageFilterSettings::isFilteringInternally() const
|
|
-{
|
|
- return (isFiltering() || !m_urlWhitelists.isEmpty() || !m_idWhitelists.isEmpty());
|
|
-}
|
|
-
|
|
-bool ImageFilterSettings::isFiltering() const
|
|
-{
|
|
- return isFilteringByDay() ||
|
|
- isFilteringByTags() ||
|
|
- isFilteringByText() ||
|
|
- isFilteringByRating() ||
|
|
- isFilteringByTypeMime() ||
|
|
- isFilteringByColorLabels() ||
|
|
- isFilteringByPickLabels() ||
|
|
- isFilteringByGeolocation();
|
|
-}
|
|
-
|
|
-void ImageFilterSettings::setDayFilter(const QList<QDateTime>& days)
|
|
-{
|
|
- m_dayFilter.clear();
|
|
-
|
|
- for (QList<QDateTime>::const_iterator it = days.constBegin(); it != days.constEnd(); ++it)
|
|
- {
|
|
- m_dayFilter.insert(*it, true);
|
|
- }
|
|
-}
|
|
-
|
|
-void ImageFilterSettings::setTagFilter(const QList<int>& includedTags,
|
|
- const QList<int>& excludedTags,
|
|
- MatchingCondition matchingCondition,
|
|
- bool showUnTagged,
|
|
- const QList<int>& clTagIds,
|
|
- const QList<int>& plTagIds)
|
|
-{
|
|
- m_includeTagFilter = includedTags;
|
|
- m_excludeTagFilter = excludedTags;
|
|
- m_matchingCond = matchingCondition;
|
|
- m_untaggedFilter = showUnTagged;
|
|
- m_colorLabelTagFilter = clTagIds;
|
|
- m_pickLabelTagFilter = plTagIds;
|
|
-}
|
|
-
|
|
-void ImageFilterSettings::setRatingFilter(int rating, RatingCondition ratingCondition, bool isUnratedExcluded)
|
|
-{
|
|
- m_ratingFilter = rating;
|
|
- m_ratingCond = ratingCondition;
|
|
- m_isUnratedExcluded = isUnratedExcluded;
|
|
-}
|
|
-
|
|
-void ImageFilterSettings::setMimeTypeFilter(int mime)
|
|
-{
|
|
- m_mimeTypeFilter = (MimeFilter::TypeMimeFilter)mime;
|
|
-}
|
|
-
|
|
-void ImageFilterSettings::setGeolocationFilter(const GeolocationCondition& condition)
|
|
-{
|
|
- m_geolocationCondition = condition;
|
|
-}
|
|
-
|
|
-void ImageFilterSettings::setTextFilter(const SearchTextFilterSettings& settings)
|
|
-{
|
|
- m_textFilterSettings = settings;
|
|
-}
|
|
-
|
|
-void ImageFilterSettings::setTagNames(const QHash<int, QString>& hash)
|
|
-{
|
|
- m_tagNameHash = hash;
|
|
-}
|
|
-
|
|
-void ImageFilterSettings::setAlbumNames(const QHash<int, QString>& hash)
|
|
-{
|
|
- m_albumNameHash = hash;
|
|
-}
|
|
-
|
|
-void ImageFilterSettings::setUrlWhitelist(const QList<QUrl>& urlList, const QString& id)
|
|
-{
|
|
- if (urlList.isEmpty())
|
|
- {
|
|
- m_urlWhitelists.remove(id);
|
|
- }
|
|
- else
|
|
- {
|
|
- m_urlWhitelists.insert(id, urlList);
|
|
- }
|
|
-}
|
|
-
|
|
-void ImageFilterSettings::setIdWhitelist(const QList<qlonglong>& idList, const QString& id)
|
|
-{
|
|
- if (idList.isEmpty())
|
|
- {
|
|
- m_idWhitelists.remove(id);
|
|
- }
|
|
- else
|
|
- {
|
|
- m_idWhitelists.insert(id, idList);
|
|
- }
|
|
-}
|
|
-
|
|
-template <class ContainerA, class ContainerB>
|
|
-bool containsAnyOf(const ContainerA& listA, const ContainerB& listB)
|
|
-{
|
|
- foreach (const typename ContainerA::value_type& a, listA)
|
|
- {
|
|
- if (listB.contains(a))
|
|
- {
|
|
- return true;
|
|
- }
|
|
- }
|
|
- return false;
|
|
-}
|
|
-
|
|
-template <class ContainerA, typename Value, class ContainerB>
|
|
-bool containsNoneOfExcept(const ContainerA& list, const ContainerB& noneOfList, const Value& exception)
|
|
-{
|
|
- foreach (const typename ContainerB::value_type& n, noneOfList)
|
|
- {
|
|
- if (n != exception && list.contains(n))
|
|
- {
|
|
- return false;
|
|
- }
|
|
- }
|
|
- return true;
|
|
-}
|
|
-
|
|
-bool ImageFilterSettings::matches(const ImageInfo& info, bool* const foundText) const
|
|
-{
|
|
- if (foundText)
|
|
- {
|
|
- *foundText = false;
|
|
- }
|
|
-
|
|
- if (!isFilteringInternally())
|
|
- {
|
|
- return true;
|
|
- }
|
|
-
|
|
- bool match = false;
|
|
-
|
|
- if (!m_includeTagFilter.isEmpty() || !m_excludeTagFilter.isEmpty())
|
|
- {
|
|
- QList<int> tagIds = info.tagIds();
|
|
- QList<int>::const_iterator it;
|
|
-
|
|
- match = m_includeTagFilter.isEmpty();
|
|
-
|
|
- if (m_matchingCond == OrCondition)
|
|
- {
|
|
- for (it = m_includeTagFilter.begin(); it != m_includeTagFilter.end(); ++it)
|
|
- {
|
|
- if (tagIds.contains(*it))
|
|
- {
|
|
- match = true;
|
|
- break;
|
|
- }
|
|
- }
|
|
-
|
|
- match |= (m_untaggedFilter && tagIds.isEmpty());
|
|
- }
|
|
- else // AND matching condition...
|
|
- {
|
|
- // m_untaggedFilter and non-empty tag filter, combined with AND, is logically no match
|
|
- if (!m_untaggedFilter)
|
|
- {
|
|
- for (it = m_includeTagFilter.begin(); it != m_includeTagFilter.end(); ++it)
|
|
- {
|
|
- if (!tagIds.contains(*it))
|
|
- {
|
|
- break;
|
|
- }
|
|
- }
|
|
-
|
|
- if (it == m_includeTagFilter.end())
|
|
- {
|
|
- match = true;
|
|
- }
|
|
- }
|
|
- }
|
|
-
|
|
- for (it = m_excludeTagFilter.begin(); it != m_excludeTagFilter.end(); ++it)
|
|
- {
|
|
- if (tagIds.contains(*it))
|
|
- {
|
|
- match = false;
|
|
- break;
|
|
- }
|
|
- }
|
|
- }
|
|
- else if (m_untaggedFilter)
|
|
- {
|
|
- match = !TagsCache::instance()->containsPublicTags(info.tagIds());
|
|
- }
|
|
- else
|
|
- {
|
|
- match = true;
|
|
- }
|
|
-
|
|
- //-- Filter by pick labels ------------------------------------------------
|
|
-
|
|
- if (!m_pickLabelTagFilter.isEmpty())
|
|
- {
|
|
- QList<int> tagIds = info.tagIds();
|
|
- bool matchPL = false;
|
|
-
|
|
- if (containsAnyOf(m_pickLabelTagFilter, tagIds))
|
|
- {
|
|
- matchPL = true;
|
|
- }
|
|
- else if (!matchPL)
|
|
- {
|
|
- int noPickLabelTagId = TagsCache::instance()->tagForPickLabel(NoPickLabel);
|
|
-
|
|
- if (m_pickLabelTagFilter.contains(noPickLabelTagId))
|
|
- {
|
|
- // Searching for "has no ColorLabel" requires special handling:
|
|
- // Scan that the tag ids contains none of the ColorLabel tags, except maybe the NoColorLabel tag
|
|
- matchPL = containsNoneOfExcept(tagIds, TagsCache::instance()->pickLabelTags(), noPickLabelTagId);
|
|
- }
|
|
- }
|
|
-
|
|
- match &= matchPL;
|
|
- }
|
|
-
|
|
- //-- Filter by color labels ------------------------------------------------
|
|
-
|
|
- if (!m_colorLabelTagFilter.isEmpty())
|
|
- {
|
|
- QList<int> tagIds = info.tagIds();
|
|
- bool matchCL = false;
|
|
-
|
|
- if (containsAnyOf(m_colorLabelTagFilter, tagIds))
|
|
- {
|
|
- matchCL = true;
|
|
- }
|
|
- else if (!matchCL)
|
|
- {
|
|
- int noColorLabelTagId = TagsCache::instance()->tagForColorLabel(NoColorLabel);
|
|
-
|
|
- if (m_colorLabelTagFilter.contains(noColorLabelTagId))
|
|
- {
|
|
- // Searching for "has no ColorLabel" requires special handling:
|
|
- // Scan that the tag ids contains none of the ColorLabel tags, except maybe the NoColorLabel tag
|
|
- matchCL = containsNoneOfExcept(tagIds, TagsCache::instance()->colorLabelTags(), noColorLabelTagId);
|
|
- }
|
|
- }
|
|
-
|
|
- match &= matchCL;
|
|
- }
|
|
-
|
|
- //-- Filter by date -----------------------------------------------------------
|
|
-
|
|
- if (!m_dayFilter.isEmpty())
|
|
- {
|
|
- match &= m_dayFilter.contains(QDateTime(info.dateTime().date(), QTime()));
|
|
- }
|
|
-
|
|
- //-- Filter by rating ---------------------------------------------------------
|
|
-
|
|
- if (m_ratingFilter >= 0)
|
|
- {
|
|
- // for now we treat -1 (no rating) just like a rating of 0.
|
|
- int rating = info.rating();
|
|
-
|
|
- if (rating == -1)
|
|
- {
|
|
- rating = 0;
|
|
- }
|
|
-
|
|
- if(m_isUnratedExcluded && rating == 0)
|
|
- {
|
|
- match = false;
|
|
- }
|
|
- else
|
|
- {
|
|
- if (m_ratingCond == GreaterEqualCondition)
|
|
- {
|
|
- // If the rating is not >=, i.e it is <, then it does not match.
|
|
- if (rating < m_ratingFilter)
|
|
- {
|
|
- match = false;
|
|
- }
|
|
- }
|
|
- else if (m_ratingCond == EqualCondition)
|
|
- {
|
|
- // If the rating is not =, i.e it is !=, then it does not match.
|
|
- if (rating != m_ratingFilter)
|
|
- {
|
|
- match = false;
|
|
- }
|
|
- }
|
|
- else
|
|
- {
|
|
- // If the rating is not <=, i.e it is >, then it does not match.
|
|
- if (rating > m_ratingFilter)
|
|
- {
|
|
- match = false;
|
|
- }
|
|
- }
|
|
- }
|
|
- }
|
|
-
|
|
- // -- Filter by mime type -----------------------------------------------------
|
|
-
|
|
- switch (m_mimeTypeFilter)
|
|
- {
|
|
- // info.format is a standardized string: Only one possibility per mime type
|
|
- case MimeFilter::ImageFiles:
|
|
- {
|
|
- if (info.category() != DatabaseItem::Image)
|
|
- {
|
|
- match = false;
|
|
- }
|
|
-
|
|
- break;
|
|
- }
|
|
- case MimeFilter::JPGFiles:
|
|
- {
|
|
- if (info.format() != QLatin1String("JPG"))
|
|
- {
|
|
- match = false;
|
|
- }
|
|
-
|
|
- break;
|
|
- }
|
|
- case MimeFilter::PNGFiles:
|
|
- {
|
|
- if (info.format() != QLatin1String("PNG"))
|
|
- {
|
|
- match = false;
|
|
- }
|
|
-
|
|
- break;
|
|
- }
|
|
- case MimeFilter::TIFFiles:
|
|
- {
|
|
- if (info.format() != QLatin1String("TIFF"))
|
|
- {
|
|
- match = false;
|
|
- }
|
|
-
|
|
- break;
|
|
- }
|
|
- case MimeFilter::DNGFiles:
|
|
- {
|
|
- if (info.format() != QLatin1String("RAW-DNG"))
|
|
- {
|
|
- match = false;
|
|
- }
|
|
-
|
|
- break;
|
|
- }
|
|
- case MimeFilter::NoRAWFiles:
|
|
- {
|
|
- if (info.format().startsWith(QLatin1String("RAW")))
|
|
- {
|
|
- match = false;
|
|
- }
|
|
-
|
|
- break;
|
|
- }
|
|
- case MimeFilter::RAWFiles:
|
|
- {
|
|
- if (!info.format().startsWith(QLatin1String("RAW")))
|
|
- {
|
|
- match = false;
|
|
- }
|
|
-
|
|
- break;
|
|
- }
|
|
- case MimeFilter::MoviesFiles:
|
|
- {
|
|
- if (info.category() != DatabaseItem::Video)
|
|
- {
|
|
- match = false;
|
|
- }
|
|
-
|
|
- break;
|
|
- }
|
|
- case MimeFilter::AudioFiles:
|
|
- {
|
|
- if (info.category() != DatabaseItem::Audio)
|
|
- {
|
|
- match = false;
|
|
- }
|
|
-
|
|
- break;
|
|
- }
|
|
- case MimeFilter::RasterFiles:
|
|
- {
|
|
- if (info.format() != QLatin1String("PSD") && // Adobe Photoshop Document
|
|
- info.format() != QLatin1String("PSB") && // Adobe Photoshop Big
|
|
- info.format() != QLatin1String("XCF") && // Gimp
|
|
- info.format() != QLatin1String("KRA") && // Krita
|
|
- info.format() != QLatin1String("ORA") // Open Raster
|
|
- )
|
|
- {
|
|
- match = false;
|
|
- }
|
|
-
|
|
- break;
|
|
- }
|
|
- default:
|
|
- {
|
|
- // All Files: do nothing...
|
|
- break;
|
|
- }
|
|
- }
|
|
-
|
|
- //-- Filter by geolocation ----------------------------------------------------
|
|
-
|
|
- if (m_geolocationCondition!=GeolocationNoFilter)
|
|
- {
|
|
- if (m_geolocationCondition==GeolocationNoCoordinates)
|
|
- {
|
|
- if (info.hasCoordinates())
|
|
- {
|
|
- match = false;
|
|
- }
|
|
- }
|
|
- else if (m_geolocationCondition==GeolocationHasCoordinates)
|
|
- {
|
|
- if (!info.hasCoordinates())
|
|
- {
|
|
- match = false;
|
|
- }
|
|
- }
|
|
- }
|
|
-
|
|
- //-- Filter by text -----------------------------------------------------------
|
|
-
|
|
- if (!m_textFilterSettings.text.isEmpty())
|
|
- {
|
|
- bool textMatch = false;
|
|
-
|
|
- // Image name
|
|
- if (m_textFilterSettings.textFields & SearchTextFilterSettings::ImageName &&
|
|
- info.name().contains(m_textFilterSettings.text, m_textFilterSettings.caseSensitive))
|
|
- {
|
|
- textMatch = true;
|
|
- }
|
|
-
|
|
- // Image title
|
|
- if (m_textFilterSettings.textFields & SearchTextFilterSettings::ImageTitle &&
|
|
- info.title().contains(m_textFilterSettings.text, m_textFilterSettings.caseSensitive))
|
|
- {
|
|
- textMatch = true;
|
|
- }
|
|
-
|
|
- // Image comment
|
|
- if (m_textFilterSettings.textFields & SearchTextFilterSettings::ImageComment &&
|
|
- info.comment().contains(m_textFilterSettings.text, m_textFilterSettings.caseSensitive))
|
|
- {
|
|
- textMatch = true;
|
|
- }
|
|
-
|
|
- // Tag names
|
|
- foreach(int id, info.tagIds())
|
|
- {
|
|
- if (m_textFilterSettings.textFields & SearchTextFilterSettings::TagName &&
|
|
- m_tagNameHash.value(id).contains(m_textFilterSettings.text, m_textFilterSettings.caseSensitive))
|
|
- {
|
|
- textMatch = true;
|
|
- }
|
|
- }
|
|
-
|
|
- // Album names
|
|
- if (m_textFilterSettings.textFields & SearchTextFilterSettings::AlbumName &&
|
|
- m_albumNameHash.value(info.albumId()).contains(m_textFilterSettings.text, m_textFilterSettings.caseSensitive))
|
|
- {
|
|
- textMatch = true;
|
|
- }
|
|
-
|
|
- // Image Aspect Ratio
|
|
- if (m_textFilterSettings.textFields & SearchTextFilterSettings::ImageAspectRatio)
|
|
- {
|
|
- QRegExp expRatio (QLatin1String("^\\d+:\\d+$"));
|
|
- QRegExp expFloat (QLatin1String("^\\d+(.\\d+)?$"));
|
|
-
|
|
- if (expRatio.indexIn(m_textFilterSettings.text) > -1 && m_textFilterSettings.text.contains(QRegExp(QLatin1String(":\\d+"))))
|
|
- {
|
|
- QString trimmedTextFilterSettingsText = m_textFilterSettings.text;
|
|
- QStringList numberStringList = trimmedTextFilterSettingsText.split(QLatin1String(":"), QString::SkipEmptyParts);
|
|
-
|
|
- if (numberStringList.length() == 2)
|
|
- {
|
|
- QString numString = (QString)numberStringList.at(0), denomString = (QString)numberStringList.at(1);
|
|
- bool canConverseNum = false;
|
|
- bool canConverseDenom = false;
|
|
- int num = numString.toInt(&canConverseNum, 10), denom = denomString.toInt(&canConverseDenom, 10);
|
|
-
|
|
- if (canConverseNum && canConverseDenom)
|
|
- {
|
|
- if (fabs(info.aspectRatio() - (double)num / denom) < 0.1)
|
|
- textMatch = true;
|
|
- }
|
|
- }
|
|
- }
|
|
- else if (expFloat.indexIn(m_textFilterSettings.text) > -1)
|
|
- {
|
|
- QString trimmedTextFilterSettingsText = m_textFilterSettings.text;
|
|
- bool canConverse = false;
|
|
- double ratio = trimmedTextFilterSettingsText.toDouble(&canConverse);
|
|
-
|
|
- if (canConverse)
|
|
- {
|
|
- if (fabs(info.aspectRatio() - ratio) < 0.1)
|
|
- textMatch = true;
|
|
- }
|
|
- }
|
|
- }
|
|
-
|
|
- // Image Pixel Size
|
|
- // See bug #341053 for details.
|
|
-
|
|
- if (m_textFilterSettings.textFields & SearchTextFilterSettings::ImagePixelSize)
|
|
- {
|
|
- QSize size = info.dimensions();
|
|
- int pixelSize = size.height()*size.width();
|
|
- QString text = m_textFilterSettings.text;
|
|
-
|
|
- if(text.contains(QRegExp(QLatin1String("^>\\d{1,15}$"))) && pixelSize > (text.remove(0,1)).toInt())
|
|
- {
|
|
- textMatch = true;
|
|
- }
|
|
- else if(text.contains(QRegExp(QLatin1String("^<\\d{1,15}$"))) && pixelSize < (text.remove(0,1)).toInt())
|
|
- {
|
|
- textMatch = true;
|
|
- }
|
|
- else if(text.contains(QRegExp(QLatin1String("^\\d+$"))) && pixelSize == text.toInt())
|
|
- {
|
|
- textMatch = true;
|
|
- }
|
|
- }
|
|
-
|
|
- match &= textMatch;
|
|
-
|
|
- if (foundText)
|
|
- {
|
|
- *foundText = textMatch;
|
|
- }
|
|
- }
|
|
-
|
|
- // -- filter by URL-whitelists ------------------------------------------------
|
|
- // NOTE: whitelists are always AND for now.
|
|
-
|
|
- if (match)
|
|
- {
|
|
- const QUrl url = info.fileUrl();
|
|
-
|
|
- for (QHash<QString, QList<QUrl>>::const_iterator it = m_urlWhitelists.constBegin();
|
|
- it!=m_urlWhitelists.constEnd(); ++it)
|
|
- {
|
|
- match = it->contains(url);
|
|
-
|
|
- if (!match)
|
|
- {
|
|
- break;
|
|
- }
|
|
- }
|
|
- }
|
|
-
|
|
- if (match)
|
|
- {
|
|
- const qlonglong id = info.id();
|
|
-
|
|
- for (QHash<QString, QList<qlonglong> >::const_iterator it = m_idWhitelists.constBegin();
|
|
- it!=m_idWhitelists.constEnd(); ++it)
|
|
- {
|
|
- match = it->contains(id);
|
|
-
|
|
- if (!match)
|
|
- {
|
|
- break;
|
|
- }
|
|
- }
|
|
- }
|
|
-
|
|
- return match;
|
|
-}
|
|
-
|
|
-// -------------------------------------------------------------------------------------------------
|
|
-
|
|
-VersionImageFilterSettings::VersionImageFilterSettings()
|
|
-{
|
|
- m_includeTagFilter = 0;
|
|
- m_exceptionTagFilter = 0;
|
|
-}
|
|
-
|
|
-VersionImageFilterSettings::VersionImageFilterSettings(const VersionManagerSettings& settings)
|
|
-{
|
|
- setVersionManagerSettings(settings);
|
|
-}
|
|
-
|
|
-bool VersionImageFilterSettings::operator==(const VersionImageFilterSettings& other) const
|
|
-{
|
|
- return m_excludeTagFilter == other.m_excludeTagFilter &&
|
|
- m_exceptionLists == other.m_exceptionLists;
|
|
-}
|
|
-
|
|
-bool VersionImageFilterSettings::matches(const ImageInfo& info) const
|
|
-{
|
|
- if (!isFiltering())
|
|
- {
|
|
- return true;
|
|
- }
|
|
-
|
|
- const qlonglong id = info.id();
|
|
-
|
|
- for (QHash<QString, QList<qlonglong> >::const_iterator it = m_exceptionLists.constBegin();
|
|
- it != m_exceptionLists.constEnd(); ++it)
|
|
- {
|
|
- if (it->contains(id))
|
|
- {
|
|
- return true;
|
|
- }
|
|
- }
|
|
-
|
|
- bool match = true;
|
|
- QList<int> tagIds = info.tagIds();
|
|
-
|
|
- if (!tagIds.contains(m_includeTagFilter))
|
|
- {
|
|
- for (QList<int>::const_iterator it = m_excludeTagFilter.begin();
|
|
- it != m_excludeTagFilter.end(); ++it)
|
|
- {
|
|
- if (tagIds.contains(*it))
|
|
- {
|
|
- match = false;
|
|
- break;
|
|
- }
|
|
- }
|
|
- }
|
|
-
|
|
- if (!match)
|
|
- {
|
|
- if (tagIds.contains(m_exceptionTagFilter))
|
|
- {
|
|
- match = true;
|
|
- }
|
|
- }
|
|
-
|
|
- return match;
|
|
-}
|
|
-
|
|
-bool VersionImageFilterSettings::isHiddenBySettings(const ImageInfo& info) const
|
|
-{
|
|
- QList<int> tagIds = info.tagIds();
|
|
-
|
|
- foreach(int tagId, m_excludeTagFilter)
|
|
- {
|
|
- if (tagIds.contains(tagId))
|
|
- {
|
|
- return true;
|
|
- }
|
|
- }
|
|
-
|
|
- return false;
|
|
-}
|
|
-
|
|
-bool VersionImageFilterSettings::isExemptedBySettings(const ImageInfo& info) const
|
|
-{
|
|
- return info.tagIds().contains(m_exceptionTagFilter);
|
|
-}
|
|
-
|
|
-void VersionImageFilterSettings::setVersionManagerSettings(const VersionManagerSettings& settings)
|
|
-{
|
|
- m_excludeTagFilter.clear();
|
|
-
|
|
- if (!settings.enabled)
|
|
- {
|
|
- return;
|
|
- }
|
|
-
|
|
- if (!(settings.showInViewFlags & VersionManagerSettings::ShowOriginal))
|
|
- {
|
|
- m_excludeTagFilter << TagsCache::instance()->getOrCreateInternalTag(InternalTagName::originalVersion());
|
|
- }
|
|
-
|
|
- if (!(settings.showInViewFlags & VersionManagerSettings::ShowIntermediates))
|
|
- {
|
|
- m_excludeTagFilter << TagsCache::instance()->getOrCreateInternalTag(InternalTagName::intermediateVersion());
|
|
- }
|
|
-
|
|
- m_includeTagFilter = TagsCache::instance()->getOrCreateInternalTag(InternalTagName::currentVersion());
|
|
- m_exceptionTagFilter = TagsCache::instance()->getOrCreateInternalTag(InternalTagName::versionAlwaysVisible());
|
|
-}
|
|
-
|
|
-void VersionImageFilterSettings::setExceptionList(const QList<qlonglong>& idList, const QString& id)
|
|
-{
|
|
- if (idList.isEmpty())
|
|
- {
|
|
- m_exceptionLists.remove(id);
|
|
- }
|
|
- else
|
|
- {
|
|
- m_exceptionLists.insert(id, idList);
|
|
- }
|
|
-}
|
|
-
|
|
-bool VersionImageFilterSettings::isFiltering() const
|
|
-{
|
|
- return !m_excludeTagFilter.isEmpty();
|
|
-}
|
|
-
|
|
-bool VersionImageFilterSettings::isFilteringByTags() const
|
|
-{
|
|
- return isFiltering();
|
|
-}
|
|
-
|
|
-// -------------------------------------------------------------------------------------------------
|
|
-
|
|
-GroupImageFilterSettings::GroupImageFilterSettings()
|
|
- : m_allOpen(false)
|
|
-{
|
|
-}
|
|
-
|
|
-bool GroupImageFilterSettings::operator==(const GroupImageFilterSettings& other) const
|
|
-{
|
|
- return (m_allOpen == other.m_allOpen &&
|
|
- m_openGroups == other.m_openGroups);
|
|
-}
|
|
-
|
|
-bool GroupImageFilterSettings::matches(const ImageInfo& info) const
|
|
-{
|
|
- if (m_allOpen)
|
|
- {
|
|
- return true;
|
|
- }
|
|
-
|
|
- if (info.isGrouped())
|
|
- {
|
|
- return m_openGroups.contains(info.groupImage().id());
|
|
- }
|
|
- return true;
|
|
-}
|
|
-
|
|
-void GroupImageFilterSettings::setOpen(qlonglong group, bool open)
|
|
-{
|
|
- if (open)
|
|
- {
|
|
- m_openGroups << group;
|
|
- }
|
|
- else
|
|
- {
|
|
- m_openGroups.remove(group);
|
|
- }
|
|
-}
|
|
-
|
|
-bool GroupImageFilterSettings::isOpen(qlonglong group) const
|
|
-{
|
|
- return m_openGroups.contains(group);
|
|
-}
|
|
-
|
|
-void GroupImageFilterSettings::setAllOpen(bool open)
|
|
-{
|
|
- m_allOpen = open;
|
|
-}
|
|
-
|
|
-bool GroupImageFilterSettings::isAllOpen() const
|
|
-{
|
|
- return m_allOpen;
|
|
-}
|
|
-
|
|
-bool GroupImageFilterSettings::isFiltering() const
|
|
-{
|
|
- return !m_allOpen;
|
|
-}
|
|
-
|
|
-DatabaseFields::Set GroupImageFilterSettings::watchFlags() const
|
|
-{
|
|
- return DatabaseFields::ImageRelations;
|
|
-}
|
|
-
|
|
-} // namespace Digikam
|
|
diff --git a/libs/models/imagefiltersettings.h b/libs/models/imagefiltersettings.h
|
|
deleted file mode 100644
|
|
index 0e7beae..0000000
|
|
--- a/libs/models/imagefiltersettings.h
|
|
+++ /dev/null
|
|
@@ -1,349 +0,0 @@
|
|
-/* ============================================================
|
|
- *
|
|
- * This file is a part of digiKam project
|
|
- * http://www.digikam.org
|
|
- *
|
|
- * Date : 2009-03-05
|
|
- * Description : Filter values for use with ImageFilterModel
|
|
- *
|
|
- * Copyright (C) 2009-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
|
|
- * Copyright (C) 2011-2017 by Gilles Caulier <caulier dot gilles at gmail dot com>
|
|
- * Copyright (C) 2010 by Andi Clemens <andi dot clemens at gmail dot com>
|
|
- * Copyright (C) 2011 by Michael G. Hansen <mike at mghansen dot de>
|
|
- * Copyright (C) 2014 by Mohamed Anwer <m dot anwer at gmx dot com>
|
|
- *
|
|
- * This program is free software; you can redistribute it
|
|
- * and/or modify it under the terms of the GNU General
|
|
- * Public License as published by the Free Software Foundation;
|
|
- * either version 2, or (at your option)
|
|
- * any later version.
|
|
- *
|
|
- * This program is distributed in the hope that it will be useful,
|
|
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
- * GNU General Public License for more details.
|
|
- *
|
|
- * ============================================================ */
|
|
-
|
|
-#ifndef IMAGEFILTERSETTINGS_H
|
|
-#define IMAGEFILTERSETTINGS_H
|
|
-
|
|
-// Qt includes
|
|
-
|
|
-#include <QHash>
|
|
-#include <QList>
|
|
-#include <QMap>
|
|
-#include <QString>
|
|
-#include <QSet>
|
|
-#include <QUrl>
|
|
-
|
|
-// Local includes
|
|
-
|
|
-#include "searchtextbar.h"
|
|
-#include "mimefilter.h"
|
|
-#include "digikam_export.h"
|
|
-
|
|
-namespace Digikam
|
|
-{
|
|
-
|
|
-class ImageInfo;
|
|
-class VersionManagerSettings;
|
|
-
|
|
-namespace DatabaseFields
|
|
-{
|
|
- class Set;
|
|
-}
|
|
-
|
|
-// ---------------------------------------------------------------------------------------
|
|
-
|
|
-class DIGIKAM_DATABASE_EXPORT SearchTextFilterSettings : public SearchTextSettings
|
|
-{
|
|
-
|
|
-public:
|
|
-
|
|
- enum TextFilterFields
|
|
- {
|
|
- None = 0x00,
|
|
- ImageName = 0x01,
|
|
- ImageTitle = 0x02,
|
|
- ImageComment = 0x04,
|
|
- TagName = 0x08,
|
|
- AlbumName = 0x10,
|
|
- ImageAspectRatio = 0x20,
|
|
- ImagePixelSize = 0x40,
|
|
- All = ImageName | ImageTitle | ImageComment | TagName | AlbumName | ImageAspectRatio | ImagePixelSize
|
|
- };
|
|
-
|
|
-public:
|
|
-
|
|
- SearchTextFilterSettings()
|
|
- {
|
|
- textFields = None;
|
|
- }
|
|
-
|
|
- explicit SearchTextFilterSettings(const SearchTextSettings& settings)
|
|
- {
|
|
- caseSensitive = settings.caseSensitive;
|
|
- text = settings.text;
|
|
- textFields = None;
|
|
- }
|
|
-
|
|
- TextFilterFields textFields;
|
|
-};
|
|
-
|
|
-// ---------------------------------------------------------------------------------------
|
|
-
|
|
-class DIGIKAM_DATABASE_EXPORT ImageFilterSettings
|
|
-{
|
|
-public:
|
|
-
|
|
- ImageFilterSettings();
|
|
-
|
|
- /**
|
|
- * Returns true if the given ImageInfo matches the filter criteria.
|
|
- * Optionally, foundText is set to true if it matched by text search.
|
|
- */
|
|
- bool matches(const ImageInfo& info, bool* const foundText = 0) const;
|
|
-
|
|
-public:
|
|
-
|
|
- /// --- Tags filter ---
|
|
-
|
|
- /// Possible logical matching condition used to sort tags id.
|
|
- enum MatchingCondition
|
|
- {
|
|
- OrCondition,
|
|
- AndCondition
|
|
- };
|
|
-
|
|
- void setTagFilter(const QList<int>& includedTags,
|
|
- const QList<int>& excludedTags,
|
|
- MatchingCondition matchingCond,
|
|
- bool showUnTagged,
|
|
- const QList<int>& clTagIds,
|
|
- const QList<int>& plTagIds);
|
|
-
|
|
-public:
|
|
-
|
|
- /// --- Rating filter ---
|
|
-
|
|
- /// Possible conditions used to filter rating: >=, =, <=
|
|
- enum RatingCondition
|
|
- {
|
|
- GreaterEqualCondition,
|
|
- EqualCondition,
|
|
- LessEqualCondition
|
|
- };
|
|
-
|
|
- void setRatingFilter(int rating, RatingCondition ratingCond, bool isUnratedExcluded);
|
|
-
|
|
-public:
|
|
-
|
|
- /// --- Date filter ---
|
|
- void setDayFilter(const QList<QDateTime>& days);
|
|
-
|
|
-public:
|
|
-
|
|
- /// --- Text filter ---
|
|
- void setTextFilter(const SearchTextFilterSettings& settings);
|
|
- void setTagNames(const QHash<int, QString>& tagNameHash);
|
|
- void setAlbumNames(const QHash<int, QString>& albumNameHash);
|
|
-
|
|
-public:
|
|
-
|
|
- /// --- Mime filter ---
|
|
- void setMimeTypeFilter(int mimeTypeFilter);
|
|
-
|
|
-public:
|
|
-
|
|
- /// --- Geolocation filter
|
|
- enum GeolocationCondition
|
|
- {
|
|
- GeolocationNoFilter = 0,
|
|
- GeolocationNoCoordinates = 1 << 1,
|
|
- GeolocationHasCoordinates = 1 << 2
|
|
- };
|
|
-
|
|
- void setGeolocationFilter(const GeolocationCondition& condition);
|
|
-
|
|
-public:
|
|
-
|
|
- /// Returns if the day is a filter criteria
|
|
- bool isFilteringByDay() const;
|
|
-
|
|
- /// Returns if the type mime is a filter criteria
|
|
- bool isFilteringByTypeMime() const;
|
|
-
|
|
- /// Returns whether geolocation is a filter criteria
|
|
- bool isFilteringByGeolocation() const;
|
|
-
|
|
- /// Returns if the rating is a filter criteria
|
|
- bool isFilteringByRating() const;
|
|
-
|
|
- /// Returns if the pick labels is a filter criteria
|
|
- bool isFilteringByPickLabels() const;
|
|
-
|
|
- /// Returns if the color labels is a filter criteria
|
|
- bool isFilteringByColorLabels() const;
|
|
-
|
|
- /// Returns if the tag is a filter criteria
|
|
- bool isFilteringByTags() const;
|
|
-
|
|
- /// Returns if the text (including comment) is a filter criteria
|
|
- bool isFilteringByText() const;
|
|
-
|
|
- /// Returns if images will be filtered by these criteria at all
|
|
- bool isFiltering() const;
|
|
-
|
|
-public:
|
|
-
|
|
- /// --- URL whitelist filter
|
|
- void setUrlWhitelist(const QList<QUrl>& urlList, const QString& id);
|
|
-
|
|
-public:
|
|
-
|
|
- /// --- ID whitelist filter
|
|
- void setIdWhitelist(const QList<qlonglong>& idList, const QString& id);
|
|
-
|
|
-public:
|
|
-
|
|
- /// --- Change notification ---
|
|
-
|
|
- /** Returns database fields a change in which would affect the current filtering.
|
|
- * To find out if an image tag change affects filtering, test isFilteringByTags().
|
|
- * The text filter will also be affected by changes in tags and album names.
|
|
- */
|
|
- DatabaseFields::Set watchFlags() const;
|
|
-
|
|
-private:
|
|
-
|
|
- /**
|
|
- * @brief Returns whether some internal filtering (whitelist by id or URL) or normal filtering is going on
|
|
- */
|
|
- bool isFilteringInternally() const;
|
|
-
|
|
-private:
|
|
-
|
|
- /// --- Tags filter ---
|
|
- bool m_untaggedFilter;
|
|
- QList<int> m_includeTagFilter;
|
|
- QList<int> m_excludeTagFilter;
|
|
- MatchingCondition m_matchingCond;
|
|
- QList<int> m_colorLabelTagFilter;
|
|
- QList<int> m_pickLabelTagFilter;
|
|
-
|
|
- /// --- Rating filter ---
|
|
- int m_ratingFilter;
|
|
- RatingCondition m_ratingCond;
|
|
- bool m_isUnratedExcluded;
|
|
-
|
|
- /// --- Date filter ---
|
|
- QMap<QDateTime, bool> m_dayFilter;
|
|
-
|
|
- /// --- Text filter ---
|
|
- SearchTextFilterSettings m_textFilterSettings;
|
|
-
|
|
- /// Helpers for text search: Set these if you want to search album or tag names with text search
|
|
- QHash<int, QString> m_tagNameHash;
|
|
- QHash<int, QString> m_albumNameHash;
|
|
-
|
|
- /// --- Mime filter ---
|
|
- MimeFilter::TypeMimeFilter m_mimeTypeFilter;
|
|
-
|
|
- /// --- Geolocation filter
|
|
- GeolocationCondition m_geolocationCondition;
|
|
-
|
|
- /// --- URL whitelist filter
|
|
- QHash<QString,QList<QUrl>> m_urlWhitelists;
|
|
-
|
|
- /// --- ID whitelist filter
|
|
- QHash<QString,QList<qlonglong> > m_idWhitelists;
|
|
-};
|
|
-
|
|
-// ---------------------------------------------------------------------------------------
|
|
-
|
|
-class DIGIKAM_DATABASE_EXPORT VersionImageFilterSettings
|
|
-{
|
|
-public:
|
|
-
|
|
- VersionImageFilterSettings();
|
|
- explicit VersionImageFilterSettings(const VersionManagerSettings& settings);
|
|
-
|
|
- bool operator==(const VersionImageFilterSettings& other) const;
|
|
-
|
|
- /**
|
|
- * Returns true if the given ImageInfo matches the filter criteria.
|
|
- */
|
|
- bool matches(const ImageInfo& info) const;
|
|
-
|
|
- bool isHiddenBySettings(const ImageInfo& info) const;
|
|
- bool isExemptedBySettings(const ImageInfo& info) const;
|
|
-
|
|
- /// --- Tags filter ---
|
|
-
|
|
- void setVersionManagerSettings(const VersionManagerSettings& settings);
|
|
-
|
|
- /**
|
|
- * Add list with exceptions: These images will be exempted from filtering by this filter
|
|
- */
|
|
- void setExceptionList(const QList<qlonglong>& idlist, const QString& id);
|
|
-
|
|
- /// Returns if images will be filtered by these criteria at all
|
|
- bool isFiltering() const;
|
|
-
|
|
- /// Returns if the tag is a filter criteria
|
|
- bool isFilteringByTags() const;
|
|
-
|
|
- /// DatabaseFields::Set watchFlags() const: Would return 0
|
|
-
|
|
-protected:
|
|
-
|
|
- QList<int> m_excludeTagFilter;
|
|
- int m_includeTagFilter;
|
|
- int m_exceptionTagFilter;
|
|
- QHash<QString,QList<qlonglong> > m_exceptionLists;
|
|
-};
|
|
-
|
|
-// ---------------------------------------------------------------------------------------
|
|
-
|
|
-class DIGIKAM_DATABASE_EXPORT GroupImageFilterSettings
|
|
-{
|
|
-public:
|
|
-
|
|
- GroupImageFilterSettings();
|
|
-
|
|
- bool operator==(const GroupImageFilterSettings& other) const;
|
|
-
|
|
- /**
|
|
- * Returns true if the given ImageInfo matches the filter criteria.
|
|
- */
|
|
- bool matches(const ImageInfo& info) const;
|
|
-
|
|
- /**
|
|
- * Open or close a group.
|
|
- */
|
|
- void setOpen(qlonglong group, bool open);
|
|
- bool isOpen(qlonglong group) const;
|
|
-
|
|
- /**
|
|
- * Open all groups
|
|
- */
|
|
- void setAllOpen(bool open);
|
|
- bool isAllOpen() const;
|
|
-
|
|
- /// Returns if images will be filtered by these criteria at all
|
|
- bool isFiltering() const;
|
|
-
|
|
- DatabaseFields::Set watchFlags() const;
|
|
-
|
|
-protected:
|
|
-
|
|
- bool m_allOpen;
|
|
- QSet<qlonglong> m_openGroups;
|
|
-};
|
|
-
|
|
-} // namespace Digikam
|
|
-
|
|
-Q_DECLARE_METATYPE(Digikam::ImageFilterSettings::GeolocationCondition)
|
|
-
|
|
-#endif // IMAGEFILTERSETTINGS_H
|
|
diff --git a/libs/models/imagelistmodel.cpp b/libs/models/imagelistmodel.cpp
|
|
deleted file mode 100644
|
|
index fafce34..0000000
|
|
--- a/libs/models/imagelistmodel.cpp
|
|
+++ /dev/null
|
|
@@ -1,70 +0,0 @@
|
|
-/* ============================================================
|
|
- *
|
|
- * This file is a part of digiKam project
|
|
- * http://www.digikam.org
|
|
- *
|
|
- * Date : 2010-12-06
|
|
- * Description : An image model based on a static list
|
|
- *
|
|
- * Copyright (C) 2010-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
|
|
- *
|
|
- * This program is free software; you can redistribute it
|
|
- * and/or modify it under the terms of the GNU General
|
|
- * Public License as published by the Free Software Foundation;
|
|
- * either version 2, or (at your option)
|
|
- * any later version.
|
|
- *
|
|
- * This program is distributed in the hope that it will be useful,
|
|
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
- * GNU General Public License for more details.
|
|
- *
|
|
- * ============================================================ */
|
|
-
|
|
-#include "imagelistmodel.h"
|
|
-
|
|
-// Local includes
|
|
-
|
|
-#include "digikam_debug.h"
|
|
-#include "coredbaccess.h"
|
|
-#include "coredbchangesets.h"
|
|
-#include "coredbwatch.h"
|
|
-#include "imageinfo.h"
|
|
-#include "imageinfolist.h"
|
|
-
|
|
-namespace Digikam
|
|
-{
|
|
-
|
|
-ImageListModel::ImageListModel(QObject* parent)
|
|
- : ImageThumbnailModel(parent)
|
|
-{
|
|
- connect(CoreDbAccess::databaseWatch(), SIGNAL(collectionImageChange(CollectionImageChangeset)),
|
|
- this, SLOT(slotCollectionImageChange(CollectionImageChangeset)));
|
|
-}
|
|
-
|
|
-ImageListModel::~ImageListModel()
|
|
-{
|
|
-}
|
|
-
|
|
-void ImageListModel::slotCollectionImageChange(const CollectionImageChangeset& changeset)
|
|
-{
|
|
- if (isEmpty())
|
|
- {
|
|
- return;
|
|
- }
|
|
-
|
|
- switch (changeset.operation())
|
|
- {
|
|
- case CollectionImageChangeset::Added:
|
|
- break;
|
|
- case CollectionImageChangeset::Removed:
|
|
- case CollectionImageChangeset::RemovedAll:
|
|
- removeImageInfos(ImageInfoList(changeset.ids()));
|
|
- break;
|
|
-
|
|
- default:
|
|
- break;
|
|
- }
|
|
-}
|
|
-
|
|
-} // namespace Digikam
|
|
diff --git a/libs/models/imagelistmodel.h b/libs/models/imagelistmodel.h
|
|
deleted file mode 100644
|
|
index a225b1b..0000000
|
|
--- a/libs/models/imagelistmodel.h
|
|
+++ /dev/null
|
|
@@ -1,63 +0,0 @@
|
|
-/* ============================================================
|
|
- *
|
|
- * This file is a part of digiKam project
|
|
- * http://www.digikam.org
|
|
- *
|
|
- * Date : 2010-12-06
|
|
- * Description : An image model based on a static list
|
|
- *
|
|
- * Copyright (C) 2010-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
|
|
- *
|
|
- * This program is free software; you can redistribute it
|
|
- * and/or modify it under the terms of the GNU General
|
|
- * Public License as published by the Free Software Foundation;
|
|
- * either version 2, or (at your option)
|
|
- * any later version.
|
|
- *
|
|
- * This program is distributed in the hope that it will be useful,
|
|
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
- * GNU General Public License for more details.
|
|
- *
|
|
- * ============================================================ */
|
|
-
|
|
-#ifndef IMAGELISTMODEL_H
|
|
-#define IMAGELISTMODEL_H
|
|
-
|
|
-// Local includes
|
|
-
|
|
-#include "imagethumbnailmodel.h"
|
|
-#include "digikam_export.h"
|
|
-
|
|
-namespace Digikam
|
|
-{
|
|
-
|
|
-class ImageChangeset;
|
|
-class CollectionImageChangeset;
|
|
-
|
|
-class DIGIKAM_DATABASE_EXPORT ImageListModel : public ImageThumbnailModel
|
|
-{
|
|
- Q_OBJECT
|
|
-
|
|
-public:
|
|
-
|
|
- explicit ImageListModel(QObject* parent = 0);
|
|
- ~ImageListModel();
|
|
-
|
|
- // NOTE: necessary methods to add and remove ImageInfos to the model are inherited from ImageModel
|
|
-
|
|
-Q_SIGNALS:
|
|
-
|
|
- /**
|
|
- * Emitted when images are removed from the model because they are removed in the database
|
|
- */
|
|
- void imageInfosRemoved(const QList<ImageInfo>& infos);
|
|
-
|
|
-protected Q_SLOTS:
|
|
-
|
|
- void slotCollectionImageChange(const CollectionImageChangeset& changeset);
|
|
-};
|
|
-
|
|
-} // namespace Digikam
|
|
-
|
|
-#endif // IMAGELISTMODEL_H
|
|
diff --git a/libs/models/imagemodel.cpp b/libs/models/imagemodel.cpp
|
|
deleted file mode 100644
|
|
index 41b43cf..0000000
|
|
--- a/libs/models/imagemodel.cpp
|
|
+++ /dev/null
|
|
@@ -1,1368 +0,0 @@
|
|
-/* ============================================================
|
|
- *
|
|
- * This file is a part of digiKam project
|
|
- * http://www.digikam.org
|
|
- *
|
|
- * Date : 2009-03-05
|
|
- * Description : Qt item model for database entries
|
|
- *
|
|
- * Copyright (C) 2009-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
|
|
- *
|
|
- * This program is free software; you can redistribute it
|
|
- * and/or modify it under the terms of the GNU General
|
|
- * Public License as published by the Free Software Foundation;
|
|
- * either version 2, or (at your option)
|
|
- * any later version.
|
|
- *
|
|
- * This program is distributed in the hope that it will be useful,
|
|
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
- * GNU General Public License for more details.
|
|
- *
|
|
- * ============================================================ */
|
|
-
|
|
-#include "imagemodel.h"
|
|
-
|
|
-// Qt includes
|
|
-
|
|
-#include <QHash>
|
|
-#include <QItemSelection>
|
|
-
|
|
-// Local includes
|
|
-
|
|
-#include "digikam_debug.h"
|
|
-#include "coredbchangesets.h"
|
|
-#include "coredbfields.h"
|
|
-#include "coredbwatch.h"
|
|
-#include "imageinfo.h"
|
|
-#include "imageinfolist.h"
|
|
-#include "abstractitemdragdrophandler.h"
|
|
-
|
|
-namespace Digikam
|
|
-{
|
|
-
|
|
-class ImageModel::Private
|
|
-{
|
|
-public:
|
|
-
|
|
- Private()
|
|
- {
|
|
- preprocessor = 0;
|
|
- keepFilePathCache = false;
|
|
- sendRemovalSignals = false;
|
|
- incrementalUpdater = 0;
|
|
- refreshing = false;
|
|
- reAdding = false;
|
|
- incrementalRefreshRequested = false;
|
|
- }
|
|
-
|
|
- ImageInfoList infos;
|
|
- QList<QVariant> extraValues;
|
|
- QHash<qlonglong, int> idHash;
|
|
-
|
|
- bool keepFilePathCache;
|
|
- QHash<QString, qlonglong> filePathHash;
|
|
-
|
|
- bool sendRemovalSignals;
|
|
-
|
|
- QObject* preprocessor;
|
|
- bool refreshing;
|
|
- bool reAdding;
|
|
- bool incrementalRefreshRequested;
|
|
-
|
|
- DatabaseFields::Set watchFlags;
|
|
-
|
|
- class ImageModelIncrementalUpdater* incrementalUpdater;
|
|
-
|
|
- ImageInfoList pendingInfos;
|
|
- QList<QVariant> pendingExtraValues;
|
|
-
|
|
- inline bool isValid(const QModelIndex& index)
|
|
- {
|
|
- if (!index.isValid())
|
|
- {
|
|
- return false;
|
|
- }
|
|
-
|
|
- if (index.row() < 0 || index.row() >= infos.size())
|
|
- {
|
|
- qCDebug(DIGIKAM_GENERAL_LOG) << "Invalid index" << index;
|
|
- return false;
|
|
- }
|
|
-
|
|
- return true;
|
|
- }
|
|
- inline bool extraValueValid(const QModelIndex& index)
|
|
- {
|
|
- // we assume isValid() being called before, no duplicate checks
|
|
- if (index.row() >= extraValues.size())
|
|
- {
|
|
- qCDebug(DIGIKAM_GENERAL_LOG) << "Invalid index for extraData" << index;
|
|
- return false;
|
|
- }
|
|
-
|
|
- return true;
|
|
- }
|
|
-};
|
|
-
|
|
-typedef QPair<int, int> IntPair; // to make foreach macro happy
|
|
-typedef QList<IntPair> IntPairList;
|
|
-
|
|
-class ImageModelIncrementalUpdater
|
|
-{
|
|
-public:
|
|
-
|
|
- explicit ImageModelIncrementalUpdater(ImageModel::Private* d);
|
|
-
|
|
- void appendInfos(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues);
|
|
- void aboutToBeRemovedInModel(const IntPairList& aboutToBeRemoved);
|
|
- QList<IntPair> oldIndexes();
|
|
-
|
|
- static QList<IntPair> toContiguousPairs(const QList<int>& ids);
|
|
-
|
|
-public:
|
|
-
|
|
- QHash<qlonglong, int> oldIds;
|
|
- QList<QVariant> oldExtraValues;
|
|
- QList<ImageInfo> newInfos;
|
|
- QList<QVariant> newExtraValues;
|
|
- QList<IntPairList> modelRemovals;
|
|
-};
|
|
-
|
|
-ImageModel::ImageModel(QObject* parent)
|
|
- : QAbstractListModel(parent),
|
|
- d(new Private)
|
|
-{
|
|
- connect(CoreDbAccess::databaseWatch(), SIGNAL(imageChange(ImageChangeset)),
|
|
- this, SLOT(slotImageChange(ImageChangeset)));
|
|
-
|
|
- connect(CoreDbAccess::databaseWatch(), SIGNAL(imageTagChange(ImageTagChangeset)),
|
|
- this, SLOT(slotImageTagChange(ImageTagChangeset)));
|
|
-}
|
|
-
|
|
-ImageModel::~ImageModel()
|
|
-{
|
|
- delete d->incrementalUpdater;
|
|
- delete d;
|
|
-}
|
|
-
|
|
-// ------------ Access methods -------------
|
|
-
|
|
-void ImageModel::setKeepsFilePathCache(bool keepCache)
|
|
-{
|
|
- d->keepFilePathCache = keepCache;
|
|
-}
|
|
-
|
|
-bool ImageModel::keepsFilePathCache() const
|
|
-{
|
|
- return d->keepFilePathCache;
|
|
-}
|
|
-
|
|
-bool ImageModel::isEmpty() const
|
|
-{
|
|
- return d->infos.isEmpty();
|
|
-}
|
|
-
|
|
-void ImageModel::setWatchFlags(const DatabaseFields::Set& set)
|
|
-{
|
|
- d->watchFlags = set;
|
|
-}
|
|
-
|
|
-ImageInfo ImageModel::imageInfo(const QModelIndex& index) const
|
|
-{
|
|
- if (!d->isValid(index))
|
|
- {
|
|
- return ImageInfo();
|
|
- }
|
|
-
|
|
- return d->infos.at(index.row());
|
|
-}
|
|
-
|
|
-ImageInfo& ImageModel::imageInfoRef(const QModelIndex& index) const
|
|
-{
|
|
- return d->infos[index.row()];
|
|
-}
|
|
-
|
|
-qlonglong ImageModel::imageId(const QModelIndex& index) const
|
|
-{
|
|
- if (!d->isValid(index))
|
|
- {
|
|
- return 0;
|
|
- }
|
|
-
|
|
- return d->infos.at(index.row()).id();
|
|
-}
|
|
-
|
|
-QList<ImageInfo> ImageModel::imageInfos(const QList<QModelIndex>& indexes) const
|
|
-{
|
|
- QList<ImageInfo> infos;
|
|
-
|
|
- foreach(const QModelIndex& index, indexes)
|
|
- {
|
|
- infos << imageInfo(index);
|
|
- }
|
|
-
|
|
- return infos;
|
|
-}
|
|
-
|
|
-QList<qlonglong> ImageModel::imageIds(const QList<QModelIndex>& indexes) const
|
|
-{
|
|
- QList<qlonglong> ids;
|
|
-
|
|
- foreach(const QModelIndex& index, indexes)
|
|
- {
|
|
- ids << imageId(index);
|
|
- }
|
|
-
|
|
- return ids;
|
|
-}
|
|
-
|
|
-ImageInfo ImageModel::imageInfo(int row) const
|
|
-{
|
|
- if (row >= d->infos.size())
|
|
- {
|
|
- return ImageInfo();
|
|
- }
|
|
-
|
|
- return d->infos.at(row);
|
|
-}
|
|
-
|
|
-ImageInfo& ImageModel::imageInfoRef(int row) const
|
|
-{
|
|
- return d->infos[row];
|
|
-}
|
|
-
|
|
-qlonglong ImageModel::imageId(int row) const
|
|
-{
|
|
- if (row < 0 || row >= d->infos.size())
|
|
- {
|
|
- return -1;
|
|
- }
|
|
-
|
|
- return d->infos.at(row).id();
|
|
-}
|
|
-
|
|
-QModelIndex ImageModel::indexForImageInfo(const ImageInfo& info) const
|
|
-{
|
|
- return indexForImageId(info.id());
|
|
-}
|
|
-
|
|
-QModelIndex ImageModel::indexForImageInfo(const ImageInfo& info, const QVariant& extraValue) const
|
|
-{
|
|
- return indexForImageId(info.id(), extraValue);
|
|
-}
|
|
-
|
|
-QList<QModelIndex> ImageModel::indexesForImageInfo(const ImageInfo& info) const
|
|
-{
|
|
- return indexesForImageId(info.id());
|
|
-}
|
|
-
|
|
-QModelIndex ImageModel::indexForImageId(qlonglong id) const
|
|
-{
|
|
- int index = d->idHash.value(id, -1);
|
|
-
|
|
- if (index != -1)
|
|
- {
|
|
- return createIndex(index, 0);
|
|
- }
|
|
-
|
|
- return QModelIndex();
|
|
-}
|
|
-
|
|
-QModelIndex ImageModel::indexForImageId(qlonglong id, const QVariant& extraValue) const
|
|
-{
|
|
- if (d->extraValues.isEmpty())
|
|
- return indexForImageId(id);
|
|
-
|
|
- QHash<qlonglong, int>::const_iterator it;
|
|
-
|
|
- for (it = d->idHash.constFind(id); it != d->idHash.constEnd() && it.key() == id; ++it)
|
|
- {
|
|
- if (d->extraValues.at(it.value()) == extraValue)
|
|
- return createIndex(it.value(), 0);
|
|
- }
|
|
-
|
|
- return QModelIndex();
|
|
-}
|
|
-
|
|
-QList<QModelIndex> ImageModel::indexesForImageId(qlonglong id) const
|
|
-{
|
|
- QList<QModelIndex> indexes;
|
|
- QHash<qlonglong, int>::const_iterator it;
|
|
-
|
|
- for (it = d->idHash.constFind(id); it != d->idHash.constEnd() && it.key() == id; ++it)
|
|
- {
|
|
- indexes << createIndex(it.value(), 0);
|
|
- }
|
|
-
|
|
- return indexes;
|
|
-}
|
|
-
|
|
-int ImageModel::numberOfIndexesForImageInfo(const ImageInfo& info) const
|
|
-{
|
|
- return numberOfIndexesForImageId(info.id());
|
|
-}
|
|
-
|
|
-int ImageModel::numberOfIndexesForImageId(qlonglong id) const
|
|
-{
|
|
- if (d->extraValues.isEmpty())
|
|
- {
|
|
- return 0;
|
|
- }
|
|
-
|
|
- int count = 0;
|
|
- QHash<qlonglong,int>::const_iterator it;
|
|
-
|
|
- for (it = d->idHash.constFind(id); it != d->idHash.constEnd() && it.key() == id; ++it)
|
|
- {
|
|
- ++count;
|
|
- }
|
|
-
|
|
- return count;
|
|
-}
|
|
-
|
|
-// static method
|
|
-ImageInfo ImageModel::retrieveImageInfo(const QModelIndex& index)
|
|
-{
|
|
- if (!index.isValid())
|
|
- {
|
|
- return ImageInfo();
|
|
- }
|
|
-
|
|
- ImageModel* const model = index.data(ImageModelPointerRole).value<ImageModel*>();
|
|
- int row = index.data(ImageModelInternalId).toInt();
|
|
-
|
|
- if (!model)
|
|
- {
|
|
- return ImageInfo();
|
|
- }
|
|
-
|
|
- return model->imageInfo(row);
|
|
-}
|
|
-
|
|
-// static method
|
|
-qlonglong ImageModel::retrieveImageId(const QModelIndex& index)
|
|
-{
|
|
- if (!index.isValid())
|
|
- {
|
|
- return 0;
|
|
- }
|
|
-
|
|
- ImageModel* const model = index.data(ImageModelPointerRole).value<ImageModel*>();
|
|
- int row = index.data(ImageModelInternalId).toInt();
|
|
-
|
|
- if (!model)
|
|
- {
|
|
- return 0;
|
|
- }
|
|
-
|
|
- return model->imageId(row);
|
|
-}
|
|
-
|
|
-QModelIndex ImageModel::indexForPath(const QString& filePath) const
|
|
-{
|
|
- if (d->keepFilePathCache)
|
|
- {
|
|
- return indexForImageId(d->filePathHash.value(filePath));
|
|
- }
|
|
- else
|
|
- {
|
|
- const int size = d->infos.size();
|
|
-
|
|
- for (int i=0; i<size; ++i)
|
|
- {
|
|
- if (d->infos.at(i).filePath() == filePath)
|
|
- {
|
|
- return createIndex(i, 0);
|
|
- }
|
|
- }
|
|
- }
|
|
-
|
|
- return QModelIndex();
|
|
-}
|
|
-
|
|
-QList<QModelIndex> ImageModel::indexesForPath(const QString& filePath) const
|
|
-{
|
|
- if (d->keepFilePathCache)
|
|
- {
|
|
- return indexesForImageId(d->filePathHash.value(filePath));
|
|
- }
|
|
- else
|
|
- {
|
|
- QList<QModelIndex> indexes;
|
|
- const int size = d->infos.size();
|
|
-
|
|
- for (int i=0; i<size; ++i)
|
|
- {
|
|
- if (d->infos.at(i).filePath() == filePath)
|
|
- {
|
|
- indexes << createIndex(i, 0);
|
|
- }
|
|
- }
|
|
-
|
|
- return indexes;
|
|
- }
|
|
-}
|
|
-
|
|
-ImageInfo ImageModel::imageInfo(const QString& filePath) const
|
|
-{
|
|
- if (d->keepFilePathCache)
|
|
- {
|
|
- qlonglong id = d->filePathHash.value(filePath);
|
|
-
|
|
- if (id)
|
|
- {
|
|
- int index = d->idHash.value(id, -1);
|
|
-
|
|
- if (index != -1)
|
|
- {
|
|
- return d->infos.at(index);
|
|
- }
|
|
- }
|
|
- }
|
|
- else
|
|
- {
|
|
- foreach(const ImageInfo& info, d->infos)
|
|
- {
|
|
- if (info.filePath() == filePath)
|
|
- {
|
|
- return info;
|
|
- }
|
|
- }
|
|
- }
|
|
-
|
|
- return ImageInfo();
|
|
-}
|
|
-
|
|
-QList<ImageInfo> ImageModel::imageInfos(const QString& filePath) const
|
|
-{
|
|
- QList<ImageInfo> infos;
|
|
-
|
|
- if (d->keepFilePathCache)
|
|
- {
|
|
- qlonglong id = d->filePathHash.value(filePath);
|
|
-
|
|
- if (id)
|
|
- {
|
|
- foreach(int index, d->idHash.values(id))
|
|
- {
|
|
- infos << d->infos.at(index);
|
|
- }
|
|
- }
|
|
- }
|
|
- else
|
|
- {
|
|
- foreach(const ImageInfo& info, d->infos)
|
|
- {
|
|
- if (info.filePath() == filePath)
|
|
- {
|
|
- infos << info;
|
|
- }
|
|
- }
|
|
- }
|
|
-
|
|
- return infos;
|
|
-}
|
|
-
|
|
-void ImageModel::addImageInfo(const ImageInfo& info)
|
|
-{
|
|
- addImageInfos(QList<ImageInfo>() << info, QList<QVariant>());
|
|
-}
|
|
-
|
|
-void ImageModel::addImageInfos(const QList<ImageInfo>& infos)
|
|
-{
|
|
- addImageInfos(infos, QList<QVariant>());
|
|
-}
|
|
-
|
|
-void ImageModel::addImageInfos(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues)
|
|
-{
|
|
- if (infos.isEmpty())
|
|
- {
|
|
- return;
|
|
- }
|
|
-
|
|
- if (d->incrementalUpdater)
|
|
- {
|
|
- d->incrementalUpdater->appendInfos(infos, extraValues);
|
|
- }
|
|
- else
|
|
- {
|
|
- appendInfos(infos, extraValues);
|
|
- }
|
|
-}
|
|
-
|
|
-void ImageModel::addImageInfoSynchronously(const ImageInfo& info)
|
|
-{
|
|
- addImageInfosSynchronously(QList<ImageInfo>() << info, QList<QVariant>());
|
|
-}
|
|
-
|
|
-void ImageModel::addImageInfosSynchronously(const QList<ImageInfo>& infos)
|
|
-{
|
|
- addImageInfos(infos, QList<QVariant>());
|
|
-}
|
|
-
|
|
-void ImageModel::addImageInfosSynchronously(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues)
|
|
-{
|
|
- if (infos.isEmpty())
|
|
- {
|
|
- return;
|
|
- }
|
|
-
|
|
- publiciseInfos(infos, extraValues);
|
|
- emit processAdded(infos, extraValues);
|
|
-}
|
|
-
|
|
-void ImageModel::ensureHasImageInfo(const ImageInfo& info)
|
|
-{
|
|
- ensureHasImageInfos(QList<ImageInfo>() << info, QList<QVariant>());
|
|
-}
|
|
-
|
|
-void ImageModel::ensureHasImageInfos(const QList<ImageInfo>& infos)
|
|
-{
|
|
- ensureHasImageInfos(infos, QList<QVariant>());
|
|
-}
|
|
-
|
|
-void ImageModel::ensureHasImageInfos(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues)
|
|
-{
|
|
- if (extraValues.isEmpty())
|
|
- {
|
|
- if (!d->pendingExtraValues.isEmpty())
|
|
- {
|
|
- qCDebug(DIGIKAM_GENERAL_LOG) << "ExtraValue / No Extra Value mismatch. Ignoring added infos.";
|
|
- return;
|
|
- }
|
|
- }
|
|
- else
|
|
- {
|
|
- if (d->pendingInfos.size() != d->pendingExtraValues.size())
|
|
- {
|
|
- qCDebug(DIGIKAM_GENERAL_LOG) << "ExtraValue / No Extra Value mismatch. Ignoring added infos.";
|
|
- return;
|
|
- }
|
|
- }
|
|
-
|
|
- d->pendingInfos << infos;
|
|
- d->pendingExtraValues << extraValues;
|
|
- cleanSituationChecks();
|
|
-}
|
|
-
|
|
-void ImageModel::clearImageInfos()
|
|
-{
|
|
- d->infos.clear();
|
|
- d->extraValues.clear();
|
|
- d->idHash.clear();
|
|
- d->filePathHash.clear();
|
|
- delete d->incrementalUpdater;
|
|
- d->incrementalUpdater = 0;
|
|
- d->pendingInfos.clear();
|
|
- d->pendingExtraValues.clear();
|
|
- d->refreshing = false;
|
|
- d->reAdding = false;
|
|
- d->incrementalRefreshRequested = false;
|
|
-
|
|
- beginResetModel();
|
|
- endResetModel();
|
|
-
|
|
- imageInfosCleared();
|
|
-}
|
|
-
|
|
-void ImageModel::setImageInfos(const QList<ImageInfo>& infos)
|
|
-{
|
|
- clearImageInfos();
|
|
- addImageInfos(infos);
|
|
-}
|
|
-
|
|
-QList<ImageInfo> ImageModel::imageInfos() const
|
|
-{
|
|
- return d->infos;
|
|
-}
|
|
-
|
|
-QList<qlonglong> ImageModel::imageIds() const
|
|
-{
|
|
- return d->idHash.keys();
|
|
-}
|
|
-
|
|
-bool ImageModel::hasImage(qlonglong id) const
|
|
-{
|
|
- return d->idHash.contains(id);
|
|
-}
|
|
-
|
|
-bool ImageModel::hasImage(const ImageInfo& info) const
|
|
-{
|
|
- return d->idHash.contains(info.id());
|
|
-}
|
|
-
|
|
-bool ImageModel::hasImage(const ImageInfo& info, const QVariant& extraValue) const
|
|
-{
|
|
- return hasImage(info.id(), extraValue);
|
|
-}
|
|
-
|
|
-bool ImageModel::hasImage(qlonglong id, const QVariant& extraValue) const
|
|
-{
|
|
- if (d->extraValues.isEmpty())
|
|
- return hasImage(id);
|
|
-
|
|
- QHash<qlonglong, int>::const_iterator it;
|
|
-
|
|
- for (it = d->idHash.constFind(id); it != d->idHash.constEnd() && it.key() == id; ++it)
|
|
- {
|
|
- if (d->extraValues.at(it.value()) == extraValue)
|
|
- return true;
|
|
- }
|
|
-
|
|
- return false;;
|
|
-}
|
|
-
|
|
-QList<ImageInfo> ImageModel::uniqueImageInfos() const
|
|
-{
|
|
- if (d->extraValues.isEmpty())
|
|
- {
|
|
- return d->infos;
|
|
- }
|
|
-
|
|
- QList<ImageInfo> uniqueInfos;
|
|
- const int size = d->infos.size();
|
|
-
|
|
- for (int i=0; i<size; ++i)
|
|
- {
|
|
- const ImageInfo& info = d->infos.at(i);
|
|
-
|
|
- if (d->idHash.value(info.id()) == i)
|
|
- {
|
|
- uniqueInfos << info;
|
|
- }
|
|
- }
|
|
-
|
|
- return uniqueInfos;
|
|
-}
|
|
-
|
|
-void ImageModel::emitDataChangedForAll()
|
|
-{
|
|
- if (d->infos.isEmpty())
|
|
- {
|
|
- return;
|
|
- }
|
|
-
|
|
- QModelIndex first = createIndex(0, 0);
|
|
- QModelIndex last = createIndex(d->infos.size() - 1, 0);
|
|
- emit dataChanged(first, last);
|
|
-}
|
|
-
|
|
-void ImageModel::emitDataChangedForSelection(const QItemSelection& selection)
|
|
-{
|
|
- if (!selection.isEmpty())
|
|
- {
|
|
- foreach(const QItemSelectionRange& range, selection)
|
|
- {
|
|
- emit dataChanged(range.topLeft(), range.bottomRight());
|
|
- }
|
|
- }
|
|
-}
|
|
-
|
|
-void ImageModel::ensureHasGroupedImages(const ImageInfo& groupLeader)
|
|
-{
|
|
- ensureHasImageInfos(groupLeader.groupedImages());
|
|
-}
|
|
-
|
|
-// ------------ Preprocessing -------------
|
|
-
|
|
-void ImageModel::setPreprocessor(QObject* preprocessor)
|
|
-{
|
|
- unsetPreprocessor(d->preprocessor);
|
|
- d->preprocessor = preprocessor;
|
|
-}
|
|
-
|
|
-void ImageModel::unsetPreprocessor(QObject* preprocessor)
|
|
-{
|
|
- if (preprocessor && d->preprocessor == preprocessor)
|
|
- {
|
|
- disconnect(this, SIGNAL(preprocess(QList<ImageInfo>,QList<QVariant>)), 0, 0);
|
|
- disconnect(d->preprocessor, 0, this, SLOT(reAddImageInfos(QList<ImageInfo>,QList<QVariant>)));
|
|
- disconnect(d->preprocessor, 0, this, SLOT(reAddingFinished()));
|
|
- }
|
|
-}
|
|
-
|
|
-void ImageModel::appendInfos(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues)
|
|
-{
|
|
- if (infos.isEmpty())
|
|
- {
|
|
- return;
|
|
- }
|
|
-
|
|
- if (d->preprocessor)
|
|
- {
|
|
- d->reAdding = true;
|
|
- emit preprocess(infos, extraValues);
|
|
- }
|
|
- else
|
|
- {
|
|
- publiciseInfos(infos, extraValues);
|
|
- }
|
|
-}
|
|
-
|
|
-void ImageModel::appendInfosChecked(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues)
|
|
-{
|
|
- // This method does deduplication. It is private because in context of readding or refreshing it is of no use.
|
|
-
|
|
- if (extraValues.isEmpty())
|
|
- {
|
|
- QList<ImageInfo> checkedInfos;
|
|
-
|
|
- foreach (const ImageInfo& info, infos)
|
|
- {
|
|
- if (!hasImage(info))
|
|
- {
|
|
- checkedInfos << info;
|
|
- }
|
|
- }
|
|
-
|
|
- appendInfos(checkedInfos, QList<QVariant>());
|
|
- }
|
|
- else
|
|
- {
|
|
- QList<ImageInfo> checkedInfos;
|
|
- QList<QVariant> checkedExtraValues;
|
|
- const int size = infos.size();
|
|
-
|
|
- for (int i=0; i<size; i++)
|
|
- {
|
|
- if (!hasImage(infos[i], extraValues[i]))
|
|
- {
|
|
- checkedInfos << infos[i];
|
|
- checkedExtraValues << extraValues[i];
|
|
- }
|
|
- }
|
|
-
|
|
- appendInfos(checkedInfos, checkedExtraValues);
|
|
- }
|
|
-}
|
|
-
|
|
-void ImageModel::reAddImageInfos(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues)
|
|
-{
|
|
- // addImageInfos -> appendInfos -> preprocessor -> reAddImageInfos
|
|
- publiciseInfos(infos, extraValues);
|
|
-}
|
|
-
|
|
-void ImageModel::reAddingFinished()
|
|
-{
|
|
- d->reAdding = false;
|
|
- cleanSituationChecks();
|
|
-}
|
|
-
|
|
-void ImageModel::startRefresh()
|
|
-{
|
|
- d->refreshing = true;
|
|
-}
|
|
-
|
|
-void ImageModel::finishRefresh()
|
|
-{
|
|
- d->refreshing = false;
|
|
- cleanSituationChecks();
|
|
-}
|
|
-
|
|
-bool ImageModel::isRefreshing() const
|
|
-{
|
|
- return d->refreshing;
|
|
-}
|
|
-
|
|
-void ImageModel::cleanSituationChecks()
|
|
-{
|
|
- // For starting an incremental refresh we want a clear situation:
|
|
- // Any remaining batches from non-incremental refreshing subclasses have been received in appendInfos(),
|
|
- // any batches sent to preprocessor for re-adding have been re-added.
|
|
- if (d->refreshing || d->reAdding)
|
|
- {
|
|
- return;
|
|
- }
|
|
-
|
|
- if (!d->pendingInfos.isEmpty())
|
|
- {
|
|
- appendInfosChecked(d->pendingInfos, d->pendingExtraValues);
|
|
- d->pendingInfos.clear();
|
|
- d->pendingExtraValues.clear();
|
|
- cleanSituationChecks();
|
|
- return;
|
|
- }
|
|
-
|
|
- if (d->incrementalRefreshRequested)
|
|
- {
|
|
- d->incrementalRefreshRequested = false;
|
|
- emit readyForIncrementalRefresh();
|
|
- }
|
|
- else
|
|
- {
|
|
- emit allRefreshingFinished();
|
|
- }
|
|
-}
|
|
-
|
|
-void ImageModel::publiciseInfos(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues)
|
|
-{
|
|
- if (infos.isEmpty())
|
|
- {
|
|
- return;
|
|
- }
|
|
-
|
|
- Q_ASSERT(infos.size() == extraValues.size() || (extraValues.isEmpty() && d->extraValues.isEmpty()));
|
|
-
|
|
- emit imageInfosAboutToBeAdded(infos);
|
|
- const int firstNewIndex = d->infos.size();
|
|
- const int lastNewIndex = d->infos.size() + infos.size() - 1;
|
|
- beginInsertRows(QModelIndex(), firstNewIndex, lastNewIndex);
|
|
- d->infos << infos;
|
|
- d->extraValues << extraValues;
|
|
-
|
|
- for (int i=firstNewIndex; i<=lastNewIndex; ++i)
|
|
- {
|
|
- const ImageInfo& info = d->infos.at(i);
|
|
- qlonglong id = info.id();
|
|
- d->idHash.insertMulti(id, i);
|
|
-
|
|
- if (d->keepFilePathCache)
|
|
- {
|
|
- d->filePathHash[info.filePath()] = id;
|
|
- }
|
|
- }
|
|
-
|
|
- endInsertRows();
|
|
- emit imageInfosAdded(infos);
|
|
-}
|
|
-
|
|
-void ImageModel::requestIncrementalRefresh()
|
|
-{
|
|
- if (d->reAdding)
|
|
- {
|
|
- d->incrementalRefreshRequested = true;
|
|
- }
|
|
- else
|
|
- {
|
|
- emit readyForIncrementalRefresh();
|
|
- }
|
|
-}
|
|
-
|
|
-bool ImageModel::hasIncrementalRefreshPending() const
|
|
-{
|
|
- return d->incrementalRefreshRequested;
|
|
-}
|
|
-
|
|
-void ImageModel::startIncrementalRefresh()
|
|
-{
|
|
- delete d->incrementalUpdater;
|
|
-
|
|
- d->incrementalUpdater = new ImageModelIncrementalUpdater(d);
|
|
-}
|
|
-
|
|
-void ImageModel::finishIncrementalRefresh()
|
|
-{
|
|
- if (!d->incrementalUpdater)
|
|
- {
|
|
- return;
|
|
- }
|
|
-
|
|
- // remove old entries
|
|
- QList<QPair<int, int> > pairs = d->incrementalUpdater->oldIndexes();
|
|
- removeRowPairs(pairs);
|
|
-
|
|
- // add new indexes
|
|
- appendInfos(d->incrementalUpdater->newInfos, d->incrementalUpdater->newExtraValues);
|
|
-
|
|
- delete d->incrementalUpdater;
|
|
- d->incrementalUpdater = 0;
|
|
-}
|
|
-
|
|
-void ImageModel::removeIndex(const QModelIndex& index)
|
|
-{
|
|
- removeIndexes(QList<QModelIndex>() << index);
|
|
-}
|
|
-
|
|
-void ImageModel::removeIndexes(const QList<QModelIndex>& indexes)
|
|
-{
|
|
- QList<int> listIndexes;
|
|
-
|
|
- foreach(const QModelIndex& index, indexes)
|
|
- {
|
|
- if (d->isValid(index))
|
|
- {
|
|
- listIndexes << index.row();
|
|
- }
|
|
- }
|
|
-
|
|
- if (listIndexes.isEmpty())
|
|
- {
|
|
- return;
|
|
- }
|
|
-
|
|
- removeRowPairsWithCheck(ImageModelIncrementalUpdater::toContiguousPairs(listIndexes));
|
|
-}
|
|
-
|
|
-void ImageModel::removeImageInfo(const ImageInfo& info)
|
|
-{
|
|
- removeImageInfos(QList<ImageInfo>() << info);
|
|
-}
|
|
-
|
|
-void ImageModel::removeImageInfos(const QList<ImageInfo>& infos)
|
|
-{
|
|
- QList<int> listIndexes;
|
|
-
|
|
- foreach(const ImageInfo& info, infos)
|
|
- {
|
|
- QModelIndex index = indexForImageId(info.id());
|
|
-
|
|
- if (index.isValid())
|
|
- {
|
|
- listIndexes << index.row();
|
|
- }
|
|
- }
|
|
- removeRowPairsWithCheck(ImageModelIncrementalUpdater::toContiguousPairs(listIndexes));
|
|
-}
|
|
-
|
|
-void ImageModel::removeImageInfos(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues)
|
|
-{
|
|
- if (extraValues.isEmpty())
|
|
- {
|
|
- removeImageInfos(infos);
|
|
- return;
|
|
- }
|
|
-
|
|
- QList<int> listIndexes;
|
|
-
|
|
- for (int i=0; i<infos.size(); ++i)
|
|
- {
|
|
- QModelIndex index = indexForImageId(infos.at(i).id(), extraValues.at(i));
|
|
-
|
|
- if (index.isValid())
|
|
- {
|
|
- listIndexes << index.row();
|
|
- }
|
|
- }
|
|
-
|
|
- removeRowPairsWithCheck(ImageModelIncrementalUpdater::toContiguousPairs(listIndexes));
|
|
-}
|
|
-
|
|
-void ImageModel::setSendRemovalSignals(bool send)
|
|
-{
|
|
- d->sendRemovalSignals = send;
|
|
-}
|
|
-
|
|
-template <class List, typename T>
|
|
-static bool pairsContain(const List& list, T value)
|
|
-{
|
|
- typename List::const_iterator middle;
|
|
- typename List::const_iterator begin = list.begin();
|
|
- typename List::const_iterator end = list.end();
|
|
- int n = int(end - begin);
|
|
- int half;
|
|
-
|
|
- while (n > 0)
|
|
- {
|
|
- half = n >> 1;
|
|
- middle = begin + half;
|
|
-
|
|
- if (middle->first <= value && middle->second >= value)
|
|
- {
|
|
- return true;
|
|
- }
|
|
- else if (middle->second < value)
|
|
- {
|
|
- begin = middle + 1;
|
|
- n -= half + 1;
|
|
- }
|
|
- else
|
|
- {
|
|
- n = half;
|
|
- }
|
|
- }
|
|
-
|
|
- return false;
|
|
-}
|
|
-
|
|
-void ImageModel::removeRowPairsWithCheck(const QList<QPair<int, int> >& toRemove)
|
|
-{
|
|
- if (d->incrementalUpdater)
|
|
- {
|
|
- d->incrementalUpdater->aboutToBeRemovedInModel(toRemove);
|
|
- }
|
|
-
|
|
- removeRowPairs(toRemove);
|
|
-}
|
|
-
|
|
-void ImageModel::removeRowPairs(const QList<QPair<int, int> >& toRemove)
|
|
-{
|
|
- if (toRemove.isEmpty())
|
|
- {
|
|
- return;
|
|
- }
|
|
-
|
|
- // Remove old indexes
|
|
- // Keep in mind that when calling beginRemoveRows all structures announced to be removed
|
|
- // must still be valid, and this includes our hashes as well, which limits what we can optimize
|
|
-
|
|
- int removedRows = 0, offset = 0;
|
|
- typedef QPair<int, int> IntPair; // to make foreach macro happy
|
|
-
|
|
- foreach(const IntPair& pair, toRemove)
|
|
- {
|
|
- const int begin = pair.first - offset;
|
|
- const int end = pair.second - offset; // inclusive
|
|
- removedRows = end - begin + 1;
|
|
-
|
|
- // when removing from the list, all subsequent indexes are affected
|
|
- offset += removedRows;
|
|
-
|
|
- QList<ImageInfo> removedInfos;
|
|
-
|
|
- if (d->sendRemovalSignals)
|
|
- {
|
|
- qCopy(d->infos.begin() + begin, d->infos.begin() + end, removedInfos.begin());
|
|
- emit imageInfosAboutToBeRemoved(removedInfos);
|
|
- }
|
|
-
|
|
- imageInfosAboutToBeRemoved(begin, end);
|
|
- beginRemoveRows(QModelIndex(), begin, end);
|
|
-
|
|
- // update idHash - which points to indexes of d->infos, and these change now!
|
|
- QHash<qlonglong, int>::iterator it;
|
|
-
|
|
- for (it = d->idHash.begin(); it != d->idHash.end(); )
|
|
- {
|
|
- if (it.value() >= begin)
|
|
- {
|
|
- if (it.value() > end)
|
|
- {
|
|
- // after the removed interval: adjust index
|
|
- it.value() -= removedRows;
|
|
- }
|
|
- else
|
|
- {
|
|
- // in the removed interval
|
|
- it = d->idHash.erase(it);
|
|
- continue;
|
|
- }
|
|
- }
|
|
-
|
|
- ++it;
|
|
- }
|
|
-
|
|
- // remove from list
|
|
- d->infos.erase(d->infos.begin() + begin, d->infos.begin() + (end + 1));
|
|
-
|
|
- if (!d->extraValues.isEmpty())
|
|
- {
|
|
- d->extraValues.erase(d->extraValues.begin() + begin, d->extraValues.begin() + (end + 1));
|
|
- }
|
|
-
|
|
- endRemoveRows();
|
|
-
|
|
- if (d->sendRemovalSignals)
|
|
- {
|
|
- emit imageInfosRemoved(removedInfos);
|
|
- }
|
|
- }
|
|
-
|
|
- // tidy up: remove old indexes from file path hash now
|
|
- if (d->keepFilePathCache)
|
|
- {
|
|
- QHash<QString, qlonglong>::iterator it;
|
|
-
|
|
- for (it = d->filePathHash.begin(); it != d->filePathHash.end(); )
|
|
- {
|
|
- if (pairsContain(toRemove, it.value()))
|
|
- {
|
|
- it = d->filePathHash.erase(it);
|
|
- }
|
|
- else
|
|
- {
|
|
- ++it;
|
|
- }
|
|
- }
|
|
- }
|
|
-}
|
|
-
|
|
-ImageModelIncrementalUpdater::ImageModelIncrementalUpdater(ImageModel::Private* d)
|
|
-{
|
|
- oldIds = d->idHash;
|
|
- oldExtraValues = d->extraValues;
|
|
-}
|
|
-
|
|
-void ImageModelIncrementalUpdater::appendInfos(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues)
|
|
-{
|
|
- if (extraValues.isEmpty())
|
|
- {
|
|
- foreach(const ImageInfo& info, infos)
|
|
- {
|
|
- QHash<qlonglong,int>::iterator it = oldIds.find(info.id());
|
|
-
|
|
- if (it != oldIds.end())
|
|
- {
|
|
- oldIds.erase(it);
|
|
- }
|
|
- else
|
|
- {
|
|
- newInfos << info;
|
|
- }
|
|
- }
|
|
- }
|
|
- else
|
|
- {
|
|
- for (int i=0; i<infos.size(); ++i)
|
|
- {
|
|
- const ImageInfo& info = infos.at(i);
|
|
- bool found = false;
|
|
- QHash<qlonglong,int>::iterator it;
|
|
-
|
|
- for (it = oldIds.find(info.id()); it != oldIds.end() && it.key() == info.id(); ++it)
|
|
- {
|
|
- // first check is for bug #262596. Not sure if needed.
|
|
- if (it.value() < oldExtraValues.size() && extraValues.at(i) == oldExtraValues.at(it.value()))
|
|
- {
|
|
- found = true;
|
|
- break;
|
|
- }
|
|
- }
|
|
-
|
|
- if (found)
|
|
- {
|
|
- oldIds.erase(it);
|
|
- // do not erase from oldExtraValues - oldIds is a hash id -> index.
|
|
- }
|
|
- else
|
|
- {
|
|
- newInfos << info;
|
|
- newExtraValues << extraValues.at(i);
|
|
- }
|
|
- }
|
|
- }
|
|
-}
|
|
-
|
|
-void ImageModelIncrementalUpdater::aboutToBeRemovedInModel(const IntPairList& toRemove)
|
|
-{
|
|
- modelRemovals << toRemove;
|
|
-}
|
|
-
|
|
-QList<QPair<int, int> > ImageModelIncrementalUpdater::oldIndexes()
|
|
-{
|
|
- // first, apply all changes to indexes by direct removal in model
|
|
- // while the updater was active
|
|
- foreach(const IntPairList& list, modelRemovals)
|
|
- {
|
|
- int removedRows = 0, offset = 0;
|
|
-
|
|
- foreach(const IntPair& pair, list)
|
|
- {
|
|
- const int begin = pair.first - offset;
|
|
- const int end = pair.second - offset; // inclusive
|
|
- removedRows = end - begin + 1;
|
|
-
|
|
- // when removing from the list, all subsequent indexes are affected
|
|
- offset += removedRows;
|
|
-
|
|
- // update idHash - which points to indexes of d->infos, and these change now!
|
|
- QHash<qlonglong, int>::iterator it;
|
|
-
|
|
- for (it = oldIds.begin(); it != oldIds.end(); )
|
|
- {
|
|
- if (it.value() >= begin)
|
|
- {
|
|
- if (it.value() > end)
|
|
- {
|
|
- // after the removed interval: adjust index
|
|
- it.value() -= removedRows;
|
|
- }
|
|
- else
|
|
- {
|
|
- // in the removed interval
|
|
- it = oldIds.erase(it);
|
|
- continue;
|
|
- }
|
|
- }
|
|
-
|
|
- ++it;
|
|
- }
|
|
- }
|
|
- }
|
|
-
|
|
- modelRemovals.clear();
|
|
-
|
|
- return toContiguousPairs(oldIds.values());
|
|
-}
|
|
-
|
|
-QList<QPair<int, int> > ImageModelIncrementalUpdater::toContiguousPairs(const QList<int>& unsorted)
|
|
-{
|
|
- // Take the given indices and return them as contiguous pairs [begin, end]
|
|
-
|
|
- QList<QPair<int, int> > pairs;
|
|
-
|
|
- if (unsorted.isEmpty())
|
|
- {
|
|
- return pairs;
|
|
- }
|
|
-
|
|
- QList<int> indices(unsorted);
|
|
- qSort(indices);
|
|
-
|
|
- QPair<int, int> pair(indices.first(), indices.first());
|
|
-
|
|
- for (int i=1; i<indices.size(); ++i)
|
|
- {
|
|
- const int &index = indices.at(i);
|
|
-
|
|
- if (index == pair.second + 1)
|
|
- {
|
|
- pair.second = index;
|
|
- continue;
|
|
- }
|
|
-
|
|
- pairs << pair; // insert last pair
|
|
- pair.first = index;
|
|
- pair.second = index;
|
|
- }
|
|
-
|
|
- pairs << pair;
|
|
-
|
|
- return pairs;
|
|
-}
|
|
-
|
|
-// ------------ QAbstractItemModel implementation -------------
|
|
-
|
|
-QVariant ImageModel::data(const QModelIndex& index, int role) const
|
|
-{
|
|
- if (!d->isValid(index))
|
|
- {
|
|
- return QVariant();
|
|
- }
|
|
-
|
|
- switch (role)
|
|
- {
|
|
- case Qt::DisplayRole:
|
|
- case Qt::ToolTipRole:
|
|
- return d->infos.at(index.row()).name();
|
|
-
|
|
- case ImageModelPointerRole:
|
|
- return QVariant::fromValue(const_cast<ImageModel*>(this));
|
|
-
|
|
- case ImageModelInternalId:
|
|
- return index.row();
|
|
-
|
|
- case CreationDateRole:
|
|
- return d->infos.at(index.row()).dateTime();
|
|
-
|
|
- case ExtraDataRole:
|
|
-
|
|
- if (d->extraValueValid(index))
|
|
- {
|
|
- return d->extraValues.at(index.row());
|
|
- }
|
|
- else
|
|
- {
|
|
- return QVariant();
|
|
- }
|
|
-
|
|
- case ExtraDataDuplicateCount:
|
|
- {
|
|
- qlonglong id = d->infos.at(index.row()).id();
|
|
- return numberOfIndexesForImageId(id);
|
|
- }
|
|
- }
|
|
-
|
|
- return QVariant();
|
|
-}
|
|
-
|
|
-QVariant ImageModel::headerData(int section, Qt::Orientation orientation, int role) const
|
|
-{
|
|
- Q_UNUSED(section)
|
|
- Q_UNUSED(orientation)
|
|
- Q_UNUSED(role)
|
|
- return QVariant();
|
|
-}
|
|
-
|
|
-int ImageModel::rowCount(const QModelIndex& parent) const
|
|
-{
|
|
- if (parent.isValid())
|
|
- {
|
|
- return 0;
|
|
- }
|
|
-
|
|
- return d->infos.size();
|
|
-}
|
|
-
|
|
-Qt::ItemFlags ImageModel::flags(const QModelIndex& index) const
|
|
-{
|
|
- if (!d->isValid(index))
|
|
- {
|
|
- return 0;
|
|
- }
|
|
-
|
|
- Qt::ItemFlags f = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
|
|
-
|
|
- f |= dragDropFlags(index);
|
|
-
|
|
- return f;
|
|
-}
|
|
-
|
|
-QModelIndex ImageModel::index(int row, int column, const QModelIndex& parent) const
|
|
-{
|
|
- if (column != 0 || row < 0 || parent.isValid() || row >= d->infos.size())
|
|
- {
|
|
- return QModelIndex();
|
|
- }
|
|
-
|
|
- return createIndex(row, 0);
|
|
-}
|
|
-
|
|
-// ------------ Database watch -------------
|
|
-
|
|
-void ImageModel::slotImageChange(const ImageChangeset& changeset)
|
|
-{
|
|
- if (d->infos.isEmpty())
|
|
- {
|
|
- return;
|
|
- }
|
|
-
|
|
- if (d->watchFlags & changeset.changes())
|
|
- {
|
|
- QItemSelection items;
|
|
-
|
|
- foreach(const qlonglong& id, changeset.ids())
|
|
- {
|
|
- QModelIndex index = indexForImageId(id);
|
|
-
|
|
- if (index.isValid())
|
|
- {
|
|
- items.select(index, index);
|
|
- }
|
|
- }
|
|
-
|
|
- if (!items.isEmpty())
|
|
- {
|
|
- emitDataChangedForSelection(items);
|
|
- emit imageChange(changeset, items);
|
|
- }
|
|
- }
|
|
-}
|
|
-
|
|
-void ImageModel::slotImageTagChange(const ImageTagChangeset& changeset)
|
|
-{
|
|
- if (d->infos.isEmpty())
|
|
- {
|
|
- return;
|
|
- }
|
|
-
|
|
- QItemSelection items;
|
|
-
|
|
- foreach(const qlonglong& id, changeset.ids())
|
|
- {
|
|
- QModelIndex index = indexForImageId(id);
|
|
-
|
|
- if (index.isValid())
|
|
- {
|
|
- items.select(index, index);
|
|
- }
|
|
- }
|
|
-
|
|
- if (!items.isEmpty())
|
|
- {
|
|
- emitDataChangedForSelection(items);
|
|
- emit imageTagChange(changeset, items);
|
|
- }
|
|
-}
|
|
-
|
|
-} // namespace Digikam
|
|
diff --git a/libs/models/imagemodel.h b/libs/models/imagemodel.h
|
|
deleted file mode 100644
|
|
index dcf94c2..0000000
|
|
--- a/libs/models/imagemodel.h
|
|
+++ /dev/null
|
|
@@ -1,364 +0,0 @@
|
|
-/* ============================================================
|
|
- *
|
|
- * This file is a part of digiKam project
|
|
- * http://www.digikam.org
|
|
- *
|
|
- * Date : 2009-03-05
|
|
- * Description : Qt item model for database entries
|
|
- *
|
|
- * Copyright (C) 2009-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
|
|
- *
|
|
- * This program is free software; you can redistribute it
|
|
- * and/or modify it under the terms of the GNU General
|
|
- * Public License as published by the Free Software Foundation;
|
|
- * either version 2, or (at your option)
|
|
- * any later version.
|
|
- *
|
|
- * This program is distributed in the hope that it will be useful,
|
|
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
- * GNU General Public License for more details.
|
|
- *
|
|
- * ============================================================ */
|
|
-
|
|
-#ifndef IMAGEMODEL_H
|
|
-#define IMAGEMODEL_H
|
|
-
|
|
-// Qt includes
|
|
-
|
|
-#include <QAbstractListModel>
|
|
-
|
|
-// Local includes
|
|
-
|
|
-#include "dragdropimplementations.h"
|
|
-#include "imageinfo.h"
|
|
-#include "digikam_export.h"
|
|
-
|
|
-class QItemSelection;
|
|
-
|
|
-namespace Digikam
|
|
-{
|
|
-
|
|
-class ImageChangeset;
|
|
-class ImageTagChangeset;
|
|
-
|
|
-namespace DatabaseFields
|
|
-{
|
|
-class Set;
|
|
-}
|
|
-
|
|
-class DIGIKAM_DATABASE_EXPORT ImageModel : public QAbstractListModel, public DragDropModelImplementation
|
|
-{
|
|
- Q_OBJECT
|
|
-
|
|
-public:
|
|
-
|
|
- enum ImageModelRoles
|
|
- {
|
|
- /// An ImageModel* pointer to this model
|
|
- ImageModelPointerRole = Qt::UserRole,
|
|
- ImageModelInternalId = Qt::UserRole + 1,
|
|
- /// Returns a thumbnail pixmap. May be implemented by subclasses.
|
|
- /// Returns either a valid pixmap or a null QVariant.
|
|
- ThumbnailRole = Qt::UserRole + 2,
|
|
- /// Returns a QDateTime with the creation date
|
|
- CreationDateRole = Qt::UserRole + 3,
|
|
- /// Return (optional) extraData field
|
|
- ExtraDataRole = Qt::UserRole + 5,
|
|
- /// Returns the number of duplicate indexes for the same image id
|
|
- ExtraDataDuplicateCount = Qt::UserRole + 6,
|
|
-
|
|
- // Roles which are defined here but not implemented by ImageModel
|
|
- /// Returns position of item in Left Light Table preview.
|
|
- LTLeftPanelRole = Qt::UserRole + 50,
|
|
- /// Returns position of item in Right Light Table preview.
|
|
- LTRightPanelRole = Qt::UserRole + 51,
|
|
-
|
|
- // For use by subclasses
|
|
- SubclassRoles = Qt::UserRole + 100,
|
|
- // For use by filter models
|
|
- FilterModelRoles = Qt::UserRole + 500
|
|
- };
|
|
-
|
|
-public:
|
|
-
|
|
- explicit ImageModel(QObject* parent = 0);
|
|
- ~ImageModel();
|
|
-
|
|
- /** If a cache is kept, lookup by file path is fast,
|
|
- * without a cache it is O(n). Default is false.
|
|
- */
|
|
- void setKeepsFilePathCache(bool keepCache);
|
|
- bool keepsFilePathCache() const;
|
|
-
|
|
- /** Set a set of database fields to watch.
|
|
- * If either of these is changed, dataChanged() will be emitted.
|
|
- * Default is no flag (no signal will be emitted).
|
|
- */
|
|
- void setWatchFlags(const DatabaseFields::Set& set);
|
|
-
|
|
- /** Returns the ImageInfo object, reference or image id from the underlying data
|
|
- * pointed to by the index.
|
|
- * If the index is not valid, imageInfo will return a null ImageInfo, imageId will
|
|
- * return 0, imageInfoRef must not be called with an invalid index.
|
|
- */
|
|
- ImageInfo imageInfo(const QModelIndex& index) const;
|
|
- ImageInfo& imageInfoRef(const QModelIndex& index) const;
|
|
- qlonglong imageId(const QModelIndex& index) const;
|
|
- QList<ImageInfo> imageInfos(const QList<QModelIndex>& indexes) const;
|
|
- QList<qlonglong> imageIds(const QList<QModelIndex>& indexes) const;
|
|
-
|
|
- /** Returns the ImageInfo object, reference or image id from the underlying data
|
|
- * of the given row (parent is the invalid QModelIndex, column is 0).
|
|
- * Note that imageInfoRef will crash if index is invalid.
|
|
- */
|
|
- ImageInfo imageInfo(int row) const;
|
|
- ImageInfo& imageInfoRef(int row) const;
|
|
- qlonglong imageId(int row) const;
|
|
-
|
|
- /** Return the index for the given ImageInfo or id, if contained in this model.
|
|
- */
|
|
- QModelIndex indexForImageInfo(const ImageInfo& info) const;
|
|
- QModelIndex indexForImageInfo(const ImageInfo& info, const QVariant& extraValue) const;
|
|
- QModelIndex indexForImageId(qlonglong id) const;
|
|
- QModelIndex indexForImageId(qlonglong id, const QVariant& extraValue) const;
|
|
- QList<QModelIndex> indexesForImageInfo(const ImageInfo& info) const;
|
|
- QList<QModelIndex> indexesForImageId(qlonglong id) const;
|
|
-
|
|
- int numberOfIndexesForImageInfo(const ImageInfo& info) const;
|
|
- int numberOfIndexesForImageId(qlonglong id) const;
|
|
-
|
|
- /** Returns the index or ImageInfo object from the underlying data
|
|
- * for the given file path. This is fast if keepsFilePathCache is enabled.
|
|
- * The file path is as returned by ImageInfo.filePath().
|
|
- * In case of multiple occurrences of the same file, the simpler variants return
|
|
- * any one found first, use the QList methods to retrieve all occurrences.
|
|
- */
|
|
- QModelIndex indexForPath(const QString& filePath) const;
|
|
- ImageInfo imageInfo(const QString& filePath) const;
|
|
- QList<QModelIndex> indexesForPath(const QString& filePath) const;
|
|
- QList<ImageInfo> imageInfos(const QString& filePath) const;
|
|
-
|
|
- /** Main entry point for subclasses adding image infos to the model.
|
|
- * If you list entries not unique per image id, you must add an extraValue
|
|
- * so that every entry is unique by imageId and extraValues.
|
|
- * Please note that these methods do not prevent addition of duplicate entries.
|
|
- */
|
|
- void addImageInfo(const ImageInfo& info);
|
|
- void addImageInfos(const QList<ImageInfo>& infos);
|
|
- void addImageInfos(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues);
|
|
-
|
|
- /** Clears image infos and resets model.
|
|
- */
|
|
- void clearImageInfos();
|
|
-
|
|
- /** Clears and adds the infos.
|
|
- */
|
|
- void setImageInfos(const QList<ImageInfo>& infos);
|
|
-
|
|
- /**
|
|
- * Directly remove the given indexes or infos from the model.
|
|
- */
|
|
- void removeIndex(const QModelIndex& indexes);
|
|
- void removeIndexes(const QList<QModelIndex>& indexes);
|
|
- void removeImageInfo(const ImageInfo& info);
|
|
- void removeImageInfos(const QList<ImageInfo>& infos);
|
|
- void removeImageInfos(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues);
|
|
-
|
|
- /**
|
|
- * addImageInfo() is asynchronous if a prepocessor is set.
|
|
- * This method first adds the info, synchronously.
|
|
- * Only afterwards, the preprocessor will have the opportunity to process it.
|
|
- * This method also bypasses any incremental updates.
|
|
- * Please note that these methods do not prevent addition of duplicate entries.
|
|
- */
|
|
- void addImageInfoSynchronously(const ImageInfo& info);
|
|
- void addImageInfosSynchronously(const QList<ImageInfo>& infos);
|
|
- void addImageInfosSynchronously(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues);
|
|
-
|
|
- /**
|
|
- * Add the given entries. Method returns immediately, the
|
|
- * addition may happen later asynchronously.
|
|
- * These methods prevent the addition of duplicate entries.
|
|
- */
|
|
- void ensureHasImageInfo(const ImageInfo& info);
|
|
- void ensureHasImageInfos(const QList<ImageInfo>& infos);
|
|
- void ensureHasImageInfos(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues);
|
|
-
|
|
- /**
|
|
- * Ensure that all images grouped on the given leader are contained in the model.
|
|
- */
|
|
- void ensureHasGroupedImages(const ImageInfo& groupLeader);
|
|
-
|
|
- QList<ImageInfo> imageInfos() const;
|
|
- QList<qlonglong> imageIds() const;
|
|
- QList<ImageInfo> uniqueImageInfos() const;
|
|
-
|
|
- bool hasImage(qlonglong id) const;
|
|
- bool hasImage(const ImageInfo& info) const;
|
|
- bool hasImage(const ImageInfo& info, const QVariant& extraValue) const;
|
|
- bool hasImage(qlonglong id, const QVariant& extraValue) const;
|
|
-
|
|
- bool isEmpty() const;
|
|
-
|
|
- // Drag and Drop
|
|
- DECLARE_MODEL_DRAG_DROP_METHODS
|
|
-
|
|
- /**
|
|
- * Install an object as a preprocessor for ImageInfos added to this model.
|
|
- * For every QList of ImageInfos added to addImageInfo, the signal preprocess()
|
|
- * will be emitted. The preprocessor may process the items and shall then readd
|
|
- * them by calling reAddImageInfos(). It may take some time to process.
|
|
- * It shall discard any held infos when the modelReset() signal is sent.
|
|
- * It shall call readdFinished() when no reset occurred and all infos on the way have been readded.
|
|
- * This means that only after calling this method, you shall make three connections
|
|
- * (preprocess -> your slot, your signal -> reAddImageInfos, your signal -> reAddingFinished)
|
|
- * and make or already hold a connection modelReset() -> your slot.
|
|
- * There is only one preprocessor at a time, a previously set object will be disconnected.
|
|
- */
|
|
- void setPreprocessor(QObject* processor);
|
|
- void unsetPreprocessor(QObject* processor);
|
|
-
|
|
- /**
|
|
- * Returns true if this model is currently refreshing.
|
|
- * For a preprocessor this means that, although the preprocessor may currently have
|
|
- * processed all it got, more batches are to be expected.
|
|
- */
|
|
- bool isRefreshing() const;
|
|
-
|
|
- /**
|
|
- * Enable sending of imageInfosAboutToBeRemoved and imageInfosRemoved signals.
|
|
- * Default: false
|
|
- */
|
|
- void setSendRemovalSignals(bool send);
|
|
-
|
|
- virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
|
|
- virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
|
|
- virtual int rowCount(const QModelIndex& parent = QModelIndex()) const;
|
|
- virtual Qt::ItemFlags flags(const QModelIndex& index) const;
|
|
- virtual QModelIndex index(int row, int column = 0, const QModelIndex& parent = QModelIndex()) const;
|
|
-
|
|
- /** Retrieves the imageInfo object from the data() method of the given index.
|
|
- * The index may be from a QSortFilterProxyModel as long as an ImageModel is at the end. */
|
|
- static ImageInfo retrieveImageInfo(const QModelIndex& index);
|
|
- static qlonglong retrieveImageId(const QModelIndex& index);
|
|
-
|
|
-Q_SIGNALS:
|
|
-
|
|
- /** Informs that ImageInfos will be added to the model.
|
|
- * This signal is sent before the model data is changed and views are informed.
|
|
- */
|
|
- void imageInfosAboutToBeAdded(const QList<ImageInfo>& infos);
|
|
-
|
|
- /** Informs that ImageInfos have been added to the model.
|
|
- * This signal is sent after the model data is changed and views are informed.
|
|
- */
|
|
- void imageInfosAdded(const QList<ImageInfo>& infos);
|
|
-
|
|
- /** Informs that ImageInfos will be removed from the model.
|
|
- * This signal is sent before the model data is changed and views are informed.
|
|
- * Note: You need to explicitly enable sending of this signal. It is not sent
|
|
- * in clearImageInfos().
|
|
- */
|
|
- void imageInfosAboutToBeRemoved(const QList<ImageInfo>& infos);
|
|
-
|
|
- /** Informs that ImageInfos have been removed from the model.
|
|
- * This signal is sent after the model data is changed and views are informed. *
|
|
- * Note: You need to explicitly enable sending of this signal. It is not sent
|
|
- * in clearImageInfos().
|
|
- */
|
|
- void imageInfosRemoved(const QList<ImageInfo>& infos);
|
|
-
|
|
- /** Connect to this signal only if you are the current preprocessor.
|
|
- */
|
|
- void preprocess(const QList<ImageInfo>& infos, const QList<QVariant>&);
|
|
- void processAdded(const QList<ImageInfo>& infos, const QList<QVariant>&);
|
|
-
|
|
- /** If an ImageChangeset affected indexes of this model with changes as set in watchFlags(),
|
|
- * this signal contains the changeset and the affected indexes.
|
|
- */
|
|
- void imageChange(const ImageChangeset&, const QItemSelection&);
|
|
-
|
|
- /** If an ImageTagChangeset affected indexes of this model,
|
|
- * this signal contains the changeset and the affected indexes.
|
|
- */
|
|
- void imageTagChange(const ImageTagChangeset&, const QItemSelection&);
|
|
-
|
|
- /** Signals that the model is right now ready to start an incremental refresh.
|
|
- * This is guaranteed only for the scope of emitting this signal.
|
|
- */
|
|
- void readyForIncrementalRefresh();
|
|
-
|
|
- /** Signals that the model has finished currently with all scheduled
|
|
- * refreshing, full or incremental, and all preprocessing.
|
|
- * The model is in polished, clean situation right now.
|
|
- */
|
|
- void allRefreshingFinished();
|
|
-
|
|
-public Q_SLOTS:
|
|
-
|
|
- void reAddImageInfos(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues);
|
|
- void reAddingFinished();
|
|
-
|
|
-protected:
|
|
-
|
|
- /** Subclasses that add ImageInfos in batches shall call startRefresh()
|
|
- * when they start sending batches and finishRefresh() when they have finished.
|
|
- * No incremental refreshes will be started while listing.
|
|
- * A clearImageInfos() always stops listing, calling finishRefresh() is then not necessary.
|
|
- */
|
|
- void startRefresh();
|
|
- void finishRefresh();
|
|
-
|
|
- /** As soon as the model is ready to start an incremental refresh, the signal
|
|
- * readyForIncrementalRefresh() will be emitted. The signal will be emitted inline
|
|
- * if the model is ready right now.
|
|
- */
|
|
- void requestIncrementalRefresh();
|
|
- bool hasIncrementalRefreshPending() const;
|
|
-
|
|
- /** Starts an incremental refresh operation. You shall only call this method from a slot
|
|
- * connected to readyForIncrementalRefresh(). To initiate an incremental refresh,
|
|
- * call requestIncrementalRefresh().
|
|
- */
|
|
- void startIncrementalRefresh();
|
|
- void finishIncrementalRefresh();
|
|
-
|
|
- void emitDataChangedForAll();
|
|
- void emitDataChangedForSelection(const QItemSelection& selection);
|
|
-
|
|
- // Called when the internal storage is cleared
|
|
- virtual void imageInfosCleared() {};
|
|
-
|
|
- // Called before rowsAboutToBeRemoved
|
|
- virtual void imageInfosAboutToBeRemoved(int /*begin*/, int /*end*/) {};
|
|
-
|
|
-protected Q_SLOTS:
|
|
-
|
|
- virtual void slotImageChange(const ImageChangeset& changeset);
|
|
- virtual void slotImageTagChange(const ImageTagChangeset& changeset);
|
|
-
|
|
-private:
|
|
-
|
|
- void appendInfos(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues);
|
|
- void appendInfosChecked(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues);
|
|
- void publiciseInfos(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues);
|
|
- void cleanSituationChecks();
|
|
- void removeRowPairsWithCheck(const QList<QPair<int, int> >& toRemove);
|
|
- void removeRowPairs(const QList<QPair<int, int> >& toRemove);
|
|
-
|
|
-public:
|
|
-
|
|
- // Declared public because it's used in ImageModelIncrementalUpdater class
|
|
- class Private;
|
|
-
|
|
-private:
|
|
-
|
|
- Private* const d;
|
|
-};
|
|
-
|
|
-} // namespace Digikam
|
|
-
|
|
-Q_DECLARE_METATYPE(Digikam::ImageModel*)
|
|
-
|
|
-#endif // IMAGEMODEL_H
|
|
diff --git a/libs/models/imagesortsettings.cpp b/libs/models/imagesortsettings.cpp
|
|
deleted file mode 100644
|
|
index 39ee6e1..0000000
|
|
--- a/libs/models/imagesortsettings.cpp
|
|
+++ /dev/null
|
|
@@ -1,400 +0,0 @@
|
|
-/* ============================================================
|
|
- *
|
|
- * This file is a part of digiKam project
|
|
- * http://www.digikam.org
|
|
- *
|
|
- * Date : 2009-03-05
|
|
- * Description : Filter values for use with ImageFilterModel
|
|
- *
|
|
- * Copyright (C) 2009 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
|
|
- * Copyright (C) 2014 by Mohamed Anwer <m dot anwer at gmx dot com>
|
|
- *
|
|
- * This program is free software; you can redistribute it
|
|
- * and/or modify it under the terms of the GNU General
|
|
- * Public License as published by the Free Software Foundation;
|
|
- * either version 2, or (at your option)
|
|
- * any later version.
|
|
- *
|
|
- * This program is distributed in the hope that it will be useful,
|
|
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
- * GNU General Public License for more details.
|
|
- *
|
|
- * ============================================================ */
|
|
-
|
|
-#include "imagesortsettings.h"
|
|
-
|
|
-// Qt includes
|
|
-
|
|
-#include <QDateTime>
|
|
-#include <QRectF>
|
|
-
|
|
-// Local includes
|
|
-
|
|
-#include "coredbfields.h"
|
|
-#include "imageinfo.h"
|
|
-
|
|
-namespace Digikam
|
|
-{
|
|
-
|
|
-ImageSortSettings::ImageSortSettings()
|
|
-{
|
|
- categorizationMode = NoCategories;
|
|
- categorizationSortOrder = DefaultOrder;
|
|
- categorizationCaseSensitivity = Qt::CaseSensitive;
|
|
- sortRole = SortByFileName;
|
|
- sortOrder = DefaultOrder;
|
|
- strTypeNatural = true;
|
|
- sortCaseSensitivity = Qt::CaseSensitive;
|
|
- currentCategorizationSortOrder = Qt::AscendingOrder;
|
|
- currentSortOrder = Qt::AscendingOrder;
|
|
-}
|
|
-
|
|
-bool ImageSortSettings::operator==(const ImageSortSettings& other) const
|
|
-{
|
|
- return
|
|
- categorizationMode == other.categorizationMode &&
|
|
- categorizationSortOrder == other.categorizationSortOrder &&
|
|
- categorizationCaseSensitivity == other.categorizationCaseSensitivity &&
|
|
- sortRole == other.sortRole &&
|
|
- sortOrder == other.sortOrder &&
|
|
- sortCaseSensitivity == other.sortCaseSensitivity;
|
|
-}
|
|
-
|
|
-void ImageSortSettings::setCategorizationMode(CategorizationMode mode)
|
|
-{
|
|
- categorizationMode = mode;
|
|
-
|
|
- if (categorizationSortOrder == DefaultOrder)
|
|
- {
|
|
- currentCategorizationSortOrder = defaultSortOrderForCategorizationMode(categorizationMode);
|
|
- }
|
|
-}
|
|
-
|
|
-void ImageSortSettings::setCategorizationSortOrder(SortOrder order)
|
|
-{
|
|
- categorizationSortOrder = order;
|
|
-
|
|
- if (categorizationSortOrder == DefaultOrder)
|
|
- {
|
|
- currentCategorizationSortOrder = defaultSortOrderForCategorizationMode(categorizationMode);
|
|
- }
|
|
- else
|
|
- {
|
|
- currentCategorizationSortOrder = (Qt::SortOrder)categorizationSortOrder;
|
|
- }
|
|
-}
|
|
-
|
|
-void ImageSortSettings::setSortRole(SortRole role)
|
|
-{
|
|
- sortRole = role;
|
|
-
|
|
- if (sortOrder == DefaultOrder)
|
|
- {
|
|
- currentSortOrder = defaultSortOrderForSortRole(sortRole);
|
|
- }
|
|
-}
|
|
-
|
|
-void ImageSortSettings::setSortOrder(SortOrder order)
|
|
-{
|
|
- sortOrder = order;
|
|
-
|
|
- if (sortOrder == DefaultOrder)
|
|
- {
|
|
- currentSortOrder = defaultSortOrderForSortRole(sortRole);
|
|
- }
|
|
- else
|
|
- {
|
|
- currentSortOrder = (Qt::SortOrder)order;
|
|
- }
|
|
-}
|
|
-
|
|
-void ImageSortSettings::setStringTypeNatural(bool natural)
|
|
-{
|
|
- strTypeNatural = natural;
|
|
-}
|
|
-
|
|
-Qt::SortOrder ImageSortSettings::defaultSortOrderForCategorizationMode(CategorizationMode mode)
|
|
-{
|
|
- switch (mode)
|
|
- {
|
|
- case NoCategories:
|
|
- case OneCategory:
|
|
- case CategoryByAlbum:
|
|
- case CategoryByFormat:
|
|
- default:
|
|
- return Qt::AscendingOrder;
|
|
- }
|
|
-}
|
|
-
|
|
-Qt::SortOrder ImageSortSettings::defaultSortOrderForSortRole(SortRole role)
|
|
-{
|
|
- switch (role)
|
|
- {
|
|
- case SortByFileName:
|
|
- case SortByFilePath:
|
|
- return Qt::AscendingOrder;
|
|
- case SortByFileSize:
|
|
- return Qt::DescendingOrder;
|
|
- case SortByModificationDate:
|
|
- case SortByCreationDate:
|
|
- return Qt::AscendingOrder;
|
|
- case SortByRating:
|
|
- case SortByImageSize:
|
|
- return Qt::DescendingOrder;
|
|
- case SortByAspectRatio:
|
|
- return Qt::DescendingOrder;
|
|
- case SortBySimilarity:
|
|
- return Qt::DescendingOrder;
|
|
- default:
|
|
- return Qt::AscendingOrder;
|
|
- }
|
|
-}
|
|
-
|
|
-int ImageSortSettings::compareCategories(const ImageInfo& left, const ImageInfo& right) const
|
|
-{
|
|
- switch (categorizationMode)
|
|
- {
|
|
- case NoCategories:
|
|
- case OneCategory:
|
|
- return 0;
|
|
- case CategoryByAlbum:
|
|
- {
|
|
- int leftAlbum = left.albumId();
|
|
- int rightAlbum = right.albumId();
|
|
-
|
|
- // return comparation result
|
|
- if (leftAlbum == rightAlbum)
|
|
- {
|
|
- return 0;
|
|
- }
|
|
- else if (lessThanByOrder(leftAlbum, rightAlbum, currentCategorizationSortOrder))
|
|
- {
|
|
- return -1;
|
|
- }
|
|
- else
|
|
- {
|
|
- return 1;
|
|
- }
|
|
- }
|
|
- case CategoryByFormat:
|
|
- {
|
|
- return naturalCompare(left.format(), right.format(),
|
|
- currentCategorizationSortOrder, categorizationCaseSensitivity, strTypeNatural);
|
|
- }
|
|
- default:
|
|
- return 0;
|
|
- }
|
|
-}
|
|
-
|
|
-bool ImageSortSettings::lessThan(const ImageInfo& left, const ImageInfo& right) const
|
|
-{
|
|
- int result = compare(left, right, sortRole);
|
|
-
|
|
- if (result != 0)
|
|
- {
|
|
- return result < 0;
|
|
- }
|
|
-
|
|
- // are they identical?
|
|
- if (left == right)
|
|
- {
|
|
- return false;
|
|
- }
|
|
-
|
|
- // If left and right equal for first sort order, use a hierarchy of all sort orders
|
|
- if ( (result = compare(left, right, SortByFileName)) != 0)
|
|
- {
|
|
- return result < 0;
|
|
- }
|
|
-
|
|
- if ( (result = compare(left, right, SortByCreationDate)) != 0)
|
|
- {
|
|
- return result < 0;
|
|
- }
|
|
-
|
|
- if ( (result = compare(left, right, SortByModificationDate)) != 0)
|
|
- {
|
|
- return result < 0;
|
|
- }
|
|
-
|
|
- if ( (result = compare(left, right, SortByFilePath)) != 0)
|
|
- {
|
|
- return result < 0;
|
|
- }
|
|
-
|
|
- if ( (result = compare(left, right, SortByFileSize)) != 0)
|
|
- {
|
|
- return result < 0;
|
|
- }
|
|
-
|
|
- if ( (result = compare(left, right, SortBySimilarity)) != 0)
|
|
- {
|
|
- return result < 0;
|
|
- }
|
|
-
|
|
- return false;
|
|
-}
|
|
-
|
|
-int ImageSortSettings::compare(const ImageInfo& left, const ImageInfo& right) const
|
|
-{
|
|
- return compare(left, right, sortRole);
|
|
-}
|
|
-
|
|
-int ImageSortSettings::compare(const ImageInfo& left, const ImageInfo& right, SortRole role) const
|
|
-{
|
|
- switch (role)
|
|
- {
|
|
- case SortByFileName:
|
|
- {
|
|
- bool versioning = (left.name().contains(QLatin1String("_v"), Qt::CaseInsensitive) ||
|
|
- right.name().contains(QLatin1String("_v"), Qt::CaseInsensitive));
|
|
- return naturalCompare(left.name(), right.name(), currentSortOrder, sortCaseSensitivity, strTypeNatural, versioning);
|
|
- }
|
|
- case SortByFilePath:
|
|
- return naturalCompare(left.filePath(), right.filePath(), currentSortOrder, sortCaseSensitivity, strTypeNatural);
|
|
- case SortByFileSize:
|
|
- return compareByOrder(left.fileSize(), right.fileSize(), currentSortOrder);
|
|
- case SortByModificationDate:
|
|
- return compareByOrder(left.modDateTime(), right.modDateTime(), currentSortOrder);
|
|
- case SortByCreationDate:
|
|
- return compareByOrder(left.dateTime(), right.dateTime(), currentSortOrder);
|
|
- case SortByRating:
|
|
- // I have the feeling that inverting the sort order for rating is the natural order
|
|
- return - compareByOrder(left.rating(), right.rating(), currentSortOrder);
|
|
- case SortByImageSize:
|
|
- {
|
|
- QSize leftSize = left.dimensions();
|
|
- QSize rightSize = right.dimensions();
|
|
- int leftPixels = leftSize.width() * leftSize.height();
|
|
- int rightPixels = rightSize.width() * rightSize.height();
|
|
- return compareByOrder(leftPixels, rightPixels, currentSortOrder);
|
|
- }
|
|
- case SortByAspectRatio:
|
|
- {
|
|
- QSize leftSize = left.dimensions();
|
|
- QSize rightSize = right.dimensions();
|
|
- int leftAR = (double(leftSize.width()) / double(leftSize.height())) * 1000000;
|
|
- int rightAR = (double(rightSize.width()) / double(rightSize.height())) * 1000000;
|
|
- return compareByOrder(leftAR, rightAR, currentSortOrder);
|
|
- }
|
|
- case SortBySimilarity:
|
|
- {
|
|
- qlonglong leftReferenceImageId = left.currentReferenceImage();
|
|
- qlonglong rightReferenceImageId = right.currentReferenceImage();
|
|
- // make sure that the original image has always the highest similarity.
|
|
- double leftSimilarity = left.id() == leftReferenceImageId ? 1.1 : left.currentSimilarity();
|
|
- double rightSimilarity = right.id() == rightReferenceImageId ? 1.1 : right.currentSimilarity();
|
|
- return compareByOrder(leftSimilarity, rightSimilarity, currentSortOrder);
|
|
- }
|
|
- default:
|
|
- return 1;
|
|
- }
|
|
-}
|
|
-
|
|
-bool ImageSortSettings::lessThan(const QVariant& left, const QVariant& right) const
|
|
-{
|
|
- if (left.type() != right.type())
|
|
- {
|
|
- return false;
|
|
- }
|
|
-
|
|
- switch (left.type())
|
|
- {
|
|
- case QVariant::Int:
|
|
- return compareByOrder(left.toInt(), right.toInt(), currentSortOrder);
|
|
- case QVariant::UInt:
|
|
- return compareByOrder(left.toUInt(), right.toUInt(), currentSortOrder);
|
|
- case QVariant::LongLong:
|
|
- return compareByOrder(left.toLongLong(), right.toLongLong(), currentSortOrder);
|
|
- case QVariant::ULongLong:
|
|
- return compareByOrder(left.toULongLong(), right.toULongLong(), currentSortOrder);
|
|
- case QVariant::Double:
|
|
- return compareByOrder(left.toDouble(), right.toDouble(), currentSortOrder);
|
|
- case QVariant::Date:
|
|
- return compareByOrder(left.toDate(), right.toDate(), currentSortOrder);
|
|
- case QVariant::DateTime:
|
|
- return compareByOrder(left.toDateTime(), right.toDateTime(), currentSortOrder);
|
|
- case QVariant::Time:
|
|
- return compareByOrder(left.toTime(), right.toTime(), currentSortOrder);
|
|
- case QVariant::Rect:
|
|
- case QVariant::RectF:
|
|
- {
|
|
- QRectF rectLeft = left.toRectF();
|
|
- QRectF rectRight = right.toRectF();
|
|
- int result;
|
|
-
|
|
- if ((result = compareByOrder(rectLeft.top(), rectRight.top(), currentSortOrder)) != 0)
|
|
- {
|
|
- return result < 0;
|
|
- }
|
|
-
|
|
- if ((result = compareByOrder(rectLeft.left(), rectRight.left(), currentSortOrder)) != 0)
|
|
- {
|
|
- return result < 0;
|
|
- }
|
|
-
|
|
- QSizeF sizeLeft = rectLeft.size(), sizeRight = rectRight.size();
|
|
-
|
|
- if ((result = compareByOrder(sizeLeft.width()*sizeLeft.height(), sizeRight.width()*sizeRight.height(), currentSortOrder)) != 0)
|
|
- {
|
|
- return result < 0;
|
|
- }
|
|
- // FIXME: fall through?? If not, add "break" here
|
|
- }
|
|
- default:
|
|
- return naturalCompare(left.toString(), right.toString(), currentSortOrder, sortCaseSensitivity, strTypeNatural);
|
|
- }
|
|
-}
|
|
-
|
|
-DatabaseFields::Set ImageSortSettings::watchFlags() const
|
|
-{
|
|
- DatabaseFields::Set set;
|
|
-
|
|
- switch (sortRole)
|
|
- {
|
|
- case SortByFileName:
|
|
- set |= DatabaseFields::Name;
|
|
- break;
|
|
- case SortByFilePath:
|
|
- set |= DatabaseFields::Name;
|
|
- break;
|
|
- case SortByFileSize:
|
|
- set |= DatabaseFields::FileSize;
|
|
- break;
|
|
- case SortByModificationDate:
|
|
- set |= DatabaseFields::ModificationDate;
|
|
- break;
|
|
- case SortByCreationDate:
|
|
- set |= DatabaseFields::CreationDate;
|
|
- break;
|
|
- case SortByRating:
|
|
- set |= DatabaseFields::Rating;
|
|
- break;
|
|
- case SortByImageSize:
|
|
- set |= DatabaseFields::Width | DatabaseFields::Height;
|
|
- break;
|
|
- case SortByAspectRatio:
|
|
- set |= DatabaseFields::Width | DatabaseFields::Height;
|
|
- break;
|
|
- case SortBySimilarity:
|
|
- // TODO: Not sure what to do here....
|
|
- set |= DatabaseFields::Name;
|
|
- break;
|
|
- }
|
|
-
|
|
- switch (categorizationMode)
|
|
- {
|
|
- case NoCategories:
|
|
- case OneCategory:
|
|
- case CategoryByAlbum:
|
|
- break;
|
|
- case CategoryByFormat:
|
|
- set |= DatabaseFields::Format;
|
|
- break;
|
|
- }
|
|
-
|
|
- return set;
|
|
-}
|
|
-
|
|
-} // namespace Digikam
|
|
diff --git a/libs/models/imagesortsettings.h b/libs/models/imagesortsettings.h
|
|
deleted file mode 100644
|
|
index 2a5fd8c..0000000
|
|
--- a/libs/models/imagesortsettings.h
|
|
+++ /dev/null
|
|
@@ -1,225 +0,0 @@
|
|
-/* ============================================================
|
|
- *
|
|
- * This file is a part of digiKam project
|
|
- * http://www.digikam.org
|
|
- *
|
|
- * Date : 2009-05-31
|
|
- * Description : Sort settings for use with ImageFilterModel
|
|
- *
|
|
- * Copyright (C) 2009 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
|
|
- *
|
|
- * This program is free software; you can redistribute it
|
|
- * and/or modify it under the terms of the GNU General
|
|
- * Public License as published by the Free Software Foundation;
|
|
- * either version 2, or (at your option)
|
|
- * any later version.
|
|
- *
|
|
- * This program is distributed in the hope that it will be useful,
|
|
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
- * GNU General Public License for more details.
|
|
- *
|
|
- * ============================================================ */
|
|
-
|
|
-#ifndef IMAGESORTSETTINGS_H
|
|
-#define IMAGESORTSETTINGS_H
|
|
-
|
|
-// Qt includes
|
|
-
|
|
-#include <QHash>
|
|
-#include <QList>
|
|
-#include <QMap>
|
|
-#include <QString>
|
|
-#include <QCollator>
|
|
-
|
|
-// Local includes
|
|
-
|
|
-#include "digikam_export.h"
|
|
-
|
|
-namespace Digikam
|
|
-{
|
|
-
|
|
-class ImageInfo;
|
|
-
|
|
-namespace DatabaseFields
|
|
-{
|
|
- class Set;
|
|
-}
|
|
-
|
|
-class DIGIKAM_DATABASE_EXPORT ImageSortSettings
|
|
-{
|
|
-public:
|
|
-
|
|
- ImageSortSettings();
|
|
-
|
|
- bool operator==(const ImageSortSettings& other) const;
|
|
-
|
|
- /** Compares the categories of left and right.
|
|
- * Return -1 if left is less than right, 0 if both fall in the same category,
|
|
- * and 1 if left is greater than right.
|
|
- * Adheres to set categorization mode and current category sort order.
|
|
- */
|
|
- int compareCategories(const ImageInfo& left, const ImageInfo& right) const;
|
|
-
|
|
- /** Returns true if left is less than right.
|
|
- * Adheres to current sort role and sort order.
|
|
- */
|
|
- bool lessThan(const ImageInfo& left, const ImageInfo& right) const;
|
|
-
|
|
- /** Compares the ImageInfos left and right.
|
|
- * Return -1 if left is less than right, 1 if left is greater than right,
|
|
- * and 0 if left equals right comparing the current sort role's value.
|
|
- * Adheres to set sort role and sort order.
|
|
- */
|
|
- int compare(const ImageInfo& left, const ImageInfo& right) const;
|
|
-
|
|
- /** Returns true if left QVariant is less than right.
|
|
- * Adheres to current sort role and sort order.
|
|
- * Use for extraValue, if necessary.
|
|
- */
|
|
- bool lessThan(const QVariant& left, const QVariant& right) const;
|
|
-
|
|
- enum SortOrder
|
|
- {
|
|
- AscendingOrder = Qt::AscendingOrder,
|
|
- DescendingOrder = Qt::DescendingOrder,
|
|
- DefaultOrder /// sort order depends on the chosen sort role
|
|
- };
|
|
-
|
|
- /// --- Categories ---
|
|
-
|
|
- enum CategorizationMode
|
|
- {
|
|
- NoCategories, /// categorization switched off
|
|
- OneCategory, /// all items in one global category
|
|
- CategoryByAlbum,
|
|
- CategoryByFormat
|
|
- };
|
|
-
|
|
- CategorizationMode categorizationMode;
|
|
- SortOrder categorizationSortOrder;
|
|
-
|
|
- void setCategorizationMode(CategorizationMode mode);
|
|
- void setCategorizationSortOrder(SortOrder order);
|
|
-
|
|
- /// Only Ascending or Descending, never DefaultOrder
|
|
- Qt::SortOrder currentCategorizationSortOrder;
|
|
- Qt::CaseSensitivity categorizationCaseSensitivity;
|
|
-
|
|
- bool isCategorized() const { return categorizationMode >= CategoryByAlbum; }
|
|
-
|
|
- /// --- Image Sorting ---
|
|
-
|
|
- enum SortRole
|
|
- {
|
|
- // Note: For legacy reasons, the order of the first five entries must remain unchanged
|
|
- SortByFileName,
|
|
- SortByFilePath,
|
|
- SortByCreationDate,
|
|
- SortByFileSize,
|
|
- SortByRating,
|
|
- SortByModificationDate,
|
|
- SortByImageSize, // pixel number
|
|
- SortByAspectRatio, // width / height * 100000
|
|
- SortBySimilarity
|
|
- };
|
|
-
|
|
- SortRole sortRole;
|
|
- SortOrder sortOrder;
|
|
- bool strTypeNatural;
|
|
-
|
|
- void setSortRole(SortRole role);
|
|
- void setSortOrder(SortOrder order);
|
|
- void setStringTypeNatural(bool natural);
|
|
-
|
|
- Qt::SortOrder currentSortOrder;
|
|
- Qt::CaseSensitivity sortCaseSensitivity;
|
|
-
|
|
- int compare(const ImageInfo& left, const ImageInfo& right, SortRole sortRole) const;
|
|
-
|
|
- // --- ---
|
|
-
|
|
- static Qt::SortOrder defaultSortOrderForCategorizationMode(CategorizationMode mode);
|
|
- static Qt::SortOrder defaultSortOrderForSortRole(SortRole role);
|
|
-
|
|
- /// --- Change notification ---
|
|
-
|
|
- /** Returns database fields a change in which would affect the current sorting.
|
|
- */
|
|
- DatabaseFields::Set watchFlags() const;
|
|
-
|
|
- /// --- Utilities ---
|
|
-
|
|
- /** Returns a < b if sortOrder is Ascending, or b < a if order is descending.
|
|
- */
|
|
- template <typename T>
|
|
- static inline bool lessThanByOrder(const T& a, const T& b, Qt::SortOrder sortOrder)
|
|
- {
|
|
- if (sortOrder == Qt::AscendingOrder)
|
|
- {
|
|
- return a < b;
|
|
- }
|
|
- else
|
|
- {
|
|
- return b < a;
|
|
- }
|
|
- }
|
|
-
|
|
- /** Returns the usual compare result of -1, 0, or 1 for lessThan, equals and greaterThan.
|
|
- */
|
|
- template <typename T>
|
|
- static inline int compareValue(const T& a, const T& b)
|
|
- {
|
|
- if (a == b)
|
|
- {
|
|
- return 0;
|
|
- }
|
|
-
|
|
- if (a < b)
|
|
- {
|
|
- return -1;
|
|
- }
|
|
- else
|
|
- {
|
|
- return 1;
|
|
- }
|
|
- }
|
|
-
|
|
- /** Takes a typical result from a compare method (0 is equal, -1 is less than, 1 is greater than)
|
|
- * and applies the given sort order to it.
|
|
- */
|
|
- static inline int compareByOrder(int compareResult, Qt::SortOrder sortOrder)
|
|
- {
|
|
- if (sortOrder == Qt::AscendingOrder)
|
|
- {
|
|
- return compareResult;
|
|
- }
|
|
- else
|
|
- {
|
|
- return - compareResult;
|
|
- }
|
|
- }
|
|
-
|
|
- template <typename T>
|
|
- static inline int compareByOrder(const T& a, const T& b, Qt::SortOrder sortOrder)
|
|
- {
|
|
- return compareByOrder(compareValue(a, b), sortOrder);
|
|
- }
|
|
-
|
|
- /** Compares the two string by natural comparison and adheres to given sort order
|
|
- */
|
|
- static inline int naturalCompare(const QString& a, const QString& b, Qt::SortOrder sortOrder,
|
|
- Qt::CaseSensitivity caseSensitive = Qt::CaseSensitive,
|
|
- bool natural = true, bool versioning = false)
|
|
- {
|
|
- QCollator collator;
|
|
- collator.setNumericMode(natural);
|
|
- collator.setIgnorePunctuation(versioning);
|
|
- collator.setCaseSensitivity(caseSensitive);
|
|
- return (compareByOrder(collator.compare(a, b), sortOrder));
|
|
- }
|
|
-};
|
|
-
|
|
-} // namespace Digikam
|
|
-
|
|
-#endif // IMAGESORTSETTINGS_H
|
|
diff --git a/libs/models/imagethumbnailmodel.cpp b/libs/models/imagethumbnailmodel.cpp
|
|
deleted file mode 100644
|
|
index b7f5661..0000000
|
|
--- a/libs/models/imagethumbnailmodel.cpp
|
|
+++ /dev/null
|
|
@@ -1,323 +0,0 @@
|
|
-/* ============================================================
|
|
- *
|
|
- * This file is a part of digiKam project
|
|
- * http://www.digikam.org
|
|
- *
|
|
- * Date : 2009-03-05
|
|
- * Description : Qt item model for database entries with support for thumbnail loading
|
|
- *
|
|
- * Copyright (C) 2009-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
|
|
- * Copyright (C) 2011-2017 by Gilles Caulier <caulier dot gilles at gmail dot com>
|
|
- *
|
|
- * This program is free software; you can redistribute it
|
|
- * and/or modify it under the terms of the GNU General
|
|
- * Public License as published by the Free Software Foundation;
|
|
- * either version 2, or (at your option)
|
|
- * any later version.
|
|
- *
|
|
- * This program is distributed in the hope that it will be useful,
|
|
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
- * GNU General Public License for more details.
|
|
- *
|
|
- * ============================================================ */
|
|
-
|
|
-#include "imagethumbnailmodel.h"
|
|
-
|
|
-// Qt includes
|
|
-
|
|
-#include <QHash>
|
|
-
|
|
-// Local includes
|
|
-
|
|
-#include "digikam_debug.h"
|
|
-#include "thumbnailloadthread.h"
|
|
-#include "digikam_export.h"
|
|
-#include "digikam_globals.h"
|
|
-
|
|
-namespace Digikam
|
|
-{
|
|
-
|
|
-class ImageThumbnailModel::ImageThumbnailModelPriv
|
|
-{
|
|
-public:
|
|
-
|
|
- ImageThumbnailModelPriv() :
|
|
- thread(0),
|
|
- preloadThread(0),
|
|
- thumbSize(0),
|
|
- lastGlobalThumbSize(0),
|
|
- preloadThumbSize(0),
|
|
- emitDataChanged(true)
|
|
- {
|
|
- staticListContainingThumbnailRole << ImageModel::ThumbnailRole;
|
|
- }
|
|
-
|
|
- ThumbnailLoadThread* thread;
|
|
- ThumbnailLoadThread* preloadThread;
|
|
- ThumbnailSize thumbSize;
|
|
- ThumbnailSize lastGlobalThumbSize;
|
|
- ThumbnailSize preloadThumbSize;
|
|
- QRect detailRect;
|
|
- QVector<int> staticListContainingThumbnailRole;
|
|
-
|
|
- bool emitDataChanged;
|
|
-
|
|
- int preloadThumbnailSize() const
|
|
- {
|
|
- if (preloadThumbSize.size())
|
|
- {
|
|
- return preloadThumbSize.size();
|
|
- }
|
|
-
|
|
- return thumbSize.size();
|
|
- }
|
|
-};
|
|
-
|
|
-ImageThumbnailModel::ImageThumbnailModel(QObject* parent)
|
|
- : ImageModel(parent), d(new ImageThumbnailModelPriv)
|
|
-{
|
|
- setKeepsFilePathCache(true);
|
|
-}
|
|
-
|
|
-ImageThumbnailModel::~ImageThumbnailModel()
|
|
-{
|
|
- delete d->preloadThread;
|
|
- delete d;
|
|
-}
|
|
-
|
|
-void ImageThumbnailModel::setThumbnailLoadThread(ThumbnailLoadThread* thread)
|
|
-{
|
|
- d->thread = thread;
|
|
-
|
|
- connect(d->thread, SIGNAL(signalThumbnailLoaded(LoadingDescription,QPixmap)),
|
|
- this, SLOT(slotThumbnailLoaded(LoadingDescription,QPixmap)));
|
|
-}
|
|
-
|
|
-ThumbnailLoadThread* ImageThumbnailModel::thumbnailLoadThread() const
|
|
-{
|
|
- return d->thread;
|
|
-}
|
|
-
|
|
-ThumbnailSize ImageThumbnailModel::thumbnailSize() const
|
|
-{
|
|
- return d->thumbSize;
|
|
-}
|
|
-
|
|
-void ImageThumbnailModel::setThumbnailSize(const ThumbnailSize& size)
|
|
-{
|
|
- d->lastGlobalThumbSize = size;
|
|
- d->thumbSize = size;
|
|
-}
|
|
-
|
|
-void ImageThumbnailModel::setPreloadThumbnailSize(const ThumbnailSize& size)
|
|
-{
|
|
- d->preloadThumbSize = size;
|
|
-}
|
|
-
|
|
-void ImageThumbnailModel::setEmitDataChanged(bool emitSignal)
|
|
-{
|
|
- d->emitDataChanged = emitSignal;
|
|
-}
|
|
-
|
|
-void ImageThumbnailModel::setPreloadThumbnails(bool preload)
|
|
-{
|
|
- if (preload)
|
|
- {
|
|
- if (!d->preloadThread)
|
|
- {
|
|
- d->preloadThread = new ThumbnailLoadThread;
|
|
- d->preloadThread->setPixmapRequested(false);
|
|
- d->preloadThread->setPriority(QThread::LowestPriority);
|
|
- }
|
|
-
|
|
- connect(this, SIGNAL(allRefreshingFinished()),
|
|
- this, SLOT(preloadAllThumbnails()));
|
|
- }
|
|
- else
|
|
- {
|
|
- delete d->preloadThread;
|
|
- d->preloadThread = 0;
|
|
- disconnect(this, SIGNAL(allRefreshingFinished()),
|
|
- this, SLOT(preloadAllThumbnails()));
|
|
- }
|
|
-}
|
|
-
|
|
-void ImageThumbnailModel::prepareThumbnails(const QList<QModelIndex>& indexesToPrepare)
|
|
-{
|
|
- prepareThumbnails(indexesToPrepare, d->thumbSize);
|
|
-}
|
|
-
|
|
-void ImageThumbnailModel::prepareThumbnails(const QList<QModelIndex>& indexesToPrepare, const ThumbnailSize& thumbSize)
|
|
-{
|
|
- if (!d->thread)
|
|
- {
|
|
- return;
|
|
- }
|
|
-
|
|
- QList<ThumbnailIdentifier> ids;
|
|
- foreach(const QModelIndex& index, indexesToPrepare)
|
|
- {
|
|
- ids << imageInfoRef(index).thumbnailIdentifier();
|
|
- }
|
|
- d->thread->findGroup(ids, thumbSize.size());
|
|
-}
|
|
-
|
|
-void ImageThumbnailModel::preloadThumbnails(const QList<ImageInfo>& infos)
|
|
-{
|
|
- if (!d->preloadThread)
|
|
- {
|
|
- return;
|
|
- }
|
|
-
|
|
- QList<ThumbnailIdentifier> ids;
|
|
- foreach(const ImageInfo& info, infos)
|
|
- {
|
|
- ids << info.thumbnailIdentifier();
|
|
- }
|
|
- d->preloadThread->pregenerateGroup(ids, d->preloadThumbnailSize());
|
|
-}
|
|
-
|
|
-void ImageThumbnailModel::preloadThumbnails(const QList<QModelIndex>& indexesToPreload)
|
|
-{
|
|
- if (!d->preloadThread)
|
|
- {
|
|
- return;
|
|
- }
|
|
-
|
|
- QList<ThumbnailIdentifier> ids;
|
|
- foreach(const QModelIndex& index, indexesToPreload)
|
|
- {
|
|
- ids << imageInfoRef(index).thumbnailIdentifier();
|
|
- }
|
|
- d->preloadThread->stopAllTasks();
|
|
- d->preloadThread->pregenerateGroup(ids, d->preloadThumbnailSize());
|
|
-}
|
|
-
|
|
-void ImageThumbnailModel::preloadAllThumbnails()
|
|
-{
|
|
- preloadThumbnails(imageInfos());
|
|
-}
|
|
-
|
|
-void ImageThumbnailModel::imageInfosCleared()
|
|
-{
|
|
- if (d->preloadThread)
|
|
- {
|
|
- d->preloadThread->stopAllTasks();
|
|
- }
|
|
-}
|
|
-
|
|
-QVariant ImageThumbnailModel::data(const QModelIndex& index, int role) const
|
|
-{
|
|
- if (role == ThumbnailRole && d->thread && index.isValid())
|
|
- {
|
|
- QPixmap thumbnail;
|
|
- ImageInfo info = imageInfo(index);
|
|
- QString path = info.filePath();
|
|
-
|
|
- if (info.isNull())
|
|
- {
|
|
- return QVariant(QVariant::Pixmap);
|
|
- }
|
|
-
|
|
- if (!d->detailRect.isNull())
|
|
- {
|
|
- if (d->thread->find(info.thumbnailIdentifier(), d->detailRect, thumbnail, d->thumbSize.size()))
|
|
- {
|
|
- return thumbnail;
|
|
- }
|
|
- }
|
|
- else
|
|
- {
|
|
- if (d->thread->find(info.thumbnailIdentifier(), thumbnail, d->thumbSize.size()))
|
|
- {
|
|
- return thumbnail;
|
|
- }
|
|
- }
|
|
-
|
|
- return QVariant(QVariant::Pixmap);
|
|
- }
|
|
-
|
|
- return ImageModel::data(index, role);
|
|
-}
|
|
-
|
|
-bool ImageThumbnailModel::setData(const QModelIndex& index, const QVariant& value, int role)
|
|
-{
|
|
- if (role == ThumbnailRole)
|
|
- {
|
|
- switch (value.type())
|
|
- {
|
|
- case QVariant::Invalid:
|
|
- d->thumbSize = d->lastGlobalThumbSize;
|
|
- d->detailRect = QRect();
|
|
- break;
|
|
-
|
|
- case QVariant::Int:
|
|
-
|
|
- if (value.isNull())
|
|
- {
|
|
- d->thumbSize = d->lastGlobalThumbSize;
|
|
- }
|
|
- else
|
|
- {
|
|
- d->thumbSize = value.toInt();
|
|
- }
|
|
- break;
|
|
-
|
|
- case QVariant::Rect:
|
|
-
|
|
- if (value.isNull())
|
|
- {
|
|
- d->detailRect = QRect();
|
|
- }
|
|
- else
|
|
- {
|
|
- d->detailRect = value.toRect();
|
|
- }
|
|
- break;
|
|
-
|
|
- default:
|
|
- break;
|
|
- }
|
|
- }
|
|
-
|
|
- return ImageModel::setData(index, value, role);
|
|
-}
|
|
-
|
|
-void ImageThumbnailModel::slotThumbnailLoaded(const LoadingDescription& loadingDescription, const QPixmap& thumb)
|
|
-{
|
|
- if (thumb.isNull())
|
|
- {
|
|
- return;
|
|
- }
|
|
-
|
|
- // In case of multiple occurrence, we currently do not know which thumbnail is this. Signal change on all.
|
|
- QModelIndexList indexes;
|
|
- ThumbnailIdentifier thumbId = loadingDescription.thumbnailIdentifier();
|
|
- if (thumbId.filePath.isEmpty())
|
|
- {
|
|
- indexes = indexesForImageId(thumbId.id);
|
|
- }
|
|
- else
|
|
- {
|
|
- indexes = indexesForPath(thumbId.filePath);
|
|
- }
|
|
- foreach(const QModelIndex& index, indexes)
|
|
- {
|
|
- if (thumb.isNull())
|
|
- {
|
|
- emit thumbnailFailed(index, loadingDescription.previewParameters.size);
|
|
- }
|
|
- else
|
|
- {
|
|
- emit thumbnailAvailable(index, loadingDescription.previewParameters.size);
|
|
-
|
|
- if (d->emitDataChanged)
|
|
- {
|
|
- emit dataChanged(index, index, d->staticListContainingThumbnailRole);
|
|
- }
|
|
- }
|
|
- }
|
|
-}
|
|
-
|
|
-} // namespace Digikam
|
|
diff --git a/libs/models/imagethumbnailmodel.h b/libs/models/imagethumbnailmodel.h
|
|
deleted file mode 100644
|
|
index 366ca65..0000000
|
|
--- a/libs/models/imagethumbnailmodel.h
|
|
+++ /dev/null
|
|
@@ -1,140 +0,0 @@
|
|
-/* ============================================================
|
|
- *
|
|
- * This file is a part of digiKam project
|
|
- * http://www.digikam.org
|
|
- *
|
|
- * Date : 2009-03-05
|
|
- * Description : Qt item model for database entries with support for thumbnail loading
|
|
- *
|
|
- * Copyright (C) 2009-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
|
|
- * Copyright (C) 2011 by Gilles Caulier <caulier dot gilles at gmail dot com>
|
|
- *
|
|
- * This program is free software; you can redistribute it
|
|
- * and/or modify it under the terms of the GNU General
|
|
- * Public License as published by the Free Software Foundation;
|
|
- * either version 2, or (at your option)
|
|
- * any later version.
|
|
- *
|
|
- * This program is distributed in the hope that it will be useful,
|
|
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
- * GNU General Public License for more details.
|
|
- *
|
|
- * ============================================================ */
|
|
-
|
|
-#ifndef IMAGETHUMBNAILMODEL_H
|
|
-#define IMAGETHUMBNAILMODEL_H
|
|
-
|
|
-// Local includes
|
|
-
|
|
-#include "imagemodel.h"
|
|
-#include "thumbnailsize.h"
|
|
-#include "digikam_export.h"
|
|
-
|
|
-namespace Digikam
|
|
-{
|
|
-
|
|
-class LoadingDescription;
|
|
-class ThumbnailLoadThread;
|
|
-
|
|
-class DIGIKAM_DATABASE_EXPORT ImageThumbnailModel : public ImageModel
|
|
-{
|
|
- Q_OBJECT
|
|
-
|
|
-public:
|
|
-
|
|
- /**
|
|
- * An ImageModel that supports thumbnail loading.
|
|
- * You need to set a ThumbnailLoadThread to enable thumbnail loading.
|
|
- * Adjust the thumbnail size to your needs.
|
|
- * Note that setKeepsFilePathCache is enabled per default.
|
|
- */
|
|
- explicit ImageThumbnailModel(QObject* parent);
|
|
- ~ImageThumbnailModel();
|
|
-
|
|
- /** Enable thumbnail loading and set the thread that shall be used.
|
|
- * The thumbnail size of this thread will be adjusted.
|
|
- */
|
|
- void setThumbnailLoadThread(ThumbnailLoadThread* thread);
|
|
- ThumbnailLoadThread* thumbnailLoadThread() const;
|
|
-
|
|
- /// Set the thumbnail size to use
|
|
- void setThumbnailSize(const ThumbnailSize& thumbSize);
|
|
-
|
|
- /// If you want to fix a size for preloading, do it here.
|
|
- void setPreloadThumbnailSize(const ThumbnailSize& thumbSize);
|
|
-
|
|
- void setExifRotate(bool rotate);
|
|
-
|
|
- /**
|
|
- * Enable emitting dataChanged() when a thumbnail becomes available.
|
|
- * The thumbnailAvailable() signal will be emitted in any case.
|
|
- * Default is true.
|
|
- */
|
|
- void setEmitDataChanged(bool emitSignal);
|
|
-
|
|
- /**
|
|
- * Enable preloading of thumbnails:
|
|
- * If preloading is enabled, for every entry in the model a thumbnail generation is started.
|
|
- * Default: false.
|
|
- */
|
|
- void setPreloadThumbnails(bool preload);
|
|
-
|
|
- ThumbnailSize thumbnailSize() const;
|
|
-
|
|
- /**
|
|
- * Handles the ThumbnailRole.
|
|
- * If the pixmap is available, returns it in the QVariant.
|
|
- * If it still needs to be loaded, returns a null QVariant and emits
|
|
- * thumbnailAvailable() as soon as it is available.
|
|
- */
|
|
- virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
|
|
-
|
|
- /**
|
|
- * You can override the current thumbnail size by giving an integer value for ThumbnailRole.
|
|
- * Set a null QVariant to use the thumbnail size set by setThumbnailSize() again.
|
|
- * The index given here is ignored for this purpose.
|
|
- */
|
|
- virtual bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::DisplayRole);
|
|
-
|
|
-public Q_SLOTS:
|
|
-
|
|
- /** Prepare the thumbnail loading for the given indexes
|
|
- */
|
|
- void prepareThumbnails(const QList<QModelIndex>& indexesToPrepare);
|
|
- void prepareThumbnails(const QList<QModelIndex>& indexesToPrepare, const ThumbnailSize& thumbSize);
|
|
-
|
|
- /**
|
|
- * Preload thumbnail for the given infos resp. indexes.
|
|
- * Note: Use setPreloadThumbnails to automatically preload all entries in the model.
|
|
- * Note: This only ensures thumbnail generation. It is not guaranteed that pixmaps
|
|
- * are stored in the cache. For thumbnails that are expect to be drawn immediately,
|
|
- * include them in prepareThumbnails().
|
|
- * Note: Stops preloading of previously added thumbnails.
|
|
- */
|
|
- void preloadThumbnails(const QList<ImageInfo>&);
|
|
- void preloadThumbnails(const QList<QModelIndex>&);
|
|
- void preloadAllThumbnails();
|
|
-
|
|
-Q_SIGNALS:
|
|
-
|
|
- void thumbnailAvailable(const QModelIndex& index, int requestedSize);
|
|
- void thumbnailFailed(const QModelIndex& index, int requestedSize);
|
|
-
|
|
-protected:
|
|
-
|
|
- virtual void imageInfosCleared();
|
|
-
|
|
-protected Q_SLOTS:
|
|
-
|
|
- void slotThumbnailLoaded(const LoadingDescription& loadingDescription, const QPixmap& thumb);
|
|
-
|
|
-private:
|
|
-
|
|
- class ImageThumbnailModelPriv;
|
|
- ImageThumbnailModelPriv* const d;
|
|
-};
|
|
-
|
|
-} // namespace Digikam
|
|
-
|
|
-#endif /* IMAGETHUMBNAILMODEL_H */
|
|
diff --git a/libs/models/imageversionsmodel.cpp b/libs/models/imageversionsmodel.cpp
|
|
deleted file mode 100644
|
|
index e6ba582..0000000
|
|
--- a/libs/models/imageversionsmodel.cpp
|
|
+++ /dev/null
|
|
@@ -1,183 +0,0 @@
|
|
-/* ============================================================
|
|
- *
|
|
- * This file is a part of digiKam project
|
|
- * http://www.digikam.org
|
|
- *
|
|
- * Date : 2010-07-13
|
|
- * Description : Model for image versions
|
|
- *
|
|
- * Copyright (C) 2010 by Martin Klapetek <martin dot klapetek at gmail dot com>
|
|
- *
|
|
- * This program is free software; you can redistribute it
|
|
- * and/or modify it under the terms of the GNU General
|
|
- * Public License as published by the Free Software Foundation;
|
|
- * either version 2, or (at your option)
|
|
- * any later version.
|
|
- *
|
|
- * This program is distributed in the hope that it will be useful,
|
|
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
- * GNU General Public License for more details.
|
|
- *
|
|
- * ============================================================ */
|
|
-
|
|
-#include "imageversionsmodel.h"
|
|
-
|
|
-// KDE includes
|
|
-
|
|
-#include <klocalizedstring.h>
|
|
-
|
|
-// Local includes
|
|
-
|
|
-#include "digikam_debug.h"
|
|
-#include "workingwidget.h"
|
|
-
|
|
-namespace Digikam
|
|
-{
|
|
-
|
|
-class ImageVersionsModel::Private
|
|
-{
|
|
-public:
|
|
-
|
|
- Private()
|
|
- {
|
|
- data = 0;
|
|
- paintTree = false;
|
|
- }
|
|
-
|
|
- ///Complete paths with filenames and tree level
|
|
- QList<QPair<QString, int> >* data;
|
|
- ///This is for delegate to paint it as selected
|
|
- QString currentSelectedImage;
|
|
- ///If true, the delegate will paint items as a tree
|
|
- ///if false, it will be painted as a list
|
|
- bool paintTree;
|
|
-};
|
|
-
|
|
-ImageVersionsModel::ImageVersionsModel(QObject* parent)
|
|
- : QAbstractListModel(parent),
|
|
- d(new Private)
|
|
-{
|
|
- d->data = new QList<QPair<QString, int> >;
|
|
-}
|
|
-
|
|
-ImageVersionsModel::~ImageVersionsModel()
|
|
-{
|
|
- //qDeleteAll(d->data);
|
|
- delete d;
|
|
-}
|
|
-
|
|
-Qt::ItemFlags ImageVersionsModel::flags(const QModelIndex& index) const
|
|
-{
|
|
- if (!index.isValid())
|
|
- {
|
|
- return 0;
|
|
- }
|
|
-
|
|
- return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
|
|
-}
|
|
-
|
|
-QVariant ImageVersionsModel::data(const QModelIndex& index, int role) const
|
|
-{
|
|
- if (!index.isValid())
|
|
- {
|
|
- return QVariant();
|
|
- }
|
|
-
|
|
- if (role == Qt::DisplayRole && !d->data->isEmpty())
|
|
- {
|
|
- return d->data->at(index.row()).first;
|
|
- }
|
|
- else if (role == Qt::UserRole && !d->data->isEmpty())
|
|
- {
|
|
- return d->data->at(index.row()).second;
|
|
- }
|
|
- else if (role == Qt::DisplayRole && d->data->isEmpty())
|
|
- {
|
|
- //TODO: make this text Italic
|
|
- return QVariant(QString(i18n("No image selected")));
|
|
- }
|
|
-
|
|
- return QVariant();
|
|
-}
|
|
-
|
|
-int ImageVersionsModel::rowCount(const QModelIndex& parent) const
|
|
-{
|
|
- Q_UNUSED(parent)
|
|
- return d->data->count();
|
|
-}
|
|
-
|
|
-void ImageVersionsModel::setupModelData(QList<QPair<QString, int> >& data)
|
|
-{
|
|
- beginResetModel();
|
|
-
|
|
- d->data->clear();
|
|
-
|
|
- if (!data.isEmpty())
|
|
- {
|
|
- d->data->append(data);
|
|
- }
|
|
- else
|
|
- {
|
|
- d->data->append(qMakePair(QString(i18n("This is the original image")), 0));
|
|
- }
|
|
-
|
|
- endResetModel();
|
|
-}
|
|
-
|
|
-void ImageVersionsModel::clearModelData()
|
|
-{
|
|
- beginResetModel();
|
|
-
|
|
- if (!d->data->isEmpty())
|
|
- {
|
|
- d->data->clear();
|
|
- }
|
|
-
|
|
- endResetModel();
|
|
-}
|
|
-
|
|
-void ImageVersionsModel::slotAnimationStep()
|
|
-{
|
|
- emit dataChanged(createIndex(0, 0), createIndex(rowCount()-1, 1));
|
|
-}
|
|
-
|
|
-QString ImageVersionsModel::currentSelectedImage() const
|
|
-{
|
|
- return d->currentSelectedImage;
|
|
-}
|
|
-
|
|
-void ImageVersionsModel::setCurrentSelectedImage(const QString& path)
|
|
-{
|
|
- d->currentSelectedImage = path;
|
|
-}
|
|
-
|
|
-QModelIndex ImageVersionsModel::currentSelectedImageIndex() const
|
|
-{
|
|
- return index(listIndexOf(d->currentSelectedImage), 0);
|
|
-}
|
|
-
|
|
-bool ImageVersionsModel::paintTree() const
|
|
-{
|
|
- return d->paintTree;
|
|
-}
|
|
-
|
|
-void ImageVersionsModel::setPaintTree(bool paint)
|
|
-{
|
|
- d->paintTree = paint;
|
|
-}
|
|
-
|
|
-int ImageVersionsModel::listIndexOf(const QString& item) const
|
|
-{
|
|
- for (int i = 0; i < d->data->size(); ++i)
|
|
- {
|
|
- if (d->data->at(i).first == item)
|
|
- {
|
|
- return i;
|
|
- }
|
|
- }
|
|
-
|
|
- return -1;
|
|
-}
|
|
-
|
|
-} // namespace Digikam
|
|
diff --git a/libs/models/imageversionsmodel.h b/libs/models/imageversionsmodel.h
|
|
deleted file mode 100644
|
|
index ed08529..0000000
|
|
--- a/libs/models/imageversionsmodel.h
|
|
+++ /dev/null
|
|
@@ -1,75 +0,0 @@
|
|
-/* ============================================================
|
|
- *
|
|
- * This file is a part of digiKam project
|
|
- * http://www.digikam.org
|
|
- *
|
|
- * Date : 2010-07-13
|
|
- * Description : Model for image versions
|
|
- *
|
|
- * Copyright (C) 2010 by Martin Klapetek <martin dot klapetek at gmail dot com>
|
|
- *
|
|
- * This program is free software; you can redistribute it
|
|
- * and/or modify it under the terms of the GNU General
|
|
- * Public License as published by the Free Software Foundation;
|
|
- * either version 2, or (at your option)
|
|
- * any later version.
|
|
- *
|
|
- * This program is distributed in the hope that it will be useful,
|
|
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
- * GNU General Public License for more details.
|
|
- *
|
|
- * ============================================================ */
|
|
-
|
|
-#ifndef IMAGEVERSIONSMODEL_H
|
|
-#define IMAGEVERSIONSMODEL_H
|
|
-
|
|
-// Qt includes
|
|
-
|
|
-#include <QModelIndex>
|
|
-#include <QPixmap>
|
|
-
|
|
-// Local includes
|
|
-
|
|
-#include "digikam_export.h"
|
|
-
|
|
-namespace Digikam
|
|
-{
|
|
-
|
|
-class DIGIKAM_DATABASE_EXPORT ImageVersionsModel : public QAbstractListModel
|
|
-{
|
|
- Q_OBJECT
|
|
-
|
|
-public:
|
|
-
|
|
- explicit ImageVersionsModel(QObject* parent = 0);
|
|
- ~ImageVersionsModel();
|
|
-
|
|
- Qt::ItemFlags flags(const QModelIndex& index) const;
|
|
- QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
|
|
- int rowCount(const QModelIndex& parent = QModelIndex()) const;
|
|
-
|
|
- void setupModelData(QList<QPair<QString, int> >& data);
|
|
- void clearModelData();
|
|
-
|
|
- QString currentSelectedImage() const;
|
|
- void setCurrentSelectedImage(const QString& path);
|
|
- QModelIndex currentSelectedImageIndex() const;
|
|
-
|
|
- bool paintTree() const;
|
|
- int listIndexOf(const QString& item) const;
|
|
-
|
|
-public Q_SLOTS:
|
|
-
|
|
- void slotAnimationStep();
|
|
- void setPaintTree(bool paint);
|
|
-
|
|
-private:
|
|
-
|
|
- class Private;
|
|
- Private* const d;
|
|
-};
|
|
-
|
|
-} // namespace Digikam
|
|
-
|
|
-#endif // IMAGEVERSIONSMODEL_H
|
|
--
|
|
cgit v0.11.2
|
|
|