digikam/fix-build-cmake39.patch

13338 lines
386 KiB
Diff
Raw Normal View History

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