diff --git a/digikam-5.6.0.tar.xz b/digikam-5.6.0.tar.xz deleted file mode 100644 index 5e68548..0000000 --- a/digikam-5.6.0.tar.xz +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:65bdd3f15668e314b852d523a0bf95da32450f31fcfda60da62e57d4622a663c -size 166913492 diff --git a/digikam-5.7.0.tar.xz b/digikam-5.7.0.tar.xz new file mode 100644 index 0000000..9f0b6d3 --- /dev/null +++ b/digikam-5.7.0.tar.xz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3605ffb5b6e8fbd6b725e5075f74f505d7edee7531789c2882d11df2d20150f5 +size 123315064 diff --git a/digikam.changes b/digikam.changes index bc10ef0..82944c1 100644 --- a/digikam.changes +++ b/digikam.changes @@ -1,3 +1,37 @@ +------------------------------------------------------------------- +Tue Sep 12 12:29:53 UTC 2017 - wbauer@tmo.at + +- Update to 5.7.0: + * https://www.digikam.org/news/2017-09-11-5.7.0_release_announcement/ +- New features (from NEWS): +General : Add online handbook reader not dependant of desktop. +General : New tool to export albums by email. +General : New tool to create advance print layout. +General : Send by Mail tool can remove all metadata to attached items. +General : Replaced the minimum and maximum similarity selection boxes in fuzzy + search, duplicates search and maintenance by a new, generic DIntRangeBox + which makes the look, feel and behavious consistent in all places. + Also, this makes redundant code obsolete and reduces the probability of bugs. +PrintCreator: Add option to create printing from albums or images selection. +PrintCreator: Add option to customize output image properties to render printing. +PrintCreator: Tool is now multi-threaded to process images while preview and printing. +ImageEditor : Add SendByMail and PrintCreator tools support. +Showfoto : Add SendByMail and PrintCreator tools support. +LightTable : Add SendByMail and PrintCreator tools support. +Panorama : Add Hugin 2017.0 support. +Geolocation : Add new tool to export GPS trace to KML file from geolocation editor. + +- Many bugs fixed +- Drop fix-build-cmake39.patch, now upstream +- Drop find_libastro-qt5.patch, 42.1 is no longer supported and + its Qt5 is too old anyway +- Drop unneeded exiv2 requirement, digikam uses libexiv2 only +- Add libkvkontakte-devel and libmediawiki-devel build requirements + (both have been released as KF5 versions meanwhile) to enable the + corresponding kipi plugins +- Build with opencv-devel instead of opencv-qt5-devel, the latter + doesn't exist any more + ------------------------------------------------------------------- Sat Aug 26 09:16:31 UTC 2017 - fabian@ritter-vogt.de diff --git a/digikam.spec b/digikam.spec index bb467ec..e306af3 100644 --- a/digikam.spec +++ b/digikam.spec @@ -17,17 +17,13 @@ Name: digikam -Version: 5.6.0 +Version: 5.7.0 Release: 0 Summary: A KDE Photo Manager License: GPL-2.0+ Group: Productivity/Graphics/Viewers Url: http://www.digikam.org/ Source0: http://download.kde.org/stable/%{name}/%{name}-%{version}.tar.xz -# PATCH-FIX-OPENSUSE find_libastro-qt5.patch -- fix build of geolocation support in Leap 42.1 -Patch0: find_libastro-qt5.patch -# PATCH-FIX-UPSTREAM -Patch1: fix-build-cmake39.patch #This pulls in QWebEngine, which is not available on ppc64 %ifarch %ix86 x86_64 %arm aarch64 mips mips64 BuildRequires: akonadi-contact-devel @@ -48,7 +44,7 @@ BuildRequires: knotifyconfig-devel BuildRequires: lensfun BuildRequires: lensfun-devel BuildRequires: libeigen3-devel -BuildRequires: libexiv2-devel +BuildRequires: libexiv2-devel >= 0.26 BuildRequires: libexpat-devel BuildRequires: libgcrypt-devel BuildRequires: libgphoto2-devel >= 2.4.0 @@ -63,8 +59,10 @@ BuildRequires: libkface-devel >= 15.12.0 BuildRequires: libkgeomap-devel > 15.12.0 BuildRequires: libkipi-devel >= 16.04.0 BuildRequires: libksane-devel >= 15.12.0 +BuildRequires: libkvkontakte-devel BuildRequires: liblcms2-devel BuildRequires: liblqr-devel +BuildRequires: libmediawiki-devel BuildRequires: libmysqlclient-devel BuildRequires: libmysqld-devel BuildRequires: libpgf-devel @@ -73,7 +71,7 @@ BuildRequires: libtiff-devel BuildRequires: libusb-devel BuildRequires: marble-devel BuildRequires: mysql -BuildRequires: opencv-qt5-devel +BuildRequires: opencv-devel BuildRequires: threadweaver-devel >= 5.1.0 BuildRequires: update-desktop-files BuildRequires: xorg-x11-devel @@ -92,7 +90,6 @@ Requires: kipi-plugins >= %{version} Recommends: %{name}-doc Recommends: %{name}-lang BuildRoot: %{_tmppath}/%{name}-%{version}-build -Requires: exiv2 >= 0.25 Recommends: marble Recommends: showfoto # Got merged into libimageeditor in 5.2.0 @@ -167,13 +164,6 @@ The main digikam libraries that are being shared between showfoto and digikam %prep %setup -q -n %{name}-%{version} -%if 0%{?is_opensuse} && 0%{?sle_version} == 120100 -# we renamed libastro to libastro-qt5 in Leap 42.1, make FindMARBLE.cmake find it -%patch0 -p1 -%endif -pushd core -%patch1 -p1 -popd # Remove build time references so build-compare can do its work FAKE_BUILDDATE=$(LC_ALL=C date -u -r %{_sourcedir}/%{name}.changes '+%%b %%e %%Y') @@ -184,7 +174,6 @@ sed -i "s/__DATE__/\"$FAKE_BUILDDATE\"/g" core/libs/dimg/filters/greycstoration/ sed -i "s/__TIME__/\"$FAKE_BUILDTIME\"/g" core/libs/dimg/filters/greycstoration/cimg/CImg.h # Workaround for kde#369517 - vkontakte installs translations even if not built -rm -f po/*/kipiplugin_vkontakte.po rm -f po/*/libkvkontakte.po %build @@ -219,7 +208,7 @@ rm -rf $RPM_BUILD_ROOT/usr/share/locale/x-test %find_lang %{name} %find_lang kipiplugins kipiplugin.lang -for i in dropbox googleservices sendimages facebook flashexport flickr imageshack imgur kmlexport piwigo printimages rajce remotestorage sendimages smug yandexfotki +for i in dropbox googleservices sendimages facebook flashexport flickr imageshack imgur kmlexport piwigo printimages rajce remotestorage sendimages smug vkontakte yandexfotki do %find_lang kipiplugin_$i kipiplugin.lang done @@ -250,8 +239,6 @@ done %_datadir/solid/actions/digikam-opencamera.desktop %_datadir/kxmlgui5/digikam/ %_datadir/knotifications5/digikam.notifyrc -# For Leap 42.1 -%dir %{_kf5_appstreamdir} %{_kf5_appstreamdir}/org.kde.digikam.appdata.xml %_kf5_iconsdir/hicolor/*/apps/panorama.* @@ -262,8 +249,6 @@ done %_kf5_iconsdir/hicolor/*/apps/showfoto.* %_datadir/showfoto/ %_datadir/kxmlgui5/showfoto/ -# For Leap 42.1 -%dir %{_kf5_appstreamdir} %{_kf5_appstreamdir}/org.kde.showfoto.appdata.xml %files -n libdigikamcore5 diff --git a/find_libastro-qt5.patch b/find_libastro-qt5.patch deleted file mode 100644 index b1e7a1c..0000000 --- a/find_libastro-qt5.patch +++ /dev/null @@ -1,12 +0,0 @@ -diff -urB digikam/core/cmake/modules/FindMarble.cmake new/core/cmake/modules/FindMarble.cmake ---- digikam/core/cmake/modules/FindMarble.cmake 2015-10-17 08:33:01.347270719 +0200 -+++ new/core/cmake/modules/FindMarble.cmake 2015-10-17 12:07:56.423127921 +0200 -@@ -19,7 +19,7 @@ - - FIND_PATH( MARBLE_INCLUDE_DIR NAMES marble/MarbleModel.h ) - FIND_LIBRARY( MARBLE_LIBRARIES NAMES marblewidget-qt5 marblewidget-qt5d ) --FIND_LIBRARY( ASTRO_LIBRARIES NAMES astro astrod ) -+FIND_LIBRARY( ASTRO_LIBRARIES NAMES astro-qt5 astro-qt5d ) - - INCLUDE( FindPackageHandleStandardArgs ) - diff --git a/fix-build-cmake39.patch b/fix-build-cmake39.patch deleted file mode 100644 index 1a7d67c..0000000 --- a/fix-build-cmake39.patch +++ /dev/null @@ -1,13337 +0,0 @@ -From 7e00441c257e7e9e5dc5ab983fc06046fb72b0c5 Mon Sep 17 00:00:00 2001 -From: Gilles Caulier -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($) - 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 $ $) -+add_library(digikamdatabase $) - - 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 -+ * Copyright (C) 2011-2017 by Gilles Caulier -+ * Copyright (C) 2010 by Andi Clemens -+ * Copyright (C) 2011 by Michael G. Hansen -+ * Copyright (C) 2014 by Mohamed Anwer -+ * -+ * 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(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 ImageSortFilterModel::mapListToSource(const QList& indexes) const -+{ -+ QList sourceIndexes; -+ foreach(const QModelIndex& index, indexes) -+ { -+ sourceIndexes << mapToSourceImageModel(index); -+ } -+ return sourceIndexes; -+} -+ -+QList ImageSortFilterModel::mapListFromSource(const QList& sourceIndexes) const -+{ -+ QList 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 ImageSortFilterModel::imageInfos(const QList& indexes) const -+{ -+ QList infos; -+ ImageModel* const model = sourceImageModel(); -+ -+ foreach(const QModelIndex& index, indexes) -+ { -+ infos << model->imageInfo(mapToSourceImageModel(index)); -+ } -+ -+ return infos; -+} -+ -+QList ImageSortFilterModel::imageIds(const QList& indexes) const -+{ -+ QList 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 ImageSortFilterModel::imageInfosSorted() const -+{ -+ QList infos; -+ const int size = rowCount(); -+ ImageModel* const model = sourceImageModel(); -+ -+ for (int i=0; iimageInfo(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,QList)), -+ d, SLOT(preprocessInfos(QList,QList))); -+ -+ connect(d->imageModel, SIGNAL(processAdded(QList,QList)), -+ d, SLOT(processAddedInfos(QList,QList))); -+ -+ connect(d, SIGNAL(reAddImageInfos(QList,QList)), -+ d->imageModel, SLOT(reAddImageInfos(QList,QList))); -+ -+ 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(this)); -+ } -+ -+ return DCategorizedSortFilterProxyModel::data(index, role); -+} -+ -+ImageFilterModel* ImageFilterModel::imageFilterModel() const -+{ -+ return const_cast(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& days) -+{ -+ Q_D(ImageFilterModel); -+ d->filter.setDayFilter(days); -+ setImageFilterSettings(d->filter); -+} -+ -+void ImageFilterModel::setTagFilter(const QList& includedTags, const QList& excludedTags, -+ ImageFilterSettings::MatchingCondition matchingCond, -+ bool showUnTagged, const QList& clTagIds, const QList& 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 urlList, const QString& id) -+{ -+ Q_D(ImageFilterModel); -+ d->filter.setUrlWhitelist(urlList, id); -+ setImageFilterSettings(d->filter); -+} -+ -+void ImageFilterModel::setIdWhitelist(const QList& 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& 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::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 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 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 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>= 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 -+ * Copyright (C) 2011 by Gilles Caulier -+ * Copyright (C) 2010 by Andi Clemens -+ * Copyright (C) 2011 by Michael G. Hansen -+ * Copyright (C) 2014 by Mohamed Anwer -+ * -+ * 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& 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 mapListToSource(const QList& indexes) const; -+ QList mapListFromSource(const QList& sourceIndexes) const; -+ -+ ImageInfo imageInfo(const QModelIndex& index) const; -+ qlonglong imageId(const QModelIndex& index) const; -+ QList imageInfos(const QList& indexes) const; -+ QList imageIds(const QList& 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 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& days); -+ void setTagFilter(const QList& includedTags, const QList& excludedTags, -+ ImageFilterSettings::MatchingCondition matchingCond, bool showUnTagged, -+ const QList& clTagIds, const QList& 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 urlList, const QString& id); -+ void setIdWhitelist(const QList& idList, const QString& id); -+ -+ void setVersionManagerSettings(const VersionManagerSettings& settings); -+ void setExceptionList(const QList& 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& infos); -+ void imageInfosAboutToBeRemoved(const QList& 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 -+ * Copyright (C) 2011-2017 by Gilles Caulier -+ * Copyright (C) 2010 by Andi Clemens -+ * Copyright (C) 2011 by Michael G. Hansen -+ * Copyright (C) 2014 by Mohamed Anwer -+ * -+ * 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"); -+} -+ -+void ImageFilterModel::ImageFilterModelPrivate::preprocessInfos(const QList& infos, const QList& extraValues) -+{ -+ infosToProcess(infos, extraValues, true); -+} -+ -+void ImageFilterModel::ImageFilterModelPrivate::processAddedInfos(const QList& infos, const QList& 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& infos) -+{ -+ infosToProcess(infos, QList(), false); -+} -+ -+void ImageFilterModel::ImageFilterModelPrivate::infosToProcess(const QList& infos, const QList& 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::const_iterator it = infos.constBegin(), end; -+ QList::const_iterator xit = extraValues.constBegin(), xend; -+ int index = 0; -+ QVector infoVector; -+ QVector 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::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 -+ * -+ * 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 -+#include -+#include -+#include -+#include -+#include -+#include -+ -+// 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& infos, const QVector& extraValues, int version, bool isForReAdd) -+ : infos(infos), extraValues(extraValues), version(version), isForReAdd(isForReAdd) -+ { -+ } -+ -+ QVector infos; -+ QVector extraValues; -+ unsigned int version; -+ bool isForReAdd; -+ QHash 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& infos); -+ void infosToProcess(const QList& infos, const QList& 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 filterResults; -+ bool hasOneMatch; -+ bool hasOneMatchForText; -+ -+ QList prepareHooks; -+ -+/* -+ QHash > categoryCountHashInt; -+ QHash > categoryCountHashString; -+ -+public: -+ -+ void cacheCategoryCount(int id, qlonglong imageid) const -+ { const_cast(this)->categoryCountHashInt[id].insert(imageid); } -+ void cacheCategoryCount(const QString& id, qlonglong imageid) const -+ { const_cast(this)->categoryCountHashString[id].insert(imageid); } -+*/ -+ -+public Q_SLOTS: -+ -+ void preprocessInfos(const QList& infos, const QList& extraValues); -+ void processAddedInfos(const QList& infos, const QList& 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& infos, const QList& 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 -+ * Copyright (C) 2011-2017 by Gilles Caulier -+ * Copyright (C) 2010 by Andi Clemens -+ * Copyright (C) 2011 by Michael G. Hansen -+ * Copyright (C) 2014 by Mohamed Anwer -+ * -+ * 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 -+ * -+ * 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 -+ -+// 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 -+ * Copyright (C) 2011-2017 by Gilles Caulier -+ * Copyright (C) 2010 by Andi Clemens -+ * Copyright (C) 2011 by Michael G. Hansen -+ * Copyright (C) 2014 by Mohamed Anwer -+ * -+ * 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 -+ -+// Qt includes -+ -+#include -+ -+// 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& days) -+{ -+ m_dayFilter.clear(); -+ -+ for (QList::const_iterator it = days.constBegin(); it != days.constEnd(); ++it) -+ { -+ m_dayFilter.insert(*it, true); -+ } -+} -+ -+void ImageFilterSettings::setTagFilter(const QList& includedTags, -+ const QList& excludedTags, -+ MatchingCondition matchingCondition, -+ bool showUnTagged, -+ const QList& clTagIds, -+ const QList& 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& hash) -+{ -+ m_tagNameHash = hash; -+} -+ -+void ImageFilterSettings::setAlbumNames(const QHash& hash) -+{ -+ m_albumNameHash = hash; -+} -+ -+void ImageFilterSettings::setUrlWhitelist(const QList& urlList, const QString& id) -+{ -+ if (urlList.isEmpty()) -+ { -+ m_urlWhitelists.remove(id); -+ } -+ else -+ { -+ m_urlWhitelists.insert(id, urlList); -+ } -+} -+ -+void ImageFilterSettings::setIdWhitelist(const QList& idList, const QString& id) -+{ -+ if (idList.isEmpty()) -+ { -+ m_idWhitelists.remove(id); -+ } -+ else -+ { -+ m_idWhitelists.insert(id, idList); -+ } -+} -+ -+template -+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 -+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 tagIds = info.tagIds(); -+ QList::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 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 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>::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 >::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 >::const_iterator it = m_exceptionLists.constBegin(); -+ it != m_exceptionLists.constEnd(); ++it) -+ { -+ if (it->contains(id)) -+ { -+ return true; -+ } -+ } -+ -+ bool match = true; -+ QList tagIds = info.tagIds(); -+ -+ if (!tagIds.contains(m_includeTagFilter)) -+ { -+ for (QList::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 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& 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 -+ * Copyright (C) 2011-2017 by Gilles Caulier -+ * Copyright (C) 2010 by Andi Clemens -+ * Copyright (C) 2011 by Michael G. Hansen -+ * Copyright (C) 2014 by Mohamed Anwer -+ * -+ * 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 -+#include -+#include -+#include -+#include -+#include -+ -+// 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& includedTags, -+ const QList& excludedTags, -+ MatchingCondition matchingCond, -+ bool showUnTagged, -+ const QList& clTagIds, -+ const QList& 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& days); -+ -+public: -+ -+ /// --- Text filter --- -+ void setTextFilter(const SearchTextFilterSettings& settings); -+ void setTagNames(const QHash& tagNameHash); -+ void setAlbumNames(const QHash& 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& urlList, const QString& id); -+ -+public: -+ -+ /// --- ID whitelist filter -+ void setIdWhitelist(const QList& 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 m_includeTagFilter; -+ QList m_excludeTagFilter; -+ MatchingCondition m_matchingCond; -+ QList m_colorLabelTagFilter; -+ QList m_pickLabelTagFilter; -+ -+ /// --- Rating filter --- -+ int m_ratingFilter; -+ RatingCondition m_ratingCond; -+ bool m_isUnratedExcluded; -+ -+ /// --- Date filter --- -+ QMap 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 m_tagNameHash; -+ QHash m_albumNameHash; -+ -+ /// --- Mime filter --- -+ MimeFilter::TypeMimeFilter m_mimeTypeFilter; -+ -+ /// --- Geolocation filter -+ GeolocationCondition m_geolocationCondition; -+ -+ /// --- URL whitelist filter -+ QHash> m_urlWhitelists; -+ -+ /// --- ID whitelist filter -+ QHash > 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& 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 m_excludeTagFilter; -+ int m_includeTagFilter; -+ int m_exceptionTagFilter; -+ QHash > 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 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 -+ * -+ * 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 -+ * -+ * 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& 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 -+ * -+ * 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 -+#include -+ -+// 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 extraValues; -+ QHash idHash; -+ -+ bool keepFilePathCache; -+ QHash filePathHash; -+ -+ bool sendRemovalSignals; -+ -+ QObject* preprocessor; -+ bool refreshing; -+ bool reAdding; -+ bool incrementalRefreshRequested; -+ -+ DatabaseFields::Set watchFlags; -+ -+ class ImageModelIncrementalUpdater* incrementalUpdater; -+ -+ ImageInfoList pendingInfos; -+ QList 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 IntPair; // to make foreach macro happy -+typedef QList IntPairList; -+ -+class ImageModelIncrementalUpdater -+{ -+public: -+ -+ explicit ImageModelIncrementalUpdater(ImageModel::Private* d); -+ -+ void appendInfos(const QList& infos, const QList& extraValues); -+ void aboutToBeRemovedInModel(const IntPairList& aboutToBeRemoved); -+ QList oldIndexes(); -+ -+ static QList toContiguousPairs(const QList& ids); -+ -+public: -+ -+ QHash oldIds; -+ QList oldExtraValues; -+ QList newInfos; -+ QList newExtraValues; -+ QList 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 ImageModel::imageInfos(const QList& indexes) const -+{ -+ QList infos; -+ -+ foreach(const QModelIndex& index, indexes) -+ { -+ infos << imageInfo(index); -+ } -+ -+ return infos; -+} -+ -+QList ImageModel::imageIds(const QList& indexes) const -+{ -+ QList 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 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::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 ImageModel::indexesForImageId(qlonglong id) const -+{ -+ QList indexes; -+ QHash::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::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(); -+ 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(); -+ 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; iinfos.at(i).filePath() == filePath) -+ { -+ return createIndex(i, 0); -+ } -+ } -+ } -+ -+ return QModelIndex(); -+} -+ -+QList ImageModel::indexesForPath(const QString& filePath) const -+{ -+ if (d->keepFilePathCache) -+ { -+ return indexesForImageId(d->filePathHash.value(filePath)); -+ } -+ else -+ { -+ QList indexes; -+ const int size = d->infos.size(); -+ -+ for (int i=0; iinfos.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 ImageModel::imageInfos(const QString& filePath) const -+{ -+ QList 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() << info, QList()); -+} -+ -+void ImageModel::addImageInfos(const QList& infos) -+{ -+ addImageInfos(infos, QList()); -+} -+ -+void ImageModel::addImageInfos(const QList& infos, const QList& 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() << info, QList()); -+} -+ -+void ImageModel::addImageInfosSynchronously(const QList& infos) -+{ -+ addImageInfos(infos, QList()); -+} -+ -+void ImageModel::addImageInfosSynchronously(const QList& infos, const QList& extraValues) -+{ -+ if (infos.isEmpty()) -+ { -+ return; -+ } -+ -+ publiciseInfos(infos, extraValues); -+ emit processAdded(infos, extraValues); -+} -+ -+void ImageModel::ensureHasImageInfo(const ImageInfo& info) -+{ -+ ensureHasImageInfos(QList() << info, QList()); -+} -+ -+void ImageModel::ensureHasImageInfos(const QList& infos) -+{ -+ ensureHasImageInfos(infos, QList()); -+} -+ -+void ImageModel::ensureHasImageInfos(const QList& infos, const QList& 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& infos) -+{ -+ clearImageInfos(); -+ addImageInfos(infos); -+} -+ -+QList ImageModel::imageInfos() const -+{ -+ return d->infos; -+} -+ -+QList 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::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 ImageModel::uniqueImageInfos() const -+{ -+ if (d->extraValues.isEmpty()) -+ { -+ return d->infos; -+ } -+ -+ QList uniqueInfos; -+ const int size = d->infos.size(); -+ -+ for (int i=0; iinfos.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,QList)), 0, 0); -+ disconnect(d->preprocessor, 0, this, SLOT(reAddImageInfos(QList,QList))); -+ disconnect(d->preprocessor, 0, this, SLOT(reAddingFinished())); -+ } -+} -+ -+void ImageModel::appendInfos(const QList& infos, const QList& extraValues) -+{ -+ if (infos.isEmpty()) -+ { -+ return; -+ } -+ -+ if (d->preprocessor) -+ { -+ d->reAdding = true; -+ emit preprocess(infos, extraValues); -+ } -+ else -+ { -+ publiciseInfos(infos, extraValues); -+ } -+} -+ -+void ImageModel::appendInfosChecked(const QList& infos, const QList& extraValues) -+{ -+ // This method does deduplication. It is private because in context of readding or refreshing it is of no use. -+ -+ if (extraValues.isEmpty()) -+ { -+ QList checkedInfos; -+ -+ foreach (const ImageInfo& info, infos) -+ { -+ if (!hasImage(info)) -+ { -+ checkedInfos << info; -+ } -+ } -+ -+ appendInfos(checkedInfos, QList()); -+ } -+ else -+ { -+ QList checkedInfos; -+ QList checkedExtraValues; -+ const int size = infos.size(); -+ -+ for (int i=0; i& infos, const QList& 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& infos, const QList& 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 > 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() << index); -+} -+ -+void ImageModel::removeIndexes(const QList& indexes) -+{ -+ QList 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() << info); -+} -+ -+void ImageModel::removeImageInfos(const QList& infos) -+{ -+ QList 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& infos, const QList& extraValues) -+{ -+ if (extraValues.isEmpty()) -+ { -+ removeImageInfos(infos); -+ return; -+ } -+ -+ QList listIndexes; -+ -+ for (int i=0; isendRemovalSignals = send; -+} -+ -+template -+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 >& toRemove) -+{ -+ if (d->incrementalUpdater) -+ { -+ d->incrementalUpdater->aboutToBeRemovedInModel(toRemove); -+ } -+ -+ removeRowPairs(toRemove); -+} -+ -+void ImageModel::removeRowPairs(const QList >& 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 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 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::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::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& infos, const QList& extraValues) -+{ -+ if (extraValues.isEmpty()) -+ { -+ foreach(const ImageInfo& info, infos) -+ { -+ QHash::iterator it = oldIds.find(info.id()); -+ -+ if (it != oldIds.end()) -+ { -+ oldIds.erase(it); -+ } -+ else -+ { -+ newInfos << info; -+ } -+ } -+ } -+ else -+ { -+ for (int i=0; i::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 > 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::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 > ImageModelIncrementalUpdater::toContiguousPairs(const QList& unsorted) -+{ -+ // Take the given indices and return them as contiguous pairs [begin, end] -+ -+ QList > pairs; -+ -+ if (unsorted.isEmpty()) -+ { -+ return pairs; -+ } -+ -+ QList indices(unsorted); -+ qSort(indices); -+ -+ QPair pair(indices.first(), indices.first()); -+ -+ for (int i=1; iisValid(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(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 -+ * -+ * 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 -+ -+// 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 imageInfos(const QList& indexes) const; -+ QList imageIds(const QList& 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 indexesForImageInfo(const ImageInfo& info) const; -+ QList 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 indexesForPath(const QString& filePath) const; -+ QList 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& infos); -+ void addImageInfos(const QList& infos, const QList& extraValues); -+ -+ /** Clears image infos and resets model. -+ */ -+ void clearImageInfos(); -+ -+ /** Clears and adds the infos. -+ */ -+ void setImageInfos(const QList& infos); -+ -+ /** -+ * Directly remove the given indexes or infos from the model. -+ */ -+ void removeIndex(const QModelIndex& indexes); -+ void removeIndexes(const QList& indexes); -+ void removeImageInfo(const ImageInfo& info); -+ void removeImageInfos(const QList& infos); -+ void removeImageInfos(const QList& infos, const QList& 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& infos); -+ void addImageInfosSynchronously(const QList& infos, const QList& 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& infos); -+ void ensureHasImageInfos(const QList& infos, const QList& extraValues); -+ -+ /** -+ * Ensure that all images grouped on the given leader are contained in the model. -+ */ -+ void ensureHasGroupedImages(const ImageInfo& groupLeader); -+ -+ QList imageInfos() const; -+ QList imageIds() const; -+ QList 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& 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& 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& 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& infos); -+ -+ /** Connect to this signal only if you are the current preprocessor. -+ */ -+ void preprocess(const QList& infos, const QList&); -+ void processAdded(const QList& infos, const QList&); -+ -+ /** 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& infos, const QList& 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& infos, const QList& extraValues); -+ void appendInfosChecked(const QList& infos, const QList& extraValues); -+ void publiciseInfos(const QList& infos, const QList& extraValues); -+ void cleanSituationChecks(); -+ void removeRowPairsWithCheck(const QList >& toRemove); -+ void removeRowPairs(const QList >& 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 -+ * Copyright (C) 2014 by Mohamed Anwer -+ * -+ * 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 -+#include -+ -+// 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 -+ * -+ * 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 -+#include -+#include -+#include -+#include -+ -+// 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 -+ 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 -+ 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 -+ 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 -+ * Copyright (C) 2011-2017 by Gilles Caulier -+ * -+ * 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 -+ -+// 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 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& indexesToPrepare) -+{ -+ prepareThumbnails(indexesToPrepare, d->thumbSize); -+} -+ -+void ImageThumbnailModel::prepareThumbnails(const QList& indexesToPrepare, const ThumbnailSize& thumbSize) -+{ -+ if (!d->thread) -+ { -+ return; -+ } -+ -+ QList ids; -+ foreach(const QModelIndex& index, indexesToPrepare) -+ { -+ ids << imageInfoRef(index).thumbnailIdentifier(); -+ } -+ d->thread->findGroup(ids, thumbSize.size()); -+} -+ -+void ImageThumbnailModel::preloadThumbnails(const QList& infos) -+{ -+ if (!d->preloadThread) -+ { -+ return; -+ } -+ -+ QList ids; -+ foreach(const ImageInfo& info, infos) -+ { -+ ids << info.thumbnailIdentifier(); -+ } -+ d->preloadThread->pregenerateGroup(ids, d->preloadThumbnailSize()); -+} -+ -+void ImageThumbnailModel::preloadThumbnails(const QList& indexesToPreload) -+{ -+ if (!d->preloadThread) -+ { -+ return; -+ } -+ -+ QList 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 -+ * Copyright (C) 2011 by Gilles Caulier -+ * -+ * 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& indexesToPrepare); -+ void prepareThumbnails(const QList& 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&); -+ void preloadThumbnails(const QList&); -+ 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 -+ * -+ * 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 -+ -+// 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 >* 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 >; -+} -+ -+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 >& 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 -+ * -+ * 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 -+#include -+ -+// 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 >& 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 -- * Copyright (C) 2011-2017 by Gilles Caulier -- * Copyright (C) 2010 by Andi Clemens -- * Copyright (C) 2011 by Michael G. Hansen -- * Copyright (C) 2014 by Mohamed Anwer -- * -- * 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(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 ImageSortFilterModel::mapListToSource(const QList& indexes) const --{ -- QList sourceIndexes; -- foreach(const QModelIndex& index, indexes) -- { -- sourceIndexes << mapToSourceImageModel(index); -- } -- return sourceIndexes; --} -- --QList ImageSortFilterModel::mapListFromSource(const QList& sourceIndexes) const --{ -- QList 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 ImageSortFilterModel::imageInfos(const QList& indexes) const --{ -- QList infos; -- ImageModel* const model = sourceImageModel(); -- -- foreach(const QModelIndex& index, indexes) -- { -- infos << model->imageInfo(mapToSourceImageModel(index)); -- } -- -- return infos; --} -- --QList ImageSortFilterModel::imageIds(const QList& indexes) const --{ -- QList 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 ImageSortFilterModel::imageInfosSorted() const --{ -- QList infos; -- const int size = rowCount(); -- ImageModel* const model = sourceImageModel(); -- -- for (int i=0; iimageInfo(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,QList)), -- d, SLOT(preprocessInfos(QList,QList))); -- -- connect(d->imageModel, SIGNAL(processAdded(QList,QList)), -- d, SLOT(processAddedInfos(QList,QList))); -- -- connect(d, SIGNAL(reAddImageInfos(QList,QList)), -- d->imageModel, SLOT(reAddImageInfos(QList,QList))); -- -- 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(this)); -- } -- -- return DCategorizedSortFilterProxyModel::data(index, role); --} -- --ImageFilterModel* ImageFilterModel::imageFilterModel() const --{ -- return const_cast(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& days) --{ -- Q_D(ImageFilterModel); -- d->filter.setDayFilter(days); -- setImageFilterSettings(d->filter); --} -- --void ImageFilterModel::setTagFilter(const QList& includedTags, const QList& excludedTags, -- ImageFilterSettings::MatchingCondition matchingCond, -- bool showUnTagged, const QList& clTagIds, const QList& 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 urlList, const QString& id) --{ -- Q_D(ImageFilterModel); -- d->filter.setUrlWhitelist(urlList, id); -- setImageFilterSettings(d->filter); --} -- --void ImageFilterModel::setIdWhitelist(const QList& 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& 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::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 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 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 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>= 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 -- * Copyright (C) 2011 by Gilles Caulier -- * Copyright (C) 2010 by Andi Clemens -- * Copyright (C) 2011 by Michael G. Hansen -- * Copyright (C) 2014 by Mohamed Anwer -- * -- * 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& 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 mapListToSource(const QList& indexes) const; -- QList mapListFromSource(const QList& sourceIndexes) const; -- -- ImageInfo imageInfo(const QModelIndex& index) const; -- qlonglong imageId(const QModelIndex& index) const; -- QList imageInfos(const QList& indexes) const; -- QList imageIds(const QList& 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 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& days); -- void setTagFilter(const QList& includedTags, const QList& excludedTags, -- ImageFilterSettings::MatchingCondition matchingCond, bool showUnTagged, -- const QList& clTagIds, const QList& 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 urlList, const QString& id); -- void setIdWhitelist(const QList& idList, const QString& id); -- -- void setVersionManagerSettings(const VersionManagerSettings& settings); -- void setExceptionList(const QList& 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& infos); -- void imageInfosAboutToBeRemoved(const QList& 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 -- * Copyright (C) 2011-2017 by Gilles Caulier -- * Copyright (C) 2010 by Andi Clemens -- * Copyright (C) 2011 by Michael G. Hansen -- * Copyright (C) 2014 by Mohamed Anwer -- * -- * 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"); --} -- --void ImageFilterModel::ImageFilterModelPrivate::preprocessInfos(const QList& infos, const QList& extraValues) --{ -- infosToProcess(infos, extraValues, true); --} -- --void ImageFilterModel::ImageFilterModelPrivate::processAddedInfos(const QList& infos, const QList& 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& infos) --{ -- infosToProcess(infos, QList(), false); --} -- --void ImageFilterModel::ImageFilterModelPrivate::infosToProcess(const QList& infos, const QList& 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::const_iterator it = infos.constBegin(), end; -- QList::const_iterator xit = extraValues.constBegin(), xend; -- int index = 0; -- QVector infoVector; -- QVector 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::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 -- * -- * 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 --#include --#include --#include --#include --#include --#include -- --// 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& infos, const QVector& extraValues, int version, bool isForReAdd) -- : infos(infos), extraValues(extraValues), version(version), isForReAdd(isForReAdd) -- { -- } -- -- QVector infos; -- QVector extraValues; -- unsigned int version; -- bool isForReAdd; -- QHash 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& infos); -- void infosToProcess(const QList& infos, const QList& 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 filterResults; -- bool hasOneMatch; -- bool hasOneMatchForText; -- -- QList prepareHooks; -- --/* -- QHash > categoryCountHashInt; -- QHash > categoryCountHashString; -- --public: -- -- void cacheCategoryCount(int id, qlonglong imageid) const -- { const_cast(this)->categoryCountHashInt[id].insert(imageid); } -- void cacheCategoryCount(const QString& id, qlonglong imageid) const -- { const_cast(this)->categoryCountHashString[id].insert(imageid); } --*/ -- --public Q_SLOTS: -- -- void preprocessInfos(const QList& infos, const QList& extraValues); -- void processAddedInfos(const QList& infos, const QList& 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& infos, const QList& 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 -- * Copyright (C) 2011-2017 by Gilles Caulier -- * Copyright (C) 2010 by Andi Clemens -- * Copyright (C) 2011 by Michael G. Hansen -- * Copyright (C) 2014 by Mohamed Anwer -- * -- * 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 -- * -- * 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 -- --// 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 -- * Copyright (C) 2011-2017 by Gilles Caulier -- * Copyright (C) 2010 by Andi Clemens -- * Copyright (C) 2011 by Michael G. Hansen -- * Copyright (C) 2014 by Mohamed Anwer -- * -- * 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 -- --// Qt includes -- --#include -- --// 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& days) --{ -- m_dayFilter.clear(); -- -- for (QList::const_iterator it = days.constBegin(); it != days.constEnd(); ++it) -- { -- m_dayFilter.insert(*it, true); -- } --} -- --void ImageFilterSettings::setTagFilter(const QList& includedTags, -- const QList& excludedTags, -- MatchingCondition matchingCondition, -- bool showUnTagged, -- const QList& clTagIds, -- const QList& 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& hash) --{ -- m_tagNameHash = hash; --} -- --void ImageFilterSettings::setAlbumNames(const QHash& hash) --{ -- m_albumNameHash = hash; --} -- --void ImageFilterSettings::setUrlWhitelist(const QList& urlList, const QString& id) --{ -- if (urlList.isEmpty()) -- { -- m_urlWhitelists.remove(id); -- } -- else -- { -- m_urlWhitelists.insert(id, urlList); -- } --} -- --void ImageFilterSettings::setIdWhitelist(const QList& idList, const QString& id) --{ -- if (idList.isEmpty()) -- { -- m_idWhitelists.remove(id); -- } -- else -- { -- m_idWhitelists.insert(id, idList); -- } --} -- --template --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 --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 tagIds = info.tagIds(); -- QList::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 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 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>::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 >::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 >::const_iterator it = m_exceptionLists.constBegin(); -- it != m_exceptionLists.constEnd(); ++it) -- { -- if (it->contains(id)) -- { -- return true; -- } -- } -- -- bool match = true; -- QList tagIds = info.tagIds(); -- -- if (!tagIds.contains(m_includeTagFilter)) -- { -- for (QList::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 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& 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 -- * Copyright (C) 2011-2017 by Gilles Caulier -- * Copyright (C) 2010 by Andi Clemens -- * Copyright (C) 2011 by Michael G. Hansen -- * Copyright (C) 2014 by Mohamed Anwer -- * -- * 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 --#include --#include --#include --#include --#include -- --// 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& includedTags, -- const QList& excludedTags, -- MatchingCondition matchingCond, -- bool showUnTagged, -- const QList& clTagIds, -- const QList& 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& days); -- --public: -- -- /// --- Text filter --- -- void setTextFilter(const SearchTextFilterSettings& settings); -- void setTagNames(const QHash& tagNameHash); -- void setAlbumNames(const QHash& 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& urlList, const QString& id); -- --public: -- -- /// --- ID whitelist filter -- void setIdWhitelist(const QList& 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 m_includeTagFilter; -- QList m_excludeTagFilter; -- MatchingCondition m_matchingCond; -- QList m_colorLabelTagFilter; -- QList m_pickLabelTagFilter; -- -- /// --- Rating filter --- -- int m_ratingFilter; -- RatingCondition m_ratingCond; -- bool m_isUnratedExcluded; -- -- /// --- Date filter --- -- QMap 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 m_tagNameHash; -- QHash m_albumNameHash; -- -- /// --- Mime filter --- -- MimeFilter::TypeMimeFilter m_mimeTypeFilter; -- -- /// --- Geolocation filter -- GeolocationCondition m_geolocationCondition; -- -- /// --- URL whitelist filter -- QHash> m_urlWhitelists; -- -- /// --- ID whitelist filter -- QHash > 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& 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 m_excludeTagFilter; -- int m_includeTagFilter; -- int m_exceptionTagFilter; -- QHash > 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 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 -- * -- * 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 -- * -- * 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& 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 -- * -- * 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 --#include -- --// 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 extraValues; -- QHash idHash; -- -- bool keepFilePathCache; -- QHash filePathHash; -- -- bool sendRemovalSignals; -- -- QObject* preprocessor; -- bool refreshing; -- bool reAdding; -- bool incrementalRefreshRequested; -- -- DatabaseFields::Set watchFlags; -- -- class ImageModelIncrementalUpdater* incrementalUpdater; -- -- ImageInfoList pendingInfos; -- QList 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 IntPair; // to make foreach macro happy --typedef QList IntPairList; -- --class ImageModelIncrementalUpdater --{ --public: -- -- explicit ImageModelIncrementalUpdater(ImageModel::Private* d); -- -- void appendInfos(const QList& infos, const QList& extraValues); -- void aboutToBeRemovedInModel(const IntPairList& aboutToBeRemoved); -- QList oldIndexes(); -- -- static QList toContiguousPairs(const QList& ids); -- --public: -- -- QHash oldIds; -- QList oldExtraValues; -- QList newInfos; -- QList newExtraValues; -- QList 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 ImageModel::imageInfos(const QList& indexes) const --{ -- QList infos; -- -- foreach(const QModelIndex& index, indexes) -- { -- infos << imageInfo(index); -- } -- -- return infos; --} -- --QList ImageModel::imageIds(const QList& indexes) const --{ -- QList 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 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::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 ImageModel::indexesForImageId(qlonglong id) const --{ -- QList indexes; -- QHash::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::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(); -- 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(); -- 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; iinfos.at(i).filePath() == filePath) -- { -- return createIndex(i, 0); -- } -- } -- } -- -- return QModelIndex(); --} -- --QList ImageModel::indexesForPath(const QString& filePath) const --{ -- if (d->keepFilePathCache) -- { -- return indexesForImageId(d->filePathHash.value(filePath)); -- } -- else -- { -- QList indexes; -- const int size = d->infos.size(); -- -- for (int i=0; iinfos.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 ImageModel::imageInfos(const QString& filePath) const --{ -- QList 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() << info, QList()); --} -- --void ImageModel::addImageInfos(const QList& infos) --{ -- addImageInfos(infos, QList()); --} -- --void ImageModel::addImageInfos(const QList& infos, const QList& 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() << info, QList()); --} -- --void ImageModel::addImageInfosSynchronously(const QList& infos) --{ -- addImageInfos(infos, QList()); --} -- --void ImageModel::addImageInfosSynchronously(const QList& infos, const QList& extraValues) --{ -- if (infos.isEmpty()) -- { -- return; -- } -- -- publiciseInfos(infos, extraValues); -- emit processAdded(infos, extraValues); --} -- --void ImageModel::ensureHasImageInfo(const ImageInfo& info) --{ -- ensureHasImageInfos(QList() << info, QList()); --} -- --void ImageModel::ensureHasImageInfos(const QList& infos) --{ -- ensureHasImageInfos(infos, QList()); --} -- --void ImageModel::ensureHasImageInfos(const QList& infos, const QList& 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& infos) --{ -- clearImageInfos(); -- addImageInfos(infos); --} -- --QList ImageModel::imageInfos() const --{ -- return d->infos; --} -- --QList 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::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 ImageModel::uniqueImageInfos() const --{ -- if (d->extraValues.isEmpty()) -- { -- return d->infos; -- } -- -- QList uniqueInfos; -- const int size = d->infos.size(); -- -- for (int i=0; iinfos.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,QList)), 0, 0); -- disconnect(d->preprocessor, 0, this, SLOT(reAddImageInfos(QList,QList))); -- disconnect(d->preprocessor, 0, this, SLOT(reAddingFinished())); -- } --} -- --void ImageModel::appendInfos(const QList& infos, const QList& extraValues) --{ -- if (infos.isEmpty()) -- { -- return; -- } -- -- if (d->preprocessor) -- { -- d->reAdding = true; -- emit preprocess(infos, extraValues); -- } -- else -- { -- publiciseInfos(infos, extraValues); -- } --} -- --void ImageModel::appendInfosChecked(const QList& infos, const QList& extraValues) --{ -- // This method does deduplication. It is private because in context of readding or refreshing it is of no use. -- -- if (extraValues.isEmpty()) -- { -- QList checkedInfos; -- -- foreach (const ImageInfo& info, infos) -- { -- if (!hasImage(info)) -- { -- checkedInfos << info; -- } -- } -- -- appendInfos(checkedInfos, QList()); -- } -- else -- { -- QList checkedInfos; -- QList checkedExtraValues; -- const int size = infos.size(); -- -- for (int i=0; i& infos, const QList& 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& infos, const QList& 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 > 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() << index); --} -- --void ImageModel::removeIndexes(const QList& indexes) --{ -- QList 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() << info); --} -- --void ImageModel::removeImageInfos(const QList& infos) --{ -- QList 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& infos, const QList& extraValues) --{ -- if (extraValues.isEmpty()) -- { -- removeImageInfos(infos); -- return; -- } -- -- QList listIndexes; -- -- for (int i=0; isendRemovalSignals = send; --} -- --template --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 >& toRemove) --{ -- if (d->incrementalUpdater) -- { -- d->incrementalUpdater->aboutToBeRemovedInModel(toRemove); -- } -- -- removeRowPairs(toRemove); --} -- --void ImageModel::removeRowPairs(const QList >& 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 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 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::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::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& infos, const QList& extraValues) --{ -- if (extraValues.isEmpty()) -- { -- foreach(const ImageInfo& info, infos) -- { -- QHash::iterator it = oldIds.find(info.id()); -- -- if (it != oldIds.end()) -- { -- oldIds.erase(it); -- } -- else -- { -- newInfos << info; -- } -- } -- } -- else -- { -- for (int i=0; i::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 > 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::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 > ImageModelIncrementalUpdater::toContiguousPairs(const QList& unsorted) --{ -- // Take the given indices and return them as contiguous pairs [begin, end] -- -- QList > pairs; -- -- if (unsorted.isEmpty()) -- { -- return pairs; -- } -- -- QList indices(unsorted); -- qSort(indices); -- -- QPair pair(indices.first(), indices.first()); -- -- for (int i=1; iisValid(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(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 -- * -- * 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 -- --// 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 imageInfos(const QList& indexes) const; -- QList imageIds(const QList& 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 indexesForImageInfo(const ImageInfo& info) const; -- QList 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 indexesForPath(const QString& filePath) const; -- QList 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& infos); -- void addImageInfos(const QList& infos, const QList& extraValues); -- -- /** Clears image infos and resets model. -- */ -- void clearImageInfos(); -- -- /** Clears and adds the infos. -- */ -- void setImageInfos(const QList& infos); -- -- /** -- * Directly remove the given indexes or infos from the model. -- */ -- void removeIndex(const QModelIndex& indexes); -- void removeIndexes(const QList& indexes); -- void removeImageInfo(const ImageInfo& info); -- void removeImageInfos(const QList& infos); -- void removeImageInfos(const QList& infos, const QList& 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& infos); -- void addImageInfosSynchronously(const QList& infos, const QList& 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& infos); -- void ensureHasImageInfos(const QList& infos, const QList& extraValues); -- -- /** -- * Ensure that all images grouped on the given leader are contained in the model. -- */ -- void ensureHasGroupedImages(const ImageInfo& groupLeader); -- -- QList imageInfos() const; -- QList imageIds() const; -- QList 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& 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& 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& 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& infos); -- -- /** Connect to this signal only if you are the current preprocessor. -- */ -- void preprocess(const QList& infos, const QList&); -- void processAdded(const QList& infos, const QList&); -- -- /** 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& infos, const QList& 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& infos, const QList& extraValues); -- void appendInfosChecked(const QList& infos, const QList& extraValues); -- void publiciseInfos(const QList& infos, const QList& extraValues); -- void cleanSituationChecks(); -- void removeRowPairsWithCheck(const QList >& toRemove); -- void removeRowPairs(const QList >& 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 -- * Copyright (C) 2014 by Mohamed Anwer -- * -- * 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 --#include -- --// 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 -- * -- * 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 --#include --#include --#include --#include -- --// 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 -- 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 -- 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 -- 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 -- * Copyright (C) 2011-2017 by Gilles Caulier -- * -- * 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 -- --// 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 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& indexesToPrepare) --{ -- prepareThumbnails(indexesToPrepare, d->thumbSize); --} -- --void ImageThumbnailModel::prepareThumbnails(const QList& indexesToPrepare, const ThumbnailSize& thumbSize) --{ -- if (!d->thread) -- { -- return; -- } -- -- QList ids; -- foreach(const QModelIndex& index, indexesToPrepare) -- { -- ids << imageInfoRef(index).thumbnailIdentifier(); -- } -- d->thread->findGroup(ids, thumbSize.size()); --} -- --void ImageThumbnailModel::preloadThumbnails(const QList& infos) --{ -- if (!d->preloadThread) -- { -- return; -- } -- -- QList ids; -- foreach(const ImageInfo& info, infos) -- { -- ids << info.thumbnailIdentifier(); -- } -- d->preloadThread->pregenerateGroup(ids, d->preloadThumbnailSize()); --} -- --void ImageThumbnailModel::preloadThumbnails(const QList& indexesToPreload) --{ -- if (!d->preloadThread) -- { -- return; -- } -- -- QList 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 -- * Copyright (C) 2011 by Gilles Caulier -- * -- * 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& indexesToPrepare); -- void prepareThumbnails(const QList& 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&); -- void preloadThumbnails(const QList&); -- 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 -- * -- * 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 -- --// 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 >* 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 >; --} -- --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 >& 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 -- * -- * 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 --#include -- --// 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 >& 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 -