From 93574b31679d2596b864af040f9b7b0b687b20e4072f2b82a10a7a06fbdde08e Mon Sep 17 00:00:00 2001
From: OBS User buildservice-autocommit <null@suse.de>
Date: Thu, 14 Sep 2017 19:15:21 +0000
Subject: [PATCH] Updating link to change in openSUSE:Factory/digikam revision
 161.0

OBS-URL: https://build.opensuse.org/package/show/KDE:Extra/digikam?expand=0&rev=d3e55265498de3adddabc97d4a3c5938
---
 digikam-5.6.0.tar.xz    |     3 -
 digikam-5.7.0.tar.xz    |     3 +
 digikam.changes         |    34 +
 digikam.spec            |    27 +-
 find_libastro-qt5.patch |    12 -
 fix-build-cmake39.patch | 13337 --------------------------------------
 6 files changed, 43 insertions(+), 13373 deletions(-)
 delete mode 100644 digikam-5.6.0.tar.xz
 create mode 100644 digikam-5.7.0.tar.xz
 delete mode 100644 find_libastro-qt5.patch
 delete mode 100644 fix-build-cmake39.patch

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 <caulier.gilles@gmail.com>
-Date: Sat, 22 Jul 2017 15:46:08 +0200
-Subject: fix broken linking stage under MacOS with macports. move database
- models into libdigikamdatabase. Let's others model in place to be included
- into libdigikamcore
-
----
- libs/database/CMakeLists.txt                     |   16 +-
- libs/database/models/imagefiltermodel.cpp        | 1116 ++++++++++++++++++
- libs/database/models/imagefiltermodel.h          |  299 +++++
- libs/database/models/imagefiltermodelpriv.cpp    |  258 ++++
- libs/database/models/imagefiltermodelpriv.h      |  159 +++
- libs/database/models/imagefiltermodelthreads.cpp |   40 +
- libs/database/models/imagefiltermodelthreads.h   |  100 ++
- libs/database/models/imagefiltersettings.cpp     |  952 +++++++++++++++
- libs/database/models/imagefiltersettings.h       |  349 ++++++
- libs/database/models/imagelistmodel.cpp          |   70 ++
- libs/database/models/imagelistmodel.h            |   63 +
- libs/database/models/imagemodel.cpp              | 1368 ++++++++++++++++++++++
- libs/database/models/imagemodel.h                |  364 ++++++
- libs/database/models/imagesortsettings.cpp       |  400 +++++++
- libs/database/models/imagesortsettings.h         |  225 ++++
- libs/database/models/imagethumbnailmodel.cpp     |  323 +++++
- libs/database/models/imagethumbnailmodel.h       |  140 +++
- libs/database/models/imageversionsmodel.cpp      |  183 +++
- libs/database/models/imageversionsmodel.h        |   75 ++
- libs/models/CMakeLists.txt                       |   15 +-
- libs/models/imagefiltermodel.cpp                 | 1116 ------------------
- libs/models/imagefiltermodel.h                   |  299 -----
- libs/models/imagefiltermodelpriv.cpp             |  258 ----
- libs/models/imagefiltermodelpriv.h               |  159 ---
- libs/models/imagefiltermodelthreads.cpp          |   40 -
- libs/models/imagefiltermodelthreads.h            |  100 --
- libs/models/imagefiltersettings.cpp              |  952 ---------------
- libs/models/imagefiltersettings.h                |  349 ------
- libs/models/imagelistmodel.cpp                   |   70 --
- libs/models/imagelistmodel.h                     |   63 -
- libs/models/imagemodel.cpp                       | 1368 ----------------------
- libs/models/imagemodel.h                         |  364 ------
- libs/models/imagesortsettings.cpp                |  400 -------
- libs/models/imagesortsettings.h                  |  225 ----
- libs/models/imagethumbnailmodel.cpp              |  323 -----
- libs/models/imagethumbnailmodel.h                |  140 ---
- libs/models/imageversionsmodel.cpp               |  183 ---
- libs/models/imageversionsmodel.h                 |   75 --
- 38 files changed, 6499 insertions(+), 6500 deletions(-)
- create mode 100644 libs/database/models/imagefiltermodel.cpp
- create mode 100644 libs/database/models/imagefiltermodel.h
- create mode 100644 libs/database/models/imagefiltermodelpriv.cpp
- create mode 100644 libs/database/models/imagefiltermodelpriv.h
- create mode 100644 libs/database/models/imagefiltermodelthreads.cpp
- create mode 100644 libs/database/models/imagefiltermodelthreads.h
- create mode 100644 libs/database/models/imagefiltersettings.cpp
- create mode 100644 libs/database/models/imagefiltersettings.h
- create mode 100644 libs/database/models/imagelistmodel.cpp
- create mode 100644 libs/database/models/imagelistmodel.h
- create mode 100644 libs/database/models/imagemodel.cpp
- create mode 100644 libs/database/models/imagemodel.h
- create mode 100644 libs/database/models/imagesortsettings.cpp
- create mode 100644 libs/database/models/imagesortsettings.h
- create mode 100644 libs/database/models/imagethumbnailmodel.cpp
- create mode 100644 libs/database/models/imagethumbnailmodel.h
- create mode 100644 libs/database/models/imageversionsmodel.cpp
- create mode 100644 libs/database/models/imageversionsmodel.h
- delete mode 100644 libs/models/imagefiltermodel.cpp
- delete mode 100644 libs/models/imagefiltermodel.h
- delete mode 100644 libs/models/imagefiltermodelpriv.cpp
- delete mode 100644 libs/models/imagefiltermodelpriv.h
- delete mode 100644 libs/models/imagefiltermodelthreads.cpp
- delete mode 100644 libs/models/imagefiltermodelthreads.h
- delete mode 100644 libs/models/imagefiltersettings.cpp
- delete mode 100644 libs/models/imagefiltersettings.h
- delete mode 100644 libs/models/imagelistmodel.cpp
- delete mode 100644 libs/models/imagelistmodel.h
- delete mode 100644 libs/models/imagemodel.cpp
- delete mode 100644 libs/models/imagemodel.h
- delete mode 100644 libs/models/imagesortsettings.cpp
- delete mode 100644 libs/models/imagesortsettings.h
- delete mode 100644 libs/models/imagethumbnailmodel.cpp
- delete mode 100644 libs/models/imagethumbnailmodel.h
- delete mode 100644 libs/models/imageversionsmodel.cpp
- delete mode 100644 libs/models/imageversionsmodel.h
-
-diff --git a/libs/database/CMakeLists.txt b/libs/database/CMakeLists.txt
-index 7d05536..a431a36 100644
---- a/libs/database/CMakeLists.txt
-+++ b/libs/database/CMakeLists.txt
-@@ -13,6 +13,18 @@ endif (POLICY CMP0063)
- # Boost uses operator names (and, not, ...)
- string(REPLACE "-fno-operator-names" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
- 
-+set(libdatabasemodels_SRCS
-+    models/imagemodel.cpp
-+    models/imagefiltermodel.cpp
-+    models/imagefiltermodelpriv.cpp
-+    models/imagefiltermodelthreads.cpp
-+    models/imagefiltersettings.cpp
-+    models/imagelistmodel.cpp
-+    models/imagesortsettings.cpp
-+    models/imagethumbnailmodel.cpp
-+    models/imageversionsmodel.cpp
-+)
-+
- set(libdatabasecore_SRCS
-     server/databaseserverstarter.cpp
-     server/databaseservererror.cpp
-@@ -152,10 +164,10 @@ if(ENABLE_DBUS)
-     include_directories($<TARGET_PROPERTY:Qt5::DBus,INTERFACE_INCLUDE_DIRECTORIES>)
- endif()
- 
--add_library(digikamdatabase_src     OBJECT ${digikamdatabase_LIB_SRCS})
-+add_library(digikamdatabase_src     OBJECT ${digikamdatabase_LIB_SRCS} ${libdatabasemodels_SRCS})
- add_library(digikamdatabasemain_src OBJECT ${libdatabaseutils_SRCS} ${libimgqsort_SRCS})
- add_library(digikamdatabasecore_src OBJECT ${libdatabasecore_SRCS})
--add_library(digikamdatabase         SHARED $<TARGET_OBJECTS:digikamdatabase_src> $<TARGET_OBJECTS:digikamdatabasemodels_src>)
-+add_library(digikamdatabase         $<TARGET_OBJECTS:digikamdatabase_src>)
- 
- generate_export_header(digikamdatabase
-                        BASE_NAME digikam_database
-diff --git a/libs/database/models/imagefiltermodel.cpp b/libs/database/models/imagefiltermodel.cpp
-new file mode 100644
-index 0000000..3d57e05
---- /dev/null
-+++ b/libs/database/models/imagefiltermodel.cpp
-@@ -0,0 +1,1116 @@
-+/* ============================================================
-+ *
-+ * This file is a part of digiKam project
-+ * http://www.digikam.org
-+ *
-+ * Date        : 2009-03-05
-+ * Description : Qt item model for database entries
-+ *
-+ * Copyright (C) 2009-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
-+ * Copyright (C) 2011-2017 by Gilles Caulier <caulier dot gilles at gmail dot com>
-+ * Copyright (C)      2010 by Andi Clemens <andi dot clemens at gmail dot com>
-+ * Copyright (C)      2011 by Michael G. Hansen <mike at mghansen dot de>
-+ * Copyright (C)      2014 by Mohamed Anwer <m dot anwer at gmx dot com>
-+ *
-+ * This program is free software; you can redistribute it
-+ * and/or modify it under the terms of the GNU General
-+ * Public License as published by the Free Software Foundation;
-+ * either version 2, or (at your option)
-+ * any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * ============================================================ */
-+
-+#include "imagefiltermodel.h"
-+#include "imagefiltermodelpriv.h"
-+#include "imagefiltermodelthreads.h"
-+
-+// Local includes
-+
-+#include "digikam_debug.h"
-+#include "coredbaccess.h"
-+#include "coredbchangesets.h"
-+#include "coredbwatch.h"
-+#include "imageinfolist.h"
-+#include "imagemodel.h"
-+
-+namespace Digikam
-+{
-+
-+ImageSortFilterModel::ImageSortFilterModel(QObject* parent)
-+    : DCategorizedSortFilterProxyModel(parent), m_chainedModel(0)
-+{
-+}
-+
-+void ImageSortFilterModel::setSourceImageModel(ImageModel* source)
-+{
-+    if (m_chainedModel)
-+    {
-+        m_chainedModel->setSourceImageModel(source);
-+    }
-+    else
-+    {
-+        setDirectSourceImageModel(source);
-+    }
-+}
-+
-+void ImageSortFilterModel::setSourceFilterModel(ImageSortFilterModel* source)
-+{
-+    if (source)
-+    {
-+        ImageModel* const model = sourceImageModel();
-+
-+        if (model)
-+        {
-+            source->setSourceImageModel(model);
-+        }
-+    }
-+
-+    m_chainedModel = source;
-+    setSourceModel(source);
-+}
-+
-+void ImageSortFilterModel::setDirectSourceImageModel(ImageModel* model)
-+{
-+    setSourceModel(model);
-+}
-+
-+void ImageSortFilterModel::setSourceModel(QAbstractItemModel* model)
-+{
-+    // made it protected, only setSourceImageModel is public
-+    DCategorizedSortFilterProxyModel::setSourceModel(model);
-+}
-+
-+ImageModel* ImageSortFilterModel::sourceImageModel() const
-+{
-+    if (m_chainedModel)
-+    {
-+        return m_chainedModel->sourceImageModel();
-+    }
-+
-+    return static_cast<ImageModel*>(sourceModel());
-+}
-+
-+ImageSortFilterModel* ImageSortFilterModel::sourceFilterModel() const
-+{
-+    return m_chainedModel;
-+}
-+
-+ImageFilterModel* ImageSortFilterModel::imageFilterModel() const
-+{
-+    // reimplemented in ImageFilterModel
-+    if (m_chainedModel)
-+    {
-+        return m_chainedModel->imageFilterModel();
-+    }
-+
-+    return 0;
-+}
-+
-+QModelIndex ImageSortFilterModel::mapToSourceImageModel(const QModelIndex& index) const
-+{
-+    if (m_chainedModel)
-+    {
-+        return m_chainedModel->mapToSourceImageModel(mapToSource(index));
-+    }
-+
-+    return mapToSource(index);
-+}
-+
-+QModelIndex ImageSortFilterModel::mapFromSourceImageModel(const QModelIndex& albummodel_index) const
-+{
-+    if (m_chainedModel)
-+    {
-+        return mapFromSource(m_chainedModel->mapFromSourceImageModel(albummodel_index));
-+    }
-+
-+    return mapFromSource(albummodel_index);
-+}
-+
-+
-+QModelIndex ImageSortFilterModel::mapFromDirectSourceToSourceImageModel(const QModelIndex& sourceModel_index) const
-+{
-+    if (m_chainedModel)
-+    {
-+        return m_chainedModel->mapToSourceImageModel(sourceModel_index);
-+    }
-+    return sourceModel_index;
-+}
-+
-+// -------------- Convenience mappers -------------------------------------------------------------------
-+
-+QList<QModelIndex> ImageSortFilterModel::mapListToSource(const QList<QModelIndex>& indexes) const
-+{
-+    QList<QModelIndex> sourceIndexes;
-+    foreach(const QModelIndex& index, indexes)
-+    {
-+        sourceIndexes << mapToSourceImageModel(index);
-+    }
-+    return sourceIndexes;
-+}
-+
-+QList<QModelIndex> ImageSortFilterModel::mapListFromSource(const QList<QModelIndex>& sourceIndexes) const
-+{
-+    QList<QModelIndex> indexes;
-+    foreach(const QModelIndex& index, sourceIndexes)
-+    {
-+        indexes << mapFromSourceImageModel(index);
-+    }
-+    return indexes;
-+}
-+
-+ImageInfo ImageSortFilterModel::imageInfo(const QModelIndex& index) const
-+{
-+    return sourceImageModel()->imageInfo(mapToSourceImageModel(index));
-+}
-+
-+qlonglong ImageSortFilterModel::imageId(const QModelIndex& index) const
-+{
-+    return sourceImageModel()->imageId(mapToSourceImageModel(index));
-+}
-+
-+QList<ImageInfo> ImageSortFilterModel::imageInfos(const QList<QModelIndex>& indexes) const
-+{
-+    QList<ImageInfo> infos;
-+    ImageModel* const model = sourceImageModel();
-+
-+    foreach(const QModelIndex& index, indexes)
-+    {
-+        infos << model->imageInfo(mapToSourceImageModel(index));
-+    }
-+
-+    return infos;
-+}
-+
-+QList<qlonglong> ImageSortFilterModel::imageIds(const QList<QModelIndex>& indexes) const
-+{
-+    QList<qlonglong> ids;
-+    ImageModel* const model = sourceImageModel();
-+
-+    foreach(const QModelIndex& index, indexes)
-+    {
-+        ids << model->imageId(mapToSourceImageModel(index));
-+    }
-+
-+    return ids;
-+}
-+
-+QModelIndex ImageSortFilterModel::indexForPath(const QString& filePath) const
-+{
-+    return mapFromSourceImageModel(sourceImageModel()->indexForPath(filePath));
-+}
-+
-+QModelIndex ImageSortFilterModel::indexForImageInfo(const ImageInfo& info) const
-+{
-+    return mapFromSourceImageModel(sourceImageModel()->indexForImageInfo(info));
-+}
-+
-+QModelIndex ImageSortFilterModel::indexForImageId(qlonglong id) const
-+{
-+    return mapFromSourceImageModel(sourceImageModel()->indexForImageId(id));
-+}
-+
-+QList<ImageInfo> ImageSortFilterModel::imageInfosSorted() const
-+{
-+    QList<ImageInfo>  infos;
-+    const int         size  = rowCount();
-+    ImageModel* const model = sourceImageModel();
-+
-+    for (int i=0; i<size; ++i)
-+    {
-+        infos << model->imageInfo(mapToSourceImageModel(index(i, 0)));
-+    }
-+
-+    return infos;
-+}
-+
-+// --------------------------------------------------------------------------------------------
-+
-+ImageFilterModel::ImageFilterModel(QObject* parent)
-+    : ImageSortFilterModel(parent),
-+      d_ptr(new ImageFilterModelPrivate)
-+{
-+    d_ptr->init(this);
-+}
-+
-+ImageFilterModel::ImageFilterModel(ImageFilterModelPrivate& dd, QObject* parent)
-+    : ImageSortFilterModel(parent),
-+      d_ptr(&dd)
-+{
-+    d_ptr->init(this);
-+}
-+
-+ImageFilterModel::~ImageFilterModel()
-+{
-+    Q_D(ImageFilterModel);
-+    delete d;
-+}
-+
-+void ImageFilterModel::setDirectSourceImageModel(ImageModel* sourceModel)
-+{
-+    Q_D(ImageFilterModel);
-+
-+    if (d->imageModel)
-+    {
-+        d->imageModel->unsetPreprocessor(d);
-+        disconnect(d->imageModel, SIGNAL(modelReset()),
-+                   this, SLOT(slotModelReset()));
-+        slotModelReset();
-+    }
-+
-+    d->imageModel = sourceModel;
-+
-+    if (d->imageModel)
-+    {
-+        d->imageModel->setPreprocessor(d);
-+
-+        connect(d->imageModel, SIGNAL(preprocess(QList<ImageInfo>,QList<QVariant>)),
-+                d, SLOT(preprocessInfos(QList<ImageInfo>,QList<QVariant>)));
-+
-+        connect(d->imageModel, SIGNAL(processAdded(QList<ImageInfo>,QList<QVariant>)),
-+                d, SLOT(processAddedInfos(QList<ImageInfo>,QList<QVariant>)));
-+
-+        connect(d, SIGNAL(reAddImageInfos(QList<ImageInfo>,QList<QVariant>)),
-+                d->imageModel, SLOT(reAddImageInfos(QList<ImageInfo>,QList<QVariant>)));
-+
-+        connect(d, SIGNAL(reAddingFinished()),
-+                d->imageModel, SLOT(reAddingFinished()));
-+
-+        connect(d->imageModel, SIGNAL(modelReset()),
-+                this, SLOT(slotModelReset()));
-+
-+        connect(d->imageModel, SIGNAL(imageChange(ImageChangeset,QItemSelection)),
-+                this, SLOT(slotImageChange(ImageChangeset)));
-+
-+        connect(d->imageModel, SIGNAL(imageTagChange(ImageTagChangeset,QItemSelection)),
-+                this, SLOT(slotImageTagChange(ImageTagChangeset)));
-+    }
-+
-+    setSourceModel(d->imageModel);
-+}
-+
-+QVariant ImageFilterModel::data(const QModelIndex& index, int role) const
-+{
-+    Q_D(const ImageFilterModel);
-+
-+    if (!index.isValid())
-+    {
-+        return QVariant();
-+    }
-+
-+    switch (role)
-+    {
-+            // Attention: This breaks should there ever be another filter model between this and the ImageModel
-+
-+        case DCategorizedSortFilterProxyModel::CategoryDisplayRole:
-+            return categoryIdentifier(d->imageModel->imageInfoRef(mapToSource(index)));
-+        case CategorizationModeRole:
-+            return d->sorter.categorizationMode;
-+        case SortOrderRole:
-+            return d->sorter.sortRole;
-+            //case CategoryCountRole:
-+            //  return categoryCount(d->imageModel->imageInfoRef(mapToSource(index)));
-+        case CategoryAlbumIdRole:
-+            return d->imageModel->imageInfoRef(mapToSource(index)).albumId();
-+        case CategoryFormatRole:
-+            return d->imageModel->imageInfoRef(mapToSource(index)).format();
-+        case GroupIsOpenRole:
-+            return d->groupFilter.isAllOpen() ||
-+                   d->groupFilter.isOpen(d->imageModel->imageInfoRef(mapToSource(index)).id());
-+        case ImageFilterModelPointerRole:
-+            return QVariant::fromValue(const_cast<ImageFilterModel*>(this));
-+    }
-+
-+    return DCategorizedSortFilterProxyModel::data(index, role);
-+}
-+
-+ImageFilterModel* ImageFilterModel::imageFilterModel() const
-+{
-+    return const_cast<ImageFilterModel*>(this);
-+}
-+
-+DatabaseFields::Set ImageFilterModel::suggestedWatchFlags() const
-+{
-+    DatabaseFields::Set watchFlags;
-+    watchFlags |= DatabaseFields::Name   | DatabaseFields::FileSize     | DatabaseFields::ModificationDate;
-+    watchFlags |= DatabaseFields::Rating | DatabaseFields::CreationDate | DatabaseFields::Orientation |
-+                  DatabaseFields::Width  | DatabaseFields::Height;
-+    watchFlags |= DatabaseFields::Comment;
-+    watchFlags |= DatabaseFields::ImageRelations;
-+    return watchFlags;
-+}
-+
-+// -------------- Filter settings --------------
-+
-+void ImageFilterModel::setDayFilter(const QList<QDateTime>& days)
-+{
-+    Q_D(ImageFilterModel);
-+    d->filter.setDayFilter(days);
-+    setImageFilterSettings(d->filter);
-+}
-+
-+void ImageFilterModel::setTagFilter(const QList<int>& includedTags, const QList<int>& excludedTags,
-+                                    ImageFilterSettings::MatchingCondition matchingCond,
-+                                    bool showUnTagged, const QList<int>& clTagIds, const QList<int>& plTagIds)
-+{
-+    Q_D(ImageFilterModel);
-+    d->filter.setTagFilter(includedTags, excludedTags, matchingCond, showUnTagged, clTagIds, plTagIds);
-+    setImageFilterSettings(d->filter);
-+}
-+
-+void ImageFilterModel::setRatingFilter(int rating, ImageFilterSettings::RatingCondition ratingCond, bool isUnratedExcluded)
-+{
-+    Q_D(ImageFilterModel);
-+    d->filter.setRatingFilter(rating, ratingCond, isUnratedExcluded);
-+    setImageFilterSettings(d->filter);
-+}
-+
-+void ImageFilterModel::setUrlWhitelist(const QList<QUrl> urlList, const QString& id)
-+{
-+    Q_D(ImageFilterModel);
-+    d->filter.setUrlWhitelist(urlList, id);
-+    setImageFilterSettings(d->filter);
-+}
-+
-+void ImageFilterModel::setIdWhitelist(const QList<qlonglong>& idList, const QString& id)
-+{
-+    Q_D(ImageFilterModel);
-+    d->filter.setIdWhitelist(idList, id);
-+    setImageFilterSettings(d->filter);
-+}
-+
-+void ImageFilterModel::setMimeTypeFilter(int mimeTypeFilter)
-+{
-+    Q_D(ImageFilterModel);
-+    d->filter.setMimeTypeFilter(mimeTypeFilter);
-+    setImageFilterSettings(d->filter);
-+}
-+
-+void ImageFilterModel::setGeolocationFilter(const ImageFilterSettings::GeolocationCondition& condition)
-+{
-+    Q_D(ImageFilterModel);
-+    d->filter.setGeolocationFilter(condition);
-+    setImageFilterSettings(d->filter);
-+}
-+
-+void ImageFilterModel::setTextFilter(const SearchTextFilterSettings& settings)
-+{
-+    Q_D(ImageFilterModel);
-+    d->filter.setTextFilter(settings);
-+    setImageFilterSettings(d->filter);
-+}
-+
-+void ImageFilterModel::setImageFilterSettings(const ImageFilterSettings& settings)
-+{
-+    Q_D(ImageFilterModel);
-+
-+    {
-+        QMutexLocker lock(&d->mutex);
-+        d->version++;
-+        d->filter              = settings;
-+        d->filterCopy          = settings;
-+        d->versionFilterCopy   = d->versionFilter;
-+        d->groupFilterCopy     = d->groupFilter;
-+
-+        d->needPrepareComments = settings.isFilteringByText();
-+        d->needPrepareTags     = settings.isFilteringByTags();
-+        d->needPrepareGroups   = true;
-+        d->needPrepare         = d->needPrepareComments || d->needPrepareTags || d->needPrepareGroups;
-+
-+        d->hasOneMatch         = false;
-+        d->hasOneMatchForText  = false;
-+    }
-+
-+    d->filterResults.clear();
-+
-+    //d->categoryCountHashInt.clear();
-+    //d->categoryCountHashString.clear();
-+    if (d->imageModel)
-+    {
-+        d->infosToProcess(d->imageModel->imageInfos());
-+    }
-+
-+    emit filterSettingsChanged(settings);
-+}
-+
-+void ImageFilterModel::setVersionManagerSettings(const VersionManagerSettings& settings)
-+{
-+    Q_D(ImageFilterModel);
-+    d->versionFilter.setVersionManagerSettings(settings);
-+    setVersionImageFilterSettings(d->versionFilter);
-+}
-+
-+void ImageFilterModel::setExceptionList(const QList<qlonglong>& idList, const QString& id)
-+{
-+    Q_D(ImageFilterModel);
-+    d->versionFilter.setExceptionList(idList, id);
-+    setVersionImageFilterSettings(d->versionFilter);
-+}
-+
-+void ImageFilterModel::setVersionImageFilterSettings(const VersionImageFilterSettings& settings)
-+{
-+    Q_D(ImageFilterModel);
-+    d->versionFilter = settings;
-+    slotUpdateFilter();
-+}
-+
-+bool ImageFilterModel::isGroupOpen(qlonglong group) const
-+{
-+    Q_D(const ImageFilterModel);
-+    return d->groupFilter.isOpen(group);
-+}
-+
-+bool ImageFilterModel::isAllGroupsOpen() const
-+{
-+    Q_D(const ImageFilterModel);
-+    return d->groupFilter.isAllOpen();
-+}
-+
-+void ImageFilterModel::setGroupOpen(qlonglong group, bool open)
-+{
-+    Q_D(ImageFilterModel);
-+    d->groupFilter.setOpen(group, open);
-+    setGroupImageFilterSettings(d->groupFilter);
-+}
-+
-+void ImageFilterModel::toggleGroupOpen(qlonglong group)
-+{
-+    setGroupOpen(group, !isGroupOpen(group));
-+}
-+
-+void ImageFilterModel::setAllGroupsOpen(bool open)
-+{
-+    Q_D(ImageFilterModel);
-+    d->groupFilter.setAllOpen(open);
-+    setGroupImageFilterSettings(d->groupFilter);
-+}
-+
-+void ImageFilterModel::setGroupImageFilterSettings(const GroupImageFilterSettings& settings)
-+{
-+    Q_D(ImageFilterModel);
-+    d->groupFilter = settings;
-+    slotUpdateFilter();
-+}
-+
-+void ImageFilterModel::slotUpdateFilter()
-+{
-+    Q_D(ImageFilterModel);
-+    setImageFilterSettings(d->filter);
-+}
-+
-+ImageFilterSettings ImageFilterModel::imageFilterSettings() const
-+{
-+    Q_D(const ImageFilterModel);
-+    return d->filter;
-+}
-+
-+ImageSortSettings ImageFilterModel::imageSortSettings() const
-+{
-+    Q_D(const ImageFilterModel);
-+    return d->sorter;
-+}
-+
-+VersionImageFilterSettings ImageFilterModel::versionImageFilterSettings() const
-+{
-+    Q_D(const ImageFilterModel);
-+    return d->versionFilter;
-+}
-+
-+GroupImageFilterSettings ImageFilterModel::groupImageFilterSettings() const
-+{
-+    Q_D(const ImageFilterModel);
-+    return d->groupFilter;
-+}
-+
-+void ImageFilterModel::slotModelReset()
-+{
-+    Q_D(ImageFilterModel);
-+    {
-+        QMutexLocker lock(&d->mutex);
-+        // discard all packages on the way that are marked as send out for re-add
-+        d->lastDiscardVersion = d->version;
-+        d->sentOutForReAdd    = 0;
-+        // discard all packages on the way
-+        d->version++;
-+        d->sentOut            = 0;
-+
-+        d->hasOneMatch        = false;
-+        d->hasOneMatchForText = false;
-+    }
-+    d->filterResults.clear();
-+}
-+
-+bool ImageFilterModel::filterAcceptsRow(int source_row, const QModelIndex& source_parent) const
-+{
-+    Q_D(const ImageFilterModel);
-+
-+    if (source_parent.isValid())
-+    {
-+        return false;
-+    }
-+
-+    qlonglong id                              = d->imageModel->imageId(source_row);
-+    QHash<qlonglong, bool>::const_iterator it = d->filterResults.constFind(id);
-+
-+    if (it != d->filterResults.constEnd())
-+    {
-+        return it.value();
-+    }
-+
-+    // usually done in thread and cache, unless source model changed
-+    ImageInfo info = d->imageModel->imageInfo(source_row);
-+    bool match     = d->filter.matches(info);
-+    match          = match ? d->versionFilter.matches(info) : false;
-+
-+    return match ? d->groupFilter.matches(info) : false;
-+}
-+
-+void ImageFilterModel::setSendImageInfoSignals(bool sendSignals)
-+{
-+    if (sendSignals)
-+    {
-+        connect(this, SIGNAL(rowsInserted(QModelIndex,int,int)),
-+                this, SLOT(slotRowsInserted(QModelIndex,int,int)));
-+
-+        connect(this, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
-+                this, SLOT(slotRowsAboutToBeRemoved(QModelIndex,int,int)));
-+    }
-+    else
-+    {
-+        disconnect(this, SIGNAL(rowsInserted(QModelIndex,int,int)),
-+                   this, SLOT(slotRowsInserted(QModelIndex,int,int)));
-+
-+        disconnect(this, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
-+                   this, SLOT(slotRowsAboutToBeRemoved(QModelIndex,int,int)));
-+    }
-+}
-+
-+void ImageFilterModel::slotRowsInserted(const QModelIndex& /*parent*/, int start, int end)
-+{
-+    QList<ImageInfo> infos;
-+
-+    for (int i=start; i<=end; ++i)
-+    {
-+        infos << imageInfo(index(i, 0));
-+    }
-+
-+    emit imageInfosAdded(infos);
-+}
-+
-+void ImageFilterModel::slotRowsAboutToBeRemoved(const QModelIndex& /*parent*/, int start, int end)
-+{
-+    QList<ImageInfo> infos;
-+
-+    for (int i=start; i<=end; ++i)
-+    {
-+        infos << imageInfo(index(i, 0));
-+    }
-+
-+    emit imageInfosAboutToBeRemoved(infos);
-+}
-+
-+// -------------- Threaded preparation & filtering --------------
-+
-+void ImageFilterModel::addPrepareHook(ImageFilterModelPrepareHook* hook)
-+{
-+    Q_D(ImageFilterModel);
-+    QMutexLocker lock(&d->mutex);
-+    d->prepareHooks << hook;
-+}
-+
-+void ImageFilterModel::removePrepareHook(ImageFilterModelPrepareHook* hook)
-+{
-+    Q_D(ImageFilterModel);
-+    QMutexLocker lock(&d->mutex);
-+    d->prepareHooks.removeAll(hook);
-+}
-+
-+void ImageFilterModelPreparer::process(ImageFilterModelTodoPackage package)
-+{
-+    if (!checkVersion(package))
-+    {
-+        emit discarded(package);
-+        return;
-+    }
-+
-+    // get thread-local copy
-+    bool needPrepareTags, needPrepareComments, needPrepareGroups;
-+    QList<ImageFilterModelPrepareHook*> prepareHooks;
-+    {
-+        QMutexLocker lock(&d->mutex);
-+        needPrepareTags     = d->needPrepareTags;
-+        needPrepareComments = d->needPrepareComments;
-+        needPrepareGroups   = d->needPrepareGroups;
-+        prepareHooks        = d->prepareHooks;
-+    }
-+
-+    //TODO: Make efficient!!
-+    if (needPrepareComments)
-+    {
-+        foreach(const ImageInfo& info, package.infos)
-+        {
-+            info.comment();
-+        }
-+    }
-+
-+    if (!checkVersion(package))
-+    {
-+        emit discarded(package);
-+        return;
-+    }
-+
-+    // The downside of QVector: At some point, we may need a QList for an API.
-+    // Nonetheless, QList and ImageInfo is fast. We could as well
-+    // reimplement ImageInfoList to ImageInfoVector (internally with templates?)
-+    ImageInfoList infoList;
-+
-+    if (needPrepareTags || needPrepareGroups)
-+    {
-+        infoList = package.infos.toList();
-+    }
-+
-+    if (needPrepareTags)
-+    {
-+        infoList.loadTagIds();
-+    }
-+
-+    if (needPrepareGroups)
-+    {
-+        infoList.loadGroupImageIds();
-+    }
-+
-+    foreach(ImageFilterModelPrepareHook* hook, prepareHooks)
-+    {
-+        hook->prepare(package.infos);
-+    }
-+
-+    emit processed(package);
-+}
-+
-+void ImageFilterModelFilterer::process(ImageFilterModelTodoPackage package)
-+{
-+    if (!checkVersion(package))
-+    {
-+        emit discarded(package);
-+        return;
-+    }
-+
-+    // get thread-local copy
-+    ImageFilterSettings        localFilter;
-+    VersionImageFilterSettings localVersionFilter;
-+    GroupImageFilterSettings   localGroupFilter;
-+    bool                       hasOneMatch;
-+    bool                       hasOneMatchForText;
-+    {
-+        QMutexLocker lock(&d->mutex);
-+        localFilter        = d->filterCopy;
-+        localVersionFilter = d->versionFilterCopy;
-+        localGroupFilter   = d->groupFilterCopy;
-+        hasOneMatch        = d->hasOneMatch;
-+        hasOneMatchForText = d->hasOneMatchForText;
-+    }
-+
-+    // Actual filtering. The variants to spare checking hasOneMatch over and over again.
-+    if (hasOneMatch && hasOneMatchForText)
-+    {
-+        foreach(const ImageInfo& info, package.infos)
-+        {
-+            package.filterResults[info.id()] = localFilter.matches(info)        &&
-+                                               localVersionFilter.matches(info) &&
-+                                               localGroupFilter.matches(info);
-+        }
-+    }
-+    else if (hasOneMatch)
-+    {
-+        bool matchForText;
-+
-+        foreach(const ImageInfo& info, package.infos)
-+        {
-+            package.filterResults[info.id()] = localFilter.matches(info, &matchForText) &&
-+                                               localVersionFilter.matches(info)         &&
-+                                               localGroupFilter.matches(info);
-+
-+            if (matchForText)
-+            {
-+                hasOneMatchForText = true;
-+            }
-+        }
-+    }
-+    else
-+    {
-+        bool result, matchForText;
-+
-+        foreach(const ImageInfo& info, package.infos)
-+        {
-+            result                           = localFilter.matches(info, &matchForText) &&
-+                                               localVersionFilter.matches(info)         &&
-+                                               localGroupFilter.matches(info);
-+            package.filterResults[info.id()] = result;
-+
-+            if (result)
-+            {
-+                hasOneMatch = true;
-+            }
-+
-+            if (matchForText)
-+            {
-+                hasOneMatchForText = true;
-+            }
-+        }
-+    }
-+
-+    if (checkVersion(package))
-+    {
-+        QMutexLocker lock(&d->mutex);
-+        d->hasOneMatch        = hasOneMatch;
-+        d->hasOneMatchForText = hasOneMatchForText;
-+    }
-+
-+    emit processed(package);
-+}
-+
-+// -------------- Sorting and Categorization -------------------------------------------------------
-+
-+void ImageFilterModel::setImageSortSettings(const ImageSortSettings& sorter)
-+{
-+    Q_D(ImageFilterModel);
-+    d->sorter = sorter;
-+    setCategorizedModel(d->sorter.categorizationMode != ImageSortSettings::NoCategories);
-+    invalidate();
-+}
-+
-+void ImageFilterModel::setCategorizationMode(ImageSortSettings::CategorizationMode mode)
-+{
-+    Q_D(ImageFilterModel);
-+    d->sorter.setCategorizationMode(mode);
-+    setImageSortSettings(d->sorter);
-+}
-+
-+void ImageFilterModel::setCategorizationSortOrder(ImageSortSettings::SortOrder order)
-+{
-+    Q_D(ImageFilterModel);
-+    d->sorter.setCategorizationSortOrder(order);
-+    setImageSortSettings(d->sorter);
-+}
-+
-+void ImageFilterModel::setSortRole(ImageSortSettings::SortRole role)
-+{
-+    Q_D(ImageFilterModel);
-+    d->sorter.setSortRole(role);
-+    setImageSortSettings(d->sorter);
-+}
-+
-+void ImageFilterModel::setSortOrder(ImageSortSettings::SortOrder order)
-+{
-+    Q_D(ImageFilterModel);
-+    d->sorter.setSortOrder(order);
-+    setImageSortSettings(d->sorter);
-+}
-+
-+void ImageFilterModel::setStringTypeNatural(bool natural)
-+{
-+    Q_D(ImageFilterModel);
-+    d->sorter.setStringTypeNatural(natural);
-+    setImageSortSettings(d->sorter);
-+}
-+
-+int ImageFilterModel::compareCategories(const QModelIndex& left, const QModelIndex& right) const
-+{
-+    // source indexes
-+    Q_D(const ImageFilterModel);
-+
-+    if (!d->sorter.isCategorized())
-+    {
-+        return 0;
-+    }
-+
-+    if (!left.isValid() || !right.isValid())
-+    {
-+        return -1;
-+    }
-+
-+    const ImageInfo& leftInfo  = d->imageModel->imageInfoRef(left);
-+    const ImageInfo& rightInfo = d->imageModel->imageInfoRef(right);
-+
-+    // Check grouping
-+    qlonglong leftGroupImageId = leftInfo.groupImageId();
-+    qlonglong rightGroupImageId = rightInfo.groupImageId();
-+
-+    return compareInfosCategories(leftGroupImageId  == -1 ? leftInfo  : ImageInfo(leftGroupImageId),
-+                                  rightGroupImageId == -1 ? rightInfo : ImageInfo(rightGroupImageId));
-+}
-+
-+bool ImageFilterModel::subSortLessThan(const QModelIndex& left, const QModelIndex& right) const
-+{
-+    // source indexes
-+    Q_D(const ImageFilterModel);
-+
-+    if (!left.isValid() || !right.isValid())
-+    {
-+        return true;
-+    }
-+
-+    if (left == right)
-+    {
-+        return false;
-+    }
-+
-+    const ImageInfo& leftInfo  = d->imageModel->imageInfoRef(left);
-+    const ImageInfo& rightInfo = d->imageModel->imageInfoRef(right);
-+
-+    if (leftInfo == rightInfo)
-+    {
-+        return d->sorter.lessThan(left.data(ImageModel::ExtraDataRole), right.data(ImageModel::ExtraDataRole));
-+    }
-+
-+    // Check grouping
-+    qlonglong leftGroupImageId = leftInfo.groupImageId();
-+    qlonglong rightGroupImageId = rightInfo.groupImageId();
-+
-+    // Either no grouping (-1), or same group image, or same image
-+    if (leftGroupImageId == rightGroupImageId)
-+    {
-+        return infosLessThan(leftInfo, rightInfo);
-+    }
-+
-+   // We have grouping to handle
-+
-+    // Is one grouped on the other? Sort behind leader.
-+    if (leftGroupImageId == rightInfo.id())
-+    {
-+        return false;
-+    }
-+    if (rightGroupImageId == leftInfo.id())
-+    {
-+        return true;
-+    }
-+
-+    // Use the group leader for sorting
-+    return infosLessThan(leftGroupImageId  == -1 ? leftInfo  : ImageInfo(leftGroupImageId),
-+                         rightGroupImageId == -1 ? rightInfo : ImageInfo(rightGroupImageId));
-+}
-+
-+int ImageFilterModel::compareInfosCategories(const ImageInfo& left, const ImageInfo& right) const
-+{
-+    // Note: reimplemented in ImageAlbumFilterModel
-+    Q_D(const ImageFilterModel);
-+    return d->sorter.compareCategories(left, right);
-+}
-+
-+// Feel free to optimize. QString::number is 3x slower.
-+static inline QString fastNumberToString(int id)
-+{
-+    const int size = sizeof(int) * 2;
-+    char c[size+1];
-+    c[size]    = '\0';
-+    char* p    = c;
-+    int number = id;
-+
-+    for (int i=0; i<size; ++i)
-+    {
-+        *p = 'a' + (number & 0xF);
-+        number >>= 4;
-+        ++p;
-+    }
-+
-+    return QString::fromLatin1(c);
-+}
-+
-+QString ImageFilterModel::categoryIdentifier(const ImageInfo& i) const
-+{
-+    Q_D(const ImageFilterModel);
-+
-+    if (!d->sorter.isCategorized())
-+    {
-+        return QString();
-+    }
-+
-+    qlonglong groupedImageId = i.groupImageId();
-+    ImageInfo info = groupedImageId == -1 ? i : ImageInfo(groupedImageId);
-+
-+    switch (d->sorter.categorizationMode)
-+    {
-+        case ImageSortSettings::NoCategories:
-+            return QString();
-+        case ImageSortSettings::OneCategory:
-+            return QString();
-+        case ImageSortSettings::CategoryByAlbum:
-+            return fastNumberToString(info.albumId());
-+        case ImageSortSettings::CategoryByFormat:
-+            return info.format();
-+        default:
-+            return QString();
-+    }
-+}
-+
-+bool ImageFilterModel::infosLessThan(const ImageInfo& left, const ImageInfo& right) const
-+{
-+    Q_D(const ImageFilterModel);
-+    return d->sorter.lessThan(left, right);
-+}
-+
-+// -------------- Watching changes -----------------------------------------------------------------
-+
-+void ImageFilterModel::slotImageTagChange(const ImageTagChangeset& changeset)
-+{
-+    Q_D(ImageFilterModel);
-+
-+    if (!d->imageModel || d->imageModel->isEmpty())
-+    {
-+        return;
-+    }
-+
-+    // already scheduled to re-filter?
-+    if (d->updateFilterTimer->isActive())
-+    {
-+        return;
-+    }
-+
-+    // do we filter at all?
-+    if (!d->versionFilter.isFilteringByTags() &&
-+        !d->filter.isFilteringByTags()        &&
-+        !d->filter.isFilteringByText())
-+    {
-+        return;
-+    }
-+
-+    // is one of our images affected?
-+    foreach(const qlonglong& id, changeset.ids())
-+    {
-+        // if one matching image id is found, trigger a refresh
-+        if (d->imageModel->hasImage(id))
-+        {
-+            d->updateFilterTimer->start();
-+            return;
-+        }
-+    }
-+}
-+
-+void ImageFilterModel::slotImageChange(const ImageChangeset& changeset)
-+{
-+    Q_D(ImageFilterModel);
-+
-+    if (!d->imageModel || d->imageModel->isEmpty())
-+    {
-+        return;
-+    }
-+
-+    // already scheduled to re-filter?
-+    if (d->updateFilterTimer->isActive())
-+    {
-+        return;
-+    }
-+
-+    // is one of the values affected that we filter or sort by?
-+    DatabaseFields::Set set = changeset.changes();
-+    bool sortAffected       = (set & d->sorter.watchFlags());
-+    bool filterAffected     = (set & d->filter.watchFlags()) || (set & d->groupFilter.watchFlags());
-+
-+    if (!sortAffected && !filterAffected)
-+    {
-+        return;
-+    }
-+
-+    // is one of our images affected?
-+    bool imageAffected = false;
-+
-+    foreach(const qlonglong& id, changeset.ids())
-+    {
-+        // if one matching image id is found, trigger a refresh
-+        if (d->imageModel->hasImage(id))
-+        {
-+            imageAffected = true;
-+            break;
-+        }
-+    }
-+
-+    if (!imageAffected)
-+    {
-+        return;
-+    }
-+
-+    if (filterAffected)
-+    {
-+        d->updateFilterTimer->start();
-+    }
-+    else
-+    {
-+        invalidate();    // just resort, reuse filter results
-+    }
-+}
-+
-+// -------------------------------------------------------------------------------------------------------
-+
-+NoDuplicatesImageFilterModel::NoDuplicatesImageFilterModel(QObject* parent)
-+    : ImageSortFilterModel(parent)
-+{
-+}
-+
-+bool NoDuplicatesImageFilterModel::filterAcceptsRow(int source_row, const QModelIndex& source_parent) const
-+{
-+    QModelIndex index = sourceModel()->index(source_row, 0, source_parent);
-+
-+    if (index.data(ImageModel::ExtraDataDuplicateCount).toInt() <= 1)
-+    {
-+        return true;
-+    }
-+
-+    QModelIndex previousIndex = sourceModel()->index(source_row - 1, 0, source_parent);
-+
-+    if (!previousIndex.isValid())
-+    {
-+        return true;
-+    }
-+
-+    if (sourceImageModel()->imageId(mapFromDirectSourceToSourceImageModel(index)) == sourceImageModel()->imageId(mapFromDirectSourceToSourceImageModel(previousIndex)))
-+    {
-+        return false;
-+    }
-+    return true;
-+}
-+
-+/*
-+void NoDuplicatesImageFilterModel::setSourceModel(QAbstractItemModel* model)
-+{
-+    if (sourceModel())
-+    {
-+    }
-+
-+    ImageSortFilterModel::setSourceModel(model);
-+
-+    if (sourceModel())
-+    {
-+        connect(sourceModel(), SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
-+                this, SLOT(slotRowsAboutToBeRemoved(QModelIndex,int,int)));
-+    }
-+}
-+
-+void NoDuplicatesImageFilterModel::slotRowsAboutToBeRemoved(const QModelIndex& parent, int begin, int end)
-+{
-+    bool needInvalidate = false;
-+
-+    for (int i = begin; i<=end; ++i)
-+    {
-+        QModelIndex index = sourceModel()->index(i, 0, parent);
-+
-+        // filtered out by us?
-+        if (!mapFromSource(index).isValid())
-+        {
-+            continue;
-+        }
-+
-+        QModelIndex sourceIndex = mapFromDirectSourceToSourceImageModel(index);
-+        qlonglong id = sourceImageModel()->imageId(sourceIndex);
-+
-+        if (sourceImageModel()->numberOfIndexesForImageId(id) > 1)
-+        {
-+            needInvalidate = true;
-+        }
-+    }
-+}*/
-+
-+} // namespace Digikam
-diff --git a/libs/database/models/imagefiltermodel.h b/libs/database/models/imagefiltermodel.h
-new file mode 100644
-index 0000000..d131b3e
---- /dev/null
-+++ b/libs/database/models/imagefiltermodel.h
-@@ -0,0 +1,299 @@
-+/* ============================================================
-+ *
-+ * This file is a part of digiKam project
-+ * http://www.digikam.org
-+ *
-+ * Date        : 2009-03-05
-+ * Description : Qt item model for database entries
-+ *
-+ * Copyright (C) 2009-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
-+ * Copyright (C)      2011 by Gilles Caulier <caulier dot gilles at gmail dot com>
-+ * Copyright (C)      2010 by Andi Clemens <andi dot clemens at gmail dot com>
-+ * Copyright (C)      2011 by Michael G. Hansen <mike at mghansen dot de>
-+ * Copyright (C)      2014 by Mohamed Anwer <m dot anwer at gmx dot com>
-+ *
-+ * This program is free software; you can redistribute it
-+ * and/or modify it under the terms of the GNU General
-+ * Public License as published by the Free Software Foundation;
-+ * either version 2, or (at your option)
-+ * any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * ============================================================ */
-+
-+#ifndef IMAGEFILTERMODEL_H
-+#define IMAGEFILTERMODEL_H
-+
-+// Local includes
-+
-+#include "dcategorizedsortfilterproxymodel.h"
-+#include "textfilter.h"
-+#include "imagefiltersettings.h"
-+#include "imagemodel.h"
-+#include "imagesortsettings.h"
-+#include "digikam_export.h"
-+
-+namespace Digikam
-+{
-+
-+class ImageChangeset;
-+class ImageFilterModel;
-+class ImageTagChangeset;
-+
-+class DIGIKAM_DATABASE_EXPORT ImageFilterModelPrepareHook
-+{
-+public:
-+
-+    virtual ~ImageFilterModelPrepareHook() {};
-+    virtual void prepare(const QVector<ImageInfo>& infos) = 0;
-+};
-+
-+// -----------------------------------------------------------------------------------------------
-+
-+class DIGIKAM_DATABASE_EXPORT ImageSortFilterModel : public DCategorizedSortFilterProxyModel
-+{
-+    Q_OBJECT
-+
-+public:
-+
-+    explicit ImageSortFilterModel(QObject* parent = 0);
-+
-+    void        setSourceImageModel(ImageModel* model);
-+    ImageModel* sourceImageModel() const;
-+
-+    void                  setSourceFilterModel(ImageSortFilterModel* model);
-+    ImageSortFilterModel* sourceFilterModel() const;
-+
-+    QModelIndex mapToSourceImageModel(const QModelIndex& index) const;
-+    QModelIndex mapFromSourceImageModel(const QModelIndex& imagemodel_index) const;
-+    QModelIndex mapFromDirectSourceToSourceImageModel(const QModelIndex& sourceModel_index) const;
-+
-+    /// Convenience methods mapped to ImageModel.
-+    /// Mentioned indexes returned come from the source image model.
-+    QList<QModelIndex> mapListToSource(const QList<QModelIndex>& indexes) const;
-+    QList<QModelIndex> mapListFromSource(const QList<QModelIndex>& sourceIndexes) const;
-+
-+    ImageInfo        imageInfo(const QModelIndex& index) const;
-+    qlonglong        imageId(const QModelIndex& index) const;
-+    QList<ImageInfo> imageInfos(const QList<QModelIndex>& indexes) const;
-+    QList<qlonglong> imageIds(const QList<QModelIndex>& indexes) const;
-+
-+    QModelIndex indexForPath(const QString& filePath) const;
-+    QModelIndex indexForImageInfo(const ImageInfo& info) const;
-+    QModelIndex indexForImageId(qlonglong id) const;
-+
-+    /** Returns a list of all image infos, sorted according to this model.
-+     *  If you do not need a sorted list, use ImageModel's imageInfos() method.
-+     */
-+    QList<ImageInfo> imageInfosSorted() const;
-+
-+    /// Returns this, any chained ImageFilterModel, or 0.
-+    virtual ImageFilterModel* imageFilterModel() const;
-+
-+protected:
-+
-+    /// Reimplement if needed. Called only when model shall be set as (direct) sourceModel.
-+    virtual void setDirectSourceImageModel(ImageModel* model);
-+
-+    // made protected
-+    virtual void setSourceModel(QAbstractItemModel* model);
-+
-+protected:
-+
-+    ImageSortFilterModel* m_chainedModel;
-+};
-+
-+// -----------------------------------------------------------------------------------------------
-+
-+class DIGIKAM_DATABASE_EXPORT ImageFilterModel : public ImageSortFilterModel
-+{
-+    Q_OBJECT
-+
-+public:
-+
-+    enum ImageFilterModelRoles
-+    {
-+        /// Returns the current categorization mode
-+        CategorizationModeRole      = ImageModel::FilterModelRoles + 1,
-+        /// Returns the current sort order
-+        SortOrderRole               = ImageModel::FilterModelRoles + 2,
-+        // / Returns the number of items in the index' category
-+        //CategoryCountRole         = ImageModel::FilterModelRoles + 3,
-+        /// Returns the id of the PAlbum of the index which is used for category
-+        CategoryAlbumIdRole         = ImageModel::FilterModelRoles + 3,
-+        /// Returns the format of the index which is used for category
-+        CategoryFormatRole          = ImageModel::FilterModelRoles + 4,
-+        /// Returns true if the given image is a group leader, and the group is opened
-+        GroupIsOpenRole             = ImageModel::FilterModelRoles + 5,
-+        ImageFilterModelPointerRole = ImageModel::FilterModelRoles + 50
-+    };
-+
-+public:
-+
-+    explicit ImageFilterModel(QObject* parent = 0);
-+    ~ImageFilterModel();
-+
-+    /** Add a hook to get added images for preparation tasks before they are added in the model */
-+    void addPrepareHook(ImageFilterModelPrepareHook* hook);
-+    void removePrepareHook(ImageFilterModelPrepareHook* hook);
-+
-+    /** Returns a set of DatabaseFields suggested to set as watch flags on the source ImageModel.
-+     *  The contained flags will be those that this model can sort or filter by. */
-+    DatabaseFields::Set suggestedWatchFlags() const;
-+
-+    ImageFilterSettings        imageFilterSettings() const;
-+    VersionImageFilterSettings versionImageFilterSettings() const;
-+    GroupImageFilterSettings   groupImageFilterSettings() const;
-+    ImageSortSettings          imageSortSettings() const;
-+
-+    // group is identified by the id of its group leader
-+    bool isGroupOpen(qlonglong group) const;
-+    bool isAllGroupsOpen() const;
-+
-+    /// Enables sending imageInfosAdded and imageInfosAboutToBeRemoved
-+    void setSendImageInfoSignals(bool sendSignals);
-+
-+    virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
-+    virtual ImageFilterModel* imageFilterModel() const;
-+
-+public Q_SLOTS:
-+
-+    /** Changes the current version image filter settings and refilters. */
-+    void setVersionImageFilterSettings(const VersionImageFilterSettings& settings);
-+
-+    /** Changes the current version image filter settings and refilters. */
-+    void setGroupImageFilterSettings(const GroupImageFilterSettings& settings);
-+
-+    /** Adjust the current ImageFilterSettings.
-+     *  Equivalent to retrieving the current filter settings, adjusting the parameter
-+     *  and calling setImageFilterSettings.
-+     *  Provided for convenience.
-+     *  It is encouraged to use setImageFilterSettings if you change more than one
-+     *  parameter at a time.
-+     */
-+    void setDayFilter(const QList<QDateTime>& days);
-+    void setTagFilter(const QList<int>& includedTags, const QList<int>& excludedTags,
-+                      ImageFilterSettings::MatchingCondition matchingCond, bool showUnTagged,
-+                      const QList<int>& clTagIds, const QList<int>& plTagIds);
-+    void setRatingFilter(int rating, ImageFilterSettings::RatingCondition ratingCond, bool isUnratedExcluded);
-+    void setMimeTypeFilter(int mimeTypeFilter);
-+    void setGeolocationFilter(const ImageFilterSettings::GeolocationCondition& condition);
-+    void setTextFilter(const SearchTextFilterSettings& settings);
-+
-+    void setCategorizationMode(ImageSortSettings::CategorizationMode mode);
-+    void setCategorizationSortOrder(ImageSortSettings::SortOrder order);
-+    void setSortRole(ImageSortSettings::SortRole role);
-+    void setSortOrder(ImageSortSettings::SortOrder order);
-+    void setStringTypeNatural(bool natural);
-+    void setUrlWhitelist(const QList<QUrl> urlList, const QString& id);
-+    void setIdWhitelist(const QList<qlonglong>& idList, const QString& id);
-+
-+    void setVersionManagerSettings(const VersionManagerSettings& settings);
-+    void setExceptionList(const QList<qlonglong>& idlist, const QString& id);
-+
-+    void setGroupOpen(qlonglong group, bool open);
-+    void toggleGroupOpen(qlonglong group);
-+    void setAllGroupsOpen(bool open);
-+
-+    /** Changes the current image filter settings and refilters. */
-+    virtual void setImageFilterSettings(const ImageFilterSettings& settings);
-+
-+    /** Changes the current image sort settings and resorts. */
-+    virtual void setImageSortSettings(const ImageSortSettings& settings);
-+
-+Q_SIGNALS:
-+
-+    /// Signals that the set filter matches at least one index
-+    void filterMatches(bool matches);
-+
-+    /** Signals that the set text filter matches at least one entry.
-+        If no text filter is set, this signal is emitted
-+        with 'false' when filterMatches() is emitted.
-+     */
-+    void filterMatchesForText(bool matchesByText);
-+
-+    /** Emitted when the filter settings have been changed
-+        (the model may not yet have been updated)
-+     */
-+    void filterSettingsChanged(const ImageFilterSettings& settings);
-+
-+    /** These signals need to be explicitly enabled with setSendImageInfoSignals()
-+     */
-+    void imageInfosAdded(const QList<ImageInfo>& infos);
-+    void imageInfosAboutToBeRemoved(const QList<ImageInfo>& infos);
-+
-+public:
-+
-+    // Declared as public because of use in sub-classes.
-+    class ImageFilterModelPrivate;
-+
-+protected:
-+
-+    ImageFilterModelPrivate* const d_ptr;
-+
-+protected:
-+
-+    ImageFilterModel(ImageFilterModelPrivate& dd, QObject* parent);
-+
-+    virtual void setDirectSourceImageModel(ImageModel* model);
-+
-+    virtual bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const;
-+
-+    virtual int  compareCategories(const QModelIndex& left, const QModelIndex& right) const;
-+    virtual bool subSortLessThan(const QModelIndex& left, const QModelIndex& right) const;
-+    //virtual int  categoryCount(const ImageInfo& info) const;
-+
-+    /** Reimplement to customize category sorting,
-+     *  Return negative if category of left < category right,
-+     *  Return 0 if left and right are in the same category, else return positive.
-+     */
-+    virtual int compareInfosCategories(const ImageInfo& left, const ImageInfo& right) const;
-+
-+    /** Reimplement to customize sorting. Do not take categories into account here.
-+     */
-+    virtual bool infosLessThan(const ImageInfo& left, const ImageInfo& right) const;
-+
-+    /** Returns a unique identifier for the category if info. The string need not be for user display.
-+     */
-+    virtual QString categoryIdentifier(const ImageInfo& info) const;
-+
-+protected Q_SLOTS:
-+
-+    void slotModelReset();
-+    void slotUpdateFilter();
-+
-+    void slotImageTagChange(const ImageTagChangeset& changeset);
-+    void slotImageChange(const ImageChangeset& changeset);
-+
-+    void slotRowsInserted(const QModelIndex& parent, int start, int end);
-+    void slotRowsAboutToBeRemoved(const QModelIndex& parent, int start, int end);
-+
-+private:
-+
-+    Q_DECLARE_PRIVATE(ImageFilterModel)
-+};
-+
-+// -----------------------------------------------------------------------------------------------------
-+
-+class DIGIKAM_DATABASE_EXPORT NoDuplicatesImageFilterModel : public ImageSortFilterModel
-+{
-+    Q_OBJECT
-+
-+public:
-+
-+    explicit NoDuplicatesImageFilterModel(QObject* parent = 0);
-+
-+protected:
-+
-+    virtual bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const;
-+};
-+
-+} // namespace Digikam
-+
-+Q_DECLARE_METATYPE(Digikam::ImageFilterModel*)
-+
-+#endif // IMAGEMODEL_H
-diff --git a/libs/database/models/imagefiltermodelpriv.cpp b/libs/database/models/imagefiltermodelpriv.cpp
-new file mode 100644
-index 0000000..07d9e79
---- /dev/null
-+++ b/libs/database/models/imagefiltermodelpriv.cpp
-@@ -0,0 +1,258 @@
-+/* ============================================================
-+ *
-+ * This file is a part of digiKam project
-+ * http://www.digikam.org
-+ *
-+ * Date        : 2009-03-05
-+ * Description : Qt item model for database entries
-+ *
-+ * Copyright (C) 2009-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
-+ * Copyright (C) 2011-2017 by Gilles Caulier <caulier dot gilles at gmail dot com>
-+ * Copyright (C)      2010 by Andi Clemens <andi dot clemens at gmail dot com>
-+ * Copyright (C)      2011 by Michael G. Hansen <mike at mghansen dot de>
-+ * Copyright (C)      2014 by Mohamed Anwer <m dot anwer at gmx dot com>
-+ *
-+ * This program is free software; you can redistribute it
-+ * and/or modify it under the terms of the GNU General
-+ * Public License as published by the Free Software Foundation;
-+ * either version 2, or (at your option)
-+ * any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * ============================================================ */
-+
-+#include "imagefiltermodelpriv.h"
-+
-+// Local includes
-+
-+#include "digikam_debug.h"
-+#include "imagefiltermodelthreads.h"
-+
-+namespace Digikam
-+{
-+
-+ImageFilterModel::ImageFilterModelPrivate::ImageFilterModelPrivate()
-+{
-+    imageModel            = 0;
-+    version               = 0;
-+    lastDiscardVersion    = 0;
-+    sentOut               = 0;
-+    sentOutForReAdd       = 0;
-+    updateFilterTimer     = 0;
-+    needPrepare           = false;
-+    needPrepareComments   = false;
-+    needPrepareTags       = false;
-+    needPrepareGroups     = false;
-+    preparer              = 0;
-+    filterer              = 0;
-+    hasOneMatch           = false;
-+    hasOneMatchForText    = false;
-+
-+    setupWorkers();
-+}
-+
-+ImageFilterModel::ImageFilterModelPrivate::~ImageFilterModelPrivate()
-+{
-+    // facilitate thread stopping
-+    ++version;
-+    preparer->deactivate();
-+    filterer->deactivate();
-+    delete preparer;
-+    delete filterer;
-+}
-+
-+void ImageFilterModel::ImageFilterModelPrivate::init(ImageFilterModel* _q)
-+{
-+    q = _q;
-+
-+    updateFilterTimer = new QTimer(this);
-+    updateFilterTimer->setSingleShot(true);
-+    updateFilterTimer->setInterval(250);
-+
-+    connect(updateFilterTimer, SIGNAL(timeout()),
-+            q, SLOT(slotUpdateFilter()));
-+
-+    // inter-thread redirection
-+    qRegisterMetaType<ImageFilterModelTodoPackage>("ImageFilterModelTodoPackage");
-+}
-+
-+void ImageFilterModel::ImageFilterModelPrivate::preprocessInfos(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues)
-+{
-+    infosToProcess(infos, extraValues, true);
-+}
-+
-+void ImageFilterModel::ImageFilterModelPrivate::processAddedInfos(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues)
-+{
-+    // These have already been added, we just process them afterwards
-+    infosToProcess(infos, extraValues, false);
-+}
-+
-+void ImageFilterModel::ImageFilterModelPrivate::setupWorkers()
-+{
-+    preparer = new ImageFilterModelPreparer(this);
-+    filterer = new ImageFilterModelFilterer(this);
-+
-+    // A package in constructed in infosToProcess.
-+    // Normal flow is infosToProcess -> preparer::process -> filterer::process -> packageFinished.
-+    // If no preparation is needed, the first step is skipped.
-+    // If filter version changes, both will discard old package and send them to packageDiscarded.
-+
-+    connect(this, SIGNAL(packageToPrepare(ImageFilterModelTodoPackage)),
-+            preparer, SLOT(process(ImageFilterModelTodoPackage)));
-+
-+    connect(this, SIGNAL(packageToFilter(ImageFilterModelTodoPackage)),
-+            filterer, SLOT(process(ImageFilterModelTodoPackage)));
-+
-+    connect(preparer, SIGNAL(processed(ImageFilterModelTodoPackage)),
-+            filterer, SLOT(process(ImageFilterModelTodoPackage)));
-+
-+    connect(filterer, SIGNAL(processed(ImageFilterModelTodoPackage)),
-+            this, SLOT(packageFinished(ImageFilterModelTodoPackage)));
-+
-+    connect(preparer, SIGNAL(discarded(ImageFilterModelTodoPackage)),
-+            this, SLOT(packageDiscarded(ImageFilterModelTodoPackage)));
-+
-+    connect(filterer, SIGNAL(discarded(ImageFilterModelTodoPackage)),
-+            this, SLOT(packageDiscarded(ImageFilterModelTodoPackage)));
-+}
-+
-+void ImageFilterModel::ImageFilterModelPrivate::infosToProcess(const QList<ImageInfo>& infos)
-+{
-+    infosToProcess(infos, QList<QVariant>(), false);
-+}
-+
-+void ImageFilterModel::ImageFilterModelPrivate::infosToProcess(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues, bool forReAdd)
-+{
-+    if (infos.isEmpty())
-+    {
-+        return;
-+    }
-+
-+    filterer->schedule();
-+
-+    if (needPrepare)
-+    {
-+        preparer->schedule();
-+    }
-+
-+    Q_ASSERT(extraValues.isEmpty() || infos.size() == extraValues.size());
-+
-+    // prepare and filter in chunks
-+    const int size                      = infos.size();
-+    const int maxChunkSize              = needPrepare ? PrepareChunkSize : FilterChunkSize;
-+    const bool hasExtraValues           = !extraValues.isEmpty();
-+    QList<ImageInfo>::const_iterator it = infos.constBegin(), end;
-+    QList<QVariant>::const_iterator xit = extraValues.constBegin(), xend;
-+    int index                           = 0;
-+    QVector<ImageInfo>  infoVector;
-+    QVector<QVariant> extraValueVector;
-+
-+    while (it != infos.constEnd())
-+    {
-+        const int chunkSize = qMin(maxChunkSize, size - index);
-+        infoVector.resize(chunkSize);
-+        end = it + chunkSize;
-+        qCopy(it, end, infoVector.begin());
-+
-+        if (hasExtraValues)
-+        {
-+            extraValueVector.resize(chunkSize);
-+            xend = xit + chunkSize;
-+            qCopy(xit, xend, extraValueVector.begin());
-+            xit = xend;
-+        }
-+
-+        it    = end;
-+        index += chunkSize;
-+
-+        ++sentOut;
-+
-+        if (forReAdd)
-+        {
-+            ++sentOutForReAdd;
-+        }
-+
-+        if (needPrepare)
-+        {
-+            emit packageToPrepare(ImageFilterModelTodoPackage(infoVector, extraValueVector, version, forReAdd));
-+        }
-+        else
-+        {
-+            emit packageToFilter(ImageFilterModelTodoPackage(infoVector, extraValueVector, version, forReAdd));
-+        }
-+    }
-+}
-+
-+void ImageFilterModel::ImageFilterModelPrivate::packageFinished(const ImageFilterModelTodoPackage& package)
-+{
-+    // check if it got discarded on the journey
-+    if (package.version != version)
-+    {
-+        packageDiscarded(package);
-+        return;
-+    }
-+
-+    // incorporate result
-+    QHash<qlonglong, bool>::const_iterator it = package.filterResults.constBegin();
-+
-+    for (; it != package.filterResults.constEnd(); ++it)
-+    {
-+        filterResults.insert(it.key(), it.value());
-+    }
-+
-+    // re-add if necessary
-+    if (package.isForReAdd)
-+    {
-+        emit reAddImageInfos(package.infos.toList(), package.extraValues.toList());
-+
-+        if (sentOutForReAdd == 1) // last package
-+        {
-+            emit reAddingFinished();
-+        }
-+    }
-+
-+    // decrement counters
-+    --sentOut;
-+
-+    if (package.isForReAdd)
-+    {
-+        --sentOutForReAdd;
-+    }
-+
-+    // If all packages have returned, filtered and readded, and no more are expected,
-+    // and there is need to tell the filter result to the view, do that
-+    if (sentOut == 0 && sentOutForReAdd == 0 && !imageModel->isRefreshing())
-+    {
-+        q->invalidate(); // use invalidate, not invalidateFilter only. Sorting may have changed as well.
-+        emit (q->filterMatches(hasOneMatch));
-+        emit (q->filterMatchesForText(hasOneMatchForText));
-+        filterer->deactivate();
-+        preparer->deactivate();
-+    }
-+}
-+
-+void ImageFilterModel::ImageFilterModelPrivate::packageDiscarded(const ImageFilterModelTodoPackage& package)
-+{
-+    // Either, the model was reset, or the filter changed
-+    // In the former case throw all away, in the latter case, recycle
-+    if (package.version > lastDiscardVersion)
-+    {
-+        // Recycle packages: Send again with current version
-+        // Do not increment sentOut or sentOutForReAdd here: it was not decremented!
-+
-+        if (needPrepare)
-+        {
-+            emit packageToPrepare(ImageFilterModelTodoPackage(package.infos, package.extraValues, version, package.isForReAdd));
-+        }
-+        else
-+        {
-+            emit packageToFilter(ImageFilterModelTodoPackage(package.infos, package.extraValues, version, package.isForReAdd));
-+        }
-+    }
-+}
-+
-+} // namespace Digikam
-diff --git a/libs/database/models/imagefiltermodelpriv.h b/libs/database/models/imagefiltermodelpriv.h
-new file mode 100644
-index 0000000..a9e3f22
---- /dev/null
-+++ b/libs/database/models/imagefiltermodelpriv.h
-@@ -0,0 +1,159 @@
-+/* ============================================================
-+ *
-+ * This file is a part of digiKam project
-+ * http://www.digikam.org
-+ *
-+ * Date        : 2009-03-11
-+ * Description : Qt item model for database entries - private shared header
-+ *
-+ * Copyright (C) 2009-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
-+ *
-+ * This program is free software; you can redistribute it
-+ * and/or modify it under the terms of the GNU General
-+ * Public License as published by the Free Software Foundation;
-+ * either version 2, or (at your option)
-+ * any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * ============================================================ */
-+
-+#ifndef IMAGEFILTERMODELPRIV_H
-+#define IMAGEFILTERMODELPRIV_H
-+
-+// Qt includes
-+
-+#include <QHash>
-+#include <QMutex>
-+#include <QMutexLocker>
-+#include <QSet>
-+#include <QThread>
-+#include <QTimer>
-+#include <QWaitCondition>
-+
-+// Local includes
-+
-+#include "imageinfo.h"
-+#include "imagefiltermodel.h"
-+
-+#include "digikam_export.h"
-+// Yes, we need the EXPORT macro in a private header because
-+// this private header is shared across binary objects.
-+// This does NOT make this classes here any more public!
-+
-+namespace Digikam
-+{
-+
-+const int PrepareChunkSize = 101;
-+const int FilterChunkSize  = 2001;
-+
-+class ImageFilterModelTodoPackage
-+{
-+public:
-+
-+    ImageFilterModelTodoPackage()
-+        : version(0), isForReAdd(false)
-+    {
-+    }
-+
-+    ImageFilterModelTodoPackage(const QVector<ImageInfo>& infos, const QVector<QVariant>& extraValues, int version, bool isForReAdd)
-+        : infos(infos), extraValues(extraValues), version(version), isForReAdd(isForReAdd)
-+    {
-+    }
-+
-+    QVector<ImageInfo>     infos;
-+    QVector<QVariant>      extraValues;
-+    unsigned int           version;
-+    bool                   isForReAdd;
-+    QHash<qlonglong, bool> filterResults;
-+};
-+
-+// ------------------------------------------------------------------------------------------------
-+
-+class ImageFilterModelPreparer;
-+class ImageFilterModelFilterer;
-+
-+class DIGIKAM_DATABASE_EXPORT ImageFilterModel::ImageFilterModelPrivate : public QObject
-+{
-+    Q_OBJECT
-+
-+public:
-+
-+    ImageFilterModelPrivate();
-+    ~ImageFilterModelPrivate();
-+
-+    void init(ImageFilterModel* q);
-+    void setupWorkers();
-+    void infosToProcess(const QList<ImageInfo>& infos);
-+    void infosToProcess(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues, bool forReAdd = true);
-+
-+public:
-+
-+    ImageFilterModel*                   q;
-+
-+    ImageModel*                         imageModel;
-+
-+    ImageFilterSettings                 filter;
-+    ImageSortSettings                   sorter;
-+    VersionImageFilterSettings          versionFilter;
-+    GroupImageFilterSettings            groupFilter;
-+
-+    volatile unsigned int               version;
-+    unsigned int                        lastDiscardVersion;
-+    unsigned int                        lastFilteredVersion;
-+    int                                 sentOut;
-+    int                                 sentOutForReAdd;
-+
-+    QTimer*                             updateFilterTimer;
-+
-+    bool                                needPrepare;
-+    bool                                needPrepareComments;
-+    bool                                needPrepareTags;
-+    bool                                needPrepareGroups;
-+
-+    QMutex                              mutex;
-+    ImageFilterSettings                 filterCopy;
-+    VersionImageFilterSettings          versionFilterCopy;
-+    GroupImageFilterSettings            groupFilterCopy;
-+    ImageFilterModelPreparer*           preparer;
-+    ImageFilterModelFilterer*           filterer;
-+
-+    QHash<qlonglong, bool>              filterResults;
-+    bool                                hasOneMatch;
-+    bool                                hasOneMatchForText;
-+
-+    QList<ImageFilterModelPrepareHook*> prepareHooks;
-+
-+/*
-+    QHash<int, QSet<qlonglong> >        categoryCountHashInt;
-+    QHash<QString, QSet<qlonglong> >    categoryCountHashString;
-+
-+public:
-+
-+        void cacheCategoryCount(int id, qlonglong imageid) const
-+        { const_cast<ImageFilterModelPrivate*>(this)->categoryCountHashInt[id].insert(imageid); }
-+        void cacheCategoryCount(const QString& id, qlonglong imageid) const
-+        { const_cast<ImageFilterModelPrivate*>(this)->categoryCountHashString[id].insert(imageid); }
-+*/
-+
-+public Q_SLOTS:
-+
-+    void preprocessInfos(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues);
-+    void processAddedInfos(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues);
-+    void packageFinished(const ImageFilterModelTodoPackage& package);
-+    void packageDiscarded(const ImageFilterModelTodoPackage& package);
-+
-+Q_SIGNALS:
-+
-+    void packageToPrepare(const ImageFilterModelTodoPackage& package);
-+    void packageToFilter(const ImageFilterModelTodoPackage& package);
-+    void reAddImageInfos(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues);
-+    void reAddingFinished();
-+};
-+
-+} // namespace Digikam
-+
-+#endif // IMAGEFILTERMODELPRIV_H
-diff --git a/libs/database/models/imagefiltermodelthreads.cpp b/libs/database/models/imagefiltermodelthreads.cpp
-new file mode 100644
-index 0000000..aa5c462
---- /dev/null
-+++ b/libs/database/models/imagefiltermodelthreads.cpp
-@@ -0,0 +1,40 @@
-+/* ============================================================
-+ *
-+ * This file is a part of digiKam project
-+ * http://www.digikam.org
-+ *
-+ * Date        : 2009-03-05
-+ * Description : Qt item model for database entries
-+ *
-+ * Copyright (C) 2009-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
-+ * Copyright (C) 2011-2017 by Gilles Caulier <caulier dot gilles at gmail dot com>
-+ * Copyright (C)      2010 by Andi Clemens <andi dot clemens at gmail dot com>
-+ * Copyright (C)      2011 by Michael G. Hansen <mike at mghansen dot de>
-+ * Copyright (C)      2014 by Mohamed Anwer <m dot anwer at gmx dot com>
-+ *
-+ * This program is free software; you can redistribute it
-+ * and/or modify it under the terms of the GNU General
-+ * Public License as published by the Free Software Foundation;
-+ * either version 2, or (at your option)
-+ * any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * ============================================================ */
-+
-+#include "imagefiltermodel.h"
-+#include "imagefiltermodelpriv.h"
-+#include "imagefiltermodelthreads.h"
-+
-+namespace Digikam
-+{
-+
-+ImageFilterModelWorker::ImageFilterModelWorker(ImageFilterModel::ImageFilterModelPrivate* const d)
-+    : d(d)
-+{
-+}
-+
-+} // namespace Digikam
-diff --git a/libs/database/models/imagefiltermodelthreads.h b/libs/database/models/imagefiltermodelthreads.h
-new file mode 100644
-index 0000000..83fa987
---- /dev/null
-+++ b/libs/database/models/imagefiltermodelthreads.h
-@@ -0,0 +1,100 @@
-+/* ============================================================
-+ *
-+ * This file is a part of digiKam project
-+ * http://www.digikam.org
-+ *
-+ * Date        : 2009-03-11
-+ * Description : Qt item model for database entries - private header
-+ *
-+ * Copyright (C) 2009-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
-+ *
-+ * This program is free software; you can redistribute it
-+ * and/or modify it under the terms of the GNU General
-+ * Public License as published by the Free Software Foundation;
-+ * either version 2, or (at your option)
-+ * any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * ============================================================ */
-+
-+#ifndef IMAGEFILTERMODELTHREADS_H
-+#define IMAGEFILTERMODELTHREADS_H
-+
-+// Qt includes
-+
-+#include <QThread>
-+
-+// Local includes
-+
-+#include "digikam_export.h"
-+#include "workerobject.h"
-+
-+namespace Digikam
-+{
-+
-+class DIGIKAM_DATABASE_EXPORT ImageFilterModelWorker : public WorkerObject
-+{
-+    Q_OBJECT
-+
-+public:
-+
-+    explicit ImageFilterModelWorker(ImageFilterModel::ImageFilterModelPrivate* const d);
-+
-+    bool checkVersion(const ImageFilterModelTodoPackage& package)
-+    {
-+        return d->version == package.version;
-+    }
-+
-+public Q_SLOTS:
-+
-+    virtual void process(ImageFilterModelTodoPackage package) = 0;
-+
-+Q_SIGNALS:
-+
-+    void processed(const ImageFilterModelTodoPackage& package);
-+    void discarded(const ImageFilterModelTodoPackage& package);
-+
-+protected:
-+
-+    ImageFilterModel::ImageFilterModelPrivate* d;
-+};
-+
-+// -----------------------------------------------------------------------------------------
-+
-+class DIGIKAM_DATABASE_EXPORT ImageFilterModelPreparer : public ImageFilterModelWorker
-+{
-+    Q_OBJECT
-+
-+public:
-+
-+    explicit ImageFilterModelPreparer(ImageFilterModel::ImageFilterModelPrivate* const d)
-+        : ImageFilterModelWorker(d)
-+    {
-+    }
-+
-+    void process(ImageFilterModelTodoPackage package);
-+};
-+
-+// ----------------------------------------------------------------------------------------
-+
-+class DIGIKAM_DATABASE_EXPORT ImageFilterModelFilterer : public ImageFilterModelWorker
-+{
-+    Q_OBJECT
-+
-+public:
-+
-+    explicit ImageFilterModelFilterer(ImageFilterModel::ImageFilterModelPrivate* const d)
-+        : ImageFilterModelWorker(d)
-+    {
-+    }
-+
-+    void process(ImageFilterModelTodoPackage package);
-+};
-+
-+} // namespace Digikam
-+
-+#endif // IMAGEFILTERMODELTHREADS_H
-diff --git a/libs/database/models/imagefiltersettings.cpp b/libs/database/models/imagefiltersettings.cpp
-new file mode 100644
-index 0000000..b61e7f9
---- /dev/null
-+++ b/libs/database/models/imagefiltersettings.cpp
-@@ -0,0 +1,952 @@
-+/* ============================================================
-+ *
-+ * This file is a part of digiKam project
-+ * http://www.digikam.org
-+ *
-+ * Date        : 2009-03-05
-+ * Description : Filter values for use with ImageFilterModel
-+ *
-+ * Copyright (C) 2009-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
-+ * Copyright (C) 2011-2017 by Gilles Caulier <caulier dot gilles at gmail dot com>
-+ * Copyright (C)      2010 by Andi Clemens <andi dot clemens at gmail dot com>
-+ * Copyright (C)      2011 by Michael G. Hansen <mike at mghansen dot de>
-+ * Copyright (C)      2014 by Mohamed Anwer <m dot anwer at gmx dot com>
-+ *
-+ * This program is free software; you can redistribute it
-+ * and/or modify it under the terms of the GNU General
-+ * Public License as published by the Free Software Foundation;
-+ * either version 2, or (at your option)
-+ * any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * ============================================================ */
-+
-+#include "imagefiltersettings.h"
-+
-+// C++ includes
-+
-+#include <cmath>
-+
-+// Qt includes
-+
-+#include <QDateTime>
-+
-+// Local includes
-+
-+#include "digikam_debug.h"
-+#include "coredbfields.h"
-+#include "digikam_globals.h"
-+#include "imageinfo.h"
-+#include "tagscache.h"
-+#include "versionmanagersettings.h"
-+
-+namespace Digikam
-+{
-+
-+ImageFilterSettings::ImageFilterSettings()
-+{
-+    m_untaggedFilter       = false;
-+    m_isUnratedExcluded    = false;
-+    m_ratingFilter         = 0;
-+    m_mimeTypeFilter       = MimeFilter::AllFiles;
-+    m_ratingCond           = GreaterEqualCondition;
-+    m_matchingCond         = OrCondition;
-+    m_geolocationCondition = GeolocationNoFilter;
-+}
-+
-+DatabaseFields::Set ImageFilterSettings::watchFlags() const
-+{
-+    DatabaseFields::Set set;
-+
-+    if (isFilteringByDay())
-+    {
-+        set |= DatabaseFields::CreationDate;
-+    }
-+
-+    if (isFilteringByText())
-+    {
-+        set |= DatabaseFields::Name;
-+        set |= DatabaseFields::Comment;
-+    }
-+
-+    if (isFilteringByRating())
-+    {
-+        set |= DatabaseFields::Rating;
-+    }
-+
-+    if (isFilteringByTypeMime())
-+    {
-+        set |= DatabaseFields::Category;
-+        set |= DatabaseFields::Format;
-+    }
-+
-+    if (isFilteringByGeolocation())
-+    {
-+        set |= DatabaseFields::ImagePositionsAll;
-+    }
-+
-+    if (isFilteringByColorLabels())
-+    {
-+        set |= DatabaseFields::ColorLabel;
-+    }
-+
-+    if (isFilteringByPickLabels())
-+    {
-+        set |= DatabaseFields::PickLabel;
-+    }
-+
-+    return set;
-+}
-+
-+bool ImageFilterSettings::isFilteringByDay() const
-+{
-+    if (!m_dayFilter.isEmpty())
-+    {
-+        return true;
-+    }
-+
-+    return false;
-+}
-+
-+bool ImageFilterSettings::isFilteringByTags() const
-+{
-+    if (!m_includeTagFilter.isEmpty() || !m_excludeTagFilter.isEmpty() || m_untaggedFilter)
-+    {
-+        return true;
-+    }
-+
-+    return false;
-+}
-+
-+bool ImageFilterSettings::isFilteringByColorLabels() const
-+{
-+    if (!m_colorLabelTagFilter.isEmpty())
-+    {
-+        return true;
-+    }
-+
-+    return false;
-+}
-+
-+bool ImageFilterSettings::isFilteringByPickLabels() const
-+{
-+    if (!m_pickLabelTagFilter.isEmpty())
-+    {
-+        return true;
-+    }
-+
-+    return false;
-+}
-+
-+bool ImageFilterSettings::isFilteringByText() const
-+{
-+    if (!m_textFilterSettings.text.isEmpty())
-+    {
-+        return true;
-+    }
-+
-+    return false;
-+}
-+
-+bool ImageFilterSettings::isFilteringByTypeMime() const
-+{
-+    if (m_mimeTypeFilter != MimeFilter::AllFiles)
-+    {
-+        return true;
-+    }
-+
-+    return false;
-+}
-+
-+bool ImageFilterSettings::isFilteringByGeolocation() const
-+{
-+    return (m_geolocationCondition != GeolocationNoFilter);
-+}
-+
-+bool ImageFilterSettings::isFilteringByRating() const
-+{
-+    if (m_ratingFilter != 0 || m_ratingCond != GreaterEqualCondition || m_isUnratedExcluded)
-+    {
-+        return true;
-+    }
-+
-+    return false;
-+}
-+
-+bool ImageFilterSettings::isFilteringInternally() const
-+{
-+    return (isFiltering() || !m_urlWhitelists.isEmpty() || !m_idWhitelists.isEmpty());
-+}
-+
-+bool ImageFilterSettings::isFiltering() const
-+{
-+    return isFilteringByDay()         ||
-+           isFilteringByTags()        ||
-+           isFilteringByText()        ||
-+           isFilteringByRating()      ||
-+           isFilteringByTypeMime()    ||
-+           isFilteringByColorLabels() ||
-+           isFilteringByPickLabels()  ||
-+           isFilteringByGeolocation();
-+}
-+
-+void ImageFilterSettings::setDayFilter(const QList<QDateTime>& days)
-+{
-+    m_dayFilter.clear();
-+
-+    for (QList<QDateTime>::const_iterator it = days.constBegin(); it != days.constEnd(); ++it)
-+    {
-+        m_dayFilter.insert(*it, true);
-+    }
-+}
-+
-+void ImageFilterSettings::setTagFilter(const QList<int>& includedTags,
-+                                       const QList<int>& excludedTags,
-+                                       MatchingCondition matchingCondition,
-+                                       bool showUnTagged,
-+                                       const QList<int>& clTagIds,
-+                                       const QList<int>& plTagIds)
-+{
-+    m_includeTagFilter    = includedTags;
-+    m_excludeTagFilter    = excludedTags;
-+    m_matchingCond        = matchingCondition;
-+    m_untaggedFilter      = showUnTagged;
-+    m_colorLabelTagFilter = clTagIds;
-+    m_pickLabelTagFilter  = plTagIds;
-+}
-+
-+void ImageFilterSettings::setRatingFilter(int rating, RatingCondition ratingCondition, bool isUnratedExcluded)
-+{
-+    m_ratingFilter      = rating;
-+    m_ratingCond        = ratingCondition;
-+    m_isUnratedExcluded = isUnratedExcluded;
-+}
-+
-+void ImageFilterSettings::setMimeTypeFilter(int mime)
-+{
-+    m_mimeTypeFilter = (MimeFilter::TypeMimeFilter)mime;
-+}
-+
-+void ImageFilterSettings::setGeolocationFilter(const GeolocationCondition& condition)
-+{
-+    m_geolocationCondition = condition;
-+}
-+
-+void ImageFilterSettings::setTextFilter(const SearchTextFilterSettings& settings)
-+{
-+    m_textFilterSettings = settings;
-+}
-+
-+void ImageFilterSettings::setTagNames(const QHash<int, QString>& hash)
-+{
-+    m_tagNameHash = hash;
-+}
-+
-+void ImageFilterSettings::setAlbumNames(const QHash<int, QString>& hash)
-+{
-+    m_albumNameHash = hash;
-+}
-+
-+void ImageFilterSettings::setUrlWhitelist(const QList<QUrl>& urlList, const QString& id)
-+{
-+    if (urlList.isEmpty())
-+    {
-+        m_urlWhitelists.remove(id);
-+    }
-+    else
-+    {
-+        m_urlWhitelists.insert(id, urlList);
-+    }
-+}
-+
-+void ImageFilterSettings::setIdWhitelist(const QList<qlonglong>& idList, const QString& id)
-+{
-+    if (idList.isEmpty())
-+    {
-+        m_idWhitelists.remove(id);
-+    }
-+    else
-+    {
-+        m_idWhitelists.insert(id, idList);
-+    }
-+}
-+
-+template <class ContainerA, class ContainerB>
-+bool containsAnyOf(const ContainerA& listA, const ContainerB& listB)
-+{
-+    foreach (const typename ContainerA::value_type& a, listA)
-+    {
-+        if (listB.contains(a))
-+        {
-+            return true;
-+        }
-+    }
-+    return false;
-+}
-+
-+template <class ContainerA, typename Value, class ContainerB>
-+bool containsNoneOfExcept(const ContainerA& list, const ContainerB& noneOfList, const Value& exception)
-+{
-+    foreach (const typename ContainerB::value_type& n, noneOfList)
-+    {
-+        if (n != exception && list.contains(n))
-+        {
-+            return false;
-+        }
-+    }
-+    return true;
-+}
-+
-+bool ImageFilterSettings::matches(const ImageInfo& info, bool* const foundText) const
-+{
-+    if (foundText)
-+    {
-+        *foundText = false;
-+    }
-+
-+    if (!isFilteringInternally())
-+    {
-+        return true;
-+    }
-+
-+    bool match = false;
-+
-+    if (!m_includeTagFilter.isEmpty() || !m_excludeTagFilter.isEmpty())
-+    {
-+        QList<int>                 tagIds = info.tagIds();
-+        QList<int>::const_iterator it;
-+
-+        match = m_includeTagFilter.isEmpty();
-+
-+        if (m_matchingCond == OrCondition)
-+        {
-+            for (it = m_includeTagFilter.begin(); it != m_includeTagFilter.end(); ++it)
-+            {
-+                if (tagIds.contains(*it))
-+                {
-+                    match = true;
-+                    break;
-+                }
-+            }
-+
-+            match |= (m_untaggedFilter && tagIds.isEmpty());
-+        }
-+        else // AND matching condition...
-+        {
-+            // m_untaggedFilter and non-empty tag filter, combined with AND, is logically no match
-+            if (!m_untaggedFilter)
-+            {
-+                for (it = m_includeTagFilter.begin(); it != m_includeTagFilter.end(); ++it)
-+                {
-+                    if (!tagIds.contains(*it))
-+                    {
-+                        break;
-+                    }
-+                }
-+
-+                if (it == m_includeTagFilter.end())
-+                {
-+                    match = true;
-+                }
-+            }
-+        }
-+
-+        for (it = m_excludeTagFilter.begin(); it != m_excludeTagFilter.end(); ++it)
-+        {
-+            if (tagIds.contains(*it))
-+            {
-+                match = false;
-+                break;
-+            }
-+        }
-+    }
-+    else if (m_untaggedFilter)
-+    {
-+        match = !TagsCache::instance()->containsPublicTags(info.tagIds());
-+    }
-+    else
-+    {
-+        match = true;
-+    }
-+
-+    //-- Filter by pick labels ------------------------------------------------
-+
-+    if (!m_pickLabelTagFilter.isEmpty())
-+    {
-+        QList<int> tagIds = info.tagIds();
-+        bool matchPL      = false;
-+
-+        if (containsAnyOf(m_pickLabelTagFilter, tagIds))
-+        {
-+            matchPL = true;
-+        }
-+        else if (!matchPL)
-+        {
-+            int noPickLabelTagId = TagsCache::instance()->tagForPickLabel(NoPickLabel);
-+
-+            if (m_pickLabelTagFilter.contains(noPickLabelTagId))
-+            {
-+                // Searching for "has no ColorLabel" requires special handling:
-+                // Scan that the tag ids contains none of the ColorLabel tags, except maybe the NoColorLabel tag
-+                matchPL = containsNoneOfExcept(tagIds, TagsCache::instance()->pickLabelTags(), noPickLabelTagId);
-+            }
-+        }
-+
-+        match &= matchPL;
-+    }
-+
-+    //-- Filter by color labels ------------------------------------------------
-+
-+    if (!m_colorLabelTagFilter.isEmpty())
-+    {
-+        QList<int> tagIds = info.tagIds();
-+        bool matchCL      = false;
-+
-+        if (containsAnyOf(m_colorLabelTagFilter, tagIds))
-+        {
-+            matchCL = true;
-+        }
-+        else if (!matchCL)
-+        {
-+            int noColorLabelTagId = TagsCache::instance()->tagForColorLabel(NoColorLabel);
-+
-+            if (m_colorLabelTagFilter.contains(noColorLabelTagId))
-+            {
-+                // Searching for "has no ColorLabel" requires special handling:
-+                // Scan that the tag ids contains none of the ColorLabel tags, except maybe the NoColorLabel tag
-+                matchCL = containsNoneOfExcept(tagIds, TagsCache::instance()->colorLabelTags(), noColorLabelTagId);
-+            }
-+        }
-+
-+        match &= matchCL;
-+    }
-+
-+    //-- Filter by date -----------------------------------------------------------
-+
-+    if (!m_dayFilter.isEmpty())
-+    {
-+        match &= m_dayFilter.contains(QDateTime(info.dateTime().date(), QTime()));
-+    }
-+
-+    //-- Filter by rating ---------------------------------------------------------
-+
-+    if (m_ratingFilter >= 0)
-+    {
-+        // for now we treat -1 (no rating) just like a rating of 0.
-+        int rating = info.rating();
-+
-+        if (rating == -1)
-+        {
-+            rating = 0;
-+        }
-+
-+        if(m_isUnratedExcluded && rating == 0)
-+        {
-+            match = false;
-+        }
-+        else
-+        {
-+            if (m_ratingCond == GreaterEqualCondition)
-+            {
-+                // If the rating is not >=, i.e it is <, then it does not match.
-+                if (rating < m_ratingFilter)
-+                {
-+                    match = false;
-+                }
-+            }
-+            else if (m_ratingCond == EqualCondition)
-+            {
-+                // If the rating is not =, i.e it is !=, then it does not match.
-+                if (rating != m_ratingFilter)
-+                {
-+                    match = false;
-+                }
-+            }
-+            else
-+            {
-+                // If the rating is not <=, i.e it is >, then it does not match.
-+                if (rating > m_ratingFilter)
-+                {
-+                    match = false;
-+                }
-+            }
-+        }
-+    }
-+
-+    // -- Filter by mime type -----------------------------------------------------
-+
-+    switch (m_mimeTypeFilter)
-+    {
-+        // info.format is a standardized string: Only one possibility per mime type
-+        case MimeFilter::ImageFiles:
-+        {
-+            if (info.category() != DatabaseItem::Image)
-+            {
-+                match = false;
-+            }
-+
-+            break;
-+        }
-+        case MimeFilter::JPGFiles:
-+        {
-+            if (info.format() != QLatin1String("JPG"))
-+            {
-+                match = false;
-+            }
-+
-+            break;
-+        }
-+        case MimeFilter::PNGFiles:
-+        {
-+            if (info.format() != QLatin1String("PNG"))
-+            {
-+                match = false;
-+            }
-+
-+            break;
-+        }
-+        case MimeFilter::TIFFiles:
-+        {
-+            if (info.format() != QLatin1String("TIFF"))
-+            {
-+                match = false;
-+            }
-+
-+            break;
-+        }
-+        case MimeFilter::DNGFiles:
-+        {
-+            if (info.format() != QLatin1String("RAW-DNG"))
-+            {
-+                match = false;
-+            }
-+
-+            break;
-+        }
-+        case MimeFilter::NoRAWFiles:
-+        {
-+            if (info.format().startsWith(QLatin1String("RAW")))
-+            {
-+                match = false;
-+            }
-+
-+            break;
-+        }
-+        case MimeFilter::RAWFiles:
-+        {
-+            if (!info.format().startsWith(QLatin1String("RAW")))
-+            {
-+                match = false;
-+            }
-+
-+            break;
-+        }
-+        case MimeFilter::MoviesFiles:
-+        {
-+            if (info.category() != DatabaseItem::Video)
-+            {
-+                match = false;
-+            }
-+
-+            break;
-+        }
-+        case MimeFilter::AudioFiles:
-+        {
-+            if (info.category() != DatabaseItem::Audio)
-+            {
-+                match = false;
-+            }
-+
-+            break;
-+        }
-+        case MimeFilter::RasterFiles:
-+        {
-+            if (info.format() != QLatin1String("PSD") &&         // Adobe Photoshop Document
-+                info.format() != QLatin1String("PSB") &&         // Adobe Photoshop Big
-+                info.format() != QLatin1String("XCF") &&         // Gimp
-+                info.format() != QLatin1String("KRA") &&         // Krita
-+                info.format() != QLatin1String("ORA")            // Open Raster
-+               )
-+            {
-+                match = false;
-+            }
-+
-+            break;
-+        }
-+        default:
-+        {
-+            // All Files: do nothing...
-+            break;
-+        }
-+    }
-+
-+    //-- Filter by geolocation ----------------------------------------------------
-+
-+    if (m_geolocationCondition!=GeolocationNoFilter)
-+    {
-+        if (m_geolocationCondition==GeolocationNoCoordinates)
-+        {
-+            if (info.hasCoordinates())
-+            {
-+                match = false;
-+            }
-+        }
-+        else if (m_geolocationCondition==GeolocationHasCoordinates)
-+        {
-+            if (!info.hasCoordinates())
-+            {
-+                match = false;
-+            }
-+        }
-+    }
-+
-+    //-- Filter by text -----------------------------------------------------------
-+
-+    if (!m_textFilterSettings.text.isEmpty())
-+    {
-+        bool textMatch = false;
-+
-+        // Image name
-+        if (m_textFilterSettings.textFields & SearchTextFilterSettings::ImageName &&
-+            info.name().contains(m_textFilterSettings.text, m_textFilterSettings.caseSensitive))
-+        {
-+            textMatch = true;
-+        }
-+
-+        // Image title
-+        if (m_textFilterSettings.textFields & SearchTextFilterSettings::ImageTitle &&
-+            info.title().contains(m_textFilterSettings.text, m_textFilterSettings.caseSensitive))
-+        {
-+            textMatch = true;
-+        }
-+
-+        // Image comment
-+        if (m_textFilterSettings.textFields & SearchTextFilterSettings::ImageComment &&
-+            info.comment().contains(m_textFilterSettings.text, m_textFilterSettings.caseSensitive))
-+        {
-+            textMatch = true;
-+        }
-+
-+        // Tag names
-+        foreach(int id, info.tagIds())
-+        {
-+            if (m_textFilterSettings.textFields & SearchTextFilterSettings::TagName &&
-+                m_tagNameHash.value(id).contains(m_textFilterSettings.text, m_textFilterSettings.caseSensitive))
-+            {
-+                textMatch = true;
-+            }
-+        }
-+
-+        // Album names
-+        if (m_textFilterSettings.textFields & SearchTextFilterSettings::AlbumName &&
-+            m_albumNameHash.value(info.albumId()).contains(m_textFilterSettings.text, m_textFilterSettings.caseSensitive))
-+        {
-+            textMatch = true;
-+        }
-+
-+        // Image Aspect Ratio
-+        if (m_textFilterSettings.textFields & SearchTextFilterSettings::ImageAspectRatio)
-+        {
-+            QRegExp expRatio (QLatin1String("^\\d+:\\d+$"));
-+            QRegExp expFloat (QLatin1String("^\\d+(.\\d+)?$"));
-+
-+            if (expRatio.indexIn(m_textFilterSettings.text) > -1 && m_textFilterSettings.text.contains(QRegExp(QLatin1String(":\\d+"))))
-+            {
-+                QString trimmedTextFilterSettingsText = m_textFilterSettings.text;
-+                QStringList numberStringList          = trimmedTextFilterSettingsText.split(QLatin1String(":"), QString::SkipEmptyParts);
-+
-+                if (numberStringList.length() == 2)
-+                {
-+                    QString numString     = (QString)numberStringList.at(0), denomString = (QString)numberStringList.at(1);
-+                    bool canConverseNum   = false;
-+                    bool canConverseDenom = false;
-+                    int num               = numString.toInt(&canConverseNum, 10), denom = denomString.toInt(&canConverseDenom, 10);
-+
-+                    if (canConverseNum && canConverseDenom)
-+                    {
-+                        if (fabs(info.aspectRatio() - (double)num / denom) < 0.1)
-+                            textMatch = true;
-+                    }
-+                }
-+            }
-+            else if (expFloat.indexIn(m_textFilterSettings.text) > -1)
-+            {
-+                QString trimmedTextFilterSettingsText = m_textFilterSettings.text;
-+                bool    canConverse                   = false;
-+                double  ratio                         = trimmedTextFilterSettingsText.toDouble(&canConverse);
-+
-+                if (canConverse)
-+                {
-+                    if (fabs(info.aspectRatio() - ratio) < 0.1)
-+                        textMatch = true;
-+                }
-+            }
-+        }
-+
-+        // Image Pixel Size
-+        // See bug #341053 for details.
-+
-+        if (m_textFilterSettings.textFields & SearchTextFilterSettings::ImagePixelSize)
-+        {
-+            QSize size    = info.dimensions();
-+            int pixelSize = size.height()*size.width();
-+            QString text  = m_textFilterSettings.text;
-+
-+            if(text.contains(QRegExp(QLatin1String("^>\\d{1,15}$"))) && pixelSize > (text.remove(0,1)).toInt())
-+            {
-+                textMatch = true;
-+            }
-+            else if(text.contains(QRegExp(QLatin1String("^<\\d{1,15}$"))) && pixelSize < (text.remove(0,1)).toInt())
-+            {
-+                textMatch = true;
-+            }
-+            else if(text.contains(QRegExp(QLatin1String("^\\d+$"))) && pixelSize == text.toInt())
-+            {
-+                textMatch = true;
-+            }
-+        }
-+
-+        match &= textMatch;
-+
-+        if (foundText)
-+        {
-+            *foundText = textMatch;
-+        }
-+    }
-+
-+    // -- filter by URL-whitelists ------------------------------------------------
-+    // NOTE: whitelists are always AND for now.
-+
-+    if (match)
-+    {
-+        const QUrl url = info.fileUrl();
-+
-+        for (QHash<QString, QList<QUrl>>::const_iterator it = m_urlWhitelists.constBegin();
-+             it!=m_urlWhitelists.constEnd(); ++it)
-+        {
-+            match = it->contains(url);
-+
-+            if (!match)
-+            {
-+                break;
-+            }
-+        }
-+    }
-+
-+    if (match)
-+    {
-+        const qlonglong id = info.id();
-+
-+        for (QHash<QString, QList<qlonglong> >::const_iterator it = m_idWhitelists.constBegin();
-+             it!=m_idWhitelists.constEnd(); ++it)
-+        {
-+            match = it->contains(id);
-+
-+            if (!match)
-+            {
-+                break;
-+            }
-+        }
-+    }
-+
-+    return match;
-+}
-+
-+// -------------------------------------------------------------------------------------------------
-+
-+VersionImageFilterSettings::VersionImageFilterSettings()
-+{
-+    m_includeTagFilter   = 0;
-+    m_exceptionTagFilter = 0;
-+}
-+
-+VersionImageFilterSettings::VersionImageFilterSettings(const VersionManagerSettings& settings)
-+{
-+    setVersionManagerSettings(settings);
-+}
-+
-+bool VersionImageFilterSettings::operator==(const VersionImageFilterSettings& other) const
-+{
-+    return m_excludeTagFilter == other.m_excludeTagFilter &&
-+           m_exceptionLists   == other.m_exceptionLists;
-+}
-+
-+bool VersionImageFilterSettings::matches(const ImageInfo& info) const
-+{
-+    if (!isFiltering())
-+    {
-+        return true;
-+    }
-+
-+    const qlonglong id = info.id();
-+
-+    for (QHash<QString, QList<qlonglong> >::const_iterator it = m_exceptionLists.constBegin();
-+         it != m_exceptionLists.constEnd(); ++it)
-+    {
-+        if (it->contains(id))
-+        {
-+            return true;
-+        }
-+    }
-+
-+    bool match        = true;
-+    QList<int> tagIds = info.tagIds();
-+
-+    if (!tagIds.contains(m_includeTagFilter))
-+    {
-+        for (QList<int>::const_iterator it = m_excludeTagFilter.begin();
-+             it != m_excludeTagFilter.end(); ++it)
-+        {
-+            if (tagIds.contains(*it))
-+            {
-+                match = false;
-+                break;
-+            }
-+        }
-+    }
-+
-+    if (!match)
-+    {
-+        if (tagIds.contains(m_exceptionTagFilter))
-+        {
-+            match = true;
-+        }
-+    }
-+
-+    return match;
-+}
-+
-+bool VersionImageFilterSettings::isHiddenBySettings(const ImageInfo& info) const
-+{
-+    QList<int> tagIds = info.tagIds();
-+
-+    foreach(int tagId, m_excludeTagFilter)
-+    {
-+        if (tagIds.contains(tagId))
-+        {
-+            return true;
-+        }
-+    }
-+
-+    return false;
-+}
-+
-+bool VersionImageFilterSettings::isExemptedBySettings(const ImageInfo& info) const
-+{
-+    return info.tagIds().contains(m_exceptionTagFilter);
-+}
-+
-+void VersionImageFilterSettings::setVersionManagerSettings(const VersionManagerSettings& settings)
-+{
-+    m_excludeTagFilter.clear();
-+
-+    if (!settings.enabled)
-+    {
-+        return;
-+    }
-+
-+    if (!(settings.showInViewFlags & VersionManagerSettings::ShowOriginal))
-+    {
-+        m_excludeTagFilter << TagsCache::instance()->getOrCreateInternalTag(InternalTagName::originalVersion());
-+    }
-+
-+    if (!(settings.showInViewFlags & VersionManagerSettings::ShowIntermediates))
-+    {
-+        m_excludeTagFilter << TagsCache::instance()->getOrCreateInternalTag(InternalTagName::intermediateVersion());
-+    }
-+
-+    m_includeTagFilter   = TagsCache::instance()->getOrCreateInternalTag(InternalTagName::currentVersion());
-+    m_exceptionTagFilter = TagsCache::instance()->getOrCreateInternalTag(InternalTagName::versionAlwaysVisible());
-+}
-+
-+void VersionImageFilterSettings::setExceptionList(const QList<qlonglong>& idList, const QString& id)
-+{
-+    if (idList.isEmpty())
-+    {
-+        m_exceptionLists.remove(id);
-+    }
-+    else
-+    {
-+        m_exceptionLists.insert(id, idList);
-+    }
-+}
-+
-+bool VersionImageFilterSettings::isFiltering() const
-+{
-+    return !m_excludeTagFilter.isEmpty();
-+}
-+
-+bool VersionImageFilterSettings::isFilteringByTags() const
-+{
-+    return isFiltering();
-+}
-+
-+// -------------------------------------------------------------------------------------------------
-+
-+GroupImageFilterSettings::GroupImageFilterSettings()
-+    : m_allOpen(false)
-+{
-+}
-+
-+bool GroupImageFilterSettings::operator==(const GroupImageFilterSettings& other) const
-+{
-+    return (m_allOpen    == other.m_allOpen &&
-+            m_openGroups == other.m_openGroups);
-+}
-+
-+bool GroupImageFilterSettings::matches(const ImageInfo& info) const
-+{
-+    if (m_allOpen)
-+    {
-+        return true;
-+    }
-+
-+    if (info.isGrouped())
-+    {
-+        return m_openGroups.contains(info.groupImage().id());
-+    }
-+    return true;
-+}
-+
-+void GroupImageFilterSettings::setOpen(qlonglong group, bool open)
-+{
-+    if (open)
-+    {
-+        m_openGroups << group;
-+    }
-+    else
-+    {
-+        m_openGroups.remove(group);
-+    }
-+}
-+
-+bool GroupImageFilterSettings::isOpen(qlonglong group) const
-+{
-+    return m_openGroups.contains(group);
-+}
-+
-+void GroupImageFilterSettings::setAllOpen(bool open)
-+{
-+    m_allOpen = open;
-+}
-+
-+bool GroupImageFilterSettings::isAllOpen() const
-+{
-+    return m_allOpen;
-+}
-+
-+bool GroupImageFilterSettings::isFiltering() const
-+{
-+    return !m_allOpen;
-+}
-+
-+DatabaseFields::Set GroupImageFilterSettings::watchFlags() const
-+{
-+    return DatabaseFields::ImageRelations;
-+}
-+
-+} // namespace Digikam
-diff --git a/libs/database/models/imagefiltersettings.h b/libs/database/models/imagefiltersettings.h
-new file mode 100644
-index 0000000..0e7beae
---- /dev/null
-+++ b/libs/database/models/imagefiltersettings.h
-@@ -0,0 +1,349 @@
-+/* ============================================================
-+ *
-+ * This file is a part of digiKam project
-+ * http://www.digikam.org
-+ *
-+ * Date        : 2009-03-05
-+ * Description : Filter values for use with ImageFilterModel
-+ *
-+ * Copyright (C) 2009-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
-+ * Copyright (C) 2011-2017 by Gilles Caulier <caulier dot gilles at gmail dot com>
-+ * Copyright (C)      2010 by Andi Clemens <andi dot clemens at gmail dot com>
-+ * Copyright (C)      2011 by Michael G. Hansen <mike at mghansen dot de>
-+ * Copyright (C)      2014 by Mohamed Anwer <m dot anwer at gmx dot com>
-+ *
-+ * This program is free software; you can redistribute it
-+ * and/or modify it under the terms of the GNU General
-+ * Public License as published by the Free Software Foundation;
-+ * either version 2, or (at your option)
-+ * any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * ============================================================ */
-+
-+#ifndef IMAGEFILTERSETTINGS_H
-+#define IMAGEFILTERSETTINGS_H
-+
-+// Qt includes
-+
-+#include <QHash>
-+#include <QList>
-+#include <QMap>
-+#include <QString>
-+#include <QSet>
-+#include <QUrl>
-+
-+// Local includes
-+
-+#include "searchtextbar.h"
-+#include "mimefilter.h"
-+#include "digikam_export.h"
-+
-+namespace Digikam
-+{
-+
-+class ImageInfo;
-+class VersionManagerSettings;
-+
-+namespace DatabaseFields
-+{
-+    class Set;
-+}
-+
-+// ---------------------------------------------------------------------------------------
-+
-+class DIGIKAM_DATABASE_EXPORT SearchTextFilterSettings : public SearchTextSettings
-+{
-+
-+public:
-+
-+    enum TextFilterFields
-+    {
-+        None             = 0x00,
-+        ImageName        = 0x01,
-+        ImageTitle       = 0x02,
-+        ImageComment     = 0x04,
-+        TagName          = 0x08,
-+        AlbumName        = 0x10,
-+        ImageAspectRatio = 0x20,
-+        ImagePixelSize   = 0x40,
-+        All              = ImageName | ImageTitle | ImageComment | TagName | AlbumName | ImageAspectRatio | ImagePixelSize
-+    };
-+
-+public:
-+
-+    SearchTextFilterSettings()
-+    {
-+        textFields = None;
-+    }
-+
-+    explicit SearchTextFilterSettings(const SearchTextSettings& settings)
-+    {
-+        caseSensitive = settings.caseSensitive;
-+        text          = settings.text;
-+        textFields    = None;
-+    }
-+
-+    TextFilterFields textFields;
-+};
-+
-+// ---------------------------------------------------------------------------------------
-+
-+class DIGIKAM_DATABASE_EXPORT ImageFilterSettings
-+{
-+public:
-+
-+    ImageFilterSettings();
-+
-+    /**
-+     *  Returns true if the given ImageInfo matches the filter criteria.
-+     *  Optionally, foundText is set to true if it matched by text search.
-+     */
-+    bool matches(const ImageInfo& info, bool* const foundText = 0) const;
-+
-+public:
-+
-+    /// --- Tags filter ---
-+
-+    /// Possible logical matching condition used to sort tags id.
-+    enum MatchingCondition
-+    {
-+        OrCondition,
-+        AndCondition
-+    };
-+
-+    void setTagFilter(const QList<int>& includedTags,
-+                      const QList<int>& excludedTags,
-+                      MatchingCondition matchingCond,
-+                      bool              showUnTagged,
-+                      const QList<int>& clTagIds,
-+                      const QList<int>& plTagIds);
-+
-+public:
-+
-+    /// --- Rating filter ---
-+
-+    /// Possible conditions used to filter rating: >=, =, <=
-+    enum RatingCondition
-+    {
-+        GreaterEqualCondition,
-+        EqualCondition,
-+        LessEqualCondition
-+    };
-+
-+    void setRatingFilter(int rating, RatingCondition ratingCond, bool isUnratedExcluded);
-+
-+public:
-+
-+    /// --- Date filter ---
-+    void setDayFilter(const QList<QDateTime>& days);
-+
-+public:
-+
-+    /// --- Text filter ---
-+    void setTextFilter(const SearchTextFilterSettings& settings);
-+    void setTagNames(const QHash<int, QString>& tagNameHash);
-+    void setAlbumNames(const QHash<int, QString>& albumNameHash);
-+
-+public:
-+
-+    /// --- Mime filter ---
-+    void setMimeTypeFilter(int mimeTypeFilter);
-+
-+public:
-+
-+    /// --- Geolocation filter
-+    enum GeolocationCondition
-+    {
-+        GeolocationNoFilter       = 0,
-+        GeolocationNoCoordinates  = 1 << 1,
-+        GeolocationHasCoordinates = 1 << 2
-+    };
-+
-+    void setGeolocationFilter(const GeolocationCondition& condition);
-+
-+public:
-+
-+    /// Returns if the day is a filter criteria
-+    bool isFilteringByDay()         const;
-+
-+    /// Returns if the type mime is a filter criteria
-+    bool isFilteringByTypeMime()    const;
-+
-+    /// Returns whether geolocation is a filter criteria
-+    bool isFilteringByGeolocation() const;
-+
-+    /// Returns if the rating is a filter criteria
-+    bool isFilteringByRating()      const;
-+
-+    /// Returns if the pick labels is a filter criteria
-+    bool isFilteringByPickLabels()  const;
-+
-+    /// Returns if the color labels is a filter criteria
-+    bool isFilteringByColorLabels() const;
-+
-+    /// Returns if the tag is a filter criteria
-+    bool isFilteringByTags()        const;
-+
-+    /// Returns if the text (including comment) is a filter criteria
-+    bool isFilteringByText()        const;
-+
-+    /// Returns if images will be filtered by these criteria at all
-+    bool isFiltering()              const;
-+
-+public:
-+
-+    /// --- URL whitelist filter
-+    void setUrlWhitelist(const QList<QUrl>& urlList, const QString& id);
-+
-+public:
-+
-+    /// --- ID whitelist filter
-+    void setIdWhitelist(const QList<qlonglong>& idList, const QString& id);
-+
-+public:
-+
-+    /// --- Change notification ---
-+
-+    /** Returns database fields a change in which would affect the current filtering.
-+     *  To find out if an image tag change affects filtering, test isFilteringByTags().
-+     *  The text filter will also be affected by changes in tags and album names.
-+     */
-+    DatabaseFields::Set watchFlags() const;
-+
-+private:
-+
-+    /**
-+     * @brief Returns whether some internal filtering (whitelist by id or URL) or normal filtering is going on
-+     */
-+    bool isFilteringInternally() const;
-+
-+private:
-+
-+    /// --- Tags filter ---
-+    bool                             m_untaggedFilter;
-+    QList<int>                       m_includeTagFilter;
-+    QList<int>                       m_excludeTagFilter;
-+    MatchingCondition                m_matchingCond;
-+    QList<int>                       m_colorLabelTagFilter;
-+    QList<int>                       m_pickLabelTagFilter;
-+
-+    /// --- Rating filter ---
-+    int                              m_ratingFilter;
-+    RatingCondition                  m_ratingCond;
-+    bool                             m_isUnratedExcluded;
-+
-+    /// --- Date filter ---
-+    QMap<QDateTime, bool>            m_dayFilter;
-+
-+    /// --- Text filter ---
-+    SearchTextFilterSettings         m_textFilterSettings;
-+
-+    /// Helpers for text search: Set these if you want to search album or tag names with text search
-+    QHash<int, QString>              m_tagNameHash;
-+    QHash<int, QString>              m_albumNameHash;
-+
-+    /// --- Mime filter ---
-+    MimeFilter::TypeMimeFilter       m_mimeTypeFilter;
-+
-+    /// --- Geolocation filter
-+    GeolocationCondition             m_geolocationCondition;
-+
-+    /// --- URL whitelist filter
-+    QHash<QString,QList<QUrl>>        m_urlWhitelists;
-+
-+    /// --- ID whitelist filter
-+    QHash<QString,QList<qlonglong> > m_idWhitelists;
-+};
-+
-+// ---------------------------------------------------------------------------------------
-+
-+class DIGIKAM_DATABASE_EXPORT VersionImageFilterSettings
-+{
-+public:
-+
-+    VersionImageFilterSettings();
-+    explicit VersionImageFilterSettings(const VersionManagerSettings& settings);
-+
-+    bool operator==(const VersionImageFilterSettings& other) const;
-+
-+    /**
-+     *  Returns true if the given ImageInfo matches the filter criteria.
-+     */
-+    bool matches(const ImageInfo& info) const;
-+
-+    bool isHiddenBySettings(const ImageInfo& info)   const;
-+    bool isExemptedBySettings(const ImageInfo& info) const;
-+
-+    /// --- Tags filter ---
-+
-+    void setVersionManagerSettings(const VersionManagerSettings& settings);
-+
-+    /**
-+     * Add list with exceptions: These images will be exempted from filtering by this filter
-+     */
-+    void setExceptionList(const QList<qlonglong>& idlist, const QString& id);
-+
-+    /// Returns if images will be filtered by these criteria at all
-+    bool isFiltering() const;
-+
-+    /// Returns if the tag is a filter criteria
-+    bool isFilteringByTags() const;
-+
-+    /// DatabaseFields::Set watchFlags() const: Would return 0
-+
-+protected:
-+
-+    QList<int>                       m_excludeTagFilter;
-+    int                              m_includeTagFilter;
-+    int                              m_exceptionTagFilter;
-+    QHash<QString,QList<qlonglong> > m_exceptionLists;
-+};
-+
-+// ---------------------------------------------------------------------------------------
-+
-+class DIGIKAM_DATABASE_EXPORT GroupImageFilterSettings
-+{
-+public:
-+
-+    GroupImageFilterSettings();
-+
-+    bool operator==(const GroupImageFilterSettings& other) const;
-+
-+    /**
-+     *  Returns true if the given ImageInfo matches the filter criteria.
-+     */
-+    bool matches(const ImageInfo& info) const;
-+
-+    /**
-+     * Open or close a group.
-+     */
-+    void setOpen(qlonglong group, bool open);
-+    bool isOpen(qlonglong group) const;
-+
-+    /**
-+     * Open all groups
-+     */
-+    void setAllOpen(bool open);
-+    bool isAllOpen() const;
-+
-+    /// Returns if images will be filtered by these criteria at all
-+    bool isFiltering() const;
-+
-+    DatabaseFields::Set watchFlags() const;
-+
-+protected:
-+
-+    bool            m_allOpen;
-+    QSet<qlonglong> m_openGroups;
-+};
-+
-+} // namespace Digikam
-+
-+Q_DECLARE_METATYPE(Digikam::ImageFilterSettings::GeolocationCondition)
-+
-+#endif // IMAGEFILTERSETTINGS_H
-diff --git a/libs/database/models/imagelistmodel.cpp b/libs/database/models/imagelistmodel.cpp
-new file mode 100644
-index 0000000..fafce34
---- /dev/null
-+++ b/libs/database/models/imagelistmodel.cpp
-@@ -0,0 +1,70 @@
-+/* ============================================================
-+ *
-+ * This file is a part of digiKam project
-+ * http://www.digikam.org
-+ *
-+ * Date        : 2010-12-06
-+ * Description : An image model based on a static list
-+ *
-+ * Copyright (C) 2010-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
-+ *
-+ * This program is free software; you can redistribute it
-+ * and/or modify it under the terms of the GNU General
-+ * Public License as published by the Free Software Foundation;
-+ * either version 2, or (at your option)
-+ * any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * ============================================================ */
-+
-+#include "imagelistmodel.h"
-+
-+// Local includes
-+
-+#include "digikam_debug.h"
-+#include "coredbaccess.h"
-+#include "coredbchangesets.h"
-+#include "coredbwatch.h"
-+#include "imageinfo.h"
-+#include "imageinfolist.h"
-+
-+namespace Digikam
-+{
-+
-+ImageListModel::ImageListModel(QObject* parent)
-+    : ImageThumbnailModel(parent)
-+{
-+    connect(CoreDbAccess::databaseWatch(), SIGNAL(collectionImageChange(CollectionImageChangeset)),
-+            this, SLOT(slotCollectionImageChange(CollectionImageChangeset)));
-+}
-+
-+ImageListModel::~ImageListModel()
-+{
-+}
-+
-+void ImageListModel::slotCollectionImageChange(const CollectionImageChangeset& changeset)
-+{
-+    if (isEmpty())
-+    {
-+        return;
-+    }
-+
-+    switch (changeset.operation())
-+    {
-+        case CollectionImageChangeset::Added:
-+            break;
-+        case CollectionImageChangeset::Removed:
-+        case CollectionImageChangeset::RemovedAll:
-+            removeImageInfos(ImageInfoList(changeset.ids()));
-+            break;
-+
-+        default:
-+            break;
-+    }
-+}
-+
-+} // namespace Digikam
-diff --git a/libs/database/models/imagelistmodel.h b/libs/database/models/imagelistmodel.h
-new file mode 100644
-index 0000000..a225b1b
---- /dev/null
-+++ b/libs/database/models/imagelistmodel.h
-@@ -0,0 +1,63 @@
-+/* ============================================================
-+ *
-+ * This file is a part of digiKam project
-+ * http://www.digikam.org
-+ *
-+ * Date        : 2010-12-06
-+ * Description : An image model based on a static list
-+ *
-+ * Copyright (C) 2010-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
-+ *
-+ * This program is free software; you can redistribute it
-+ * and/or modify it under the terms of the GNU General
-+ * Public License as published by the Free Software Foundation;
-+ * either version 2, or (at your option)
-+ * any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * ============================================================ */
-+
-+#ifndef IMAGELISTMODEL_H
-+#define IMAGELISTMODEL_H
-+
-+// Local includes
-+
-+#include "imagethumbnailmodel.h"
-+#include "digikam_export.h"
-+
-+namespace Digikam
-+{
-+
-+class ImageChangeset;
-+class CollectionImageChangeset;
-+
-+class DIGIKAM_DATABASE_EXPORT ImageListModel : public ImageThumbnailModel
-+{
-+    Q_OBJECT
-+
-+public:
-+
-+    explicit ImageListModel(QObject* parent = 0);
-+    ~ImageListModel();
-+
-+    // NOTE: necessary methods to add and remove ImageInfos to the model are inherited from ImageModel
-+
-+Q_SIGNALS:
-+
-+    /**
-+     * Emitted when images are removed from the model because they are removed in the database
-+     */
-+    void imageInfosRemoved(const QList<ImageInfo>& infos);
-+
-+protected Q_SLOTS:
-+
-+    void slotCollectionImageChange(const CollectionImageChangeset& changeset);
-+};
-+
-+} // namespace Digikam
-+
-+#endif // IMAGELISTMODEL_H
-diff --git a/libs/database/models/imagemodel.cpp b/libs/database/models/imagemodel.cpp
-new file mode 100644
-index 0000000..41b43cf
---- /dev/null
-+++ b/libs/database/models/imagemodel.cpp
-@@ -0,0 +1,1368 @@
-+/* ============================================================
-+ *
-+ * This file is a part of digiKam project
-+ * http://www.digikam.org
-+ *
-+ * Date        : 2009-03-05
-+ * Description : Qt item model for database entries
-+ *
-+ * Copyright (C) 2009-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
-+ *
-+ * This program is free software; you can redistribute it
-+ * and/or modify it under the terms of the GNU General
-+ * Public License as published by the Free Software Foundation;
-+ * either version 2, or (at your option)
-+ * any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * ============================================================ */
-+
-+#include "imagemodel.h"
-+
-+// Qt includes
-+
-+#include <QHash>
-+#include <QItemSelection>
-+
-+// Local includes
-+
-+#include "digikam_debug.h"
-+#include "coredbchangesets.h"
-+#include "coredbfields.h"
-+#include "coredbwatch.h"
-+#include "imageinfo.h"
-+#include "imageinfolist.h"
-+#include "abstractitemdragdrophandler.h"
-+
-+namespace Digikam
-+{
-+
-+class ImageModel::Private
-+{
-+public:
-+
-+    Private()
-+    {
-+        preprocessor                = 0;
-+        keepFilePathCache           = false;
-+        sendRemovalSignals          = false;
-+        incrementalUpdater          = 0;
-+        refreshing                  = false;
-+        reAdding                    = false;
-+        incrementalRefreshRequested = false;
-+    }
-+
-+    ImageInfoList                       infos;
-+    QList<QVariant>                     extraValues;
-+    QHash<qlonglong, int>               idHash;
-+
-+    bool                                keepFilePathCache;
-+    QHash<QString, qlonglong>           filePathHash;
-+
-+    bool                                sendRemovalSignals;
-+
-+    QObject*                            preprocessor;
-+    bool                                refreshing;
-+    bool                                reAdding;
-+    bool                                incrementalRefreshRequested;
-+
-+    DatabaseFields::Set                 watchFlags;
-+
-+    class ImageModelIncrementalUpdater* incrementalUpdater;
-+
-+    ImageInfoList                       pendingInfos;
-+    QList<QVariant>                     pendingExtraValues;
-+
-+    inline bool isValid(const QModelIndex& index)
-+    {
-+        if (!index.isValid())
-+        {
-+            return false;
-+        }
-+
-+        if (index.row() < 0 || index.row() >= infos.size())
-+        {
-+            qCDebug(DIGIKAM_GENERAL_LOG) << "Invalid index" << index;
-+            return false;
-+        }
-+
-+        return true;
-+    }
-+    inline bool extraValueValid(const QModelIndex& index)
-+    {
-+        // we assume isValid() being called before, no duplicate checks
-+        if (index.row() >= extraValues.size())
-+        {
-+            qCDebug(DIGIKAM_GENERAL_LOG) << "Invalid index for extraData" << index;
-+            return false;
-+        }
-+
-+        return true;
-+    }
-+};
-+
-+typedef QPair<int, int> IntPair; // to make foreach macro happy
-+typedef QList<IntPair>  IntPairList;
-+
-+class ImageModelIncrementalUpdater
-+{
-+public:
-+
-+    explicit ImageModelIncrementalUpdater(ImageModel::Private* d);
-+
-+    void                  appendInfos(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues);
-+    void                  aboutToBeRemovedInModel(const IntPairList& aboutToBeRemoved);
-+    QList<IntPair>        oldIndexes();
-+
-+    static QList<IntPair> toContiguousPairs(const QList<int>& ids);
-+
-+public:
-+
-+    QHash<qlonglong, int> oldIds;
-+    QList<QVariant>       oldExtraValues;
-+    QList<ImageInfo>      newInfos;
-+    QList<QVariant>       newExtraValues;
-+    QList<IntPairList>    modelRemovals;
-+};
-+
-+ImageModel::ImageModel(QObject* parent)
-+    : QAbstractListModel(parent),
-+      d(new Private)
-+{
-+    connect(CoreDbAccess::databaseWatch(), SIGNAL(imageChange(ImageChangeset)),
-+            this, SLOT(slotImageChange(ImageChangeset)));
-+
-+    connect(CoreDbAccess::databaseWatch(), SIGNAL(imageTagChange(ImageTagChangeset)),
-+            this, SLOT(slotImageTagChange(ImageTagChangeset)));
-+}
-+
-+ImageModel::~ImageModel()
-+{
-+    delete d->incrementalUpdater;
-+    delete d;
-+}
-+
-+// ------------ Access methods -------------
-+
-+void ImageModel::setKeepsFilePathCache(bool keepCache)
-+{
-+    d->keepFilePathCache = keepCache;
-+}
-+
-+bool ImageModel::keepsFilePathCache() const
-+{
-+    return d->keepFilePathCache;
-+}
-+
-+bool ImageModel::isEmpty() const
-+{
-+    return d->infos.isEmpty();
-+}
-+
-+void ImageModel::setWatchFlags(const DatabaseFields::Set& set)
-+{
-+    d->watchFlags = set;
-+}
-+
-+ImageInfo ImageModel::imageInfo(const QModelIndex& index) const
-+{
-+    if (!d->isValid(index))
-+    {
-+        return ImageInfo();
-+    }
-+
-+    return d->infos.at(index.row());
-+}
-+
-+ImageInfo& ImageModel::imageInfoRef(const QModelIndex& index) const
-+{
-+    return d->infos[index.row()];
-+}
-+
-+qlonglong ImageModel::imageId(const QModelIndex& index) const
-+{
-+    if (!d->isValid(index))
-+    {
-+        return 0;
-+    }
-+
-+    return d->infos.at(index.row()).id();
-+}
-+
-+QList<ImageInfo> ImageModel::imageInfos(const QList<QModelIndex>& indexes) const
-+{
-+    QList<ImageInfo> infos;
-+
-+    foreach(const QModelIndex& index, indexes)
-+    {
-+        infos << imageInfo(index);
-+    }
-+
-+    return infos;
-+}
-+
-+QList<qlonglong> ImageModel::imageIds(const QList<QModelIndex>& indexes) const
-+{
-+    QList<qlonglong> ids;
-+
-+    foreach(const QModelIndex& index, indexes)
-+    {
-+        ids << imageId(index);
-+    }
-+
-+    return ids;
-+}
-+
-+ImageInfo ImageModel::imageInfo(int row) const
-+{
-+    if (row >= d->infos.size())
-+    {
-+        return ImageInfo();
-+    }
-+
-+    return d->infos.at(row);
-+}
-+
-+ImageInfo& ImageModel::imageInfoRef(int row) const
-+{
-+    return d->infos[row];
-+}
-+
-+qlonglong ImageModel::imageId(int row) const
-+{
-+    if (row < 0 || row >= d->infos.size())
-+    {
-+        return -1;
-+    }
-+
-+    return d->infos.at(row).id();
-+}
-+
-+QModelIndex ImageModel::indexForImageInfo(const ImageInfo& info) const
-+{
-+    return indexForImageId(info.id());
-+}
-+
-+QModelIndex ImageModel::indexForImageInfo(const ImageInfo& info, const QVariant& extraValue) const
-+{
-+    return indexForImageId(info.id(), extraValue);
-+}
-+
-+QList<QModelIndex> ImageModel::indexesForImageInfo(const ImageInfo& info) const
-+{
-+    return indexesForImageId(info.id());
-+}
-+
-+QModelIndex ImageModel::indexForImageId(qlonglong id) const
-+{
-+    int index = d->idHash.value(id, -1);
-+
-+    if (index != -1)
-+    {
-+        return createIndex(index, 0);
-+    }
-+
-+    return QModelIndex();
-+}
-+
-+QModelIndex ImageModel::indexForImageId(qlonglong id, const QVariant& extraValue) const
-+{
-+    if (d->extraValues.isEmpty())
-+        return indexForImageId(id);
-+
-+    QHash<qlonglong, int>::const_iterator it;
-+
-+    for (it = d->idHash.constFind(id); it != d->idHash.constEnd() && it.key() == id; ++it)
-+    {
-+        if (d->extraValues.at(it.value()) == extraValue)
-+            return createIndex(it.value(), 0);
-+    }
-+
-+    return QModelIndex();
-+}
-+
-+QList<QModelIndex> ImageModel::indexesForImageId(qlonglong id) const
-+{
-+    QList<QModelIndex> indexes;
-+    QHash<qlonglong, int>::const_iterator it;
-+
-+    for (it = d->idHash.constFind(id); it != d->idHash.constEnd() && it.key() == id; ++it)
-+    {
-+        indexes << createIndex(it.value(), 0);
-+    }
-+
-+    return indexes;
-+}
-+
-+int ImageModel::numberOfIndexesForImageInfo(const ImageInfo& info) const
-+{
-+    return numberOfIndexesForImageId(info.id());
-+}
-+
-+int ImageModel::numberOfIndexesForImageId(qlonglong id) const
-+{
-+    if (d->extraValues.isEmpty())
-+    {
-+        return 0;
-+    }
-+
-+    int count = 0;
-+    QHash<qlonglong,int>::const_iterator it;
-+
-+    for (it = d->idHash.constFind(id); it != d->idHash.constEnd() && it.key() == id; ++it)
-+    {
-+        ++count;
-+    }
-+
-+    return count;
-+}
-+
-+// static method
-+ImageInfo ImageModel::retrieveImageInfo(const QModelIndex& index)
-+{
-+    if (!index.isValid())
-+    {
-+        return ImageInfo();
-+    }
-+
-+    ImageModel* const model = index.data(ImageModelPointerRole).value<ImageModel*>();
-+    int row                 = index.data(ImageModelInternalId).toInt();
-+
-+    if (!model)
-+    {
-+        return ImageInfo();
-+    }
-+
-+    return model->imageInfo(row);
-+}
-+
-+// static method
-+qlonglong ImageModel::retrieveImageId(const QModelIndex& index)
-+{
-+    if (!index.isValid())
-+    {
-+        return 0;
-+    }
-+
-+    ImageModel* const model = index.data(ImageModelPointerRole).value<ImageModel*>();
-+    int row                 = index.data(ImageModelInternalId).toInt();
-+
-+    if (!model)
-+    {
-+        return 0;
-+    }
-+
-+    return model->imageId(row);
-+}
-+
-+QModelIndex ImageModel::indexForPath(const QString& filePath) const
-+{
-+    if (d->keepFilePathCache)
-+    {
-+        return indexForImageId(d->filePathHash.value(filePath));
-+    }
-+    else
-+    {
-+        const int size = d->infos.size();
-+
-+        for (int i=0; i<size; ++i)
-+        {
-+            if (d->infos.at(i).filePath() == filePath)
-+            {
-+                return createIndex(i, 0);
-+            }
-+        }
-+    }
-+
-+    return QModelIndex();
-+}
-+
-+QList<QModelIndex> ImageModel::indexesForPath(const QString& filePath) const
-+{
-+    if (d->keepFilePathCache)
-+    {
-+        return indexesForImageId(d->filePathHash.value(filePath));
-+    }
-+    else
-+    {
-+        QList<QModelIndex> indexes;
-+        const int size = d->infos.size();
-+
-+        for (int i=0; i<size; ++i)
-+        {
-+            if (d->infos.at(i).filePath() == filePath)
-+            {
-+                indexes << createIndex(i, 0);
-+            }
-+        }
-+
-+        return indexes;
-+    }
-+}
-+
-+ImageInfo ImageModel::imageInfo(const QString& filePath) const
-+{
-+    if (d->keepFilePathCache)
-+    {
-+        qlonglong id = d->filePathHash.value(filePath);
-+
-+        if (id)
-+        {
-+            int index = d->idHash.value(id, -1);
-+
-+            if (index != -1)
-+            {
-+                return d->infos.at(index);
-+            }
-+        }
-+    }
-+    else
-+    {
-+        foreach(const ImageInfo& info, d->infos)
-+        {
-+            if (info.filePath() == filePath)
-+            {
-+                return info;
-+            }
-+        }
-+    }
-+
-+    return ImageInfo();
-+}
-+
-+QList<ImageInfo> ImageModel::imageInfos(const QString& filePath) const
-+{
-+    QList<ImageInfo> infos;
-+
-+    if (d->keepFilePathCache)
-+    {
-+        qlonglong id = d->filePathHash.value(filePath);
-+
-+        if (id)
-+        {
-+            foreach(int index, d->idHash.values(id))
-+            {
-+                infos << d->infos.at(index);
-+            }
-+        }
-+    }
-+    else
-+    {
-+        foreach(const ImageInfo& info, d->infos)
-+        {
-+            if (info.filePath() == filePath)
-+            {
-+                infos << info;
-+            }
-+        }
-+    }
-+
-+    return infos;
-+}
-+
-+void ImageModel::addImageInfo(const ImageInfo& info)
-+{
-+    addImageInfos(QList<ImageInfo>() << info, QList<QVariant>());
-+}
-+
-+void ImageModel::addImageInfos(const QList<ImageInfo>& infos)
-+{
-+    addImageInfos(infos, QList<QVariant>());
-+}
-+
-+void ImageModel::addImageInfos(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues)
-+{
-+    if (infos.isEmpty())
-+    {
-+        return;
-+    }
-+
-+    if (d->incrementalUpdater)
-+    {
-+        d->incrementalUpdater->appendInfos(infos, extraValues);
-+    }
-+    else
-+    {
-+        appendInfos(infos, extraValues);
-+    }
-+}
-+
-+void ImageModel::addImageInfoSynchronously(const ImageInfo& info)
-+{
-+    addImageInfosSynchronously(QList<ImageInfo>() << info, QList<QVariant>());
-+}
-+
-+void ImageModel::addImageInfosSynchronously(const QList<ImageInfo>& infos)
-+{
-+    addImageInfos(infos, QList<QVariant>());
-+}
-+
-+void ImageModel::addImageInfosSynchronously(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues)
-+{
-+    if (infos.isEmpty())
-+    {
-+        return;
-+    }
-+
-+    publiciseInfos(infos, extraValues);
-+    emit processAdded(infos, extraValues);
-+}
-+
-+void ImageModel::ensureHasImageInfo(const ImageInfo& info)
-+{
-+    ensureHasImageInfos(QList<ImageInfo>() << info, QList<QVariant>());
-+}
-+
-+void ImageModel::ensureHasImageInfos(const QList<ImageInfo>& infos)
-+{
-+    ensureHasImageInfos(infos, QList<QVariant>());
-+}
-+
-+void ImageModel::ensureHasImageInfos(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues)
-+{
-+    if (extraValues.isEmpty())
-+    {
-+        if (!d->pendingExtraValues.isEmpty())
-+        {
-+            qCDebug(DIGIKAM_GENERAL_LOG) << "ExtraValue / No Extra Value mismatch. Ignoring added infos.";
-+            return;
-+        }
-+    }
-+    else
-+    {
-+        if (d->pendingInfos.size() != d->pendingExtraValues.size())
-+        {
-+            qCDebug(DIGIKAM_GENERAL_LOG) << "ExtraValue / No Extra Value mismatch. Ignoring added infos.";
-+            return;
-+        }
-+    }
-+
-+    d->pendingInfos << infos;
-+    d->pendingExtraValues << extraValues;
-+    cleanSituationChecks();
-+}
-+
-+void ImageModel::clearImageInfos()
-+{
-+    d->infos.clear();
-+    d->extraValues.clear();
-+    d->idHash.clear();
-+    d->filePathHash.clear();
-+    delete d->incrementalUpdater;
-+    d->incrementalUpdater          = 0;
-+    d->pendingInfos.clear();
-+    d->pendingExtraValues.clear();
-+    d->refreshing                  = false;
-+    d->reAdding                    = false;
-+    d->incrementalRefreshRequested = false;
-+
-+    beginResetModel();
-+    endResetModel();
-+
-+    imageInfosCleared();
-+}
-+
-+void ImageModel::setImageInfos(const QList<ImageInfo>& infos)
-+{
-+    clearImageInfos();
-+    addImageInfos(infos);
-+}
-+
-+QList<ImageInfo> ImageModel::imageInfos() const
-+{
-+    return d->infos;
-+}
-+
-+QList<qlonglong> ImageModel::imageIds() const
-+{
-+    return d->idHash.keys();
-+}
-+
-+bool ImageModel::hasImage(qlonglong id) const
-+{
-+    return d->idHash.contains(id);
-+}
-+
-+bool ImageModel::hasImage(const ImageInfo& info) const
-+{
-+    return d->idHash.contains(info.id());
-+}
-+
-+bool ImageModel::hasImage(const ImageInfo& info, const QVariant& extraValue) const
-+{
-+    return hasImage(info.id(), extraValue);
-+}
-+
-+bool ImageModel::hasImage(qlonglong id, const QVariant& extraValue) const
-+{
-+    if (d->extraValues.isEmpty())
-+        return hasImage(id);
-+
-+    QHash<qlonglong, int>::const_iterator it;
-+
-+    for (it = d->idHash.constFind(id); it != d->idHash.constEnd() && it.key() == id; ++it)
-+    {
-+        if (d->extraValues.at(it.value()) == extraValue)
-+            return true;
-+    }
-+
-+    return false;;
-+}
-+
-+QList<ImageInfo> ImageModel::uniqueImageInfos() const
-+{
-+    if (d->extraValues.isEmpty())
-+    {
-+        return d->infos;
-+    }
-+
-+    QList<ImageInfo> uniqueInfos;
-+    const int size = d->infos.size();
-+
-+    for (int i=0; i<size; ++i)
-+    {
-+        const ImageInfo& info = d->infos.at(i);
-+
-+        if (d->idHash.value(info.id()) == i)
-+        {
-+            uniqueInfos << info;
-+        }
-+    }
-+
-+    return uniqueInfos;
-+}
-+
-+void ImageModel::emitDataChangedForAll()
-+{
-+    if (d->infos.isEmpty())
-+    {
-+        return;
-+    }
-+
-+    QModelIndex first = createIndex(0, 0);
-+    QModelIndex last  = createIndex(d->infos.size() - 1, 0);
-+    emit dataChanged(first, last);
-+}
-+
-+void ImageModel::emitDataChangedForSelection(const QItemSelection& selection)
-+{
-+    if (!selection.isEmpty())
-+    {
-+        foreach(const QItemSelectionRange& range, selection)
-+        {
-+            emit dataChanged(range.topLeft(), range.bottomRight());
-+        }
-+    }
-+}
-+
-+void ImageModel::ensureHasGroupedImages(const ImageInfo& groupLeader)
-+{
-+    ensureHasImageInfos(groupLeader.groupedImages());
-+}
-+
-+// ------------ Preprocessing -------------
-+
-+void ImageModel::setPreprocessor(QObject* preprocessor)
-+{
-+    unsetPreprocessor(d->preprocessor);
-+    d->preprocessor = preprocessor;
-+}
-+
-+void ImageModel::unsetPreprocessor(QObject* preprocessor)
-+{
-+    if (preprocessor && d->preprocessor == preprocessor)
-+    {
-+        disconnect(this, SIGNAL(preprocess(QList<ImageInfo>,QList<QVariant>)), 0, 0);
-+        disconnect(d->preprocessor, 0, this, SLOT(reAddImageInfos(QList<ImageInfo>,QList<QVariant>)));
-+        disconnect(d->preprocessor, 0, this, SLOT(reAddingFinished()));
-+    }
-+}
-+
-+void ImageModel::appendInfos(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues)
-+{
-+    if (infos.isEmpty())
-+    {
-+        return;
-+    }
-+
-+    if (d->preprocessor)
-+    {
-+        d->reAdding = true;
-+        emit preprocess(infos, extraValues);
-+    }
-+    else
-+    {
-+        publiciseInfos(infos, extraValues);
-+    }
-+}
-+
-+void ImageModel::appendInfosChecked(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues)
-+{
-+    // This method does deduplication. It is private because in context of readding or refreshing it is of no use.
-+
-+    if (extraValues.isEmpty())
-+    {
-+        QList<ImageInfo> checkedInfos;
-+
-+        foreach (const ImageInfo& info, infos)
-+        {
-+            if (!hasImage(info))
-+            {
-+                checkedInfos << info;
-+            }
-+        }
-+
-+        appendInfos(checkedInfos, QList<QVariant>());
-+    }
-+    else
-+    {
-+        QList<ImageInfo> checkedInfos;
-+        QList<QVariant>  checkedExtraValues;
-+        const int size = infos.size();
-+
-+        for (int i=0; i<size; i++)
-+        {
-+            if (!hasImage(infos[i], extraValues[i]))
-+            {
-+                checkedInfos << infos[i];
-+                checkedExtraValues << extraValues[i];
-+            }
-+        }
-+
-+        appendInfos(checkedInfos, checkedExtraValues);
-+    }
-+}
-+
-+void ImageModel::reAddImageInfos(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues)
-+{
-+    // addImageInfos -> appendInfos -> preprocessor -> reAddImageInfos
-+    publiciseInfos(infos, extraValues);
-+}
-+
-+void ImageModel::reAddingFinished()
-+{
-+    d->reAdding = false;
-+    cleanSituationChecks();
-+}
-+
-+void ImageModel::startRefresh()
-+{
-+    d->refreshing = true;
-+}
-+
-+void ImageModel::finishRefresh()
-+{
-+    d->refreshing = false;
-+    cleanSituationChecks();
-+}
-+
-+bool ImageModel::isRefreshing() const
-+{
-+    return d->refreshing;
-+}
-+
-+void ImageModel::cleanSituationChecks()
-+{
-+    // For starting an incremental refresh we want a clear situation:
-+    // Any remaining batches from non-incremental refreshing subclasses have been received in appendInfos(),
-+    // any batches sent to preprocessor for re-adding have been re-added.
-+    if (d->refreshing || d->reAdding)
-+    {
-+        return;
-+    }
-+
-+    if (!d->pendingInfos.isEmpty())
-+    {
-+        appendInfosChecked(d->pendingInfos, d->pendingExtraValues);
-+        d->pendingInfos.clear();
-+        d->pendingExtraValues.clear();
-+        cleanSituationChecks();
-+        return;
-+    }
-+
-+    if (d->incrementalRefreshRequested)
-+    {
-+        d->incrementalRefreshRequested = false;
-+        emit readyForIncrementalRefresh();
-+    }
-+    else
-+    {
-+        emit allRefreshingFinished();
-+    }
-+}
-+
-+void ImageModel::publiciseInfos(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues)
-+{
-+    if (infos.isEmpty())
-+    {
-+        return;
-+    }
-+
-+    Q_ASSERT(infos.size() == extraValues.size() || (extraValues.isEmpty() && d->extraValues.isEmpty()));
-+
-+    emit imageInfosAboutToBeAdded(infos);
-+    const int firstNewIndex = d->infos.size();
-+    const int lastNewIndex  = d->infos.size() + infos.size() - 1;
-+    beginInsertRows(QModelIndex(), firstNewIndex, lastNewIndex);
-+    d->infos << infos;
-+    d->extraValues << extraValues;
-+
-+    for (int i=firstNewIndex; i<=lastNewIndex; ++i)
-+    {
-+        const ImageInfo& info = d->infos.at(i);
-+        qlonglong id          = info.id();
-+        d->idHash.insertMulti(id, i);
-+
-+        if (d->keepFilePathCache)
-+        {
-+            d->filePathHash[info.filePath()] = id;
-+        }
-+    }
-+
-+    endInsertRows();
-+    emit imageInfosAdded(infos);
-+}
-+
-+void ImageModel::requestIncrementalRefresh()
-+{
-+    if (d->reAdding)
-+    {
-+        d->incrementalRefreshRequested = true;
-+    }
-+    else
-+    {
-+        emit readyForIncrementalRefresh();
-+    }
-+}
-+
-+bool ImageModel::hasIncrementalRefreshPending() const
-+{
-+    return d->incrementalRefreshRequested;
-+}
-+
-+void ImageModel::startIncrementalRefresh()
-+{
-+    delete d->incrementalUpdater;
-+
-+    d->incrementalUpdater = new ImageModelIncrementalUpdater(d);
-+}
-+
-+void ImageModel::finishIncrementalRefresh()
-+{
-+    if (!d->incrementalUpdater)
-+    {
-+        return;
-+    }
-+
-+    // remove old entries
-+    QList<QPair<int, int> > pairs = d->incrementalUpdater->oldIndexes();
-+    removeRowPairs(pairs);
-+
-+    // add new indexes
-+    appendInfos(d->incrementalUpdater->newInfos, d->incrementalUpdater->newExtraValues);
-+
-+    delete d->incrementalUpdater;
-+    d->incrementalUpdater = 0;
-+}
-+
-+void ImageModel::removeIndex(const QModelIndex& index)
-+{
-+    removeIndexes(QList<QModelIndex>() << index);
-+}
-+
-+void ImageModel::removeIndexes(const QList<QModelIndex>& indexes)
-+{
-+    QList<int> listIndexes;
-+
-+    foreach(const QModelIndex& index, indexes)
-+    {
-+        if (d->isValid(index))
-+        {
-+            listIndexes << index.row();
-+        }
-+    }
-+
-+    if (listIndexes.isEmpty())
-+    {
-+        return;
-+    }
-+
-+    removeRowPairsWithCheck(ImageModelIncrementalUpdater::toContiguousPairs(listIndexes));
-+}
-+
-+void ImageModel::removeImageInfo(const ImageInfo& info)
-+{
-+    removeImageInfos(QList<ImageInfo>() << info);
-+}
-+
-+void ImageModel::removeImageInfos(const QList<ImageInfo>& infos)
-+{
-+    QList<int> listIndexes;
-+
-+    foreach(const ImageInfo& info, infos)
-+    {
-+        QModelIndex index = indexForImageId(info.id());
-+
-+        if (index.isValid())
-+        {
-+            listIndexes << index.row();
-+        }
-+    }
-+    removeRowPairsWithCheck(ImageModelIncrementalUpdater::toContiguousPairs(listIndexes));
-+}
-+
-+void ImageModel::removeImageInfos(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues)
-+{
-+    if (extraValues.isEmpty())
-+    {
-+        removeImageInfos(infos);
-+        return;
-+    }
-+
-+    QList<int> listIndexes;
-+
-+    for (int i=0; i<infos.size(); ++i)
-+    {
-+        QModelIndex index = indexForImageId(infos.at(i).id(), extraValues.at(i));
-+
-+        if (index.isValid())
-+        {
-+            listIndexes << index.row();
-+        }
-+    }
-+
-+    removeRowPairsWithCheck(ImageModelIncrementalUpdater::toContiguousPairs(listIndexes));
-+}
-+
-+void ImageModel::setSendRemovalSignals(bool send)
-+{
-+    d->sendRemovalSignals = send;
-+}
-+
-+template <class List, typename T>
-+static bool pairsContain(const List& list, T value)
-+{
-+    typename List::const_iterator middle;
-+    typename List::const_iterator begin = list.begin();
-+    typename List::const_iterator end   = list.end();
-+    int n                               = int(end - begin);
-+    int half;
-+
-+    while (n > 0)
-+    {
-+        half   = n >> 1;
-+        middle = begin + half;
-+
-+        if (middle->first <= value && middle->second >= value)
-+        {
-+            return true;
-+        }
-+        else if (middle->second < value)
-+        {
-+            begin = middle + 1;
-+            n     -= half + 1;
-+        }
-+        else
-+        {
-+            n = half;
-+        }
-+    }
-+
-+    return false;
-+}
-+
-+void ImageModel::removeRowPairsWithCheck(const QList<QPair<int, int> >& toRemove)
-+{
-+    if (d->incrementalUpdater)
-+    {
-+        d->incrementalUpdater->aboutToBeRemovedInModel(toRemove);
-+    }
-+
-+    removeRowPairs(toRemove);
-+}
-+
-+void ImageModel::removeRowPairs(const QList<QPair<int, int> >& toRemove)
-+{
-+    if (toRemove.isEmpty())
-+    {
-+        return;
-+    }
-+
-+    // Remove old indexes
-+    // Keep in mind that when calling beginRemoveRows all structures announced to be removed
-+    // must still be valid, and this includes our hashes as well, which limits what we can optimize
-+
-+    int removedRows = 0, offset = 0;
-+    typedef QPair<int, int> IntPair; // to make foreach macro happy
-+
-+    foreach(const IntPair& pair, toRemove)
-+    {
-+        const int begin = pair.first - offset;
-+        const int end   = pair.second - offset; // inclusive
-+        removedRows     = end - begin + 1;
-+
-+        // when removing from the list, all subsequent indexes are affected
-+        offset += removedRows;
-+
-+        QList<ImageInfo> removedInfos;
-+
-+        if (d->sendRemovalSignals)
-+        {
-+            qCopy(d->infos.begin() + begin, d->infos.begin() + end, removedInfos.begin());
-+            emit imageInfosAboutToBeRemoved(removedInfos);
-+        }
-+
-+        imageInfosAboutToBeRemoved(begin, end);
-+        beginRemoveRows(QModelIndex(), begin, end);
-+
-+        // update idHash - which points to indexes of d->infos, and these change now!
-+        QHash<qlonglong, int>::iterator it;
-+
-+        for (it = d->idHash.begin(); it != d->idHash.end(); )
-+        {
-+            if (it.value() >= begin)
-+            {
-+                if (it.value() > end)
-+                {
-+                    // after the removed interval: adjust index
-+                    it.value() -= removedRows;
-+                }
-+                else
-+                {
-+                    // in the removed interval
-+                    it = d->idHash.erase(it);
-+                    continue;
-+                }
-+            }
-+
-+            ++it;
-+        }
-+
-+        // remove from list
-+        d->infos.erase(d->infos.begin() + begin, d->infos.begin() + (end + 1));
-+
-+        if (!d->extraValues.isEmpty())
-+        {
-+            d->extraValues.erase(d->extraValues.begin() + begin, d->extraValues.begin() + (end + 1));
-+        }
-+
-+        endRemoveRows();
-+
-+        if (d->sendRemovalSignals)
-+        {
-+            emit imageInfosRemoved(removedInfos);
-+        }
-+    }
-+
-+    // tidy up: remove old indexes from file path hash now
-+    if (d->keepFilePathCache)
-+    {
-+        QHash<QString, qlonglong>::iterator it;
-+
-+        for (it = d->filePathHash.begin(); it != d->filePathHash.end(); )
-+        {
-+            if (pairsContain(toRemove, it.value()))
-+            {
-+                it = d->filePathHash.erase(it);
-+            }
-+            else
-+            {
-+                ++it;
-+            }
-+        }
-+    }
-+}
-+
-+ImageModelIncrementalUpdater::ImageModelIncrementalUpdater(ImageModel::Private* d)
-+{
-+    oldIds         = d->idHash;
-+    oldExtraValues = d->extraValues;
-+}
-+
-+void ImageModelIncrementalUpdater::appendInfos(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues)
-+{
-+    if (extraValues.isEmpty())
-+    {
-+        foreach(const ImageInfo& info, infos)
-+        {
-+            QHash<qlonglong,int>::iterator it = oldIds.find(info.id());
-+
-+            if (it != oldIds.end())
-+            {
-+                oldIds.erase(it);
-+            }
-+            else
-+            {
-+                newInfos << info;
-+            }
-+        }
-+    }
-+    else
-+    {
-+        for (int i=0; i<infos.size(); ++i)
-+        {
-+            const ImageInfo& info = infos.at(i);
-+            bool found            = false;
-+            QHash<qlonglong,int>::iterator it;
-+
-+            for (it = oldIds.find(info.id()); it != oldIds.end() && it.key() == info.id(); ++it)
-+            {
-+                // first check is for bug #262596. Not sure if needed.
-+                if (it.value() < oldExtraValues.size() && extraValues.at(i) == oldExtraValues.at(it.value()))
-+                {
-+                    found = true;
-+                    break;
-+                }
-+            }
-+
-+            if (found)
-+            {
-+                oldIds.erase(it);
-+                // do not erase from oldExtraValues - oldIds is a hash id -> index.
-+            }
-+            else
-+            {
-+                newInfos << info;
-+                newExtraValues << extraValues.at(i);
-+            }
-+        }
-+    }
-+}
-+
-+void ImageModelIncrementalUpdater::aboutToBeRemovedInModel(const IntPairList& toRemove)
-+{
-+    modelRemovals << toRemove;
-+}
-+
-+QList<QPair<int, int> > ImageModelIncrementalUpdater::oldIndexes()
-+{
-+    // first, apply all changes to indexes by direct removal in model
-+    // while the updater was active
-+    foreach(const IntPairList& list, modelRemovals)
-+    {
-+        int removedRows = 0, offset = 0;
-+
-+        foreach(const IntPair& pair, list)
-+        {
-+            const int begin = pair.first - offset;
-+            const int end   = pair.second - offset; // inclusive
-+            removedRows     = end - begin + 1;
-+
-+            // when removing from the list, all subsequent indexes are affected
-+            offset += removedRows;
-+
-+            // update idHash - which points to indexes of d->infos, and these change now!
-+            QHash<qlonglong, int>::iterator it;
-+
-+            for (it = oldIds.begin(); it != oldIds.end(); )
-+            {
-+                if (it.value() >= begin)
-+                {
-+                    if (it.value() > end)
-+                    {
-+                        // after the removed interval: adjust index
-+                        it.value() -= removedRows;
-+                    }
-+                    else
-+                    {
-+                        // in the removed interval
-+                        it = oldIds.erase(it);
-+                        continue;
-+                    }
-+                }
-+
-+                ++it;
-+            }
-+        }
-+    }
-+
-+    modelRemovals.clear();
-+
-+    return toContiguousPairs(oldIds.values());
-+}
-+
-+QList<QPair<int, int> > ImageModelIncrementalUpdater::toContiguousPairs(const QList<int>& unsorted)
-+{
-+    // Take the given indices and return them as contiguous pairs [begin, end]
-+
-+    QList<QPair<int, int> > pairs;
-+
-+    if (unsorted.isEmpty())
-+    {
-+        return pairs;
-+    }
-+
-+    QList<int> indices(unsorted);
-+    qSort(indices);
-+
-+    QPair<int, int> pair(indices.first(), indices.first());
-+
-+    for (int i=1; i<indices.size(); ++i)
-+    {
-+        const int &index = indices.at(i);
-+
-+        if (index == pair.second + 1)
-+        {
-+            pair.second = index;
-+            continue;
-+        }
-+
-+        pairs << pair; // insert last pair
-+        pair.first  = index;
-+        pair.second = index;
-+    }
-+
-+    pairs << pair;
-+
-+    return pairs;
-+}
-+
-+// ------------ QAbstractItemModel implementation -------------
-+
-+QVariant ImageModel::data(const QModelIndex& index, int role) const
-+{
-+    if (!d->isValid(index))
-+    {
-+        return QVariant();
-+    }
-+
-+    switch (role)
-+    {
-+        case Qt::DisplayRole:
-+        case Qt::ToolTipRole:
-+            return d->infos.at(index.row()).name();
-+
-+        case ImageModelPointerRole:
-+            return QVariant::fromValue(const_cast<ImageModel*>(this));
-+
-+        case ImageModelInternalId:
-+            return index.row();
-+
-+        case CreationDateRole:
-+            return d->infos.at(index.row()).dateTime();
-+
-+        case ExtraDataRole:
-+
-+            if (d->extraValueValid(index))
-+            {
-+                return d->extraValues.at(index.row());
-+            }
-+            else
-+            {
-+                return QVariant();
-+            }
-+
-+        case ExtraDataDuplicateCount:
-+        {
-+            qlonglong id = d->infos.at(index.row()).id();
-+            return numberOfIndexesForImageId(id);
-+        }
-+    }
-+
-+    return QVariant();
-+}
-+
-+QVariant ImageModel::headerData(int section, Qt::Orientation orientation, int role) const
-+{
-+    Q_UNUSED(section)
-+    Q_UNUSED(orientation)
-+    Q_UNUSED(role)
-+    return QVariant();
-+}
-+
-+int ImageModel::rowCount(const QModelIndex& parent) const
-+{
-+    if (parent.isValid())
-+    {
-+        return 0;
-+    }
-+
-+    return d->infos.size();
-+}
-+
-+Qt::ItemFlags ImageModel::flags(const QModelIndex& index) const
-+{
-+    if (!d->isValid(index))
-+    {
-+        return 0;
-+    }
-+
-+    Qt::ItemFlags f = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
-+
-+    f |= dragDropFlags(index);
-+
-+    return f;
-+}
-+
-+QModelIndex ImageModel::index(int row, int column, const QModelIndex& parent) const
-+{
-+    if (column != 0 || row < 0 || parent.isValid() || row >= d->infos.size())
-+    {
-+        return QModelIndex();
-+    }
-+
-+    return createIndex(row, 0);
-+}
-+
-+// ------------ Database watch -------------
-+
-+void ImageModel::slotImageChange(const ImageChangeset& changeset)
-+{
-+    if (d->infos.isEmpty())
-+    {
-+        return;
-+    }
-+
-+    if (d->watchFlags & changeset.changes())
-+    {
-+        QItemSelection items;
-+
-+        foreach(const qlonglong& id, changeset.ids())
-+        {
-+            QModelIndex index = indexForImageId(id);
-+
-+            if (index.isValid())
-+            {
-+                items.select(index, index);
-+            }
-+        }
-+
-+        if (!items.isEmpty())
-+        {
-+            emitDataChangedForSelection(items);
-+            emit imageChange(changeset, items);
-+        }
-+    }
-+}
-+
-+void ImageModel::slotImageTagChange(const ImageTagChangeset& changeset)
-+{
-+    if (d->infos.isEmpty())
-+    {
-+        return;
-+    }
-+
-+    QItemSelection items;
-+
-+    foreach(const qlonglong& id, changeset.ids())
-+    {
-+        QModelIndex index = indexForImageId(id);
-+
-+        if (index.isValid())
-+        {
-+            items.select(index, index);
-+        }
-+    }
-+
-+    if (!items.isEmpty())
-+    {
-+        emitDataChangedForSelection(items);
-+        emit imageTagChange(changeset, items);
-+    }
-+}
-+
-+} // namespace Digikam
-diff --git a/libs/database/models/imagemodel.h b/libs/database/models/imagemodel.h
-new file mode 100644
-index 0000000..dcf94c2
---- /dev/null
-+++ b/libs/database/models/imagemodel.h
-@@ -0,0 +1,364 @@
-+/* ============================================================
-+ *
-+ * This file is a part of digiKam project
-+ * http://www.digikam.org
-+ *
-+ * Date        : 2009-03-05
-+ * Description : Qt item model for database entries
-+ *
-+ * Copyright (C) 2009-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
-+ *
-+ * This program is free software; you can redistribute it
-+ * and/or modify it under the terms of the GNU General
-+ * Public License as published by the Free Software Foundation;
-+ * either version 2, or (at your option)
-+ * any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * ============================================================ */
-+
-+#ifndef IMAGEMODEL_H
-+#define IMAGEMODEL_H
-+
-+// Qt includes
-+
-+#include <QAbstractListModel>
-+
-+// Local includes
-+
-+#include "dragdropimplementations.h"
-+#include "imageinfo.h"
-+#include "digikam_export.h"
-+
-+class QItemSelection;
-+
-+namespace Digikam
-+{
-+
-+class ImageChangeset;
-+class ImageTagChangeset;
-+
-+namespace DatabaseFields
-+{
-+class Set;
-+}
-+
-+class DIGIKAM_DATABASE_EXPORT ImageModel : public QAbstractListModel, public DragDropModelImplementation
-+{
-+    Q_OBJECT
-+
-+public:
-+
-+    enum ImageModelRoles
-+    {
-+        /// An ImageModel* pointer to this model
-+        ImageModelPointerRole   = Qt::UserRole,
-+        ImageModelInternalId    = Qt::UserRole + 1,
-+        /// Returns a thumbnail pixmap. May be implemented by subclasses.
-+        /// Returns either a valid pixmap or a null QVariant.
-+        ThumbnailRole           = Qt::UserRole + 2,
-+        /// Returns a QDateTime with the creation date
-+        CreationDateRole        = Qt::UserRole + 3,
-+        /// Return (optional) extraData field
-+        ExtraDataRole           = Qt::UserRole + 5,
-+        /// Returns the number of duplicate indexes for the same image id
-+        ExtraDataDuplicateCount = Qt::UserRole + 6,
-+
-+        // Roles which are defined here but not implemented by ImageModel
-+        /// Returns position of item in Left Light Table preview.
-+        LTLeftPanelRole         = Qt::UserRole + 50,
-+        /// Returns position of item in Right Light Table preview.
-+        LTRightPanelRole        = Qt::UserRole + 51,
-+
-+        // For use by subclasses
-+        SubclassRoles           = Qt::UserRole + 100,
-+        // For use by filter models
-+        FilterModelRoles        = Qt::UserRole + 500
-+    };
-+
-+public:
-+
-+    explicit ImageModel(QObject* parent = 0);
-+    ~ImageModel();
-+
-+    /** If a cache is kept, lookup by file path is fast,
-+     *  without a cache it is O(n). Default is false.
-+     */
-+    void setKeepsFilePathCache(bool keepCache);
-+    bool keepsFilePathCache() const;
-+
-+    /** Set a set of database fields to watch.
-+     *  If either of these is changed, dataChanged() will be emitted.
-+     *  Default is no flag (no signal will be emitted).
-+     */
-+    void setWatchFlags(const DatabaseFields::Set& set);
-+
-+    /** Returns the ImageInfo object, reference or image id from the underlying data
-+     *  pointed to by the index.
-+     *  If the index is not valid, imageInfo will return a null ImageInfo, imageId will
-+     *  return 0, imageInfoRef must not be called with an invalid index.
-+     */
-+    ImageInfo        imageInfo(const QModelIndex& index) const;
-+    ImageInfo&       imageInfoRef(const QModelIndex& index) const;
-+    qlonglong        imageId(const QModelIndex& index) const;
-+    QList<ImageInfo> imageInfos(const QList<QModelIndex>& indexes) const;
-+    QList<qlonglong> imageIds(const QList<QModelIndex>& indexes) const;
-+
-+    /** Returns the ImageInfo object, reference or image id from the underlying data
-+     *  of the given row (parent is the invalid QModelIndex, column is 0).
-+     *  Note that imageInfoRef will crash if index is invalid.
-+     */
-+    ImageInfo  imageInfo(int row) const;
-+    ImageInfo& imageInfoRef(int row) const;
-+    qlonglong  imageId(int row) const;
-+
-+    /** Return the index for the given ImageInfo or id, if contained in this model.
-+     */
-+    QModelIndex        indexForImageInfo(const ImageInfo& info) const;
-+    QModelIndex        indexForImageInfo(const ImageInfo& info, const QVariant& extraValue) const;
-+    QModelIndex        indexForImageId(qlonglong id) const;
-+    QModelIndex        indexForImageId(qlonglong id, const QVariant& extraValue) const;
-+    QList<QModelIndex> indexesForImageInfo(const ImageInfo& info) const;
-+    QList<QModelIndex> indexesForImageId(qlonglong id) const;
-+
-+    int numberOfIndexesForImageInfo(const ImageInfo& info) const;
-+    int numberOfIndexesForImageId(qlonglong id) const;
-+
-+    /** Returns the index or ImageInfo object from the underlying data
-+     *  for the given file path. This is fast if keepsFilePathCache is enabled.
-+     *  The file path is as returned by ImageInfo.filePath().
-+     *  In case of multiple occurrences of the same file, the simpler variants return
-+     *  any one found first, use the QList methods to retrieve all occurrences.
-+     */
-+    QModelIndex        indexForPath(const QString& filePath) const;
-+    ImageInfo          imageInfo(const QString& filePath) const;
-+    QList<QModelIndex> indexesForPath(const QString& filePath) const;
-+    QList<ImageInfo>   imageInfos(const QString& filePath) const;
-+
-+    /** Main entry point for subclasses adding image infos to the model.
-+     *  If you list entries not unique per image id, you must add an extraValue
-+     *  so that every entry is unique by imageId and extraValues.
-+     *  Please note that these methods do not prevent addition of duplicate entries.
-+     */
-+    void addImageInfo(const ImageInfo& info);
-+    void addImageInfos(const QList<ImageInfo>& infos);
-+    void addImageInfos(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues);
-+
-+    /** Clears image infos and resets model.
-+     */
-+    void clearImageInfos();
-+
-+    /** Clears and adds the infos.
-+     */
-+    void setImageInfos(const QList<ImageInfo>& infos);
-+
-+    /**
-+     * Directly remove the given indexes or infos from the model.
-+     */
-+    void removeIndex(const QModelIndex& indexes);
-+    void removeIndexes(const QList<QModelIndex>& indexes);
-+    void removeImageInfo(const ImageInfo& info);
-+    void removeImageInfos(const QList<ImageInfo>& infos);
-+    void removeImageInfos(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues);
-+
-+    /**
-+     * addImageInfo() is asynchronous if a prepocessor is set.
-+     * This method first adds the info, synchronously.
-+     * Only afterwards, the preprocessor will have the opportunity to process it.
-+     * This method also bypasses any incremental updates.
-+     * Please note that these methods do not prevent addition of duplicate entries.
-+     */
-+    void addImageInfoSynchronously(const ImageInfo& info);
-+    void addImageInfosSynchronously(const QList<ImageInfo>& infos);
-+    void addImageInfosSynchronously(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues);
-+
-+    /**
-+     * Add the given entries. Method returns immediately, the
-+     * addition may happen later asynchronously.
-+     * These methods prevent the addition of duplicate entries.
-+     */
-+    void ensureHasImageInfo(const ImageInfo& info);
-+    void ensureHasImageInfos(const QList<ImageInfo>& infos);
-+    void ensureHasImageInfos(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues);
-+
-+    /**
-+     * Ensure that all images grouped on the given leader are contained in the model.
-+     */
-+    void ensureHasGroupedImages(const ImageInfo& groupLeader);
-+
-+    QList<ImageInfo> imageInfos() const;
-+    QList<qlonglong> imageIds()    const;
-+    QList<ImageInfo> uniqueImageInfos() const;
-+
-+    bool hasImage(qlonglong id) const;
-+    bool hasImage(const ImageInfo& info) const;
-+    bool hasImage(const ImageInfo& info, const QVariant& extraValue) const;
-+    bool hasImage(qlonglong id, const QVariant& extraValue) const;
-+
-+    bool isEmpty() const;
-+
-+    // Drag and Drop
-+    DECLARE_MODEL_DRAG_DROP_METHODS
-+
-+    /**
-+     * Install an object as a preprocessor for ImageInfos added to this model.
-+     * For every QList of ImageInfos added to addImageInfo, the signal preprocess()
-+     * will be emitted. The preprocessor may process the items and shall then readd
-+     * them by calling reAddImageInfos(). It may take some time to process.
-+     * It shall discard any held infos when the modelReset() signal is sent.
-+     * It shall call readdFinished() when no reset occurred and all infos on the way have been readded.
-+     * This means that only after calling this method, you shall make three connections
-+     * (preprocess -> your slot, your signal -> reAddImageInfos, your signal -> reAddingFinished)
-+     * and make or already hold a connection modelReset() -> your slot.
-+     * There is only one preprocessor at a time, a previously set object will be disconnected.
-+     */
-+    void setPreprocessor(QObject* processor);
-+    void unsetPreprocessor(QObject* processor);
-+
-+    /**
-+     * Returns true if this model is currently refreshing.
-+     * For a preprocessor this means that, although the preprocessor may currently have
-+     * processed all it got, more batches are to be expected.
-+     */
-+    bool isRefreshing() const;
-+
-+    /**
-+     * Enable sending of imageInfosAboutToBeRemoved and imageInfosRemoved signals.
-+     * Default: false
-+     */
-+    void setSendRemovalSignals(bool send);
-+
-+    virtual QVariant      data(const QModelIndex& index, int role = Qt::DisplayRole) const;
-+    virtual QVariant      headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
-+    virtual int           rowCount(const QModelIndex& parent = QModelIndex()) const;
-+    virtual Qt::ItemFlags flags(const QModelIndex& index) const;
-+    virtual QModelIndex   index(int row, int column = 0, const QModelIndex& parent = QModelIndex()) const;
-+
-+    /** Retrieves the imageInfo object from the data() method of the given index.
-+     *  The index may be from a QSortFilterProxyModel as long as an ImageModel is at the end. */
-+    static ImageInfo retrieveImageInfo(const QModelIndex& index);
-+    static qlonglong retrieveImageId(const QModelIndex& index);
-+
-+Q_SIGNALS:
-+
-+    /** Informs that ImageInfos will be added to the model.
-+     *  This signal is sent before the model data is changed and views are informed.
-+     */
-+    void imageInfosAboutToBeAdded(const QList<ImageInfo>& infos);
-+
-+    /** Informs that ImageInfos have been added to the model.
-+     *  This signal is sent after the model data is changed and views are informed.
-+     */
-+    void imageInfosAdded(const QList<ImageInfo>& infos);
-+
-+    /** Informs that ImageInfos will be removed from the model.
-+     *  This signal is sent before the model data is changed and views are informed.
-+     *  Note: You need to explicitly enable sending of this signal. It is not sent
-+     *  in clearImageInfos().
-+     */
-+    void imageInfosAboutToBeRemoved(const QList<ImageInfo>& infos);
-+
-+    /** Informs that ImageInfos have been removed from the model.
-+     *  This signal is sent after the model data is changed and views are informed. *
-+     *  Note: You need to explicitly enable sending of this signal. It is not sent
-+     *  in clearImageInfos().
-+     */
-+    void imageInfosRemoved(const QList<ImageInfo>& infos);
-+
-+    /** Connect to this signal only if you are the current preprocessor.
-+     */
-+    void preprocess(const QList<ImageInfo>& infos, const QList<QVariant>&);
-+    void processAdded(const QList<ImageInfo>& infos, const QList<QVariant>&);
-+
-+    /** If an ImageChangeset affected indexes of this model with changes as set in watchFlags(),
-+     *  this signal contains the changeset and the affected indexes.
-+     */
-+    void imageChange(const ImageChangeset&, const QItemSelection&);
-+
-+    /** If an ImageTagChangeset affected indexes of this model,
-+     *  this signal contains the changeset and the affected indexes.
-+     */
-+    void imageTagChange(const ImageTagChangeset&, const QItemSelection&);
-+
-+    /** Signals that the model is right now ready to start an incremental refresh.
-+     *  This is guaranteed only for the scope of emitting this signal.
-+     */
-+    void readyForIncrementalRefresh();
-+
-+    /** Signals that the model has finished currently with all scheduled
-+     *  refreshing, full or incremental, and all preprocessing.
-+     *  The model is in polished, clean situation right now.
-+     */
-+    void allRefreshingFinished();
-+
-+public Q_SLOTS:
-+
-+    void reAddImageInfos(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues);
-+    void reAddingFinished();
-+
-+protected:
-+
-+    /** Subclasses that add ImageInfos in batches shall call startRefresh()
-+     *  when they start sending batches and finishRefresh() when they have finished.
-+     *  No incremental refreshes will be started while listing.
-+     *  A clearImageInfos() always stops listing, calling finishRefresh() is then not necessary.
-+     */
-+    void startRefresh();
-+    void finishRefresh();
-+
-+    /** As soon as the model is ready to start an incremental refresh, the signal
-+     *  readyForIncrementalRefresh() will be emitted. The signal will be emitted inline
-+     *  if the model is ready right now.
-+     */
-+    void requestIncrementalRefresh();
-+    bool hasIncrementalRefreshPending() const;
-+
-+    /** Starts an incremental refresh operation. You shall only call this method from a slot
-+     *  connected to readyForIncrementalRefresh(). To initiate an incremental refresh,
-+     *  call requestIncrementalRefresh().
-+     */
-+    void startIncrementalRefresh();
-+    void finishIncrementalRefresh();
-+
-+    void emitDataChangedForAll();
-+    void emitDataChangedForSelection(const QItemSelection& selection);
-+
-+    // Called when the internal storage is cleared
-+    virtual void imageInfosCleared() {};
-+
-+    // Called before rowsAboutToBeRemoved
-+    virtual void imageInfosAboutToBeRemoved(int /*begin*/, int /*end*/) {};
-+
-+protected Q_SLOTS:
-+
-+    virtual void slotImageChange(const ImageChangeset& changeset);
-+    virtual void slotImageTagChange(const ImageTagChangeset& changeset);
-+
-+private:
-+
-+    void appendInfos(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues);
-+    void appendInfosChecked(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues);
-+    void publiciseInfos(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues);
-+    void cleanSituationChecks();
-+    void removeRowPairsWithCheck(const QList<QPair<int, int> >& toRemove);
-+    void removeRowPairs(const QList<QPair<int, int> >& toRemove);
-+
-+public:
-+
-+    // Declared public because it's used in ImageModelIncrementalUpdater class
-+    class Private;
-+
-+private:
-+
-+    Private* const d;
-+};
-+
-+} // namespace Digikam
-+
-+Q_DECLARE_METATYPE(Digikam::ImageModel*)
-+
-+#endif // IMAGEMODEL_H
-diff --git a/libs/database/models/imagesortsettings.cpp b/libs/database/models/imagesortsettings.cpp
-new file mode 100644
-index 0000000..39ee6e1
---- /dev/null
-+++ b/libs/database/models/imagesortsettings.cpp
-@@ -0,0 +1,400 @@
-+/* ============================================================
-+ *
-+ * This file is a part of digiKam project
-+ * http://www.digikam.org
-+ *
-+ * Date        : 2009-03-05
-+ * Description : Filter values for use with ImageFilterModel
-+ *
-+ * Copyright (C) 2009 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
-+ * Copyright (C) 2014 by Mohamed Anwer <m dot anwer at gmx dot com>
-+ *
-+ * This program is free software; you can redistribute it
-+ * and/or modify it under the terms of the GNU General
-+ * Public License as published by the Free Software Foundation;
-+ * either version 2, or (at your option)
-+ * any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * ============================================================ */
-+
-+#include "imagesortsettings.h"
-+
-+// Qt includes
-+
-+#include <QDateTime>
-+#include <QRectF>
-+
-+// Local includes
-+
-+#include "coredbfields.h"
-+#include "imageinfo.h"
-+
-+namespace Digikam
-+{
-+
-+ImageSortSettings::ImageSortSettings()
-+{
-+    categorizationMode             = NoCategories;
-+    categorizationSortOrder        = DefaultOrder;
-+    categorizationCaseSensitivity  = Qt::CaseSensitive;
-+    sortRole                       = SortByFileName;
-+    sortOrder                      = DefaultOrder;
-+    strTypeNatural                 = true;
-+    sortCaseSensitivity            = Qt::CaseSensitive;
-+    currentCategorizationSortOrder = Qt::AscendingOrder;
-+    currentSortOrder               = Qt::AscendingOrder;
-+}
-+
-+bool ImageSortSettings::operator==(const ImageSortSettings& other) const
-+{
-+    return
-+        categorizationMode            == other.categorizationMode            &&
-+        categorizationSortOrder       == other.categorizationSortOrder       &&
-+        categorizationCaseSensitivity == other.categorizationCaseSensitivity &&
-+        sortRole                      == other.sortRole                      &&
-+        sortOrder                     == other.sortOrder                     &&
-+        sortCaseSensitivity           == other.sortCaseSensitivity;
-+}
-+
-+void ImageSortSettings::setCategorizationMode(CategorizationMode mode)
-+{
-+    categorizationMode = mode;
-+
-+    if (categorizationSortOrder == DefaultOrder)
-+    {
-+        currentCategorizationSortOrder = defaultSortOrderForCategorizationMode(categorizationMode);
-+    }
-+}
-+
-+void ImageSortSettings::setCategorizationSortOrder(SortOrder order)
-+{
-+    categorizationSortOrder = order;
-+
-+    if (categorizationSortOrder == DefaultOrder)
-+    {
-+        currentCategorizationSortOrder = defaultSortOrderForCategorizationMode(categorizationMode);
-+    }
-+    else
-+    {
-+        currentCategorizationSortOrder = (Qt::SortOrder)categorizationSortOrder;
-+    }
-+}
-+
-+void ImageSortSettings::setSortRole(SortRole role)
-+{
-+    sortRole = role;
-+
-+    if (sortOrder == DefaultOrder)
-+    {
-+        currentSortOrder = defaultSortOrderForSortRole(sortRole);
-+    }
-+}
-+
-+void ImageSortSettings::setSortOrder(SortOrder order)
-+{
-+    sortOrder = order;
-+
-+    if (sortOrder == DefaultOrder)
-+    {
-+        currentSortOrder = defaultSortOrderForSortRole(sortRole);
-+    }
-+    else
-+    {
-+        currentSortOrder = (Qt::SortOrder)order;
-+    }
-+}
-+
-+void ImageSortSettings::setStringTypeNatural(bool natural)
-+{
-+    strTypeNatural = natural;
-+}
-+
-+Qt::SortOrder ImageSortSettings::defaultSortOrderForCategorizationMode(CategorizationMode mode)
-+{
-+    switch (mode)
-+    {
-+        case NoCategories:
-+        case OneCategory:
-+        case CategoryByAlbum:
-+        case CategoryByFormat:
-+        default:
-+            return Qt::AscendingOrder;
-+    }
-+}
-+
-+Qt::SortOrder ImageSortSettings::defaultSortOrderForSortRole(SortRole role)
-+{
-+    switch (role)
-+    {
-+        case SortByFileName:
-+        case SortByFilePath:
-+            return Qt::AscendingOrder;
-+        case SortByFileSize:
-+            return Qt::DescendingOrder;
-+        case SortByModificationDate:
-+        case SortByCreationDate:
-+            return Qt::AscendingOrder;
-+        case SortByRating:
-+        case SortByImageSize:
-+            return Qt::DescendingOrder;
-+        case SortByAspectRatio:
-+            return Qt::DescendingOrder;
-+        case SortBySimilarity:
-+            return Qt::DescendingOrder;
-+        default:
-+            return Qt::AscendingOrder;
-+    }
-+}
-+
-+int ImageSortSettings::compareCategories(const ImageInfo& left, const ImageInfo& right) const
-+{
-+    switch (categorizationMode)
-+    {
-+        case NoCategories:
-+        case OneCategory:
-+            return 0;
-+        case CategoryByAlbum:
-+        {
-+            int leftAlbum = left.albumId();
-+            int rightAlbum = right.albumId();
-+
-+            // return comparation result
-+            if (leftAlbum == rightAlbum)
-+            {
-+                return 0;
-+            }
-+            else if (lessThanByOrder(leftAlbum, rightAlbum, currentCategorizationSortOrder))
-+            {
-+                return -1;
-+            }
-+            else
-+            {
-+                return 1;
-+            }
-+        }
-+        case CategoryByFormat:
-+        {
-+            return naturalCompare(left.format(), right.format(),
-+                                  currentCategorizationSortOrder, categorizationCaseSensitivity, strTypeNatural);
-+        }
-+        default:
-+            return 0;
-+    }
-+}
-+
-+bool ImageSortSettings::lessThan(const ImageInfo& left, const ImageInfo& right) const
-+{
-+    int result = compare(left, right, sortRole);
-+
-+    if (result != 0)
-+    {
-+        return result < 0;
-+    }
-+
-+    // are they identical?
-+    if (left == right)
-+    {
-+        return false;
-+    }
-+
-+    // If left and right equal for first sort order, use a hierarchy of all sort orders
-+    if ( (result = compare(left, right, SortByFileName)) != 0)
-+    {
-+        return result < 0;
-+    }
-+
-+    if ( (result = compare(left, right, SortByCreationDate)) != 0)
-+    {
-+        return result < 0;
-+    }
-+
-+    if ( (result = compare(left, right, SortByModificationDate)) != 0)
-+    {
-+        return result < 0;
-+    }
-+
-+    if ( (result = compare(left, right, SortByFilePath)) != 0)
-+    {
-+        return result < 0;
-+    }
-+
-+    if ( (result = compare(left, right, SortByFileSize)) != 0)
-+    {
-+        return result < 0;
-+    }
-+
-+    if ( (result = compare(left, right, SortBySimilarity)) != 0)
-+    {
-+        return result < 0;
-+    }
-+
-+    return false;
-+}
-+
-+int ImageSortSettings::compare(const ImageInfo& left, const ImageInfo& right) const
-+{
-+    return compare(left, right, sortRole);
-+}
-+
-+int ImageSortSettings::compare(const ImageInfo& left, const ImageInfo& right, SortRole role) const
-+{
-+    switch (role)
-+    {
-+        case SortByFileName:
-+        {
-+            bool versioning = (left.name().contains(QLatin1String("_v"), Qt::CaseInsensitive) ||
-+                               right.name().contains(QLatin1String("_v"), Qt::CaseInsensitive));
-+            return naturalCompare(left.name(), right.name(), currentSortOrder, sortCaseSensitivity, strTypeNatural, versioning);
-+        }
-+        case SortByFilePath:
-+            return naturalCompare(left.filePath(), right.filePath(), currentSortOrder, sortCaseSensitivity, strTypeNatural);
-+        case SortByFileSize:
-+            return compareByOrder(left.fileSize(), right.fileSize(), currentSortOrder);
-+        case SortByModificationDate:
-+            return compareByOrder(left.modDateTime(), right.modDateTime(), currentSortOrder);
-+        case SortByCreationDate:
-+            return compareByOrder(left.dateTime(), right.dateTime(), currentSortOrder);
-+        case SortByRating:
-+            // I have the feeling that inverting the sort order for rating is the natural order
-+            return - compareByOrder(left.rating(), right.rating(), currentSortOrder);
-+        case SortByImageSize:
-+        {
-+            QSize leftSize  = left.dimensions();
-+            QSize rightSize = right.dimensions();
-+            int leftPixels  = leftSize.width() * leftSize.height();
-+            int rightPixels = rightSize.width() * rightSize.height();
-+            return compareByOrder(leftPixels, rightPixels, currentSortOrder);
-+        }
-+        case SortByAspectRatio:
-+        {
-+            QSize leftSize = left.dimensions();
-+            QSize rightSize = right.dimensions();
-+            int leftAR = (double(leftSize.width()) / double(leftSize.height())) * 1000000;
-+            int rightAR = (double(rightSize.width()) / double(rightSize.height())) * 1000000;
-+            return compareByOrder(leftAR, rightAR, currentSortOrder);
-+        }
-+        case SortBySimilarity:
-+        {
-+            qlonglong leftReferenceImageId  = left.currentReferenceImage();
-+            qlonglong rightReferenceImageId = right.currentReferenceImage();
-+            // make sure that the original image has always the highest similarity.
-+            double leftSimilarity  = left.id() == leftReferenceImageId ? 1.1 : left.currentSimilarity();
-+            double rightSimilarity = right.id() == rightReferenceImageId ? 1.1 : right.currentSimilarity();
-+            return compareByOrder(leftSimilarity, rightSimilarity, currentSortOrder);
-+        }
-+        default:
-+            return 1;
-+    }
-+}
-+
-+bool ImageSortSettings::lessThan(const QVariant& left, const QVariant& right) const
-+{
-+    if (left.type() != right.type())
-+    {
-+        return false;
-+    }
-+
-+    switch (left.type())
-+    {
-+        case QVariant::Int:
-+            return compareByOrder(left.toInt(), right.toInt(), currentSortOrder);
-+        case QVariant::UInt:
-+            return compareByOrder(left.toUInt(), right.toUInt(), currentSortOrder);
-+        case QVariant::LongLong:
-+            return compareByOrder(left.toLongLong(), right.toLongLong(), currentSortOrder);
-+        case QVariant::ULongLong:
-+            return compareByOrder(left.toULongLong(), right.toULongLong(), currentSortOrder);
-+        case QVariant::Double:
-+            return compareByOrder(left.toDouble(), right.toDouble(), currentSortOrder);
-+        case QVariant::Date:
-+            return compareByOrder(left.toDate(), right.toDate(), currentSortOrder);
-+        case QVariant::DateTime:
-+            return compareByOrder(left.toDateTime(), right.toDateTime(), currentSortOrder);
-+        case QVariant::Time:
-+            return compareByOrder(left.toTime(), right.toTime(), currentSortOrder);
-+        case QVariant::Rect:
-+        case QVariant::RectF:
-+        {
-+            QRectF rectLeft  = left.toRectF();
-+            QRectF rectRight = right.toRectF();
-+            int result;
-+
-+            if ((result = compareByOrder(rectLeft.top(), rectRight.top(), currentSortOrder)) != 0)
-+            {
-+                return result < 0;
-+            }
-+
-+            if ((result = compareByOrder(rectLeft.left(), rectRight.left(), currentSortOrder)) != 0)
-+            {
-+                return result < 0;
-+            }
-+
-+            QSizeF sizeLeft = rectLeft.size(), sizeRight = rectRight.size();
-+
-+            if ((result = compareByOrder(sizeLeft.width()*sizeLeft.height(), sizeRight.width()*sizeRight.height(), currentSortOrder)) != 0)
-+            {
-+                return result < 0;
-+            }
-+            // FIXME: fall through?? If not, add "break" here
-+        }
-+        default:
-+            return naturalCompare(left.toString(), right.toString(), currentSortOrder, sortCaseSensitivity, strTypeNatural);
-+    }
-+}
-+
-+DatabaseFields::Set ImageSortSettings::watchFlags() const
-+{
-+    DatabaseFields::Set set;
-+
-+    switch (sortRole)
-+    {
-+        case SortByFileName:
-+            set |= DatabaseFields::Name;
-+            break;
-+        case SortByFilePath:
-+            set |= DatabaseFields::Name;
-+            break;
-+        case SortByFileSize:
-+            set |= DatabaseFields::FileSize;
-+            break;
-+        case SortByModificationDate:
-+            set |= DatabaseFields::ModificationDate;
-+            break;
-+        case SortByCreationDate:
-+            set |= DatabaseFields::CreationDate;
-+            break;
-+        case SortByRating:
-+            set |= DatabaseFields::Rating;
-+            break;
-+        case SortByImageSize:
-+            set |= DatabaseFields::Width | DatabaseFields::Height;
-+            break;
-+        case SortByAspectRatio:
-+            set |= DatabaseFields::Width | DatabaseFields::Height;
-+            break;
-+        case SortBySimilarity:
-+            // TODO: Not sure what to do here....
-+            set |= DatabaseFields::Name;
-+            break;
-+    }
-+
-+    switch (categorizationMode)
-+    {
-+        case NoCategories:
-+        case OneCategory:
-+        case CategoryByAlbum:
-+            break;
-+        case CategoryByFormat:
-+            set |= DatabaseFields::Format;
-+            break;
-+    }
-+
-+    return set;
-+}
-+
-+} // namespace Digikam
-diff --git a/libs/database/models/imagesortsettings.h b/libs/database/models/imagesortsettings.h
-new file mode 100644
-index 0000000..2a5fd8c
---- /dev/null
-+++ b/libs/database/models/imagesortsettings.h
-@@ -0,0 +1,225 @@
-+/* ============================================================
-+ *
-+ * This file is a part of digiKam project
-+ * http://www.digikam.org
-+ *
-+ * Date        : 2009-05-31
-+ * Description : Sort settings for use with ImageFilterModel
-+ *
-+ * Copyright (C) 2009 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
-+ *
-+ * This program is free software; you can redistribute it
-+ * and/or modify it under the terms of the GNU General
-+ * Public License as published by the Free Software Foundation;
-+ * either version 2, or (at your option)
-+ * any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * ============================================================ */
-+
-+#ifndef IMAGESORTSETTINGS_H
-+#define IMAGESORTSETTINGS_H
-+
-+// Qt includes
-+
-+#include <QHash>
-+#include <QList>
-+#include <QMap>
-+#include <QString>
-+#include <QCollator>
-+
-+// Local includes
-+
-+#include "digikam_export.h"
-+
-+namespace Digikam
-+{
-+
-+class ImageInfo;
-+
-+namespace DatabaseFields
-+{
-+    class Set;
-+}
-+
-+class DIGIKAM_DATABASE_EXPORT ImageSortSettings
-+{
-+public:
-+
-+    ImageSortSettings();
-+
-+    bool operator==(const ImageSortSettings& other) const;
-+
-+    /** Compares the categories of left and right.
-+     *  Return -1 if left is less than right, 0 if both fall in the same category,
-+     *  and 1 if left is greater than right.
-+     *  Adheres to set categorization mode and current category sort order.
-+     */
-+    int compareCategories(const ImageInfo& left, const ImageInfo& right) const;
-+
-+    /** Returns true if left is less than right.
-+     *  Adheres to current sort role and sort order.
-+     */
-+    bool lessThan(const ImageInfo& left, const ImageInfo& right) const;
-+
-+    /** Compares the ImageInfos left and right.
-+     *  Return -1 if left is less than right, 1 if left is greater than right,
-+     *  and 0 if left equals right comparing the current sort role's value.
-+     *  Adheres to set sort role and sort order.
-+     */
-+    int compare(const ImageInfo& left, const ImageInfo& right) const;
-+
-+    /** Returns true if left QVariant is less than right.
-+     *  Adheres to current sort role and sort order.
-+     *  Use for extraValue, if necessary.
-+     */
-+    bool lessThan(const QVariant& left, const QVariant& right) const;
-+
-+    enum SortOrder
-+    {
-+        AscendingOrder  = Qt::AscendingOrder,
-+        DescendingOrder = Qt::DescendingOrder,
-+        DefaultOrder /// sort order depends on the chosen sort role
-+    };
-+
-+    /// --- Categories ---
-+
-+    enum CategorizationMode
-+    {
-+        NoCategories, /// categorization switched off
-+        OneCategory, /// all items in one global category
-+        CategoryByAlbum,
-+        CategoryByFormat
-+    };
-+
-+    CategorizationMode      categorizationMode;
-+    SortOrder               categorizationSortOrder;
-+
-+    void setCategorizationMode(CategorizationMode mode);
-+    void setCategorizationSortOrder(SortOrder order);
-+
-+    /// Only Ascending or Descending, never DefaultOrder
-+    Qt::SortOrder           currentCategorizationSortOrder;
-+    Qt::CaseSensitivity     categorizationCaseSensitivity;
-+
-+    bool isCategorized() const { return categorizationMode >= CategoryByAlbum; }
-+
-+    /// --- Image Sorting ---
-+
-+    enum SortRole
-+    {
-+        // Note: For legacy reasons, the order of the first five entries must remain unchanged
-+        SortByFileName,
-+        SortByFilePath,
-+        SortByCreationDate,
-+        SortByFileSize,
-+        SortByRating,
-+        SortByModificationDate,
-+        SortByImageSize,            // pixel number
-+        SortByAspectRatio,          // width / height * 100000
-+        SortBySimilarity
-+    };
-+
-+    SortRole                sortRole;
-+    SortOrder               sortOrder;
-+    bool                    strTypeNatural;
-+
-+    void setSortRole(SortRole role);
-+    void setSortOrder(SortOrder order);
-+    void setStringTypeNatural(bool natural);
-+
-+    Qt::SortOrder           currentSortOrder;
-+    Qt::CaseSensitivity     sortCaseSensitivity;
-+
-+    int compare(const ImageInfo& left, const ImageInfo& right, SortRole sortRole) const;
-+
-+    // --- ---
-+
-+    static Qt::SortOrder defaultSortOrderForCategorizationMode(CategorizationMode mode);
-+    static Qt::SortOrder defaultSortOrderForSortRole(SortRole role);
-+
-+    /// --- Change notification ---
-+
-+    /** Returns database fields a change in which would affect the current sorting.
-+     */
-+    DatabaseFields::Set watchFlags() const;
-+
-+    /// --- Utilities ---
-+
-+    /** Returns a < b if sortOrder is Ascending, or b < a if order is descending.
-+     */
-+    template <typename T>
-+    static inline bool lessThanByOrder(const T& a, const T& b, Qt::SortOrder sortOrder)
-+    {
-+        if (sortOrder == Qt::AscendingOrder)
-+        {
-+            return a < b;
-+        }
-+        else
-+        {
-+            return b < a;
-+        }
-+    }
-+
-+    /** Returns the usual compare result of -1, 0, or 1 for lessThan, equals and greaterThan.
-+     */
-+    template <typename T>
-+    static inline int compareValue(const T& a, const T& b)
-+    {
-+        if (a == b)
-+        {
-+            return 0;
-+        }
-+
-+        if (a < b)
-+        {
-+            return -1;
-+        }
-+        else
-+        {
-+            return 1;
-+        }
-+    }
-+
-+    /** Takes a typical result from a compare method (0 is equal, -1 is less than, 1 is greater than)
-+     *  and applies the given sort order to it.
-+     */
-+    static inline int compareByOrder(int compareResult,  Qt::SortOrder sortOrder)
-+    {
-+        if (sortOrder == Qt::AscendingOrder)
-+        {
-+            return compareResult;
-+        }
-+        else
-+        {
-+            return - compareResult;
-+        }
-+    }
-+
-+    template <typename T>
-+    static inline int compareByOrder(const T& a, const T& b, Qt::SortOrder sortOrder)
-+    {
-+        return compareByOrder(compareValue(a, b), sortOrder);
-+    }
-+
-+    /** Compares the two string by natural comparison and adheres to given sort order
-+     */
-+    static inline int naturalCompare(const QString& a, const QString& b, Qt::SortOrder sortOrder,
-+                                     Qt::CaseSensitivity caseSensitive = Qt::CaseSensitive,
-+                                     bool natural = true, bool versioning = false)
-+    {
-+        QCollator collator;
-+        collator.setNumericMode(natural);
-+        collator.setIgnorePunctuation(versioning);
-+        collator.setCaseSensitivity(caseSensitive);
-+        return (compareByOrder(collator.compare(a, b), sortOrder));
-+    }
-+};
-+
-+} // namespace Digikam
-+
-+#endif // IMAGESORTSETTINGS_H
-diff --git a/libs/database/models/imagethumbnailmodel.cpp b/libs/database/models/imagethumbnailmodel.cpp
-new file mode 100644
-index 0000000..b7f5661
---- /dev/null
-+++ b/libs/database/models/imagethumbnailmodel.cpp
-@@ -0,0 +1,323 @@
-+/* ============================================================
-+ *
-+ * This file is a part of digiKam project
-+ * http://www.digikam.org
-+ *
-+ * Date        : 2009-03-05
-+ * Description : Qt item model for database entries with support for thumbnail loading
-+ *
-+ * Copyright (C) 2009-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
-+ * Copyright (C) 2011-2017 by Gilles Caulier <caulier dot gilles at gmail dot com>
-+ *
-+ * This program is free software; you can redistribute it
-+ * and/or modify it under the terms of the GNU General
-+ * Public License as published by the Free Software Foundation;
-+ * either version 2, or (at your option)
-+ * any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * ============================================================ */
-+
-+#include "imagethumbnailmodel.h"
-+
-+// Qt includes
-+
-+#include <QHash>
-+
-+// Local includes
-+
-+#include "digikam_debug.h"
-+#include "thumbnailloadthread.h"
-+#include "digikam_export.h"
-+#include "digikam_globals.h"
-+
-+namespace Digikam
-+{
-+
-+class ImageThumbnailModel::ImageThumbnailModelPriv
-+{
-+public:
-+
-+    ImageThumbnailModelPriv() :
-+        thread(0),
-+        preloadThread(0),
-+        thumbSize(0),
-+        lastGlobalThumbSize(0),
-+        preloadThumbSize(0),
-+        emitDataChanged(true)
-+    {
-+        staticListContainingThumbnailRole << ImageModel::ThumbnailRole;
-+    }
-+
-+    ThumbnailLoadThread*   thread;
-+    ThumbnailLoadThread*   preloadThread;
-+    ThumbnailSize          thumbSize;
-+    ThumbnailSize          lastGlobalThumbSize;
-+    ThumbnailSize          preloadThumbSize;
-+    QRect                  detailRect;
-+    QVector<int>           staticListContainingThumbnailRole;
-+
-+    bool                   emitDataChanged;
-+
-+    int preloadThumbnailSize() const
-+    {
-+        if (preloadThumbSize.size())
-+        {
-+            return preloadThumbSize.size();
-+        }
-+
-+        return thumbSize.size();
-+    }
-+};
-+
-+ImageThumbnailModel::ImageThumbnailModel(QObject* parent)
-+    : ImageModel(parent), d(new ImageThumbnailModelPriv)
-+{
-+    setKeepsFilePathCache(true);
-+}
-+
-+ImageThumbnailModel::~ImageThumbnailModel()
-+{
-+    delete d->preloadThread;
-+    delete d;
-+}
-+
-+void ImageThumbnailModel::setThumbnailLoadThread(ThumbnailLoadThread* thread)
-+{
-+    d->thread = thread;
-+
-+    connect(d->thread, SIGNAL(signalThumbnailLoaded(LoadingDescription,QPixmap)),
-+            this, SLOT(slotThumbnailLoaded(LoadingDescription,QPixmap)));
-+}
-+
-+ThumbnailLoadThread* ImageThumbnailModel::thumbnailLoadThread() const
-+{
-+    return d->thread;
-+}
-+
-+ThumbnailSize ImageThumbnailModel::thumbnailSize() const
-+{
-+    return d->thumbSize;
-+}
-+
-+void ImageThumbnailModel::setThumbnailSize(const ThumbnailSize& size)
-+{
-+    d->lastGlobalThumbSize = size;
-+    d->thumbSize = size;
-+}
-+
-+void ImageThumbnailModel::setPreloadThumbnailSize(const ThumbnailSize& size)
-+{
-+    d->preloadThumbSize = size;
-+}
-+
-+void ImageThumbnailModel::setEmitDataChanged(bool emitSignal)
-+{
-+    d->emitDataChanged = emitSignal;
-+}
-+
-+void ImageThumbnailModel::setPreloadThumbnails(bool preload)
-+{
-+    if (preload)
-+    {
-+        if (!d->preloadThread)
-+        {
-+            d->preloadThread = new ThumbnailLoadThread;
-+            d->preloadThread->setPixmapRequested(false);
-+            d->preloadThread->setPriority(QThread::LowestPriority);
-+        }
-+
-+        connect(this, SIGNAL(allRefreshingFinished()),
-+                this, SLOT(preloadAllThumbnails()));
-+    }
-+    else
-+    {
-+        delete d->preloadThread;
-+        d->preloadThread = 0;
-+        disconnect(this, SIGNAL(allRefreshingFinished()),
-+                   this, SLOT(preloadAllThumbnails()));
-+    }
-+}
-+
-+void ImageThumbnailModel::prepareThumbnails(const QList<QModelIndex>& indexesToPrepare)
-+{
-+    prepareThumbnails(indexesToPrepare, d->thumbSize);
-+}
-+
-+void ImageThumbnailModel::prepareThumbnails(const QList<QModelIndex>& indexesToPrepare, const ThumbnailSize& thumbSize)
-+{
-+    if (!d->thread)
-+    {
-+        return;
-+    }
-+
-+    QList<ThumbnailIdentifier> ids;
-+    foreach(const QModelIndex& index, indexesToPrepare)
-+    {
-+        ids << imageInfoRef(index).thumbnailIdentifier();
-+    }
-+    d->thread->findGroup(ids, thumbSize.size());
-+}
-+
-+void ImageThumbnailModel::preloadThumbnails(const QList<ImageInfo>& infos)
-+{
-+    if (!d->preloadThread)
-+    {
-+        return;
-+    }
-+
-+    QList<ThumbnailIdentifier> ids;
-+    foreach(const ImageInfo& info, infos)
-+    {
-+        ids << info.thumbnailIdentifier();
-+    }
-+    d->preloadThread->pregenerateGroup(ids, d->preloadThumbnailSize());
-+}
-+
-+void ImageThumbnailModel::preloadThumbnails(const QList<QModelIndex>& indexesToPreload)
-+{
-+    if (!d->preloadThread)
-+    {
-+        return;
-+    }
-+
-+    QList<ThumbnailIdentifier> ids;
-+    foreach(const QModelIndex& index, indexesToPreload)
-+    {
-+        ids << imageInfoRef(index).thumbnailIdentifier();
-+    }
-+    d->preloadThread->stopAllTasks();
-+    d->preloadThread->pregenerateGroup(ids, d->preloadThumbnailSize());
-+}
-+
-+void ImageThumbnailModel::preloadAllThumbnails()
-+{
-+    preloadThumbnails(imageInfos());
-+}
-+
-+void ImageThumbnailModel::imageInfosCleared()
-+{
-+    if (d->preloadThread)
-+    {
-+        d->preloadThread->stopAllTasks();
-+    }
-+}
-+
-+QVariant ImageThumbnailModel::data(const QModelIndex& index, int role) const
-+{
-+    if (role == ThumbnailRole && d->thread && index.isValid())
-+    {
-+        QPixmap   thumbnail;
-+        ImageInfo info = imageInfo(index);
-+        QString   path = info.filePath();
-+
-+        if (info.isNull())
-+        {
-+            return QVariant(QVariant::Pixmap);
-+        }
-+
-+        if (!d->detailRect.isNull())
-+        {
-+            if (d->thread->find(info.thumbnailIdentifier(), d->detailRect, thumbnail, d->thumbSize.size()))
-+            {
-+                return thumbnail;
-+            }
-+        }
-+        else
-+        {
-+            if (d->thread->find(info.thumbnailIdentifier(), thumbnail, d->thumbSize.size()))
-+            {
-+                return thumbnail;
-+            }
-+        }
-+
-+        return QVariant(QVariant::Pixmap);
-+    }
-+
-+    return ImageModel::data(index, role);
-+}
-+
-+bool ImageThumbnailModel::setData(const QModelIndex& index, const QVariant& value, int role)
-+{
-+    if (role == ThumbnailRole)
-+    {
-+        switch (value.type())
-+        {
-+            case QVariant::Invalid:
-+                d->thumbSize  = d->lastGlobalThumbSize;
-+                d->detailRect = QRect();
-+                break;
-+
-+            case QVariant::Int:
-+
-+                if (value.isNull())
-+                {
-+                    d->thumbSize = d->lastGlobalThumbSize;
-+                }
-+                else
-+                {
-+                    d->thumbSize = value.toInt();
-+                }
-+                break;
-+
-+            case QVariant::Rect:
-+
-+                if (value.isNull())
-+                {
-+                    d->detailRect = QRect();
-+                }
-+                else
-+                {
-+                    d->detailRect = value.toRect();
-+                }
-+                break;
-+
-+            default:
-+                break;
-+        }
-+    }
-+
-+    return ImageModel::setData(index, value, role);
-+}
-+
-+void ImageThumbnailModel::slotThumbnailLoaded(const LoadingDescription& loadingDescription, const QPixmap& thumb)
-+{
-+    if (thumb.isNull())
-+    {
-+        return;
-+    }
-+
-+    // In case of multiple occurrence, we currently do not know which thumbnail is this. Signal change on all.
-+    QModelIndexList indexes;
-+    ThumbnailIdentifier thumbId = loadingDescription.thumbnailIdentifier();
-+    if (thumbId.filePath.isEmpty())
-+    {
-+        indexes = indexesForImageId(thumbId.id);
-+    }
-+    else
-+    {
-+        indexes = indexesForPath(thumbId.filePath);
-+    }
-+    foreach(const QModelIndex& index, indexes)
-+    {
-+        if (thumb.isNull())
-+        {
-+            emit thumbnailFailed(index, loadingDescription.previewParameters.size);
-+        }
-+        else
-+        {
-+            emit thumbnailAvailable(index, loadingDescription.previewParameters.size);
-+
-+            if (d->emitDataChanged)
-+            {
-+                emit dataChanged(index, index, d->staticListContainingThumbnailRole);
-+            }
-+        }
-+    }
-+}
-+
-+} // namespace Digikam
-diff --git a/libs/database/models/imagethumbnailmodel.h b/libs/database/models/imagethumbnailmodel.h
-new file mode 100644
-index 0000000..366ca65
---- /dev/null
-+++ b/libs/database/models/imagethumbnailmodel.h
-@@ -0,0 +1,140 @@
-+/* ============================================================
-+ *
-+ * This file is a part of digiKam project
-+ * http://www.digikam.org
-+ *
-+ * Date        : 2009-03-05
-+ * Description : Qt item model for database entries with support for thumbnail loading
-+ *
-+ * Copyright (C) 2009-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
-+ * Copyright (C)      2011 by Gilles Caulier <caulier dot gilles at gmail dot com>
-+ *
-+ * This program is free software; you can redistribute it
-+ * and/or modify it under the terms of the GNU General
-+ * Public License as published by the Free Software Foundation;
-+ * either version 2, or (at your option)
-+ * any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * ============================================================ */
-+
-+#ifndef IMAGETHUMBNAILMODEL_H
-+#define IMAGETHUMBNAILMODEL_H
-+
-+// Local includes
-+
-+#include "imagemodel.h"
-+#include "thumbnailsize.h"
-+#include "digikam_export.h"
-+
-+namespace Digikam
-+{
-+
-+class LoadingDescription;
-+class ThumbnailLoadThread;
-+
-+class DIGIKAM_DATABASE_EXPORT ImageThumbnailModel : public ImageModel
-+{
-+    Q_OBJECT
-+
-+public:
-+
-+    /**
-+     *  An ImageModel that supports thumbnail loading.
-+     *  You need to set a ThumbnailLoadThread to enable thumbnail loading.
-+     *  Adjust the thumbnail size to your needs.
-+     *  Note that setKeepsFilePathCache is enabled per default.
-+     */
-+    explicit ImageThumbnailModel(QObject* parent);
-+    ~ImageThumbnailModel();
-+
-+    /** Enable thumbnail loading and set the thread that shall be used.
-+     *  The thumbnail size of this thread will be adjusted.
-+     */
-+    void setThumbnailLoadThread(ThumbnailLoadThread* thread);
-+    ThumbnailLoadThread* thumbnailLoadThread() const;
-+
-+    /// Set the thumbnail size to use
-+    void setThumbnailSize(const ThumbnailSize& thumbSize);
-+
-+    /// If you want to fix a size for preloading, do it here.
-+    void setPreloadThumbnailSize(const ThumbnailSize& thumbSize);
-+
-+    void setExifRotate(bool rotate);
-+
-+    /**
-+     *  Enable emitting dataChanged() when a thumbnail becomes available.
-+     *  The thumbnailAvailable() signal will be emitted in any case.
-+     *  Default is true.
-+     */
-+    void setEmitDataChanged(bool emitSignal);
-+
-+    /**
-+     * Enable preloading of thumbnails:
-+     * If preloading is enabled, for every entry in the model a thumbnail generation is started.
-+     * Default: false.
-+     */
-+    void setPreloadThumbnails(bool preload);
-+
-+    ThumbnailSize thumbnailSize() const;
-+
-+    /**
-+     *  Handles the ThumbnailRole.
-+     *  If the pixmap is available, returns it in the QVariant.
-+     *  If it still needs to be loaded, returns a null QVariant and emits
-+     *  thumbnailAvailable() as soon as it is available.
-+     */
-+    virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
-+
-+    /**
-+     * You can override the current thumbnail size by giving an integer value for ThumbnailRole.
-+     * Set a null QVariant to use the thumbnail size set by setThumbnailSize() again.
-+     * The index given here is ignored for this purpose.
-+     */
-+    virtual bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::DisplayRole);
-+
-+public Q_SLOTS:
-+
-+    /** Prepare the thumbnail loading for the given indexes
-+     */
-+    void prepareThumbnails(const QList<QModelIndex>& indexesToPrepare);
-+    void prepareThumbnails(const QList<QModelIndex>& indexesToPrepare, const ThumbnailSize& thumbSize);
-+
-+    /**
-+     *  Preload thumbnail for the given infos resp. indexes.
-+     *  Note: Use setPreloadThumbnails to automatically preload all entries in the model.
-+     *  Note: This only ensures thumbnail generation. It is not guaranteed that pixmaps
-+     *  are stored in the cache. For thumbnails that are expect to be drawn immediately,
-+     *  include them in prepareThumbnails().
-+     *  Note: Stops preloading of previously added thumbnails.
-+     */
-+    void preloadThumbnails(const QList<ImageInfo>&);
-+    void preloadThumbnails(const QList<QModelIndex>&);
-+    void preloadAllThumbnails();
-+
-+Q_SIGNALS:
-+
-+    void thumbnailAvailable(const QModelIndex& index, int requestedSize);
-+    void thumbnailFailed(const QModelIndex& index, int requestedSize);
-+
-+protected:
-+
-+    virtual void imageInfosCleared();
-+
-+protected Q_SLOTS:
-+
-+    void slotThumbnailLoaded(const LoadingDescription& loadingDescription, const QPixmap& thumb);
-+
-+private:
-+
-+    class ImageThumbnailModelPriv;
-+    ImageThumbnailModelPriv* const d;
-+};
-+
-+} // namespace Digikam
-+
-+#endif /* IMAGETHUMBNAILMODEL_H */
-diff --git a/libs/database/models/imageversionsmodel.cpp b/libs/database/models/imageversionsmodel.cpp
-new file mode 100644
-index 0000000..e6ba582
---- /dev/null
-+++ b/libs/database/models/imageversionsmodel.cpp
-@@ -0,0 +1,183 @@
-+/* ============================================================
-+ *
-+ * This file is a part of digiKam project
-+ * http://www.digikam.org
-+ *
-+ * Date        : 2010-07-13
-+ * Description : Model for image versions
-+ *
-+ * Copyright (C) 2010 by Martin Klapetek <martin dot klapetek at gmail dot com>
-+ *
-+ * This program is free software; you can redistribute it
-+ * and/or modify it under the terms of the GNU General
-+ * Public License as published by the Free Software Foundation;
-+ * either version 2, or (at your option)
-+ * any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * ============================================================ */
-+
-+#include "imageversionsmodel.h"
-+
-+// KDE includes
-+
-+#include <klocalizedstring.h>
-+
-+// Local includes
-+
-+#include "digikam_debug.h"
-+#include "workingwidget.h"
-+
-+namespace Digikam
-+{
-+
-+class ImageVersionsModel::Private
-+{
-+public:
-+
-+    Private()
-+    {
-+        data      = 0;
-+        paintTree = false;
-+    }
-+
-+    ///Complete paths with filenames and tree level
-+    QList<QPair<QString, int> >* data;
-+    ///This is for delegate to paint it as selected
-+    QString                      currentSelectedImage;
-+    ///If true, the delegate will paint items as a tree
-+    ///if false, it will be painted as a list
-+    bool                         paintTree;
-+};
-+
-+ImageVersionsModel::ImageVersionsModel(QObject* parent)
-+    : QAbstractListModel(parent),
-+      d(new Private)
-+{
-+    d->data = new QList<QPair<QString, int> >;
-+}
-+
-+ImageVersionsModel::~ImageVersionsModel()
-+{
-+    //qDeleteAll(d->data);
-+    delete d;
-+}
-+
-+Qt::ItemFlags ImageVersionsModel::flags(const QModelIndex& index) const
-+{
-+    if (!index.isValid())
-+    {
-+        return 0;
-+    }
-+
-+    return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
-+}
-+
-+QVariant ImageVersionsModel::data(const QModelIndex& index, int role) const
-+{
-+    if (!index.isValid())
-+    {
-+        return QVariant();
-+    }
-+
-+    if (role == Qt::DisplayRole && !d->data->isEmpty())
-+    {
-+        return d->data->at(index.row()).first;
-+    }
-+    else if (role == Qt::UserRole && !d->data->isEmpty())
-+    {
-+        return d->data->at(index.row()).second;
-+    }
-+    else if (role == Qt::DisplayRole && d->data->isEmpty())
-+    {
-+        //TODO: make this text Italic
-+        return QVariant(QString(i18n("No image selected")));
-+    }
-+
-+    return QVariant();
-+}
-+
-+int ImageVersionsModel::rowCount(const QModelIndex& parent) const
-+{
-+    Q_UNUSED(parent)
-+    return d->data->count();
-+}
-+
-+void ImageVersionsModel::setupModelData(QList<QPair<QString, int> >& data)
-+{
-+    beginResetModel();
-+
-+    d->data->clear();
-+
-+    if (!data.isEmpty())
-+    {
-+        d->data->append(data);
-+    }
-+    else
-+    {
-+        d->data->append(qMakePair(QString(i18n("This is the original image")), 0));
-+    }
-+
-+    endResetModel();
-+}
-+
-+void ImageVersionsModel::clearModelData()
-+{
-+    beginResetModel();
-+
-+    if (!d->data->isEmpty())
-+    {
-+        d->data->clear();
-+    }
-+
-+    endResetModel();
-+}
-+
-+void ImageVersionsModel::slotAnimationStep()
-+{
-+    emit dataChanged(createIndex(0, 0), createIndex(rowCount()-1, 1));
-+}
-+
-+QString ImageVersionsModel::currentSelectedImage() const
-+{
-+    return d->currentSelectedImage;
-+}
-+
-+void ImageVersionsModel::setCurrentSelectedImage(const QString& path)
-+{
-+    d->currentSelectedImage = path;
-+}
-+
-+QModelIndex ImageVersionsModel::currentSelectedImageIndex() const
-+{
-+    return index(listIndexOf(d->currentSelectedImage), 0);
-+}
-+
-+bool ImageVersionsModel::paintTree() const
-+{
-+    return d->paintTree;
-+}
-+
-+void ImageVersionsModel::setPaintTree(bool paint)
-+{
-+    d->paintTree = paint;
-+}
-+
-+int ImageVersionsModel::listIndexOf(const QString& item) const
-+{
-+    for (int i = 0; i < d->data->size(); ++i)
-+    {
-+        if (d->data->at(i).first == item)
-+        {
-+            return i;
-+        }
-+    }
-+
-+    return -1;
-+}
-+
-+} // namespace Digikam
-diff --git a/libs/database/models/imageversionsmodel.h b/libs/database/models/imageversionsmodel.h
-new file mode 100644
-index 0000000..ed08529
---- /dev/null
-+++ b/libs/database/models/imageversionsmodel.h
-@@ -0,0 +1,75 @@
-+/* ============================================================
-+ *
-+ * This file is a part of digiKam project
-+ * http://www.digikam.org
-+ *
-+ * Date        : 2010-07-13
-+ * Description : Model for image versions
-+ *
-+ * Copyright (C) 2010 by Martin Klapetek <martin dot klapetek at gmail dot com>
-+ *
-+ * This program is free software; you can redistribute it
-+ * and/or modify it under the terms of the GNU General
-+ * Public License as published by the Free Software Foundation;
-+ * either version 2, or (at your option)
-+ * any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * ============================================================ */
-+
-+#ifndef IMAGEVERSIONSMODEL_H
-+#define IMAGEVERSIONSMODEL_H
-+
-+// Qt includes
-+
-+#include <QModelIndex>
-+#include <QPixmap>
-+
-+// Local includes
-+
-+#include "digikam_export.h"
-+
-+namespace Digikam
-+{
-+
-+class DIGIKAM_DATABASE_EXPORT ImageVersionsModel : public QAbstractListModel
-+{
-+    Q_OBJECT
-+
-+public:
-+
-+    explicit ImageVersionsModel(QObject* parent = 0);
-+    ~ImageVersionsModel();
-+
-+    Qt::ItemFlags flags(const QModelIndex& index) const;
-+    QVariant      data(const QModelIndex& index, int role = Qt::DisplayRole) const;
-+    int           rowCount(const QModelIndex& parent = QModelIndex()) const;
-+
-+    void setupModelData(QList<QPair<QString, int> >& data);
-+    void clearModelData();
-+
-+    QString     currentSelectedImage() const;
-+    void        setCurrentSelectedImage(const QString& path);
-+    QModelIndex currentSelectedImageIndex() const;
-+
-+    bool paintTree() const;
-+    int  listIndexOf(const QString& item) const;
-+
-+public Q_SLOTS:
-+
-+    void slotAnimationStep();
-+    void setPaintTree(bool paint);
-+
-+private:
-+
-+    class Private;
-+    Private* const d;
-+};
-+
-+} // namespace Digikam
-+
-+#endif // IMAGEVERSIONSMODEL_H
-diff --git a/libs/models/CMakeLists.txt b/libs/models/CMakeLists.txt
-index cbabfaa..804456b 100644
---- a/libs/models/CMakeLists.txt
-+++ b/libs/models/CMakeLists.txt
-@@ -9,18 +9,6 @@ if (POLICY CMP0063)
-     cmake_policy(SET CMP0063 NEW)
- endif (POLICY CMP0063)
- 
--set(libdatabasemodels_SRCS
--    imagemodel.cpp
--    imagefiltermodel.cpp
--    imagefiltermodelpriv.cpp
--    imagefiltermodelthreads.cpp
--    imagefiltersettings.cpp
--    imagelistmodel.cpp
--    imagesortsettings.cpp
--    imagethumbnailmodel.cpp
--    imageversionsmodel.cpp
--)
--
- set(libalbummodels_SRCS
-     imagealbummodel.cpp
-     imagealbumfiltermodel.cpp
-@@ -52,5 +40,4 @@ endif()
- #for digikam core lib
- add_library(digikamgenericmodels_src OBJECT ${libgenericmodels_SRCS})
- 
--add_library(digikamdatabasemodels_src OBJECT ${libdatabasemodels_SRCS})
--add_library(digikammodels_src OBJECT  ${libalbummodels_SRCS} ${libgenericmodels_SRCS})
-+add_library(digikammodels_src OBJECT ${libalbummodels_SRCS} ${libgenericmodels_SRCS})
-diff --git a/libs/models/imagefiltermodel.cpp b/libs/models/imagefiltermodel.cpp
-deleted file mode 100644
-index 3d57e05..0000000
---- a/libs/models/imagefiltermodel.cpp
-+++ /dev/null
-@@ -1,1116 +0,0 @@
--/* ============================================================
-- *
-- * This file is a part of digiKam project
-- * http://www.digikam.org
-- *
-- * Date        : 2009-03-05
-- * Description : Qt item model for database entries
-- *
-- * Copyright (C) 2009-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
-- * Copyright (C) 2011-2017 by Gilles Caulier <caulier dot gilles at gmail dot com>
-- * Copyright (C)      2010 by Andi Clemens <andi dot clemens at gmail dot com>
-- * Copyright (C)      2011 by Michael G. Hansen <mike at mghansen dot de>
-- * Copyright (C)      2014 by Mohamed Anwer <m dot anwer at gmx dot com>
-- *
-- * This program is free software; you can redistribute it
-- * and/or modify it under the terms of the GNU General
-- * Public License as published by the Free Software Foundation;
-- * either version 2, or (at your option)
-- * any later version.
-- *
-- * This program is distributed in the hope that it will be useful,
-- * but WITHOUT ANY WARRANTY; without even the implied warranty of
-- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-- * GNU General Public License for more details.
-- *
-- * ============================================================ */
--
--#include "imagefiltermodel.h"
--#include "imagefiltermodelpriv.h"
--#include "imagefiltermodelthreads.h"
--
--// Local includes
--
--#include "digikam_debug.h"
--#include "coredbaccess.h"
--#include "coredbchangesets.h"
--#include "coredbwatch.h"
--#include "imageinfolist.h"
--#include "imagemodel.h"
--
--namespace Digikam
--{
--
--ImageSortFilterModel::ImageSortFilterModel(QObject* parent)
--    : DCategorizedSortFilterProxyModel(parent), m_chainedModel(0)
--{
--}
--
--void ImageSortFilterModel::setSourceImageModel(ImageModel* source)
--{
--    if (m_chainedModel)
--    {
--        m_chainedModel->setSourceImageModel(source);
--    }
--    else
--    {
--        setDirectSourceImageModel(source);
--    }
--}
--
--void ImageSortFilterModel::setSourceFilterModel(ImageSortFilterModel* source)
--{
--    if (source)
--    {
--        ImageModel* const model = sourceImageModel();
--
--        if (model)
--        {
--            source->setSourceImageModel(model);
--        }
--    }
--
--    m_chainedModel = source;
--    setSourceModel(source);
--}
--
--void ImageSortFilterModel::setDirectSourceImageModel(ImageModel* model)
--{
--    setSourceModel(model);
--}
--
--void ImageSortFilterModel::setSourceModel(QAbstractItemModel* model)
--{
--    // made it protected, only setSourceImageModel is public
--    DCategorizedSortFilterProxyModel::setSourceModel(model);
--}
--
--ImageModel* ImageSortFilterModel::sourceImageModel() const
--{
--    if (m_chainedModel)
--    {
--        return m_chainedModel->sourceImageModel();
--    }
--
--    return static_cast<ImageModel*>(sourceModel());
--}
--
--ImageSortFilterModel* ImageSortFilterModel::sourceFilterModel() const
--{
--    return m_chainedModel;
--}
--
--ImageFilterModel* ImageSortFilterModel::imageFilterModel() const
--{
--    // reimplemented in ImageFilterModel
--    if (m_chainedModel)
--    {
--        return m_chainedModel->imageFilterModel();
--    }
--
--    return 0;
--}
--
--QModelIndex ImageSortFilterModel::mapToSourceImageModel(const QModelIndex& index) const
--{
--    if (m_chainedModel)
--    {
--        return m_chainedModel->mapToSourceImageModel(mapToSource(index));
--    }
--
--    return mapToSource(index);
--}
--
--QModelIndex ImageSortFilterModel::mapFromSourceImageModel(const QModelIndex& albummodel_index) const
--{
--    if (m_chainedModel)
--    {
--        return mapFromSource(m_chainedModel->mapFromSourceImageModel(albummodel_index));
--    }
--
--    return mapFromSource(albummodel_index);
--}
--
--
--QModelIndex ImageSortFilterModel::mapFromDirectSourceToSourceImageModel(const QModelIndex& sourceModel_index) const
--{
--    if (m_chainedModel)
--    {
--        return m_chainedModel->mapToSourceImageModel(sourceModel_index);
--    }
--    return sourceModel_index;
--}
--
--// -------------- Convenience mappers -------------------------------------------------------------------
--
--QList<QModelIndex> ImageSortFilterModel::mapListToSource(const QList<QModelIndex>& indexes) const
--{
--    QList<QModelIndex> sourceIndexes;
--    foreach(const QModelIndex& index, indexes)
--    {
--        sourceIndexes << mapToSourceImageModel(index);
--    }
--    return sourceIndexes;
--}
--
--QList<QModelIndex> ImageSortFilterModel::mapListFromSource(const QList<QModelIndex>& sourceIndexes) const
--{
--    QList<QModelIndex> indexes;
--    foreach(const QModelIndex& index, sourceIndexes)
--    {
--        indexes << mapFromSourceImageModel(index);
--    }
--    return indexes;
--}
--
--ImageInfo ImageSortFilterModel::imageInfo(const QModelIndex& index) const
--{
--    return sourceImageModel()->imageInfo(mapToSourceImageModel(index));
--}
--
--qlonglong ImageSortFilterModel::imageId(const QModelIndex& index) const
--{
--    return sourceImageModel()->imageId(mapToSourceImageModel(index));
--}
--
--QList<ImageInfo> ImageSortFilterModel::imageInfos(const QList<QModelIndex>& indexes) const
--{
--    QList<ImageInfo> infos;
--    ImageModel* const model = sourceImageModel();
--
--    foreach(const QModelIndex& index, indexes)
--    {
--        infos << model->imageInfo(mapToSourceImageModel(index));
--    }
--
--    return infos;
--}
--
--QList<qlonglong> ImageSortFilterModel::imageIds(const QList<QModelIndex>& indexes) const
--{
--    QList<qlonglong> ids;
--    ImageModel* const model = sourceImageModel();
--
--    foreach(const QModelIndex& index, indexes)
--    {
--        ids << model->imageId(mapToSourceImageModel(index));
--    }
--
--    return ids;
--}
--
--QModelIndex ImageSortFilterModel::indexForPath(const QString& filePath) const
--{
--    return mapFromSourceImageModel(sourceImageModel()->indexForPath(filePath));
--}
--
--QModelIndex ImageSortFilterModel::indexForImageInfo(const ImageInfo& info) const
--{
--    return mapFromSourceImageModel(sourceImageModel()->indexForImageInfo(info));
--}
--
--QModelIndex ImageSortFilterModel::indexForImageId(qlonglong id) const
--{
--    return mapFromSourceImageModel(sourceImageModel()->indexForImageId(id));
--}
--
--QList<ImageInfo> ImageSortFilterModel::imageInfosSorted() const
--{
--    QList<ImageInfo>  infos;
--    const int         size  = rowCount();
--    ImageModel* const model = sourceImageModel();
--
--    for (int i=0; i<size; ++i)
--    {
--        infos << model->imageInfo(mapToSourceImageModel(index(i, 0)));
--    }
--
--    return infos;
--}
--
--// --------------------------------------------------------------------------------------------
--
--ImageFilterModel::ImageFilterModel(QObject* parent)
--    : ImageSortFilterModel(parent),
--      d_ptr(new ImageFilterModelPrivate)
--{
--    d_ptr->init(this);
--}
--
--ImageFilterModel::ImageFilterModel(ImageFilterModelPrivate& dd, QObject* parent)
--    : ImageSortFilterModel(parent),
--      d_ptr(&dd)
--{
--    d_ptr->init(this);
--}
--
--ImageFilterModel::~ImageFilterModel()
--{
--    Q_D(ImageFilterModel);
--    delete d;
--}
--
--void ImageFilterModel::setDirectSourceImageModel(ImageModel* sourceModel)
--{
--    Q_D(ImageFilterModel);
--
--    if (d->imageModel)
--    {
--        d->imageModel->unsetPreprocessor(d);
--        disconnect(d->imageModel, SIGNAL(modelReset()),
--                   this, SLOT(slotModelReset()));
--        slotModelReset();
--    }
--
--    d->imageModel = sourceModel;
--
--    if (d->imageModel)
--    {
--        d->imageModel->setPreprocessor(d);
--
--        connect(d->imageModel, SIGNAL(preprocess(QList<ImageInfo>,QList<QVariant>)),
--                d, SLOT(preprocessInfos(QList<ImageInfo>,QList<QVariant>)));
--
--        connect(d->imageModel, SIGNAL(processAdded(QList<ImageInfo>,QList<QVariant>)),
--                d, SLOT(processAddedInfos(QList<ImageInfo>,QList<QVariant>)));
--
--        connect(d, SIGNAL(reAddImageInfos(QList<ImageInfo>,QList<QVariant>)),
--                d->imageModel, SLOT(reAddImageInfos(QList<ImageInfo>,QList<QVariant>)));
--
--        connect(d, SIGNAL(reAddingFinished()),
--                d->imageModel, SLOT(reAddingFinished()));
--
--        connect(d->imageModel, SIGNAL(modelReset()),
--                this, SLOT(slotModelReset()));
--
--        connect(d->imageModel, SIGNAL(imageChange(ImageChangeset,QItemSelection)),
--                this, SLOT(slotImageChange(ImageChangeset)));
--
--        connect(d->imageModel, SIGNAL(imageTagChange(ImageTagChangeset,QItemSelection)),
--                this, SLOT(slotImageTagChange(ImageTagChangeset)));
--    }
--
--    setSourceModel(d->imageModel);
--}
--
--QVariant ImageFilterModel::data(const QModelIndex& index, int role) const
--{
--    Q_D(const ImageFilterModel);
--
--    if (!index.isValid())
--    {
--        return QVariant();
--    }
--
--    switch (role)
--    {
--            // Attention: This breaks should there ever be another filter model between this and the ImageModel
--
--        case DCategorizedSortFilterProxyModel::CategoryDisplayRole:
--            return categoryIdentifier(d->imageModel->imageInfoRef(mapToSource(index)));
--        case CategorizationModeRole:
--            return d->sorter.categorizationMode;
--        case SortOrderRole:
--            return d->sorter.sortRole;
--            //case CategoryCountRole:
--            //  return categoryCount(d->imageModel->imageInfoRef(mapToSource(index)));
--        case CategoryAlbumIdRole:
--            return d->imageModel->imageInfoRef(mapToSource(index)).albumId();
--        case CategoryFormatRole:
--            return d->imageModel->imageInfoRef(mapToSource(index)).format();
--        case GroupIsOpenRole:
--            return d->groupFilter.isAllOpen() ||
--                   d->groupFilter.isOpen(d->imageModel->imageInfoRef(mapToSource(index)).id());
--        case ImageFilterModelPointerRole:
--            return QVariant::fromValue(const_cast<ImageFilterModel*>(this));
--    }
--
--    return DCategorizedSortFilterProxyModel::data(index, role);
--}
--
--ImageFilterModel* ImageFilterModel::imageFilterModel() const
--{
--    return const_cast<ImageFilterModel*>(this);
--}
--
--DatabaseFields::Set ImageFilterModel::suggestedWatchFlags() const
--{
--    DatabaseFields::Set watchFlags;
--    watchFlags |= DatabaseFields::Name   | DatabaseFields::FileSize     | DatabaseFields::ModificationDate;
--    watchFlags |= DatabaseFields::Rating | DatabaseFields::CreationDate | DatabaseFields::Orientation |
--                  DatabaseFields::Width  | DatabaseFields::Height;
--    watchFlags |= DatabaseFields::Comment;
--    watchFlags |= DatabaseFields::ImageRelations;
--    return watchFlags;
--}
--
--// -------------- Filter settings --------------
--
--void ImageFilterModel::setDayFilter(const QList<QDateTime>& days)
--{
--    Q_D(ImageFilterModel);
--    d->filter.setDayFilter(days);
--    setImageFilterSettings(d->filter);
--}
--
--void ImageFilterModel::setTagFilter(const QList<int>& includedTags, const QList<int>& excludedTags,
--                                    ImageFilterSettings::MatchingCondition matchingCond,
--                                    bool showUnTagged, const QList<int>& clTagIds, const QList<int>& plTagIds)
--{
--    Q_D(ImageFilterModel);
--    d->filter.setTagFilter(includedTags, excludedTags, matchingCond, showUnTagged, clTagIds, plTagIds);
--    setImageFilterSettings(d->filter);
--}
--
--void ImageFilterModel::setRatingFilter(int rating, ImageFilterSettings::RatingCondition ratingCond, bool isUnratedExcluded)
--{
--    Q_D(ImageFilterModel);
--    d->filter.setRatingFilter(rating, ratingCond, isUnratedExcluded);
--    setImageFilterSettings(d->filter);
--}
--
--void ImageFilterModel::setUrlWhitelist(const QList<QUrl> urlList, const QString& id)
--{
--    Q_D(ImageFilterModel);
--    d->filter.setUrlWhitelist(urlList, id);
--    setImageFilterSettings(d->filter);
--}
--
--void ImageFilterModel::setIdWhitelist(const QList<qlonglong>& idList, const QString& id)
--{
--    Q_D(ImageFilterModel);
--    d->filter.setIdWhitelist(idList, id);
--    setImageFilterSettings(d->filter);
--}
--
--void ImageFilterModel::setMimeTypeFilter(int mimeTypeFilter)
--{
--    Q_D(ImageFilterModel);
--    d->filter.setMimeTypeFilter(mimeTypeFilter);
--    setImageFilterSettings(d->filter);
--}
--
--void ImageFilterModel::setGeolocationFilter(const ImageFilterSettings::GeolocationCondition& condition)
--{
--    Q_D(ImageFilterModel);
--    d->filter.setGeolocationFilter(condition);
--    setImageFilterSettings(d->filter);
--}
--
--void ImageFilterModel::setTextFilter(const SearchTextFilterSettings& settings)
--{
--    Q_D(ImageFilterModel);
--    d->filter.setTextFilter(settings);
--    setImageFilterSettings(d->filter);
--}
--
--void ImageFilterModel::setImageFilterSettings(const ImageFilterSettings& settings)
--{
--    Q_D(ImageFilterModel);
--
--    {
--        QMutexLocker lock(&d->mutex);
--        d->version++;
--        d->filter              = settings;
--        d->filterCopy          = settings;
--        d->versionFilterCopy   = d->versionFilter;
--        d->groupFilterCopy     = d->groupFilter;
--
--        d->needPrepareComments = settings.isFilteringByText();
--        d->needPrepareTags     = settings.isFilteringByTags();
--        d->needPrepareGroups   = true;
--        d->needPrepare         = d->needPrepareComments || d->needPrepareTags || d->needPrepareGroups;
--
--        d->hasOneMatch         = false;
--        d->hasOneMatchForText  = false;
--    }
--
--    d->filterResults.clear();
--
--    //d->categoryCountHashInt.clear();
--    //d->categoryCountHashString.clear();
--    if (d->imageModel)
--    {
--        d->infosToProcess(d->imageModel->imageInfos());
--    }
--
--    emit filterSettingsChanged(settings);
--}
--
--void ImageFilterModel::setVersionManagerSettings(const VersionManagerSettings& settings)
--{
--    Q_D(ImageFilterModel);
--    d->versionFilter.setVersionManagerSettings(settings);
--    setVersionImageFilterSettings(d->versionFilter);
--}
--
--void ImageFilterModel::setExceptionList(const QList<qlonglong>& idList, const QString& id)
--{
--    Q_D(ImageFilterModel);
--    d->versionFilter.setExceptionList(idList, id);
--    setVersionImageFilterSettings(d->versionFilter);
--}
--
--void ImageFilterModel::setVersionImageFilterSettings(const VersionImageFilterSettings& settings)
--{
--    Q_D(ImageFilterModel);
--    d->versionFilter = settings;
--    slotUpdateFilter();
--}
--
--bool ImageFilterModel::isGroupOpen(qlonglong group) const
--{
--    Q_D(const ImageFilterModel);
--    return d->groupFilter.isOpen(group);
--}
--
--bool ImageFilterModel::isAllGroupsOpen() const
--{
--    Q_D(const ImageFilterModel);
--    return d->groupFilter.isAllOpen();
--}
--
--void ImageFilterModel::setGroupOpen(qlonglong group, bool open)
--{
--    Q_D(ImageFilterModel);
--    d->groupFilter.setOpen(group, open);
--    setGroupImageFilterSettings(d->groupFilter);
--}
--
--void ImageFilterModel::toggleGroupOpen(qlonglong group)
--{
--    setGroupOpen(group, !isGroupOpen(group));
--}
--
--void ImageFilterModel::setAllGroupsOpen(bool open)
--{
--    Q_D(ImageFilterModel);
--    d->groupFilter.setAllOpen(open);
--    setGroupImageFilterSettings(d->groupFilter);
--}
--
--void ImageFilterModel::setGroupImageFilterSettings(const GroupImageFilterSettings& settings)
--{
--    Q_D(ImageFilterModel);
--    d->groupFilter = settings;
--    slotUpdateFilter();
--}
--
--void ImageFilterModel::slotUpdateFilter()
--{
--    Q_D(ImageFilterModel);
--    setImageFilterSettings(d->filter);
--}
--
--ImageFilterSettings ImageFilterModel::imageFilterSettings() const
--{
--    Q_D(const ImageFilterModel);
--    return d->filter;
--}
--
--ImageSortSettings ImageFilterModel::imageSortSettings() const
--{
--    Q_D(const ImageFilterModel);
--    return d->sorter;
--}
--
--VersionImageFilterSettings ImageFilterModel::versionImageFilterSettings() const
--{
--    Q_D(const ImageFilterModel);
--    return d->versionFilter;
--}
--
--GroupImageFilterSettings ImageFilterModel::groupImageFilterSettings() const
--{
--    Q_D(const ImageFilterModel);
--    return d->groupFilter;
--}
--
--void ImageFilterModel::slotModelReset()
--{
--    Q_D(ImageFilterModel);
--    {
--        QMutexLocker lock(&d->mutex);
--        // discard all packages on the way that are marked as send out for re-add
--        d->lastDiscardVersion = d->version;
--        d->sentOutForReAdd    = 0;
--        // discard all packages on the way
--        d->version++;
--        d->sentOut            = 0;
--
--        d->hasOneMatch        = false;
--        d->hasOneMatchForText = false;
--    }
--    d->filterResults.clear();
--}
--
--bool ImageFilterModel::filterAcceptsRow(int source_row, const QModelIndex& source_parent) const
--{
--    Q_D(const ImageFilterModel);
--
--    if (source_parent.isValid())
--    {
--        return false;
--    }
--
--    qlonglong id                              = d->imageModel->imageId(source_row);
--    QHash<qlonglong, bool>::const_iterator it = d->filterResults.constFind(id);
--
--    if (it != d->filterResults.constEnd())
--    {
--        return it.value();
--    }
--
--    // usually done in thread and cache, unless source model changed
--    ImageInfo info = d->imageModel->imageInfo(source_row);
--    bool match     = d->filter.matches(info);
--    match          = match ? d->versionFilter.matches(info) : false;
--
--    return match ? d->groupFilter.matches(info) : false;
--}
--
--void ImageFilterModel::setSendImageInfoSignals(bool sendSignals)
--{
--    if (sendSignals)
--    {
--        connect(this, SIGNAL(rowsInserted(QModelIndex,int,int)),
--                this, SLOT(slotRowsInserted(QModelIndex,int,int)));
--
--        connect(this, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
--                this, SLOT(slotRowsAboutToBeRemoved(QModelIndex,int,int)));
--    }
--    else
--    {
--        disconnect(this, SIGNAL(rowsInserted(QModelIndex,int,int)),
--                   this, SLOT(slotRowsInserted(QModelIndex,int,int)));
--
--        disconnect(this, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
--                   this, SLOT(slotRowsAboutToBeRemoved(QModelIndex,int,int)));
--    }
--}
--
--void ImageFilterModel::slotRowsInserted(const QModelIndex& /*parent*/, int start, int end)
--{
--    QList<ImageInfo> infos;
--
--    for (int i=start; i<=end; ++i)
--    {
--        infos << imageInfo(index(i, 0));
--    }
--
--    emit imageInfosAdded(infos);
--}
--
--void ImageFilterModel::slotRowsAboutToBeRemoved(const QModelIndex& /*parent*/, int start, int end)
--{
--    QList<ImageInfo> infos;
--
--    for (int i=start; i<=end; ++i)
--    {
--        infos << imageInfo(index(i, 0));
--    }
--
--    emit imageInfosAboutToBeRemoved(infos);
--}
--
--// -------------- Threaded preparation & filtering --------------
--
--void ImageFilterModel::addPrepareHook(ImageFilterModelPrepareHook* hook)
--{
--    Q_D(ImageFilterModel);
--    QMutexLocker lock(&d->mutex);
--    d->prepareHooks << hook;
--}
--
--void ImageFilterModel::removePrepareHook(ImageFilterModelPrepareHook* hook)
--{
--    Q_D(ImageFilterModel);
--    QMutexLocker lock(&d->mutex);
--    d->prepareHooks.removeAll(hook);
--}
--
--void ImageFilterModelPreparer::process(ImageFilterModelTodoPackage package)
--{
--    if (!checkVersion(package))
--    {
--        emit discarded(package);
--        return;
--    }
--
--    // get thread-local copy
--    bool needPrepareTags, needPrepareComments, needPrepareGroups;
--    QList<ImageFilterModelPrepareHook*> prepareHooks;
--    {
--        QMutexLocker lock(&d->mutex);
--        needPrepareTags     = d->needPrepareTags;
--        needPrepareComments = d->needPrepareComments;
--        needPrepareGroups   = d->needPrepareGroups;
--        prepareHooks        = d->prepareHooks;
--    }
--
--    //TODO: Make efficient!!
--    if (needPrepareComments)
--    {
--        foreach(const ImageInfo& info, package.infos)
--        {
--            info.comment();
--        }
--    }
--
--    if (!checkVersion(package))
--    {
--        emit discarded(package);
--        return;
--    }
--
--    // The downside of QVector: At some point, we may need a QList for an API.
--    // Nonetheless, QList and ImageInfo is fast. We could as well
--    // reimplement ImageInfoList to ImageInfoVector (internally with templates?)
--    ImageInfoList infoList;
--
--    if (needPrepareTags || needPrepareGroups)
--    {
--        infoList = package.infos.toList();
--    }
--
--    if (needPrepareTags)
--    {
--        infoList.loadTagIds();
--    }
--
--    if (needPrepareGroups)
--    {
--        infoList.loadGroupImageIds();
--    }
--
--    foreach(ImageFilterModelPrepareHook* hook, prepareHooks)
--    {
--        hook->prepare(package.infos);
--    }
--
--    emit processed(package);
--}
--
--void ImageFilterModelFilterer::process(ImageFilterModelTodoPackage package)
--{
--    if (!checkVersion(package))
--    {
--        emit discarded(package);
--        return;
--    }
--
--    // get thread-local copy
--    ImageFilterSettings        localFilter;
--    VersionImageFilterSettings localVersionFilter;
--    GroupImageFilterSettings   localGroupFilter;
--    bool                       hasOneMatch;
--    bool                       hasOneMatchForText;
--    {
--        QMutexLocker lock(&d->mutex);
--        localFilter        = d->filterCopy;
--        localVersionFilter = d->versionFilterCopy;
--        localGroupFilter   = d->groupFilterCopy;
--        hasOneMatch        = d->hasOneMatch;
--        hasOneMatchForText = d->hasOneMatchForText;
--    }
--
--    // Actual filtering. The variants to spare checking hasOneMatch over and over again.
--    if (hasOneMatch && hasOneMatchForText)
--    {
--        foreach(const ImageInfo& info, package.infos)
--        {
--            package.filterResults[info.id()] = localFilter.matches(info)        &&
--                                               localVersionFilter.matches(info) &&
--                                               localGroupFilter.matches(info);
--        }
--    }
--    else if (hasOneMatch)
--    {
--        bool matchForText;
--
--        foreach(const ImageInfo& info, package.infos)
--        {
--            package.filterResults[info.id()] = localFilter.matches(info, &matchForText) &&
--                                               localVersionFilter.matches(info)         &&
--                                               localGroupFilter.matches(info);
--
--            if (matchForText)
--            {
--                hasOneMatchForText = true;
--            }
--        }
--    }
--    else
--    {
--        bool result, matchForText;
--
--        foreach(const ImageInfo& info, package.infos)
--        {
--            result                           = localFilter.matches(info, &matchForText) &&
--                                               localVersionFilter.matches(info)         &&
--                                               localGroupFilter.matches(info);
--            package.filterResults[info.id()] = result;
--
--            if (result)
--            {
--                hasOneMatch = true;
--            }
--
--            if (matchForText)
--            {
--                hasOneMatchForText = true;
--            }
--        }
--    }
--
--    if (checkVersion(package))
--    {
--        QMutexLocker lock(&d->mutex);
--        d->hasOneMatch        = hasOneMatch;
--        d->hasOneMatchForText = hasOneMatchForText;
--    }
--
--    emit processed(package);
--}
--
--// -------------- Sorting and Categorization -------------------------------------------------------
--
--void ImageFilterModel::setImageSortSettings(const ImageSortSettings& sorter)
--{
--    Q_D(ImageFilterModel);
--    d->sorter = sorter;
--    setCategorizedModel(d->sorter.categorizationMode != ImageSortSettings::NoCategories);
--    invalidate();
--}
--
--void ImageFilterModel::setCategorizationMode(ImageSortSettings::CategorizationMode mode)
--{
--    Q_D(ImageFilterModel);
--    d->sorter.setCategorizationMode(mode);
--    setImageSortSettings(d->sorter);
--}
--
--void ImageFilterModel::setCategorizationSortOrder(ImageSortSettings::SortOrder order)
--{
--    Q_D(ImageFilterModel);
--    d->sorter.setCategorizationSortOrder(order);
--    setImageSortSettings(d->sorter);
--}
--
--void ImageFilterModel::setSortRole(ImageSortSettings::SortRole role)
--{
--    Q_D(ImageFilterModel);
--    d->sorter.setSortRole(role);
--    setImageSortSettings(d->sorter);
--}
--
--void ImageFilterModel::setSortOrder(ImageSortSettings::SortOrder order)
--{
--    Q_D(ImageFilterModel);
--    d->sorter.setSortOrder(order);
--    setImageSortSettings(d->sorter);
--}
--
--void ImageFilterModel::setStringTypeNatural(bool natural)
--{
--    Q_D(ImageFilterModel);
--    d->sorter.setStringTypeNatural(natural);
--    setImageSortSettings(d->sorter);
--}
--
--int ImageFilterModel::compareCategories(const QModelIndex& left, const QModelIndex& right) const
--{
--    // source indexes
--    Q_D(const ImageFilterModel);
--
--    if (!d->sorter.isCategorized())
--    {
--        return 0;
--    }
--
--    if (!left.isValid() || !right.isValid())
--    {
--        return -1;
--    }
--
--    const ImageInfo& leftInfo  = d->imageModel->imageInfoRef(left);
--    const ImageInfo& rightInfo = d->imageModel->imageInfoRef(right);
--
--    // Check grouping
--    qlonglong leftGroupImageId = leftInfo.groupImageId();
--    qlonglong rightGroupImageId = rightInfo.groupImageId();
--
--    return compareInfosCategories(leftGroupImageId  == -1 ? leftInfo  : ImageInfo(leftGroupImageId),
--                                  rightGroupImageId == -1 ? rightInfo : ImageInfo(rightGroupImageId));
--}
--
--bool ImageFilterModel::subSortLessThan(const QModelIndex& left, const QModelIndex& right) const
--{
--    // source indexes
--    Q_D(const ImageFilterModel);
--
--    if (!left.isValid() || !right.isValid())
--    {
--        return true;
--    }
--
--    if (left == right)
--    {
--        return false;
--    }
--
--    const ImageInfo& leftInfo  = d->imageModel->imageInfoRef(left);
--    const ImageInfo& rightInfo = d->imageModel->imageInfoRef(right);
--
--    if (leftInfo == rightInfo)
--    {
--        return d->sorter.lessThan(left.data(ImageModel::ExtraDataRole), right.data(ImageModel::ExtraDataRole));
--    }
--
--    // Check grouping
--    qlonglong leftGroupImageId = leftInfo.groupImageId();
--    qlonglong rightGroupImageId = rightInfo.groupImageId();
--
--    // Either no grouping (-1), or same group image, or same image
--    if (leftGroupImageId == rightGroupImageId)
--    {
--        return infosLessThan(leftInfo, rightInfo);
--    }
--
--   // We have grouping to handle
--
--    // Is one grouped on the other? Sort behind leader.
--    if (leftGroupImageId == rightInfo.id())
--    {
--        return false;
--    }
--    if (rightGroupImageId == leftInfo.id())
--    {
--        return true;
--    }
--
--    // Use the group leader for sorting
--    return infosLessThan(leftGroupImageId  == -1 ? leftInfo  : ImageInfo(leftGroupImageId),
--                         rightGroupImageId == -1 ? rightInfo : ImageInfo(rightGroupImageId));
--}
--
--int ImageFilterModel::compareInfosCategories(const ImageInfo& left, const ImageInfo& right) const
--{
--    // Note: reimplemented in ImageAlbumFilterModel
--    Q_D(const ImageFilterModel);
--    return d->sorter.compareCategories(left, right);
--}
--
--// Feel free to optimize. QString::number is 3x slower.
--static inline QString fastNumberToString(int id)
--{
--    const int size = sizeof(int) * 2;
--    char c[size+1];
--    c[size]    = '\0';
--    char* p    = c;
--    int number = id;
--
--    for (int i=0; i<size; ++i)
--    {
--        *p = 'a' + (number & 0xF);
--        number >>= 4;
--        ++p;
--    }
--
--    return QString::fromLatin1(c);
--}
--
--QString ImageFilterModel::categoryIdentifier(const ImageInfo& i) const
--{
--    Q_D(const ImageFilterModel);
--
--    if (!d->sorter.isCategorized())
--    {
--        return QString();
--    }
--
--    qlonglong groupedImageId = i.groupImageId();
--    ImageInfo info = groupedImageId == -1 ? i : ImageInfo(groupedImageId);
--
--    switch (d->sorter.categorizationMode)
--    {
--        case ImageSortSettings::NoCategories:
--            return QString();
--        case ImageSortSettings::OneCategory:
--            return QString();
--        case ImageSortSettings::CategoryByAlbum:
--            return fastNumberToString(info.albumId());
--        case ImageSortSettings::CategoryByFormat:
--            return info.format();
--        default:
--            return QString();
--    }
--}
--
--bool ImageFilterModel::infosLessThan(const ImageInfo& left, const ImageInfo& right) const
--{
--    Q_D(const ImageFilterModel);
--    return d->sorter.lessThan(left, right);
--}
--
--// -------------- Watching changes -----------------------------------------------------------------
--
--void ImageFilterModel::slotImageTagChange(const ImageTagChangeset& changeset)
--{
--    Q_D(ImageFilterModel);
--
--    if (!d->imageModel || d->imageModel->isEmpty())
--    {
--        return;
--    }
--
--    // already scheduled to re-filter?
--    if (d->updateFilterTimer->isActive())
--    {
--        return;
--    }
--
--    // do we filter at all?
--    if (!d->versionFilter.isFilteringByTags() &&
--        !d->filter.isFilteringByTags()        &&
--        !d->filter.isFilteringByText())
--    {
--        return;
--    }
--
--    // is one of our images affected?
--    foreach(const qlonglong& id, changeset.ids())
--    {
--        // if one matching image id is found, trigger a refresh
--        if (d->imageModel->hasImage(id))
--        {
--            d->updateFilterTimer->start();
--            return;
--        }
--    }
--}
--
--void ImageFilterModel::slotImageChange(const ImageChangeset& changeset)
--{
--    Q_D(ImageFilterModel);
--
--    if (!d->imageModel || d->imageModel->isEmpty())
--    {
--        return;
--    }
--
--    // already scheduled to re-filter?
--    if (d->updateFilterTimer->isActive())
--    {
--        return;
--    }
--
--    // is one of the values affected that we filter or sort by?
--    DatabaseFields::Set set = changeset.changes();
--    bool sortAffected       = (set & d->sorter.watchFlags());
--    bool filterAffected     = (set & d->filter.watchFlags()) || (set & d->groupFilter.watchFlags());
--
--    if (!sortAffected && !filterAffected)
--    {
--        return;
--    }
--
--    // is one of our images affected?
--    bool imageAffected = false;
--
--    foreach(const qlonglong& id, changeset.ids())
--    {
--        // if one matching image id is found, trigger a refresh
--        if (d->imageModel->hasImage(id))
--        {
--            imageAffected = true;
--            break;
--        }
--    }
--
--    if (!imageAffected)
--    {
--        return;
--    }
--
--    if (filterAffected)
--    {
--        d->updateFilterTimer->start();
--    }
--    else
--    {
--        invalidate();    // just resort, reuse filter results
--    }
--}
--
--// -------------------------------------------------------------------------------------------------------
--
--NoDuplicatesImageFilterModel::NoDuplicatesImageFilterModel(QObject* parent)
--    : ImageSortFilterModel(parent)
--{
--}
--
--bool NoDuplicatesImageFilterModel::filterAcceptsRow(int source_row, const QModelIndex& source_parent) const
--{
--    QModelIndex index = sourceModel()->index(source_row, 0, source_parent);
--
--    if (index.data(ImageModel::ExtraDataDuplicateCount).toInt() <= 1)
--    {
--        return true;
--    }
--
--    QModelIndex previousIndex = sourceModel()->index(source_row - 1, 0, source_parent);
--
--    if (!previousIndex.isValid())
--    {
--        return true;
--    }
--
--    if (sourceImageModel()->imageId(mapFromDirectSourceToSourceImageModel(index)) == sourceImageModel()->imageId(mapFromDirectSourceToSourceImageModel(previousIndex)))
--    {
--        return false;
--    }
--    return true;
--}
--
--/*
--void NoDuplicatesImageFilterModel::setSourceModel(QAbstractItemModel* model)
--{
--    if (sourceModel())
--    {
--    }
--
--    ImageSortFilterModel::setSourceModel(model);
--
--    if (sourceModel())
--    {
--        connect(sourceModel(), SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
--                this, SLOT(slotRowsAboutToBeRemoved(QModelIndex,int,int)));
--    }
--}
--
--void NoDuplicatesImageFilterModel::slotRowsAboutToBeRemoved(const QModelIndex& parent, int begin, int end)
--{
--    bool needInvalidate = false;
--
--    for (int i = begin; i<=end; ++i)
--    {
--        QModelIndex index = sourceModel()->index(i, 0, parent);
--
--        // filtered out by us?
--        if (!mapFromSource(index).isValid())
--        {
--            continue;
--        }
--
--        QModelIndex sourceIndex = mapFromDirectSourceToSourceImageModel(index);
--        qlonglong id = sourceImageModel()->imageId(sourceIndex);
--
--        if (sourceImageModel()->numberOfIndexesForImageId(id) > 1)
--        {
--            needInvalidate = true;
--        }
--    }
--}*/
--
--} // namespace Digikam
-diff --git a/libs/models/imagefiltermodel.h b/libs/models/imagefiltermodel.h
-deleted file mode 100644
-index d131b3e..0000000
---- a/libs/models/imagefiltermodel.h
-+++ /dev/null
-@@ -1,299 +0,0 @@
--/* ============================================================
-- *
-- * This file is a part of digiKam project
-- * http://www.digikam.org
-- *
-- * Date        : 2009-03-05
-- * Description : Qt item model for database entries
-- *
-- * Copyright (C) 2009-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
-- * Copyright (C)      2011 by Gilles Caulier <caulier dot gilles at gmail dot com>
-- * Copyright (C)      2010 by Andi Clemens <andi dot clemens at gmail dot com>
-- * Copyright (C)      2011 by Michael G. Hansen <mike at mghansen dot de>
-- * Copyright (C)      2014 by Mohamed Anwer <m dot anwer at gmx dot com>
-- *
-- * This program is free software; you can redistribute it
-- * and/or modify it under the terms of the GNU General
-- * Public License as published by the Free Software Foundation;
-- * either version 2, or (at your option)
-- * any later version.
-- *
-- * This program is distributed in the hope that it will be useful,
-- * but WITHOUT ANY WARRANTY; without even the implied warranty of
-- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-- * GNU General Public License for more details.
-- *
-- * ============================================================ */
--
--#ifndef IMAGEFILTERMODEL_H
--#define IMAGEFILTERMODEL_H
--
--// Local includes
--
--#include "dcategorizedsortfilterproxymodel.h"
--#include "textfilter.h"
--#include "imagefiltersettings.h"
--#include "imagemodel.h"
--#include "imagesortsettings.h"
--#include "digikam_export.h"
--
--namespace Digikam
--{
--
--class ImageChangeset;
--class ImageFilterModel;
--class ImageTagChangeset;
--
--class DIGIKAM_DATABASE_EXPORT ImageFilterModelPrepareHook
--{
--public:
--
--    virtual ~ImageFilterModelPrepareHook() {};
--    virtual void prepare(const QVector<ImageInfo>& infos) = 0;
--};
--
--// -----------------------------------------------------------------------------------------------
--
--class DIGIKAM_DATABASE_EXPORT ImageSortFilterModel : public DCategorizedSortFilterProxyModel
--{
--    Q_OBJECT
--
--public:
--
--    explicit ImageSortFilterModel(QObject* parent = 0);
--
--    void        setSourceImageModel(ImageModel* model);
--    ImageModel* sourceImageModel() const;
--
--    void                  setSourceFilterModel(ImageSortFilterModel* model);
--    ImageSortFilterModel* sourceFilterModel() const;
--
--    QModelIndex mapToSourceImageModel(const QModelIndex& index) const;
--    QModelIndex mapFromSourceImageModel(const QModelIndex& imagemodel_index) const;
--    QModelIndex mapFromDirectSourceToSourceImageModel(const QModelIndex& sourceModel_index) const;
--
--    /// Convenience methods mapped to ImageModel.
--    /// Mentioned indexes returned come from the source image model.
--    QList<QModelIndex> mapListToSource(const QList<QModelIndex>& indexes) const;
--    QList<QModelIndex> mapListFromSource(const QList<QModelIndex>& sourceIndexes) const;
--
--    ImageInfo        imageInfo(const QModelIndex& index) const;
--    qlonglong        imageId(const QModelIndex& index) const;
--    QList<ImageInfo> imageInfos(const QList<QModelIndex>& indexes) const;
--    QList<qlonglong> imageIds(const QList<QModelIndex>& indexes) const;
--
--    QModelIndex indexForPath(const QString& filePath) const;
--    QModelIndex indexForImageInfo(const ImageInfo& info) const;
--    QModelIndex indexForImageId(qlonglong id) const;
--
--    /** Returns a list of all image infos, sorted according to this model.
--     *  If you do not need a sorted list, use ImageModel's imageInfos() method.
--     */
--    QList<ImageInfo> imageInfosSorted() const;
--
--    /// Returns this, any chained ImageFilterModel, or 0.
--    virtual ImageFilterModel* imageFilterModel() const;
--
--protected:
--
--    /// Reimplement if needed. Called only when model shall be set as (direct) sourceModel.
--    virtual void setDirectSourceImageModel(ImageModel* model);
--
--    // made protected
--    virtual void setSourceModel(QAbstractItemModel* model);
--
--protected:
--
--    ImageSortFilterModel* m_chainedModel;
--};
--
--// -----------------------------------------------------------------------------------------------
--
--class DIGIKAM_DATABASE_EXPORT ImageFilterModel : public ImageSortFilterModel
--{
--    Q_OBJECT
--
--public:
--
--    enum ImageFilterModelRoles
--    {
--        /// Returns the current categorization mode
--        CategorizationModeRole      = ImageModel::FilterModelRoles + 1,
--        /// Returns the current sort order
--        SortOrderRole               = ImageModel::FilterModelRoles + 2,
--        // / Returns the number of items in the index' category
--        //CategoryCountRole         = ImageModel::FilterModelRoles + 3,
--        /// Returns the id of the PAlbum of the index which is used for category
--        CategoryAlbumIdRole         = ImageModel::FilterModelRoles + 3,
--        /// Returns the format of the index which is used for category
--        CategoryFormatRole          = ImageModel::FilterModelRoles + 4,
--        /// Returns true if the given image is a group leader, and the group is opened
--        GroupIsOpenRole             = ImageModel::FilterModelRoles + 5,
--        ImageFilterModelPointerRole = ImageModel::FilterModelRoles + 50
--    };
--
--public:
--
--    explicit ImageFilterModel(QObject* parent = 0);
--    ~ImageFilterModel();
--
--    /** Add a hook to get added images for preparation tasks before they are added in the model */
--    void addPrepareHook(ImageFilterModelPrepareHook* hook);
--    void removePrepareHook(ImageFilterModelPrepareHook* hook);
--
--    /** Returns a set of DatabaseFields suggested to set as watch flags on the source ImageModel.
--     *  The contained flags will be those that this model can sort or filter by. */
--    DatabaseFields::Set suggestedWatchFlags() const;
--
--    ImageFilterSettings        imageFilterSettings() const;
--    VersionImageFilterSettings versionImageFilterSettings() const;
--    GroupImageFilterSettings   groupImageFilterSettings() const;
--    ImageSortSettings          imageSortSettings() const;
--
--    // group is identified by the id of its group leader
--    bool isGroupOpen(qlonglong group) const;
--    bool isAllGroupsOpen() const;
--
--    /// Enables sending imageInfosAdded and imageInfosAboutToBeRemoved
--    void setSendImageInfoSignals(bool sendSignals);
--
--    virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
--    virtual ImageFilterModel* imageFilterModel() const;
--
--public Q_SLOTS:
--
--    /** Changes the current version image filter settings and refilters. */
--    void setVersionImageFilterSettings(const VersionImageFilterSettings& settings);
--
--    /** Changes the current version image filter settings and refilters. */
--    void setGroupImageFilterSettings(const GroupImageFilterSettings& settings);
--
--    /** Adjust the current ImageFilterSettings.
--     *  Equivalent to retrieving the current filter settings, adjusting the parameter
--     *  and calling setImageFilterSettings.
--     *  Provided for convenience.
--     *  It is encouraged to use setImageFilterSettings if you change more than one
--     *  parameter at a time.
--     */
--    void setDayFilter(const QList<QDateTime>& days);
--    void setTagFilter(const QList<int>& includedTags, const QList<int>& excludedTags,
--                      ImageFilterSettings::MatchingCondition matchingCond, bool showUnTagged,
--                      const QList<int>& clTagIds, const QList<int>& plTagIds);
--    void setRatingFilter(int rating, ImageFilterSettings::RatingCondition ratingCond, bool isUnratedExcluded);
--    void setMimeTypeFilter(int mimeTypeFilter);
--    void setGeolocationFilter(const ImageFilterSettings::GeolocationCondition& condition);
--    void setTextFilter(const SearchTextFilterSettings& settings);
--
--    void setCategorizationMode(ImageSortSettings::CategorizationMode mode);
--    void setCategorizationSortOrder(ImageSortSettings::SortOrder order);
--    void setSortRole(ImageSortSettings::SortRole role);
--    void setSortOrder(ImageSortSettings::SortOrder order);
--    void setStringTypeNatural(bool natural);
--    void setUrlWhitelist(const QList<QUrl> urlList, const QString& id);
--    void setIdWhitelist(const QList<qlonglong>& idList, const QString& id);
--
--    void setVersionManagerSettings(const VersionManagerSettings& settings);
--    void setExceptionList(const QList<qlonglong>& idlist, const QString& id);
--
--    void setGroupOpen(qlonglong group, bool open);
--    void toggleGroupOpen(qlonglong group);
--    void setAllGroupsOpen(bool open);
--
--    /** Changes the current image filter settings and refilters. */
--    virtual void setImageFilterSettings(const ImageFilterSettings& settings);
--
--    /** Changes the current image sort settings and resorts. */
--    virtual void setImageSortSettings(const ImageSortSettings& settings);
--
--Q_SIGNALS:
--
--    /// Signals that the set filter matches at least one index
--    void filterMatches(bool matches);
--
--    /** Signals that the set text filter matches at least one entry.
--        If no text filter is set, this signal is emitted
--        with 'false' when filterMatches() is emitted.
--     */
--    void filterMatchesForText(bool matchesByText);
--
--    /** Emitted when the filter settings have been changed
--        (the model may not yet have been updated)
--     */
--    void filterSettingsChanged(const ImageFilterSettings& settings);
--
--    /** These signals need to be explicitly enabled with setSendImageInfoSignals()
--     */
--    void imageInfosAdded(const QList<ImageInfo>& infos);
--    void imageInfosAboutToBeRemoved(const QList<ImageInfo>& infos);
--
--public:
--
--    // Declared as public because of use in sub-classes.
--    class ImageFilterModelPrivate;
--
--protected:
--
--    ImageFilterModelPrivate* const d_ptr;
--
--protected:
--
--    ImageFilterModel(ImageFilterModelPrivate& dd, QObject* parent);
--
--    virtual void setDirectSourceImageModel(ImageModel* model);
--
--    virtual bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const;
--
--    virtual int  compareCategories(const QModelIndex& left, const QModelIndex& right) const;
--    virtual bool subSortLessThan(const QModelIndex& left, const QModelIndex& right) const;
--    //virtual int  categoryCount(const ImageInfo& info) const;
--
--    /** Reimplement to customize category sorting,
--     *  Return negative if category of left < category right,
--     *  Return 0 if left and right are in the same category, else return positive.
--     */
--    virtual int compareInfosCategories(const ImageInfo& left, const ImageInfo& right) const;
--
--    /** Reimplement to customize sorting. Do not take categories into account here.
--     */
--    virtual bool infosLessThan(const ImageInfo& left, const ImageInfo& right) const;
--
--    /** Returns a unique identifier for the category if info. The string need not be for user display.
--     */
--    virtual QString categoryIdentifier(const ImageInfo& info) const;
--
--protected Q_SLOTS:
--
--    void slotModelReset();
--    void slotUpdateFilter();
--
--    void slotImageTagChange(const ImageTagChangeset& changeset);
--    void slotImageChange(const ImageChangeset& changeset);
--
--    void slotRowsInserted(const QModelIndex& parent, int start, int end);
--    void slotRowsAboutToBeRemoved(const QModelIndex& parent, int start, int end);
--
--private:
--
--    Q_DECLARE_PRIVATE(ImageFilterModel)
--};
--
--// -----------------------------------------------------------------------------------------------------
--
--class DIGIKAM_DATABASE_EXPORT NoDuplicatesImageFilterModel : public ImageSortFilterModel
--{
--    Q_OBJECT
--
--public:
--
--    explicit NoDuplicatesImageFilterModel(QObject* parent = 0);
--
--protected:
--
--    virtual bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const;
--};
--
--} // namespace Digikam
--
--Q_DECLARE_METATYPE(Digikam::ImageFilterModel*)
--
--#endif // IMAGEMODEL_H
-diff --git a/libs/models/imagefiltermodelpriv.cpp b/libs/models/imagefiltermodelpriv.cpp
-deleted file mode 100644
-index 07d9e79..0000000
---- a/libs/models/imagefiltermodelpriv.cpp
-+++ /dev/null
-@@ -1,258 +0,0 @@
--/* ============================================================
-- *
-- * This file is a part of digiKam project
-- * http://www.digikam.org
-- *
-- * Date        : 2009-03-05
-- * Description : Qt item model for database entries
-- *
-- * Copyright (C) 2009-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
-- * Copyright (C) 2011-2017 by Gilles Caulier <caulier dot gilles at gmail dot com>
-- * Copyright (C)      2010 by Andi Clemens <andi dot clemens at gmail dot com>
-- * Copyright (C)      2011 by Michael G. Hansen <mike at mghansen dot de>
-- * Copyright (C)      2014 by Mohamed Anwer <m dot anwer at gmx dot com>
-- *
-- * This program is free software; you can redistribute it
-- * and/or modify it under the terms of the GNU General
-- * Public License as published by the Free Software Foundation;
-- * either version 2, or (at your option)
-- * any later version.
-- *
-- * This program is distributed in the hope that it will be useful,
-- * but WITHOUT ANY WARRANTY; without even the implied warranty of
-- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-- * GNU General Public License for more details.
-- *
-- * ============================================================ */
--
--#include "imagefiltermodelpriv.h"
--
--// Local includes
--
--#include "digikam_debug.h"
--#include "imagefiltermodelthreads.h"
--
--namespace Digikam
--{
--
--ImageFilterModel::ImageFilterModelPrivate::ImageFilterModelPrivate()
--{
--    imageModel            = 0;
--    version               = 0;
--    lastDiscardVersion    = 0;
--    sentOut               = 0;
--    sentOutForReAdd       = 0;
--    updateFilterTimer     = 0;
--    needPrepare           = false;
--    needPrepareComments   = false;
--    needPrepareTags       = false;
--    needPrepareGroups     = false;
--    preparer              = 0;
--    filterer              = 0;
--    hasOneMatch           = false;
--    hasOneMatchForText    = false;
--
--    setupWorkers();
--}
--
--ImageFilterModel::ImageFilterModelPrivate::~ImageFilterModelPrivate()
--{
--    // facilitate thread stopping
--    ++version;
--    preparer->deactivate();
--    filterer->deactivate();
--    delete preparer;
--    delete filterer;
--}
--
--void ImageFilterModel::ImageFilterModelPrivate::init(ImageFilterModel* _q)
--{
--    q = _q;
--
--    updateFilterTimer = new QTimer(this);
--    updateFilterTimer->setSingleShot(true);
--    updateFilterTimer->setInterval(250);
--
--    connect(updateFilterTimer, SIGNAL(timeout()),
--            q, SLOT(slotUpdateFilter()));
--
--    // inter-thread redirection
--    qRegisterMetaType<ImageFilterModelTodoPackage>("ImageFilterModelTodoPackage");
--}
--
--void ImageFilterModel::ImageFilterModelPrivate::preprocessInfos(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues)
--{
--    infosToProcess(infos, extraValues, true);
--}
--
--void ImageFilterModel::ImageFilterModelPrivate::processAddedInfos(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues)
--{
--    // These have already been added, we just process them afterwards
--    infosToProcess(infos, extraValues, false);
--}
--
--void ImageFilterModel::ImageFilterModelPrivate::setupWorkers()
--{
--    preparer = new ImageFilterModelPreparer(this);
--    filterer = new ImageFilterModelFilterer(this);
--
--    // A package in constructed in infosToProcess.
--    // Normal flow is infosToProcess -> preparer::process -> filterer::process -> packageFinished.
--    // If no preparation is needed, the first step is skipped.
--    // If filter version changes, both will discard old package and send them to packageDiscarded.
--
--    connect(this, SIGNAL(packageToPrepare(ImageFilterModelTodoPackage)),
--            preparer, SLOT(process(ImageFilterModelTodoPackage)));
--
--    connect(this, SIGNAL(packageToFilter(ImageFilterModelTodoPackage)),
--            filterer, SLOT(process(ImageFilterModelTodoPackage)));
--
--    connect(preparer, SIGNAL(processed(ImageFilterModelTodoPackage)),
--            filterer, SLOT(process(ImageFilterModelTodoPackage)));
--
--    connect(filterer, SIGNAL(processed(ImageFilterModelTodoPackage)),
--            this, SLOT(packageFinished(ImageFilterModelTodoPackage)));
--
--    connect(preparer, SIGNAL(discarded(ImageFilterModelTodoPackage)),
--            this, SLOT(packageDiscarded(ImageFilterModelTodoPackage)));
--
--    connect(filterer, SIGNAL(discarded(ImageFilterModelTodoPackage)),
--            this, SLOT(packageDiscarded(ImageFilterModelTodoPackage)));
--}
--
--void ImageFilterModel::ImageFilterModelPrivate::infosToProcess(const QList<ImageInfo>& infos)
--{
--    infosToProcess(infos, QList<QVariant>(), false);
--}
--
--void ImageFilterModel::ImageFilterModelPrivate::infosToProcess(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues, bool forReAdd)
--{
--    if (infos.isEmpty())
--    {
--        return;
--    }
--
--    filterer->schedule();
--
--    if (needPrepare)
--    {
--        preparer->schedule();
--    }
--
--    Q_ASSERT(extraValues.isEmpty() || infos.size() == extraValues.size());
--
--    // prepare and filter in chunks
--    const int size                      = infos.size();
--    const int maxChunkSize              = needPrepare ? PrepareChunkSize : FilterChunkSize;
--    const bool hasExtraValues           = !extraValues.isEmpty();
--    QList<ImageInfo>::const_iterator it = infos.constBegin(), end;
--    QList<QVariant>::const_iterator xit = extraValues.constBegin(), xend;
--    int index                           = 0;
--    QVector<ImageInfo>  infoVector;
--    QVector<QVariant> extraValueVector;
--
--    while (it != infos.constEnd())
--    {
--        const int chunkSize = qMin(maxChunkSize, size - index);
--        infoVector.resize(chunkSize);
--        end = it + chunkSize;
--        qCopy(it, end, infoVector.begin());
--
--        if (hasExtraValues)
--        {
--            extraValueVector.resize(chunkSize);
--            xend = xit + chunkSize;
--            qCopy(xit, xend, extraValueVector.begin());
--            xit = xend;
--        }
--
--        it    = end;
--        index += chunkSize;
--
--        ++sentOut;
--
--        if (forReAdd)
--        {
--            ++sentOutForReAdd;
--        }
--
--        if (needPrepare)
--        {
--            emit packageToPrepare(ImageFilterModelTodoPackage(infoVector, extraValueVector, version, forReAdd));
--        }
--        else
--        {
--            emit packageToFilter(ImageFilterModelTodoPackage(infoVector, extraValueVector, version, forReAdd));
--        }
--    }
--}
--
--void ImageFilterModel::ImageFilterModelPrivate::packageFinished(const ImageFilterModelTodoPackage& package)
--{
--    // check if it got discarded on the journey
--    if (package.version != version)
--    {
--        packageDiscarded(package);
--        return;
--    }
--
--    // incorporate result
--    QHash<qlonglong, bool>::const_iterator it = package.filterResults.constBegin();
--
--    for (; it != package.filterResults.constEnd(); ++it)
--    {
--        filterResults.insert(it.key(), it.value());
--    }
--
--    // re-add if necessary
--    if (package.isForReAdd)
--    {
--        emit reAddImageInfos(package.infos.toList(), package.extraValues.toList());
--
--        if (sentOutForReAdd == 1) // last package
--        {
--            emit reAddingFinished();
--        }
--    }
--
--    // decrement counters
--    --sentOut;
--
--    if (package.isForReAdd)
--    {
--        --sentOutForReAdd;
--    }
--
--    // If all packages have returned, filtered and readded, and no more are expected,
--    // and there is need to tell the filter result to the view, do that
--    if (sentOut == 0 && sentOutForReAdd == 0 && !imageModel->isRefreshing())
--    {
--        q->invalidate(); // use invalidate, not invalidateFilter only. Sorting may have changed as well.
--        emit (q->filterMatches(hasOneMatch));
--        emit (q->filterMatchesForText(hasOneMatchForText));
--        filterer->deactivate();
--        preparer->deactivate();
--    }
--}
--
--void ImageFilterModel::ImageFilterModelPrivate::packageDiscarded(const ImageFilterModelTodoPackage& package)
--{
--    // Either, the model was reset, or the filter changed
--    // In the former case throw all away, in the latter case, recycle
--    if (package.version > lastDiscardVersion)
--    {
--        // Recycle packages: Send again with current version
--        // Do not increment sentOut or sentOutForReAdd here: it was not decremented!
--
--        if (needPrepare)
--        {
--            emit packageToPrepare(ImageFilterModelTodoPackage(package.infos, package.extraValues, version, package.isForReAdd));
--        }
--        else
--        {
--            emit packageToFilter(ImageFilterModelTodoPackage(package.infos, package.extraValues, version, package.isForReAdd));
--        }
--    }
--}
--
--} // namespace Digikam
-diff --git a/libs/models/imagefiltermodelpriv.h b/libs/models/imagefiltermodelpriv.h
-deleted file mode 100644
-index a9e3f22..0000000
---- a/libs/models/imagefiltermodelpriv.h
-+++ /dev/null
-@@ -1,159 +0,0 @@
--/* ============================================================
-- *
-- * This file is a part of digiKam project
-- * http://www.digikam.org
-- *
-- * Date        : 2009-03-11
-- * Description : Qt item model for database entries - private shared header
-- *
-- * Copyright (C) 2009-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
-- *
-- * This program is free software; you can redistribute it
-- * and/or modify it under the terms of the GNU General
-- * Public License as published by the Free Software Foundation;
-- * either version 2, or (at your option)
-- * any later version.
-- *
-- * This program is distributed in the hope that it will be useful,
-- * but WITHOUT ANY WARRANTY; without even the implied warranty of
-- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-- * GNU General Public License for more details.
-- *
-- * ============================================================ */
--
--#ifndef IMAGEFILTERMODELPRIV_H
--#define IMAGEFILTERMODELPRIV_H
--
--// Qt includes
--
--#include <QHash>
--#include <QMutex>
--#include <QMutexLocker>
--#include <QSet>
--#include <QThread>
--#include <QTimer>
--#include <QWaitCondition>
--
--// Local includes
--
--#include "imageinfo.h"
--#include "imagefiltermodel.h"
--
--#include "digikam_export.h"
--// Yes, we need the EXPORT macro in a private header because
--// this private header is shared across binary objects.
--// This does NOT make this classes here any more public!
--
--namespace Digikam
--{
--
--const int PrepareChunkSize = 101;
--const int FilterChunkSize  = 2001;
--
--class ImageFilterModelTodoPackage
--{
--public:
--
--    ImageFilterModelTodoPackage()
--        : version(0), isForReAdd(false)
--    {
--    }
--
--    ImageFilterModelTodoPackage(const QVector<ImageInfo>& infos, const QVector<QVariant>& extraValues, int version, bool isForReAdd)
--        : infos(infos), extraValues(extraValues), version(version), isForReAdd(isForReAdd)
--    {
--    }
--
--    QVector<ImageInfo>     infos;
--    QVector<QVariant>      extraValues;
--    unsigned int           version;
--    bool                   isForReAdd;
--    QHash<qlonglong, bool> filterResults;
--};
--
--// ------------------------------------------------------------------------------------------------
--
--class ImageFilterModelPreparer;
--class ImageFilterModelFilterer;
--
--class DIGIKAM_DATABASE_EXPORT ImageFilterModel::ImageFilterModelPrivate : public QObject
--{
--    Q_OBJECT
--
--public:
--
--    ImageFilterModelPrivate();
--    ~ImageFilterModelPrivate();
--
--    void init(ImageFilterModel* q);
--    void setupWorkers();
--    void infosToProcess(const QList<ImageInfo>& infos);
--    void infosToProcess(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues, bool forReAdd = true);
--
--public:
--
--    ImageFilterModel*                   q;
--
--    ImageModel*                         imageModel;
--
--    ImageFilterSettings                 filter;
--    ImageSortSettings                   sorter;
--    VersionImageFilterSettings          versionFilter;
--    GroupImageFilterSettings            groupFilter;
--
--    volatile unsigned int               version;
--    unsigned int                        lastDiscardVersion;
--    unsigned int                        lastFilteredVersion;
--    int                                 sentOut;
--    int                                 sentOutForReAdd;
--
--    QTimer*                             updateFilterTimer;
--
--    bool                                needPrepare;
--    bool                                needPrepareComments;
--    bool                                needPrepareTags;
--    bool                                needPrepareGroups;
--
--    QMutex                              mutex;
--    ImageFilterSettings                 filterCopy;
--    VersionImageFilterSettings          versionFilterCopy;
--    GroupImageFilterSettings            groupFilterCopy;
--    ImageFilterModelPreparer*           preparer;
--    ImageFilterModelFilterer*           filterer;
--
--    QHash<qlonglong, bool>              filterResults;
--    bool                                hasOneMatch;
--    bool                                hasOneMatchForText;
--
--    QList<ImageFilterModelPrepareHook*> prepareHooks;
--
--/*
--    QHash<int, QSet<qlonglong> >        categoryCountHashInt;
--    QHash<QString, QSet<qlonglong> >    categoryCountHashString;
--
--public:
--
--        void cacheCategoryCount(int id, qlonglong imageid) const
--        { const_cast<ImageFilterModelPrivate*>(this)->categoryCountHashInt[id].insert(imageid); }
--        void cacheCategoryCount(const QString& id, qlonglong imageid) const
--        { const_cast<ImageFilterModelPrivate*>(this)->categoryCountHashString[id].insert(imageid); }
--*/
--
--public Q_SLOTS:
--
--    void preprocessInfos(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues);
--    void processAddedInfos(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues);
--    void packageFinished(const ImageFilterModelTodoPackage& package);
--    void packageDiscarded(const ImageFilterModelTodoPackage& package);
--
--Q_SIGNALS:
--
--    void packageToPrepare(const ImageFilterModelTodoPackage& package);
--    void packageToFilter(const ImageFilterModelTodoPackage& package);
--    void reAddImageInfos(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues);
--    void reAddingFinished();
--};
--
--} // namespace Digikam
--
--#endif // IMAGEFILTERMODELPRIV_H
-diff --git a/libs/models/imagefiltermodelthreads.cpp b/libs/models/imagefiltermodelthreads.cpp
-deleted file mode 100644
-index aa5c462..0000000
---- a/libs/models/imagefiltermodelthreads.cpp
-+++ /dev/null
-@@ -1,40 +0,0 @@
--/* ============================================================
-- *
-- * This file is a part of digiKam project
-- * http://www.digikam.org
-- *
-- * Date        : 2009-03-05
-- * Description : Qt item model for database entries
-- *
-- * Copyright (C) 2009-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
-- * Copyright (C) 2011-2017 by Gilles Caulier <caulier dot gilles at gmail dot com>
-- * Copyright (C)      2010 by Andi Clemens <andi dot clemens at gmail dot com>
-- * Copyright (C)      2011 by Michael G. Hansen <mike at mghansen dot de>
-- * Copyright (C)      2014 by Mohamed Anwer <m dot anwer at gmx dot com>
-- *
-- * This program is free software; you can redistribute it
-- * and/or modify it under the terms of the GNU General
-- * Public License as published by the Free Software Foundation;
-- * either version 2, or (at your option)
-- * any later version.
-- *
-- * This program is distributed in the hope that it will be useful,
-- * but WITHOUT ANY WARRANTY; without even the implied warranty of
-- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-- * GNU General Public License for more details.
-- *
-- * ============================================================ */
--
--#include "imagefiltermodel.h"
--#include "imagefiltermodelpriv.h"
--#include "imagefiltermodelthreads.h"
--
--namespace Digikam
--{
--
--ImageFilterModelWorker::ImageFilterModelWorker(ImageFilterModel::ImageFilterModelPrivate* const d)
--    : d(d)
--{
--}
--
--} // namespace Digikam
-diff --git a/libs/models/imagefiltermodelthreads.h b/libs/models/imagefiltermodelthreads.h
-deleted file mode 100644
-index 83fa987..0000000
---- a/libs/models/imagefiltermodelthreads.h
-+++ /dev/null
-@@ -1,100 +0,0 @@
--/* ============================================================
-- *
-- * This file is a part of digiKam project
-- * http://www.digikam.org
-- *
-- * Date        : 2009-03-11
-- * Description : Qt item model for database entries - private header
-- *
-- * Copyright (C) 2009-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
-- *
-- * This program is free software; you can redistribute it
-- * and/or modify it under the terms of the GNU General
-- * Public License as published by the Free Software Foundation;
-- * either version 2, or (at your option)
-- * any later version.
-- *
-- * This program is distributed in the hope that it will be useful,
-- * but WITHOUT ANY WARRANTY; without even the implied warranty of
-- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-- * GNU General Public License for more details.
-- *
-- * ============================================================ */
--
--#ifndef IMAGEFILTERMODELTHREADS_H
--#define IMAGEFILTERMODELTHREADS_H
--
--// Qt includes
--
--#include <QThread>
--
--// Local includes
--
--#include "digikam_export.h"
--#include "workerobject.h"
--
--namespace Digikam
--{
--
--class DIGIKAM_DATABASE_EXPORT ImageFilterModelWorker : public WorkerObject
--{
--    Q_OBJECT
--
--public:
--
--    explicit ImageFilterModelWorker(ImageFilterModel::ImageFilterModelPrivate* const d);
--
--    bool checkVersion(const ImageFilterModelTodoPackage& package)
--    {
--        return d->version == package.version;
--    }
--
--public Q_SLOTS:
--
--    virtual void process(ImageFilterModelTodoPackage package) = 0;
--
--Q_SIGNALS:
--
--    void processed(const ImageFilterModelTodoPackage& package);
--    void discarded(const ImageFilterModelTodoPackage& package);
--
--protected:
--
--    ImageFilterModel::ImageFilterModelPrivate* d;
--};
--
--// -----------------------------------------------------------------------------------------
--
--class DIGIKAM_DATABASE_EXPORT ImageFilterModelPreparer : public ImageFilterModelWorker
--{
--    Q_OBJECT
--
--public:
--
--    explicit ImageFilterModelPreparer(ImageFilterModel::ImageFilterModelPrivate* const d)
--        : ImageFilterModelWorker(d)
--    {
--    }
--
--    void process(ImageFilterModelTodoPackage package);
--};
--
--// ----------------------------------------------------------------------------------------
--
--class DIGIKAM_DATABASE_EXPORT ImageFilterModelFilterer : public ImageFilterModelWorker
--{
--    Q_OBJECT
--
--public:
--
--    explicit ImageFilterModelFilterer(ImageFilterModel::ImageFilterModelPrivate* const d)
--        : ImageFilterModelWorker(d)
--    {
--    }
--
--    void process(ImageFilterModelTodoPackage package);
--};
--
--} // namespace Digikam
--
--#endif // IMAGEFILTERMODELTHREADS_H
-diff --git a/libs/models/imagefiltersettings.cpp b/libs/models/imagefiltersettings.cpp
-deleted file mode 100644
-index b61e7f9..0000000
---- a/libs/models/imagefiltersettings.cpp
-+++ /dev/null
-@@ -1,952 +0,0 @@
--/* ============================================================
-- *
-- * This file is a part of digiKam project
-- * http://www.digikam.org
-- *
-- * Date        : 2009-03-05
-- * Description : Filter values for use with ImageFilterModel
-- *
-- * Copyright (C) 2009-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
-- * Copyright (C) 2011-2017 by Gilles Caulier <caulier dot gilles at gmail dot com>
-- * Copyright (C)      2010 by Andi Clemens <andi dot clemens at gmail dot com>
-- * Copyright (C)      2011 by Michael G. Hansen <mike at mghansen dot de>
-- * Copyright (C)      2014 by Mohamed Anwer <m dot anwer at gmx dot com>
-- *
-- * This program is free software; you can redistribute it
-- * and/or modify it under the terms of the GNU General
-- * Public License as published by the Free Software Foundation;
-- * either version 2, or (at your option)
-- * any later version.
-- *
-- * This program is distributed in the hope that it will be useful,
-- * but WITHOUT ANY WARRANTY; without even the implied warranty of
-- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-- * GNU General Public License for more details.
-- *
-- * ============================================================ */
--
--#include "imagefiltersettings.h"
--
--// C++ includes
--
--#include <cmath>
--
--// Qt includes
--
--#include <QDateTime>
--
--// Local includes
--
--#include "digikam_debug.h"
--#include "coredbfields.h"
--#include "digikam_globals.h"
--#include "imageinfo.h"
--#include "tagscache.h"
--#include "versionmanagersettings.h"
--
--namespace Digikam
--{
--
--ImageFilterSettings::ImageFilterSettings()
--{
--    m_untaggedFilter       = false;
--    m_isUnratedExcluded    = false;
--    m_ratingFilter         = 0;
--    m_mimeTypeFilter       = MimeFilter::AllFiles;
--    m_ratingCond           = GreaterEqualCondition;
--    m_matchingCond         = OrCondition;
--    m_geolocationCondition = GeolocationNoFilter;
--}
--
--DatabaseFields::Set ImageFilterSettings::watchFlags() const
--{
--    DatabaseFields::Set set;
--
--    if (isFilteringByDay())
--    {
--        set |= DatabaseFields::CreationDate;
--    }
--
--    if (isFilteringByText())
--    {
--        set |= DatabaseFields::Name;
--        set |= DatabaseFields::Comment;
--    }
--
--    if (isFilteringByRating())
--    {
--        set |= DatabaseFields::Rating;
--    }
--
--    if (isFilteringByTypeMime())
--    {
--        set |= DatabaseFields::Category;
--        set |= DatabaseFields::Format;
--    }
--
--    if (isFilteringByGeolocation())
--    {
--        set |= DatabaseFields::ImagePositionsAll;
--    }
--
--    if (isFilteringByColorLabels())
--    {
--        set |= DatabaseFields::ColorLabel;
--    }
--
--    if (isFilteringByPickLabels())
--    {
--        set |= DatabaseFields::PickLabel;
--    }
--
--    return set;
--}
--
--bool ImageFilterSettings::isFilteringByDay() const
--{
--    if (!m_dayFilter.isEmpty())
--    {
--        return true;
--    }
--
--    return false;
--}
--
--bool ImageFilterSettings::isFilteringByTags() const
--{
--    if (!m_includeTagFilter.isEmpty() || !m_excludeTagFilter.isEmpty() || m_untaggedFilter)
--    {
--        return true;
--    }
--
--    return false;
--}
--
--bool ImageFilterSettings::isFilteringByColorLabels() const
--{
--    if (!m_colorLabelTagFilter.isEmpty())
--    {
--        return true;
--    }
--
--    return false;
--}
--
--bool ImageFilterSettings::isFilteringByPickLabels() const
--{
--    if (!m_pickLabelTagFilter.isEmpty())
--    {
--        return true;
--    }
--
--    return false;
--}
--
--bool ImageFilterSettings::isFilteringByText() const
--{
--    if (!m_textFilterSettings.text.isEmpty())
--    {
--        return true;
--    }
--
--    return false;
--}
--
--bool ImageFilterSettings::isFilteringByTypeMime() const
--{
--    if (m_mimeTypeFilter != MimeFilter::AllFiles)
--    {
--        return true;
--    }
--
--    return false;
--}
--
--bool ImageFilterSettings::isFilteringByGeolocation() const
--{
--    return (m_geolocationCondition != GeolocationNoFilter);
--}
--
--bool ImageFilterSettings::isFilteringByRating() const
--{
--    if (m_ratingFilter != 0 || m_ratingCond != GreaterEqualCondition || m_isUnratedExcluded)
--    {
--        return true;
--    }
--
--    return false;
--}
--
--bool ImageFilterSettings::isFilteringInternally() const
--{
--    return (isFiltering() || !m_urlWhitelists.isEmpty() || !m_idWhitelists.isEmpty());
--}
--
--bool ImageFilterSettings::isFiltering() const
--{
--    return isFilteringByDay()         ||
--           isFilteringByTags()        ||
--           isFilteringByText()        ||
--           isFilteringByRating()      ||
--           isFilteringByTypeMime()    ||
--           isFilteringByColorLabels() ||
--           isFilteringByPickLabels()  ||
--           isFilteringByGeolocation();
--}
--
--void ImageFilterSettings::setDayFilter(const QList<QDateTime>& days)
--{
--    m_dayFilter.clear();
--
--    for (QList<QDateTime>::const_iterator it = days.constBegin(); it != days.constEnd(); ++it)
--    {
--        m_dayFilter.insert(*it, true);
--    }
--}
--
--void ImageFilterSettings::setTagFilter(const QList<int>& includedTags,
--                                       const QList<int>& excludedTags,
--                                       MatchingCondition matchingCondition,
--                                       bool showUnTagged,
--                                       const QList<int>& clTagIds,
--                                       const QList<int>& plTagIds)
--{
--    m_includeTagFilter    = includedTags;
--    m_excludeTagFilter    = excludedTags;
--    m_matchingCond        = matchingCondition;
--    m_untaggedFilter      = showUnTagged;
--    m_colorLabelTagFilter = clTagIds;
--    m_pickLabelTagFilter  = plTagIds;
--}
--
--void ImageFilterSettings::setRatingFilter(int rating, RatingCondition ratingCondition, bool isUnratedExcluded)
--{
--    m_ratingFilter      = rating;
--    m_ratingCond        = ratingCondition;
--    m_isUnratedExcluded = isUnratedExcluded;
--}
--
--void ImageFilterSettings::setMimeTypeFilter(int mime)
--{
--    m_mimeTypeFilter = (MimeFilter::TypeMimeFilter)mime;
--}
--
--void ImageFilterSettings::setGeolocationFilter(const GeolocationCondition& condition)
--{
--    m_geolocationCondition = condition;
--}
--
--void ImageFilterSettings::setTextFilter(const SearchTextFilterSettings& settings)
--{
--    m_textFilterSettings = settings;
--}
--
--void ImageFilterSettings::setTagNames(const QHash<int, QString>& hash)
--{
--    m_tagNameHash = hash;
--}
--
--void ImageFilterSettings::setAlbumNames(const QHash<int, QString>& hash)
--{
--    m_albumNameHash = hash;
--}
--
--void ImageFilterSettings::setUrlWhitelist(const QList<QUrl>& urlList, const QString& id)
--{
--    if (urlList.isEmpty())
--    {
--        m_urlWhitelists.remove(id);
--    }
--    else
--    {
--        m_urlWhitelists.insert(id, urlList);
--    }
--}
--
--void ImageFilterSettings::setIdWhitelist(const QList<qlonglong>& idList, const QString& id)
--{
--    if (idList.isEmpty())
--    {
--        m_idWhitelists.remove(id);
--    }
--    else
--    {
--        m_idWhitelists.insert(id, idList);
--    }
--}
--
--template <class ContainerA, class ContainerB>
--bool containsAnyOf(const ContainerA& listA, const ContainerB& listB)
--{
--    foreach (const typename ContainerA::value_type& a, listA)
--    {
--        if (listB.contains(a))
--        {
--            return true;
--        }
--    }
--    return false;
--}
--
--template <class ContainerA, typename Value, class ContainerB>
--bool containsNoneOfExcept(const ContainerA& list, const ContainerB& noneOfList, const Value& exception)
--{
--    foreach (const typename ContainerB::value_type& n, noneOfList)
--    {
--        if (n != exception && list.contains(n))
--        {
--            return false;
--        }
--    }
--    return true;
--}
--
--bool ImageFilterSettings::matches(const ImageInfo& info, bool* const foundText) const
--{
--    if (foundText)
--    {
--        *foundText = false;
--    }
--
--    if (!isFilteringInternally())
--    {
--        return true;
--    }
--
--    bool match = false;
--
--    if (!m_includeTagFilter.isEmpty() || !m_excludeTagFilter.isEmpty())
--    {
--        QList<int>                 tagIds = info.tagIds();
--        QList<int>::const_iterator it;
--
--        match = m_includeTagFilter.isEmpty();
--
--        if (m_matchingCond == OrCondition)
--        {
--            for (it = m_includeTagFilter.begin(); it != m_includeTagFilter.end(); ++it)
--            {
--                if (tagIds.contains(*it))
--                {
--                    match = true;
--                    break;
--                }
--            }
--
--            match |= (m_untaggedFilter && tagIds.isEmpty());
--        }
--        else // AND matching condition...
--        {
--            // m_untaggedFilter and non-empty tag filter, combined with AND, is logically no match
--            if (!m_untaggedFilter)
--            {
--                for (it = m_includeTagFilter.begin(); it != m_includeTagFilter.end(); ++it)
--                {
--                    if (!tagIds.contains(*it))
--                    {
--                        break;
--                    }
--                }
--
--                if (it == m_includeTagFilter.end())
--                {
--                    match = true;
--                }
--            }
--        }
--
--        for (it = m_excludeTagFilter.begin(); it != m_excludeTagFilter.end(); ++it)
--        {
--            if (tagIds.contains(*it))
--            {
--                match = false;
--                break;
--            }
--        }
--    }
--    else if (m_untaggedFilter)
--    {
--        match = !TagsCache::instance()->containsPublicTags(info.tagIds());
--    }
--    else
--    {
--        match = true;
--    }
--
--    //-- Filter by pick labels ------------------------------------------------
--
--    if (!m_pickLabelTagFilter.isEmpty())
--    {
--        QList<int> tagIds = info.tagIds();
--        bool matchPL      = false;
--
--        if (containsAnyOf(m_pickLabelTagFilter, tagIds))
--        {
--            matchPL = true;
--        }
--        else if (!matchPL)
--        {
--            int noPickLabelTagId = TagsCache::instance()->tagForPickLabel(NoPickLabel);
--
--            if (m_pickLabelTagFilter.contains(noPickLabelTagId))
--            {
--                // Searching for "has no ColorLabel" requires special handling:
--                // Scan that the tag ids contains none of the ColorLabel tags, except maybe the NoColorLabel tag
--                matchPL = containsNoneOfExcept(tagIds, TagsCache::instance()->pickLabelTags(), noPickLabelTagId);
--            }
--        }
--
--        match &= matchPL;
--    }
--
--    //-- Filter by color labels ------------------------------------------------
--
--    if (!m_colorLabelTagFilter.isEmpty())
--    {
--        QList<int> tagIds = info.tagIds();
--        bool matchCL      = false;
--
--        if (containsAnyOf(m_colorLabelTagFilter, tagIds))
--        {
--            matchCL = true;
--        }
--        else if (!matchCL)
--        {
--            int noColorLabelTagId = TagsCache::instance()->tagForColorLabel(NoColorLabel);
--
--            if (m_colorLabelTagFilter.contains(noColorLabelTagId))
--            {
--                // Searching for "has no ColorLabel" requires special handling:
--                // Scan that the tag ids contains none of the ColorLabel tags, except maybe the NoColorLabel tag
--                matchCL = containsNoneOfExcept(tagIds, TagsCache::instance()->colorLabelTags(), noColorLabelTagId);
--            }
--        }
--
--        match &= matchCL;
--    }
--
--    //-- Filter by date -----------------------------------------------------------
--
--    if (!m_dayFilter.isEmpty())
--    {
--        match &= m_dayFilter.contains(QDateTime(info.dateTime().date(), QTime()));
--    }
--
--    //-- Filter by rating ---------------------------------------------------------
--
--    if (m_ratingFilter >= 0)
--    {
--        // for now we treat -1 (no rating) just like a rating of 0.
--        int rating = info.rating();
--
--        if (rating == -1)
--        {
--            rating = 0;
--        }
--
--        if(m_isUnratedExcluded && rating == 0)
--        {
--            match = false;
--        }
--        else
--        {
--            if (m_ratingCond == GreaterEqualCondition)
--            {
--                // If the rating is not >=, i.e it is <, then it does not match.
--                if (rating < m_ratingFilter)
--                {
--                    match = false;
--                }
--            }
--            else if (m_ratingCond == EqualCondition)
--            {
--                // If the rating is not =, i.e it is !=, then it does not match.
--                if (rating != m_ratingFilter)
--                {
--                    match = false;
--                }
--            }
--            else
--            {
--                // If the rating is not <=, i.e it is >, then it does not match.
--                if (rating > m_ratingFilter)
--                {
--                    match = false;
--                }
--            }
--        }
--    }
--
--    // -- Filter by mime type -----------------------------------------------------
--
--    switch (m_mimeTypeFilter)
--    {
--        // info.format is a standardized string: Only one possibility per mime type
--        case MimeFilter::ImageFiles:
--        {
--            if (info.category() != DatabaseItem::Image)
--            {
--                match = false;
--            }
--
--            break;
--        }
--        case MimeFilter::JPGFiles:
--        {
--            if (info.format() != QLatin1String("JPG"))
--            {
--                match = false;
--            }
--
--            break;
--        }
--        case MimeFilter::PNGFiles:
--        {
--            if (info.format() != QLatin1String("PNG"))
--            {
--                match = false;
--            }
--
--            break;
--        }
--        case MimeFilter::TIFFiles:
--        {
--            if (info.format() != QLatin1String("TIFF"))
--            {
--                match = false;
--            }
--
--            break;
--        }
--        case MimeFilter::DNGFiles:
--        {
--            if (info.format() != QLatin1String("RAW-DNG"))
--            {
--                match = false;
--            }
--
--            break;
--        }
--        case MimeFilter::NoRAWFiles:
--        {
--            if (info.format().startsWith(QLatin1String("RAW")))
--            {
--                match = false;
--            }
--
--            break;
--        }
--        case MimeFilter::RAWFiles:
--        {
--            if (!info.format().startsWith(QLatin1String("RAW")))
--            {
--                match = false;
--            }
--
--            break;
--        }
--        case MimeFilter::MoviesFiles:
--        {
--            if (info.category() != DatabaseItem::Video)
--            {
--                match = false;
--            }
--
--            break;
--        }
--        case MimeFilter::AudioFiles:
--        {
--            if (info.category() != DatabaseItem::Audio)
--            {
--                match = false;
--            }
--
--            break;
--        }
--        case MimeFilter::RasterFiles:
--        {
--            if (info.format() != QLatin1String("PSD") &&         // Adobe Photoshop Document
--                info.format() != QLatin1String("PSB") &&         // Adobe Photoshop Big
--                info.format() != QLatin1String("XCF") &&         // Gimp
--                info.format() != QLatin1String("KRA") &&         // Krita
--                info.format() != QLatin1String("ORA")            // Open Raster
--               )
--            {
--                match = false;
--            }
--
--            break;
--        }
--        default:
--        {
--            // All Files: do nothing...
--            break;
--        }
--    }
--
--    //-- Filter by geolocation ----------------------------------------------------
--
--    if (m_geolocationCondition!=GeolocationNoFilter)
--    {
--        if (m_geolocationCondition==GeolocationNoCoordinates)
--        {
--            if (info.hasCoordinates())
--            {
--                match = false;
--            }
--        }
--        else if (m_geolocationCondition==GeolocationHasCoordinates)
--        {
--            if (!info.hasCoordinates())
--            {
--                match = false;
--            }
--        }
--    }
--
--    //-- Filter by text -----------------------------------------------------------
--
--    if (!m_textFilterSettings.text.isEmpty())
--    {
--        bool textMatch = false;
--
--        // Image name
--        if (m_textFilterSettings.textFields & SearchTextFilterSettings::ImageName &&
--            info.name().contains(m_textFilterSettings.text, m_textFilterSettings.caseSensitive))
--        {
--            textMatch = true;
--        }
--
--        // Image title
--        if (m_textFilterSettings.textFields & SearchTextFilterSettings::ImageTitle &&
--            info.title().contains(m_textFilterSettings.text, m_textFilterSettings.caseSensitive))
--        {
--            textMatch = true;
--        }
--
--        // Image comment
--        if (m_textFilterSettings.textFields & SearchTextFilterSettings::ImageComment &&
--            info.comment().contains(m_textFilterSettings.text, m_textFilterSettings.caseSensitive))
--        {
--            textMatch = true;
--        }
--
--        // Tag names
--        foreach(int id, info.tagIds())
--        {
--            if (m_textFilterSettings.textFields & SearchTextFilterSettings::TagName &&
--                m_tagNameHash.value(id).contains(m_textFilterSettings.text, m_textFilterSettings.caseSensitive))
--            {
--                textMatch = true;
--            }
--        }
--
--        // Album names
--        if (m_textFilterSettings.textFields & SearchTextFilterSettings::AlbumName &&
--            m_albumNameHash.value(info.albumId()).contains(m_textFilterSettings.text, m_textFilterSettings.caseSensitive))
--        {
--            textMatch = true;
--        }
--
--        // Image Aspect Ratio
--        if (m_textFilterSettings.textFields & SearchTextFilterSettings::ImageAspectRatio)
--        {
--            QRegExp expRatio (QLatin1String("^\\d+:\\d+$"));
--            QRegExp expFloat (QLatin1String("^\\d+(.\\d+)?$"));
--
--            if (expRatio.indexIn(m_textFilterSettings.text) > -1 && m_textFilterSettings.text.contains(QRegExp(QLatin1String(":\\d+"))))
--            {
--                QString trimmedTextFilterSettingsText = m_textFilterSettings.text;
--                QStringList numberStringList          = trimmedTextFilterSettingsText.split(QLatin1String(":"), QString::SkipEmptyParts);
--
--                if (numberStringList.length() == 2)
--                {
--                    QString numString     = (QString)numberStringList.at(0), denomString = (QString)numberStringList.at(1);
--                    bool canConverseNum   = false;
--                    bool canConverseDenom = false;
--                    int num               = numString.toInt(&canConverseNum, 10), denom = denomString.toInt(&canConverseDenom, 10);
--
--                    if (canConverseNum && canConverseDenom)
--                    {
--                        if (fabs(info.aspectRatio() - (double)num / denom) < 0.1)
--                            textMatch = true;
--                    }
--                }
--            }
--            else if (expFloat.indexIn(m_textFilterSettings.text) > -1)
--            {
--                QString trimmedTextFilterSettingsText = m_textFilterSettings.text;
--                bool    canConverse                   = false;
--                double  ratio                         = trimmedTextFilterSettingsText.toDouble(&canConverse);
--
--                if (canConverse)
--                {
--                    if (fabs(info.aspectRatio() - ratio) < 0.1)
--                        textMatch = true;
--                }
--            }
--        }
--
--        // Image Pixel Size
--        // See bug #341053 for details.
--
--        if (m_textFilterSettings.textFields & SearchTextFilterSettings::ImagePixelSize)
--        {
--            QSize size    = info.dimensions();
--            int pixelSize = size.height()*size.width();
--            QString text  = m_textFilterSettings.text;
--
--            if(text.contains(QRegExp(QLatin1String("^>\\d{1,15}$"))) && pixelSize > (text.remove(0,1)).toInt())
--            {
--                textMatch = true;
--            }
--            else if(text.contains(QRegExp(QLatin1String("^<\\d{1,15}$"))) && pixelSize < (text.remove(0,1)).toInt())
--            {
--                textMatch = true;
--            }
--            else if(text.contains(QRegExp(QLatin1String("^\\d+$"))) && pixelSize == text.toInt())
--            {
--                textMatch = true;
--            }
--        }
--
--        match &= textMatch;
--
--        if (foundText)
--        {
--            *foundText = textMatch;
--        }
--    }
--
--    // -- filter by URL-whitelists ------------------------------------------------
--    // NOTE: whitelists are always AND for now.
--
--    if (match)
--    {
--        const QUrl url = info.fileUrl();
--
--        for (QHash<QString, QList<QUrl>>::const_iterator it = m_urlWhitelists.constBegin();
--             it!=m_urlWhitelists.constEnd(); ++it)
--        {
--            match = it->contains(url);
--
--            if (!match)
--            {
--                break;
--            }
--        }
--    }
--
--    if (match)
--    {
--        const qlonglong id = info.id();
--
--        for (QHash<QString, QList<qlonglong> >::const_iterator it = m_idWhitelists.constBegin();
--             it!=m_idWhitelists.constEnd(); ++it)
--        {
--            match = it->contains(id);
--
--            if (!match)
--            {
--                break;
--            }
--        }
--    }
--
--    return match;
--}
--
--// -------------------------------------------------------------------------------------------------
--
--VersionImageFilterSettings::VersionImageFilterSettings()
--{
--    m_includeTagFilter   = 0;
--    m_exceptionTagFilter = 0;
--}
--
--VersionImageFilterSettings::VersionImageFilterSettings(const VersionManagerSettings& settings)
--{
--    setVersionManagerSettings(settings);
--}
--
--bool VersionImageFilterSettings::operator==(const VersionImageFilterSettings& other) const
--{
--    return m_excludeTagFilter == other.m_excludeTagFilter &&
--           m_exceptionLists   == other.m_exceptionLists;
--}
--
--bool VersionImageFilterSettings::matches(const ImageInfo& info) const
--{
--    if (!isFiltering())
--    {
--        return true;
--    }
--
--    const qlonglong id = info.id();
--
--    for (QHash<QString, QList<qlonglong> >::const_iterator it = m_exceptionLists.constBegin();
--         it != m_exceptionLists.constEnd(); ++it)
--    {
--        if (it->contains(id))
--        {
--            return true;
--        }
--    }
--
--    bool match        = true;
--    QList<int> tagIds = info.tagIds();
--
--    if (!tagIds.contains(m_includeTagFilter))
--    {
--        for (QList<int>::const_iterator it = m_excludeTagFilter.begin();
--             it != m_excludeTagFilter.end(); ++it)
--        {
--            if (tagIds.contains(*it))
--            {
--                match = false;
--                break;
--            }
--        }
--    }
--
--    if (!match)
--    {
--        if (tagIds.contains(m_exceptionTagFilter))
--        {
--            match = true;
--        }
--    }
--
--    return match;
--}
--
--bool VersionImageFilterSettings::isHiddenBySettings(const ImageInfo& info) const
--{
--    QList<int> tagIds = info.tagIds();
--
--    foreach(int tagId, m_excludeTagFilter)
--    {
--        if (tagIds.contains(tagId))
--        {
--            return true;
--        }
--    }
--
--    return false;
--}
--
--bool VersionImageFilterSettings::isExemptedBySettings(const ImageInfo& info) const
--{
--    return info.tagIds().contains(m_exceptionTagFilter);
--}
--
--void VersionImageFilterSettings::setVersionManagerSettings(const VersionManagerSettings& settings)
--{
--    m_excludeTagFilter.clear();
--
--    if (!settings.enabled)
--    {
--        return;
--    }
--
--    if (!(settings.showInViewFlags & VersionManagerSettings::ShowOriginal))
--    {
--        m_excludeTagFilter << TagsCache::instance()->getOrCreateInternalTag(InternalTagName::originalVersion());
--    }
--
--    if (!(settings.showInViewFlags & VersionManagerSettings::ShowIntermediates))
--    {
--        m_excludeTagFilter << TagsCache::instance()->getOrCreateInternalTag(InternalTagName::intermediateVersion());
--    }
--
--    m_includeTagFilter   = TagsCache::instance()->getOrCreateInternalTag(InternalTagName::currentVersion());
--    m_exceptionTagFilter = TagsCache::instance()->getOrCreateInternalTag(InternalTagName::versionAlwaysVisible());
--}
--
--void VersionImageFilterSettings::setExceptionList(const QList<qlonglong>& idList, const QString& id)
--{
--    if (idList.isEmpty())
--    {
--        m_exceptionLists.remove(id);
--    }
--    else
--    {
--        m_exceptionLists.insert(id, idList);
--    }
--}
--
--bool VersionImageFilterSettings::isFiltering() const
--{
--    return !m_excludeTagFilter.isEmpty();
--}
--
--bool VersionImageFilterSettings::isFilteringByTags() const
--{
--    return isFiltering();
--}
--
--// -------------------------------------------------------------------------------------------------
--
--GroupImageFilterSettings::GroupImageFilterSettings()
--    : m_allOpen(false)
--{
--}
--
--bool GroupImageFilterSettings::operator==(const GroupImageFilterSettings& other) const
--{
--    return (m_allOpen    == other.m_allOpen &&
--            m_openGroups == other.m_openGroups);
--}
--
--bool GroupImageFilterSettings::matches(const ImageInfo& info) const
--{
--    if (m_allOpen)
--    {
--        return true;
--    }
--
--    if (info.isGrouped())
--    {
--        return m_openGroups.contains(info.groupImage().id());
--    }
--    return true;
--}
--
--void GroupImageFilterSettings::setOpen(qlonglong group, bool open)
--{
--    if (open)
--    {
--        m_openGroups << group;
--    }
--    else
--    {
--        m_openGroups.remove(group);
--    }
--}
--
--bool GroupImageFilterSettings::isOpen(qlonglong group) const
--{
--    return m_openGroups.contains(group);
--}
--
--void GroupImageFilterSettings::setAllOpen(bool open)
--{
--    m_allOpen = open;
--}
--
--bool GroupImageFilterSettings::isAllOpen() const
--{
--    return m_allOpen;
--}
--
--bool GroupImageFilterSettings::isFiltering() const
--{
--    return !m_allOpen;
--}
--
--DatabaseFields::Set GroupImageFilterSettings::watchFlags() const
--{
--    return DatabaseFields::ImageRelations;
--}
--
--} // namespace Digikam
-diff --git a/libs/models/imagefiltersettings.h b/libs/models/imagefiltersettings.h
-deleted file mode 100644
-index 0e7beae..0000000
---- a/libs/models/imagefiltersettings.h
-+++ /dev/null
-@@ -1,349 +0,0 @@
--/* ============================================================
-- *
-- * This file is a part of digiKam project
-- * http://www.digikam.org
-- *
-- * Date        : 2009-03-05
-- * Description : Filter values for use with ImageFilterModel
-- *
-- * Copyright (C) 2009-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
-- * Copyright (C) 2011-2017 by Gilles Caulier <caulier dot gilles at gmail dot com>
-- * Copyright (C)      2010 by Andi Clemens <andi dot clemens at gmail dot com>
-- * Copyright (C)      2011 by Michael G. Hansen <mike at mghansen dot de>
-- * Copyright (C)      2014 by Mohamed Anwer <m dot anwer at gmx dot com>
-- *
-- * This program is free software; you can redistribute it
-- * and/or modify it under the terms of the GNU General
-- * Public License as published by the Free Software Foundation;
-- * either version 2, or (at your option)
-- * any later version.
-- *
-- * This program is distributed in the hope that it will be useful,
-- * but WITHOUT ANY WARRANTY; without even the implied warranty of
-- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-- * GNU General Public License for more details.
-- *
-- * ============================================================ */
--
--#ifndef IMAGEFILTERSETTINGS_H
--#define IMAGEFILTERSETTINGS_H
--
--// Qt includes
--
--#include <QHash>
--#include <QList>
--#include <QMap>
--#include <QString>
--#include <QSet>
--#include <QUrl>
--
--// Local includes
--
--#include "searchtextbar.h"
--#include "mimefilter.h"
--#include "digikam_export.h"
--
--namespace Digikam
--{
--
--class ImageInfo;
--class VersionManagerSettings;
--
--namespace DatabaseFields
--{
--    class Set;
--}
--
--// ---------------------------------------------------------------------------------------
--
--class DIGIKAM_DATABASE_EXPORT SearchTextFilterSettings : public SearchTextSettings
--{
--
--public:
--
--    enum TextFilterFields
--    {
--        None             = 0x00,
--        ImageName        = 0x01,
--        ImageTitle       = 0x02,
--        ImageComment     = 0x04,
--        TagName          = 0x08,
--        AlbumName        = 0x10,
--        ImageAspectRatio = 0x20,
--        ImagePixelSize   = 0x40,
--        All              = ImageName | ImageTitle | ImageComment | TagName | AlbumName | ImageAspectRatio | ImagePixelSize
--    };
--
--public:
--
--    SearchTextFilterSettings()
--    {
--        textFields = None;
--    }
--
--    explicit SearchTextFilterSettings(const SearchTextSettings& settings)
--    {
--        caseSensitive = settings.caseSensitive;
--        text          = settings.text;
--        textFields    = None;
--    }
--
--    TextFilterFields textFields;
--};
--
--// ---------------------------------------------------------------------------------------
--
--class DIGIKAM_DATABASE_EXPORT ImageFilterSettings
--{
--public:
--
--    ImageFilterSettings();
--
--    /**
--     *  Returns true if the given ImageInfo matches the filter criteria.
--     *  Optionally, foundText is set to true if it matched by text search.
--     */
--    bool matches(const ImageInfo& info, bool* const foundText = 0) const;
--
--public:
--
--    /// --- Tags filter ---
--
--    /// Possible logical matching condition used to sort tags id.
--    enum MatchingCondition
--    {
--        OrCondition,
--        AndCondition
--    };
--
--    void setTagFilter(const QList<int>& includedTags,
--                      const QList<int>& excludedTags,
--                      MatchingCondition matchingCond,
--                      bool              showUnTagged,
--                      const QList<int>& clTagIds,
--                      const QList<int>& plTagIds);
--
--public:
--
--    /// --- Rating filter ---
--
--    /// Possible conditions used to filter rating: >=, =, <=
--    enum RatingCondition
--    {
--        GreaterEqualCondition,
--        EqualCondition,
--        LessEqualCondition
--    };
--
--    void setRatingFilter(int rating, RatingCondition ratingCond, bool isUnratedExcluded);
--
--public:
--
--    /// --- Date filter ---
--    void setDayFilter(const QList<QDateTime>& days);
--
--public:
--
--    /// --- Text filter ---
--    void setTextFilter(const SearchTextFilterSettings& settings);
--    void setTagNames(const QHash<int, QString>& tagNameHash);
--    void setAlbumNames(const QHash<int, QString>& albumNameHash);
--
--public:
--
--    /// --- Mime filter ---
--    void setMimeTypeFilter(int mimeTypeFilter);
--
--public:
--
--    /// --- Geolocation filter
--    enum GeolocationCondition
--    {
--        GeolocationNoFilter       = 0,
--        GeolocationNoCoordinates  = 1 << 1,
--        GeolocationHasCoordinates = 1 << 2
--    };
--
--    void setGeolocationFilter(const GeolocationCondition& condition);
--
--public:
--
--    /// Returns if the day is a filter criteria
--    bool isFilteringByDay()         const;
--
--    /// Returns if the type mime is a filter criteria
--    bool isFilteringByTypeMime()    const;
--
--    /// Returns whether geolocation is a filter criteria
--    bool isFilteringByGeolocation() const;
--
--    /// Returns if the rating is a filter criteria
--    bool isFilteringByRating()      const;
--
--    /// Returns if the pick labels is a filter criteria
--    bool isFilteringByPickLabels()  const;
--
--    /// Returns if the color labels is a filter criteria
--    bool isFilteringByColorLabels() const;
--
--    /// Returns if the tag is a filter criteria
--    bool isFilteringByTags()        const;
--
--    /// Returns if the text (including comment) is a filter criteria
--    bool isFilteringByText()        const;
--
--    /// Returns if images will be filtered by these criteria at all
--    bool isFiltering()              const;
--
--public:
--
--    /// --- URL whitelist filter
--    void setUrlWhitelist(const QList<QUrl>& urlList, const QString& id);
--
--public:
--
--    /// --- ID whitelist filter
--    void setIdWhitelist(const QList<qlonglong>& idList, const QString& id);
--
--public:
--
--    /// --- Change notification ---
--
--    /** Returns database fields a change in which would affect the current filtering.
--     *  To find out if an image tag change affects filtering, test isFilteringByTags().
--     *  The text filter will also be affected by changes in tags and album names.
--     */
--    DatabaseFields::Set watchFlags() const;
--
--private:
--
--    /**
--     * @brief Returns whether some internal filtering (whitelist by id or URL) or normal filtering is going on
--     */
--    bool isFilteringInternally() const;
--
--private:
--
--    /// --- Tags filter ---
--    bool                             m_untaggedFilter;
--    QList<int>                       m_includeTagFilter;
--    QList<int>                       m_excludeTagFilter;
--    MatchingCondition                m_matchingCond;
--    QList<int>                       m_colorLabelTagFilter;
--    QList<int>                       m_pickLabelTagFilter;
--
--    /// --- Rating filter ---
--    int                              m_ratingFilter;
--    RatingCondition                  m_ratingCond;
--    bool                             m_isUnratedExcluded;
--
--    /// --- Date filter ---
--    QMap<QDateTime, bool>            m_dayFilter;
--
--    /// --- Text filter ---
--    SearchTextFilterSettings         m_textFilterSettings;
--
--    /// Helpers for text search: Set these if you want to search album or tag names with text search
--    QHash<int, QString>              m_tagNameHash;
--    QHash<int, QString>              m_albumNameHash;
--
--    /// --- Mime filter ---
--    MimeFilter::TypeMimeFilter       m_mimeTypeFilter;
--
--    /// --- Geolocation filter
--    GeolocationCondition             m_geolocationCondition;
--
--    /// --- URL whitelist filter
--    QHash<QString,QList<QUrl>>        m_urlWhitelists;
--
--    /// --- ID whitelist filter
--    QHash<QString,QList<qlonglong> > m_idWhitelists;
--};
--
--// ---------------------------------------------------------------------------------------
--
--class DIGIKAM_DATABASE_EXPORT VersionImageFilterSettings
--{
--public:
--
--    VersionImageFilterSettings();
--    explicit VersionImageFilterSettings(const VersionManagerSettings& settings);
--
--    bool operator==(const VersionImageFilterSettings& other) const;
--
--    /**
--     *  Returns true if the given ImageInfo matches the filter criteria.
--     */
--    bool matches(const ImageInfo& info) const;
--
--    bool isHiddenBySettings(const ImageInfo& info)   const;
--    bool isExemptedBySettings(const ImageInfo& info) const;
--
--    /// --- Tags filter ---
--
--    void setVersionManagerSettings(const VersionManagerSettings& settings);
--
--    /**
--     * Add list with exceptions: These images will be exempted from filtering by this filter
--     */
--    void setExceptionList(const QList<qlonglong>& idlist, const QString& id);
--
--    /// Returns if images will be filtered by these criteria at all
--    bool isFiltering() const;
--
--    /// Returns if the tag is a filter criteria
--    bool isFilteringByTags() const;
--
--    /// DatabaseFields::Set watchFlags() const: Would return 0
--
--protected:
--
--    QList<int>                       m_excludeTagFilter;
--    int                              m_includeTagFilter;
--    int                              m_exceptionTagFilter;
--    QHash<QString,QList<qlonglong> > m_exceptionLists;
--};
--
--// ---------------------------------------------------------------------------------------
--
--class DIGIKAM_DATABASE_EXPORT GroupImageFilterSettings
--{
--public:
--
--    GroupImageFilterSettings();
--
--    bool operator==(const GroupImageFilterSettings& other) const;
--
--    /**
--     *  Returns true if the given ImageInfo matches the filter criteria.
--     */
--    bool matches(const ImageInfo& info) const;
--
--    /**
--     * Open or close a group.
--     */
--    void setOpen(qlonglong group, bool open);
--    bool isOpen(qlonglong group) const;
--
--    /**
--     * Open all groups
--     */
--    void setAllOpen(bool open);
--    bool isAllOpen() const;
--
--    /// Returns if images will be filtered by these criteria at all
--    bool isFiltering() const;
--
--    DatabaseFields::Set watchFlags() const;
--
--protected:
--
--    bool            m_allOpen;
--    QSet<qlonglong> m_openGroups;
--};
--
--} // namespace Digikam
--
--Q_DECLARE_METATYPE(Digikam::ImageFilterSettings::GeolocationCondition)
--
--#endif // IMAGEFILTERSETTINGS_H
-diff --git a/libs/models/imagelistmodel.cpp b/libs/models/imagelistmodel.cpp
-deleted file mode 100644
-index fafce34..0000000
---- a/libs/models/imagelistmodel.cpp
-+++ /dev/null
-@@ -1,70 +0,0 @@
--/* ============================================================
-- *
-- * This file is a part of digiKam project
-- * http://www.digikam.org
-- *
-- * Date        : 2010-12-06
-- * Description : An image model based on a static list
-- *
-- * Copyright (C) 2010-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
-- *
-- * This program is free software; you can redistribute it
-- * and/or modify it under the terms of the GNU General
-- * Public License as published by the Free Software Foundation;
-- * either version 2, or (at your option)
-- * any later version.
-- *
-- * This program is distributed in the hope that it will be useful,
-- * but WITHOUT ANY WARRANTY; without even the implied warranty of
-- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-- * GNU General Public License for more details.
-- *
-- * ============================================================ */
--
--#include "imagelistmodel.h"
--
--// Local includes
--
--#include "digikam_debug.h"
--#include "coredbaccess.h"
--#include "coredbchangesets.h"
--#include "coredbwatch.h"
--#include "imageinfo.h"
--#include "imageinfolist.h"
--
--namespace Digikam
--{
--
--ImageListModel::ImageListModel(QObject* parent)
--    : ImageThumbnailModel(parent)
--{
--    connect(CoreDbAccess::databaseWatch(), SIGNAL(collectionImageChange(CollectionImageChangeset)),
--            this, SLOT(slotCollectionImageChange(CollectionImageChangeset)));
--}
--
--ImageListModel::~ImageListModel()
--{
--}
--
--void ImageListModel::slotCollectionImageChange(const CollectionImageChangeset& changeset)
--{
--    if (isEmpty())
--    {
--        return;
--    }
--
--    switch (changeset.operation())
--    {
--        case CollectionImageChangeset::Added:
--            break;
--        case CollectionImageChangeset::Removed:
--        case CollectionImageChangeset::RemovedAll:
--            removeImageInfos(ImageInfoList(changeset.ids()));
--            break;
--
--        default:
--            break;
--    }
--}
--
--} // namespace Digikam
-diff --git a/libs/models/imagelistmodel.h b/libs/models/imagelistmodel.h
-deleted file mode 100644
-index a225b1b..0000000
---- a/libs/models/imagelistmodel.h
-+++ /dev/null
-@@ -1,63 +0,0 @@
--/* ============================================================
-- *
-- * This file is a part of digiKam project
-- * http://www.digikam.org
-- *
-- * Date        : 2010-12-06
-- * Description : An image model based on a static list
-- *
-- * Copyright (C) 2010-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
-- *
-- * This program is free software; you can redistribute it
-- * and/or modify it under the terms of the GNU General
-- * Public License as published by the Free Software Foundation;
-- * either version 2, or (at your option)
-- * any later version.
-- *
-- * This program is distributed in the hope that it will be useful,
-- * but WITHOUT ANY WARRANTY; without even the implied warranty of
-- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-- * GNU General Public License for more details.
-- *
-- * ============================================================ */
--
--#ifndef IMAGELISTMODEL_H
--#define IMAGELISTMODEL_H
--
--// Local includes
--
--#include "imagethumbnailmodel.h"
--#include "digikam_export.h"
--
--namespace Digikam
--{
--
--class ImageChangeset;
--class CollectionImageChangeset;
--
--class DIGIKAM_DATABASE_EXPORT ImageListModel : public ImageThumbnailModel
--{
--    Q_OBJECT
--
--public:
--
--    explicit ImageListModel(QObject* parent = 0);
--    ~ImageListModel();
--
--    // NOTE: necessary methods to add and remove ImageInfos to the model are inherited from ImageModel
--
--Q_SIGNALS:
--
--    /**
--     * Emitted when images are removed from the model because they are removed in the database
--     */
--    void imageInfosRemoved(const QList<ImageInfo>& infos);
--
--protected Q_SLOTS:
--
--    void slotCollectionImageChange(const CollectionImageChangeset& changeset);
--};
--
--} // namespace Digikam
--
--#endif // IMAGELISTMODEL_H
-diff --git a/libs/models/imagemodel.cpp b/libs/models/imagemodel.cpp
-deleted file mode 100644
-index 41b43cf..0000000
---- a/libs/models/imagemodel.cpp
-+++ /dev/null
-@@ -1,1368 +0,0 @@
--/* ============================================================
-- *
-- * This file is a part of digiKam project
-- * http://www.digikam.org
-- *
-- * Date        : 2009-03-05
-- * Description : Qt item model for database entries
-- *
-- * Copyright (C) 2009-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
-- *
-- * This program is free software; you can redistribute it
-- * and/or modify it under the terms of the GNU General
-- * Public License as published by the Free Software Foundation;
-- * either version 2, or (at your option)
-- * any later version.
-- *
-- * This program is distributed in the hope that it will be useful,
-- * but WITHOUT ANY WARRANTY; without even the implied warranty of
-- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-- * GNU General Public License for more details.
-- *
-- * ============================================================ */
--
--#include "imagemodel.h"
--
--// Qt includes
--
--#include <QHash>
--#include <QItemSelection>
--
--// Local includes
--
--#include "digikam_debug.h"
--#include "coredbchangesets.h"
--#include "coredbfields.h"
--#include "coredbwatch.h"
--#include "imageinfo.h"
--#include "imageinfolist.h"
--#include "abstractitemdragdrophandler.h"
--
--namespace Digikam
--{
--
--class ImageModel::Private
--{
--public:
--
--    Private()
--    {
--        preprocessor                = 0;
--        keepFilePathCache           = false;
--        sendRemovalSignals          = false;
--        incrementalUpdater          = 0;
--        refreshing                  = false;
--        reAdding                    = false;
--        incrementalRefreshRequested = false;
--    }
--
--    ImageInfoList                       infos;
--    QList<QVariant>                     extraValues;
--    QHash<qlonglong, int>               idHash;
--
--    bool                                keepFilePathCache;
--    QHash<QString, qlonglong>           filePathHash;
--
--    bool                                sendRemovalSignals;
--
--    QObject*                            preprocessor;
--    bool                                refreshing;
--    bool                                reAdding;
--    bool                                incrementalRefreshRequested;
--
--    DatabaseFields::Set                 watchFlags;
--
--    class ImageModelIncrementalUpdater* incrementalUpdater;
--
--    ImageInfoList                       pendingInfos;
--    QList<QVariant>                     pendingExtraValues;
--
--    inline bool isValid(const QModelIndex& index)
--    {
--        if (!index.isValid())
--        {
--            return false;
--        }
--
--        if (index.row() < 0 || index.row() >= infos.size())
--        {
--            qCDebug(DIGIKAM_GENERAL_LOG) << "Invalid index" << index;
--            return false;
--        }
--
--        return true;
--    }
--    inline bool extraValueValid(const QModelIndex& index)
--    {
--        // we assume isValid() being called before, no duplicate checks
--        if (index.row() >= extraValues.size())
--        {
--            qCDebug(DIGIKAM_GENERAL_LOG) << "Invalid index for extraData" << index;
--            return false;
--        }
--
--        return true;
--    }
--};
--
--typedef QPair<int, int> IntPair; // to make foreach macro happy
--typedef QList<IntPair>  IntPairList;
--
--class ImageModelIncrementalUpdater
--{
--public:
--
--    explicit ImageModelIncrementalUpdater(ImageModel::Private* d);
--
--    void                  appendInfos(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues);
--    void                  aboutToBeRemovedInModel(const IntPairList& aboutToBeRemoved);
--    QList<IntPair>        oldIndexes();
--
--    static QList<IntPair> toContiguousPairs(const QList<int>& ids);
--
--public:
--
--    QHash<qlonglong, int> oldIds;
--    QList<QVariant>       oldExtraValues;
--    QList<ImageInfo>      newInfos;
--    QList<QVariant>       newExtraValues;
--    QList<IntPairList>    modelRemovals;
--};
--
--ImageModel::ImageModel(QObject* parent)
--    : QAbstractListModel(parent),
--      d(new Private)
--{
--    connect(CoreDbAccess::databaseWatch(), SIGNAL(imageChange(ImageChangeset)),
--            this, SLOT(slotImageChange(ImageChangeset)));
--
--    connect(CoreDbAccess::databaseWatch(), SIGNAL(imageTagChange(ImageTagChangeset)),
--            this, SLOT(slotImageTagChange(ImageTagChangeset)));
--}
--
--ImageModel::~ImageModel()
--{
--    delete d->incrementalUpdater;
--    delete d;
--}
--
--// ------------ Access methods -------------
--
--void ImageModel::setKeepsFilePathCache(bool keepCache)
--{
--    d->keepFilePathCache = keepCache;
--}
--
--bool ImageModel::keepsFilePathCache() const
--{
--    return d->keepFilePathCache;
--}
--
--bool ImageModel::isEmpty() const
--{
--    return d->infos.isEmpty();
--}
--
--void ImageModel::setWatchFlags(const DatabaseFields::Set& set)
--{
--    d->watchFlags = set;
--}
--
--ImageInfo ImageModel::imageInfo(const QModelIndex& index) const
--{
--    if (!d->isValid(index))
--    {
--        return ImageInfo();
--    }
--
--    return d->infos.at(index.row());
--}
--
--ImageInfo& ImageModel::imageInfoRef(const QModelIndex& index) const
--{
--    return d->infos[index.row()];
--}
--
--qlonglong ImageModel::imageId(const QModelIndex& index) const
--{
--    if (!d->isValid(index))
--    {
--        return 0;
--    }
--
--    return d->infos.at(index.row()).id();
--}
--
--QList<ImageInfo> ImageModel::imageInfos(const QList<QModelIndex>& indexes) const
--{
--    QList<ImageInfo> infos;
--
--    foreach(const QModelIndex& index, indexes)
--    {
--        infos << imageInfo(index);
--    }
--
--    return infos;
--}
--
--QList<qlonglong> ImageModel::imageIds(const QList<QModelIndex>& indexes) const
--{
--    QList<qlonglong> ids;
--
--    foreach(const QModelIndex& index, indexes)
--    {
--        ids << imageId(index);
--    }
--
--    return ids;
--}
--
--ImageInfo ImageModel::imageInfo(int row) const
--{
--    if (row >= d->infos.size())
--    {
--        return ImageInfo();
--    }
--
--    return d->infos.at(row);
--}
--
--ImageInfo& ImageModel::imageInfoRef(int row) const
--{
--    return d->infos[row];
--}
--
--qlonglong ImageModel::imageId(int row) const
--{
--    if (row < 0 || row >= d->infos.size())
--    {
--        return -1;
--    }
--
--    return d->infos.at(row).id();
--}
--
--QModelIndex ImageModel::indexForImageInfo(const ImageInfo& info) const
--{
--    return indexForImageId(info.id());
--}
--
--QModelIndex ImageModel::indexForImageInfo(const ImageInfo& info, const QVariant& extraValue) const
--{
--    return indexForImageId(info.id(), extraValue);
--}
--
--QList<QModelIndex> ImageModel::indexesForImageInfo(const ImageInfo& info) const
--{
--    return indexesForImageId(info.id());
--}
--
--QModelIndex ImageModel::indexForImageId(qlonglong id) const
--{
--    int index = d->idHash.value(id, -1);
--
--    if (index != -1)
--    {
--        return createIndex(index, 0);
--    }
--
--    return QModelIndex();
--}
--
--QModelIndex ImageModel::indexForImageId(qlonglong id, const QVariant& extraValue) const
--{
--    if (d->extraValues.isEmpty())
--        return indexForImageId(id);
--
--    QHash<qlonglong, int>::const_iterator it;
--
--    for (it = d->idHash.constFind(id); it != d->idHash.constEnd() && it.key() == id; ++it)
--    {
--        if (d->extraValues.at(it.value()) == extraValue)
--            return createIndex(it.value(), 0);
--    }
--
--    return QModelIndex();
--}
--
--QList<QModelIndex> ImageModel::indexesForImageId(qlonglong id) const
--{
--    QList<QModelIndex> indexes;
--    QHash<qlonglong, int>::const_iterator it;
--
--    for (it = d->idHash.constFind(id); it != d->idHash.constEnd() && it.key() == id; ++it)
--    {
--        indexes << createIndex(it.value(), 0);
--    }
--
--    return indexes;
--}
--
--int ImageModel::numberOfIndexesForImageInfo(const ImageInfo& info) const
--{
--    return numberOfIndexesForImageId(info.id());
--}
--
--int ImageModel::numberOfIndexesForImageId(qlonglong id) const
--{
--    if (d->extraValues.isEmpty())
--    {
--        return 0;
--    }
--
--    int count = 0;
--    QHash<qlonglong,int>::const_iterator it;
--
--    for (it = d->idHash.constFind(id); it != d->idHash.constEnd() && it.key() == id; ++it)
--    {
--        ++count;
--    }
--
--    return count;
--}
--
--// static method
--ImageInfo ImageModel::retrieveImageInfo(const QModelIndex& index)
--{
--    if (!index.isValid())
--    {
--        return ImageInfo();
--    }
--
--    ImageModel* const model = index.data(ImageModelPointerRole).value<ImageModel*>();
--    int row                 = index.data(ImageModelInternalId).toInt();
--
--    if (!model)
--    {
--        return ImageInfo();
--    }
--
--    return model->imageInfo(row);
--}
--
--// static method
--qlonglong ImageModel::retrieveImageId(const QModelIndex& index)
--{
--    if (!index.isValid())
--    {
--        return 0;
--    }
--
--    ImageModel* const model = index.data(ImageModelPointerRole).value<ImageModel*>();
--    int row                 = index.data(ImageModelInternalId).toInt();
--
--    if (!model)
--    {
--        return 0;
--    }
--
--    return model->imageId(row);
--}
--
--QModelIndex ImageModel::indexForPath(const QString& filePath) const
--{
--    if (d->keepFilePathCache)
--    {
--        return indexForImageId(d->filePathHash.value(filePath));
--    }
--    else
--    {
--        const int size = d->infos.size();
--
--        for (int i=0; i<size; ++i)
--        {
--            if (d->infos.at(i).filePath() == filePath)
--            {
--                return createIndex(i, 0);
--            }
--        }
--    }
--
--    return QModelIndex();
--}
--
--QList<QModelIndex> ImageModel::indexesForPath(const QString& filePath) const
--{
--    if (d->keepFilePathCache)
--    {
--        return indexesForImageId(d->filePathHash.value(filePath));
--    }
--    else
--    {
--        QList<QModelIndex> indexes;
--        const int size = d->infos.size();
--
--        for (int i=0; i<size; ++i)
--        {
--            if (d->infos.at(i).filePath() == filePath)
--            {
--                indexes << createIndex(i, 0);
--            }
--        }
--
--        return indexes;
--    }
--}
--
--ImageInfo ImageModel::imageInfo(const QString& filePath) const
--{
--    if (d->keepFilePathCache)
--    {
--        qlonglong id = d->filePathHash.value(filePath);
--
--        if (id)
--        {
--            int index = d->idHash.value(id, -1);
--
--            if (index != -1)
--            {
--                return d->infos.at(index);
--            }
--        }
--    }
--    else
--    {
--        foreach(const ImageInfo& info, d->infos)
--        {
--            if (info.filePath() == filePath)
--            {
--                return info;
--            }
--        }
--    }
--
--    return ImageInfo();
--}
--
--QList<ImageInfo> ImageModel::imageInfos(const QString& filePath) const
--{
--    QList<ImageInfo> infos;
--
--    if (d->keepFilePathCache)
--    {
--        qlonglong id = d->filePathHash.value(filePath);
--
--        if (id)
--        {
--            foreach(int index, d->idHash.values(id))
--            {
--                infos << d->infos.at(index);
--            }
--        }
--    }
--    else
--    {
--        foreach(const ImageInfo& info, d->infos)
--        {
--            if (info.filePath() == filePath)
--            {
--                infos << info;
--            }
--        }
--    }
--
--    return infos;
--}
--
--void ImageModel::addImageInfo(const ImageInfo& info)
--{
--    addImageInfos(QList<ImageInfo>() << info, QList<QVariant>());
--}
--
--void ImageModel::addImageInfos(const QList<ImageInfo>& infos)
--{
--    addImageInfos(infos, QList<QVariant>());
--}
--
--void ImageModel::addImageInfos(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues)
--{
--    if (infos.isEmpty())
--    {
--        return;
--    }
--
--    if (d->incrementalUpdater)
--    {
--        d->incrementalUpdater->appendInfos(infos, extraValues);
--    }
--    else
--    {
--        appendInfos(infos, extraValues);
--    }
--}
--
--void ImageModel::addImageInfoSynchronously(const ImageInfo& info)
--{
--    addImageInfosSynchronously(QList<ImageInfo>() << info, QList<QVariant>());
--}
--
--void ImageModel::addImageInfosSynchronously(const QList<ImageInfo>& infos)
--{
--    addImageInfos(infos, QList<QVariant>());
--}
--
--void ImageModel::addImageInfosSynchronously(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues)
--{
--    if (infos.isEmpty())
--    {
--        return;
--    }
--
--    publiciseInfos(infos, extraValues);
--    emit processAdded(infos, extraValues);
--}
--
--void ImageModel::ensureHasImageInfo(const ImageInfo& info)
--{
--    ensureHasImageInfos(QList<ImageInfo>() << info, QList<QVariant>());
--}
--
--void ImageModel::ensureHasImageInfos(const QList<ImageInfo>& infos)
--{
--    ensureHasImageInfos(infos, QList<QVariant>());
--}
--
--void ImageModel::ensureHasImageInfos(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues)
--{
--    if (extraValues.isEmpty())
--    {
--        if (!d->pendingExtraValues.isEmpty())
--        {
--            qCDebug(DIGIKAM_GENERAL_LOG) << "ExtraValue / No Extra Value mismatch. Ignoring added infos.";
--            return;
--        }
--    }
--    else
--    {
--        if (d->pendingInfos.size() != d->pendingExtraValues.size())
--        {
--            qCDebug(DIGIKAM_GENERAL_LOG) << "ExtraValue / No Extra Value mismatch. Ignoring added infos.";
--            return;
--        }
--    }
--
--    d->pendingInfos << infos;
--    d->pendingExtraValues << extraValues;
--    cleanSituationChecks();
--}
--
--void ImageModel::clearImageInfos()
--{
--    d->infos.clear();
--    d->extraValues.clear();
--    d->idHash.clear();
--    d->filePathHash.clear();
--    delete d->incrementalUpdater;
--    d->incrementalUpdater          = 0;
--    d->pendingInfos.clear();
--    d->pendingExtraValues.clear();
--    d->refreshing                  = false;
--    d->reAdding                    = false;
--    d->incrementalRefreshRequested = false;
--
--    beginResetModel();
--    endResetModel();
--
--    imageInfosCleared();
--}
--
--void ImageModel::setImageInfos(const QList<ImageInfo>& infos)
--{
--    clearImageInfos();
--    addImageInfos(infos);
--}
--
--QList<ImageInfo> ImageModel::imageInfos() const
--{
--    return d->infos;
--}
--
--QList<qlonglong> ImageModel::imageIds() const
--{
--    return d->idHash.keys();
--}
--
--bool ImageModel::hasImage(qlonglong id) const
--{
--    return d->idHash.contains(id);
--}
--
--bool ImageModel::hasImage(const ImageInfo& info) const
--{
--    return d->idHash.contains(info.id());
--}
--
--bool ImageModel::hasImage(const ImageInfo& info, const QVariant& extraValue) const
--{
--    return hasImage(info.id(), extraValue);
--}
--
--bool ImageModel::hasImage(qlonglong id, const QVariant& extraValue) const
--{
--    if (d->extraValues.isEmpty())
--        return hasImage(id);
--
--    QHash<qlonglong, int>::const_iterator it;
--
--    for (it = d->idHash.constFind(id); it != d->idHash.constEnd() && it.key() == id; ++it)
--    {
--        if (d->extraValues.at(it.value()) == extraValue)
--            return true;
--    }
--
--    return false;;
--}
--
--QList<ImageInfo> ImageModel::uniqueImageInfos() const
--{
--    if (d->extraValues.isEmpty())
--    {
--        return d->infos;
--    }
--
--    QList<ImageInfo> uniqueInfos;
--    const int size = d->infos.size();
--
--    for (int i=0; i<size; ++i)
--    {
--        const ImageInfo& info = d->infos.at(i);
--
--        if (d->idHash.value(info.id()) == i)
--        {
--            uniqueInfos << info;
--        }
--    }
--
--    return uniqueInfos;
--}
--
--void ImageModel::emitDataChangedForAll()
--{
--    if (d->infos.isEmpty())
--    {
--        return;
--    }
--
--    QModelIndex first = createIndex(0, 0);
--    QModelIndex last  = createIndex(d->infos.size() - 1, 0);
--    emit dataChanged(first, last);
--}
--
--void ImageModel::emitDataChangedForSelection(const QItemSelection& selection)
--{
--    if (!selection.isEmpty())
--    {
--        foreach(const QItemSelectionRange& range, selection)
--        {
--            emit dataChanged(range.topLeft(), range.bottomRight());
--        }
--    }
--}
--
--void ImageModel::ensureHasGroupedImages(const ImageInfo& groupLeader)
--{
--    ensureHasImageInfos(groupLeader.groupedImages());
--}
--
--// ------------ Preprocessing -------------
--
--void ImageModel::setPreprocessor(QObject* preprocessor)
--{
--    unsetPreprocessor(d->preprocessor);
--    d->preprocessor = preprocessor;
--}
--
--void ImageModel::unsetPreprocessor(QObject* preprocessor)
--{
--    if (preprocessor && d->preprocessor == preprocessor)
--    {
--        disconnect(this, SIGNAL(preprocess(QList<ImageInfo>,QList<QVariant>)), 0, 0);
--        disconnect(d->preprocessor, 0, this, SLOT(reAddImageInfos(QList<ImageInfo>,QList<QVariant>)));
--        disconnect(d->preprocessor, 0, this, SLOT(reAddingFinished()));
--    }
--}
--
--void ImageModel::appendInfos(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues)
--{
--    if (infos.isEmpty())
--    {
--        return;
--    }
--
--    if (d->preprocessor)
--    {
--        d->reAdding = true;
--        emit preprocess(infos, extraValues);
--    }
--    else
--    {
--        publiciseInfos(infos, extraValues);
--    }
--}
--
--void ImageModel::appendInfosChecked(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues)
--{
--    // This method does deduplication. It is private because in context of readding or refreshing it is of no use.
--
--    if (extraValues.isEmpty())
--    {
--        QList<ImageInfo> checkedInfos;
--
--        foreach (const ImageInfo& info, infos)
--        {
--            if (!hasImage(info))
--            {
--                checkedInfos << info;
--            }
--        }
--
--        appendInfos(checkedInfos, QList<QVariant>());
--    }
--    else
--    {
--        QList<ImageInfo> checkedInfos;
--        QList<QVariant>  checkedExtraValues;
--        const int size = infos.size();
--
--        for (int i=0; i<size; i++)
--        {
--            if (!hasImage(infos[i], extraValues[i]))
--            {
--                checkedInfos << infos[i];
--                checkedExtraValues << extraValues[i];
--            }
--        }
--
--        appendInfos(checkedInfos, checkedExtraValues);
--    }
--}
--
--void ImageModel::reAddImageInfos(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues)
--{
--    // addImageInfos -> appendInfos -> preprocessor -> reAddImageInfos
--    publiciseInfos(infos, extraValues);
--}
--
--void ImageModel::reAddingFinished()
--{
--    d->reAdding = false;
--    cleanSituationChecks();
--}
--
--void ImageModel::startRefresh()
--{
--    d->refreshing = true;
--}
--
--void ImageModel::finishRefresh()
--{
--    d->refreshing = false;
--    cleanSituationChecks();
--}
--
--bool ImageModel::isRefreshing() const
--{
--    return d->refreshing;
--}
--
--void ImageModel::cleanSituationChecks()
--{
--    // For starting an incremental refresh we want a clear situation:
--    // Any remaining batches from non-incremental refreshing subclasses have been received in appendInfos(),
--    // any batches sent to preprocessor for re-adding have been re-added.
--    if (d->refreshing || d->reAdding)
--    {
--        return;
--    }
--
--    if (!d->pendingInfos.isEmpty())
--    {
--        appendInfosChecked(d->pendingInfos, d->pendingExtraValues);
--        d->pendingInfos.clear();
--        d->pendingExtraValues.clear();
--        cleanSituationChecks();
--        return;
--    }
--
--    if (d->incrementalRefreshRequested)
--    {
--        d->incrementalRefreshRequested = false;
--        emit readyForIncrementalRefresh();
--    }
--    else
--    {
--        emit allRefreshingFinished();
--    }
--}
--
--void ImageModel::publiciseInfos(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues)
--{
--    if (infos.isEmpty())
--    {
--        return;
--    }
--
--    Q_ASSERT(infos.size() == extraValues.size() || (extraValues.isEmpty() && d->extraValues.isEmpty()));
--
--    emit imageInfosAboutToBeAdded(infos);
--    const int firstNewIndex = d->infos.size();
--    const int lastNewIndex  = d->infos.size() + infos.size() - 1;
--    beginInsertRows(QModelIndex(), firstNewIndex, lastNewIndex);
--    d->infos << infos;
--    d->extraValues << extraValues;
--
--    for (int i=firstNewIndex; i<=lastNewIndex; ++i)
--    {
--        const ImageInfo& info = d->infos.at(i);
--        qlonglong id          = info.id();
--        d->idHash.insertMulti(id, i);
--
--        if (d->keepFilePathCache)
--        {
--            d->filePathHash[info.filePath()] = id;
--        }
--    }
--
--    endInsertRows();
--    emit imageInfosAdded(infos);
--}
--
--void ImageModel::requestIncrementalRefresh()
--{
--    if (d->reAdding)
--    {
--        d->incrementalRefreshRequested = true;
--    }
--    else
--    {
--        emit readyForIncrementalRefresh();
--    }
--}
--
--bool ImageModel::hasIncrementalRefreshPending() const
--{
--    return d->incrementalRefreshRequested;
--}
--
--void ImageModel::startIncrementalRefresh()
--{
--    delete d->incrementalUpdater;
--
--    d->incrementalUpdater = new ImageModelIncrementalUpdater(d);
--}
--
--void ImageModel::finishIncrementalRefresh()
--{
--    if (!d->incrementalUpdater)
--    {
--        return;
--    }
--
--    // remove old entries
--    QList<QPair<int, int> > pairs = d->incrementalUpdater->oldIndexes();
--    removeRowPairs(pairs);
--
--    // add new indexes
--    appendInfos(d->incrementalUpdater->newInfos, d->incrementalUpdater->newExtraValues);
--
--    delete d->incrementalUpdater;
--    d->incrementalUpdater = 0;
--}
--
--void ImageModel::removeIndex(const QModelIndex& index)
--{
--    removeIndexes(QList<QModelIndex>() << index);
--}
--
--void ImageModel::removeIndexes(const QList<QModelIndex>& indexes)
--{
--    QList<int> listIndexes;
--
--    foreach(const QModelIndex& index, indexes)
--    {
--        if (d->isValid(index))
--        {
--            listIndexes << index.row();
--        }
--    }
--
--    if (listIndexes.isEmpty())
--    {
--        return;
--    }
--
--    removeRowPairsWithCheck(ImageModelIncrementalUpdater::toContiguousPairs(listIndexes));
--}
--
--void ImageModel::removeImageInfo(const ImageInfo& info)
--{
--    removeImageInfos(QList<ImageInfo>() << info);
--}
--
--void ImageModel::removeImageInfos(const QList<ImageInfo>& infos)
--{
--    QList<int> listIndexes;
--
--    foreach(const ImageInfo& info, infos)
--    {
--        QModelIndex index = indexForImageId(info.id());
--
--        if (index.isValid())
--        {
--            listIndexes << index.row();
--        }
--    }
--    removeRowPairsWithCheck(ImageModelIncrementalUpdater::toContiguousPairs(listIndexes));
--}
--
--void ImageModel::removeImageInfos(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues)
--{
--    if (extraValues.isEmpty())
--    {
--        removeImageInfos(infos);
--        return;
--    }
--
--    QList<int> listIndexes;
--
--    for (int i=0; i<infos.size(); ++i)
--    {
--        QModelIndex index = indexForImageId(infos.at(i).id(), extraValues.at(i));
--
--        if (index.isValid())
--        {
--            listIndexes << index.row();
--        }
--    }
--
--    removeRowPairsWithCheck(ImageModelIncrementalUpdater::toContiguousPairs(listIndexes));
--}
--
--void ImageModel::setSendRemovalSignals(bool send)
--{
--    d->sendRemovalSignals = send;
--}
--
--template <class List, typename T>
--static bool pairsContain(const List& list, T value)
--{
--    typename List::const_iterator middle;
--    typename List::const_iterator begin = list.begin();
--    typename List::const_iterator end   = list.end();
--    int n                               = int(end - begin);
--    int half;
--
--    while (n > 0)
--    {
--        half   = n >> 1;
--        middle = begin + half;
--
--        if (middle->first <= value && middle->second >= value)
--        {
--            return true;
--        }
--        else if (middle->second < value)
--        {
--            begin = middle + 1;
--            n     -= half + 1;
--        }
--        else
--        {
--            n = half;
--        }
--    }
--
--    return false;
--}
--
--void ImageModel::removeRowPairsWithCheck(const QList<QPair<int, int> >& toRemove)
--{
--    if (d->incrementalUpdater)
--    {
--        d->incrementalUpdater->aboutToBeRemovedInModel(toRemove);
--    }
--
--    removeRowPairs(toRemove);
--}
--
--void ImageModel::removeRowPairs(const QList<QPair<int, int> >& toRemove)
--{
--    if (toRemove.isEmpty())
--    {
--        return;
--    }
--
--    // Remove old indexes
--    // Keep in mind that when calling beginRemoveRows all structures announced to be removed
--    // must still be valid, and this includes our hashes as well, which limits what we can optimize
--
--    int removedRows = 0, offset = 0;
--    typedef QPair<int, int> IntPair; // to make foreach macro happy
--
--    foreach(const IntPair& pair, toRemove)
--    {
--        const int begin = pair.first - offset;
--        const int end   = pair.second - offset; // inclusive
--        removedRows     = end - begin + 1;
--
--        // when removing from the list, all subsequent indexes are affected
--        offset += removedRows;
--
--        QList<ImageInfo> removedInfos;
--
--        if (d->sendRemovalSignals)
--        {
--            qCopy(d->infos.begin() + begin, d->infos.begin() + end, removedInfos.begin());
--            emit imageInfosAboutToBeRemoved(removedInfos);
--        }
--
--        imageInfosAboutToBeRemoved(begin, end);
--        beginRemoveRows(QModelIndex(), begin, end);
--
--        // update idHash - which points to indexes of d->infos, and these change now!
--        QHash<qlonglong, int>::iterator it;
--
--        for (it = d->idHash.begin(); it != d->idHash.end(); )
--        {
--            if (it.value() >= begin)
--            {
--                if (it.value() > end)
--                {
--                    // after the removed interval: adjust index
--                    it.value() -= removedRows;
--                }
--                else
--                {
--                    // in the removed interval
--                    it = d->idHash.erase(it);
--                    continue;
--                }
--            }
--
--            ++it;
--        }
--
--        // remove from list
--        d->infos.erase(d->infos.begin() + begin, d->infos.begin() + (end + 1));
--
--        if (!d->extraValues.isEmpty())
--        {
--            d->extraValues.erase(d->extraValues.begin() + begin, d->extraValues.begin() + (end + 1));
--        }
--
--        endRemoveRows();
--
--        if (d->sendRemovalSignals)
--        {
--            emit imageInfosRemoved(removedInfos);
--        }
--    }
--
--    // tidy up: remove old indexes from file path hash now
--    if (d->keepFilePathCache)
--    {
--        QHash<QString, qlonglong>::iterator it;
--
--        for (it = d->filePathHash.begin(); it != d->filePathHash.end(); )
--        {
--            if (pairsContain(toRemove, it.value()))
--            {
--                it = d->filePathHash.erase(it);
--            }
--            else
--            {
--                ++it;
--            }
--        }
--    }
--}
--
--ImageModelIncrementalUpdater::ImageModelIncrementalUpdater(ImageModel::Private* d)
--{
--    oldIds         = d->idHash;
--    oldExtraValues = d->extraValues;
--}
--
--void ImageModelIncrementalUpdater::appendInfos(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues)
--{
--    if (extraValues.isEmpty())
--    {
--        foreach(const ImageInfo& info, infos)
--        {
--            QHash<qlonglong,int>::iterator it = oldIds.find(info.id());
--
--            if (it != oldIds.end())
--            {
--                oldIds.erase(it);
--            }
--            else
--            {
--                newInfos << info;
--            }
--        }
--    }
--    else
--    {
--        for (int i=0; i<infos.size(); ++i)
--        {
--            const ImageInfo& info = infos.at(i);
--            bool found            = false;
--            QHash<qlonglong,int>::iterator it;
--
--            for (it = oldIds.find(info.id()); it != oldIds.end() && it.key() == info.id(); ++it)
--            {
--                // first check is for bug #262596. Not sure if needed.
--                if (it.value() < oldExtraValues.size() && extraValues.at(i) == oldExtraValues.at(it.value()))
--                {
--                    found = true;
--                    break;
--                }
--            }
--
--            if (found)
--            {
--                oldIds.erase(it);
--                // do not erase from oldExtraValues - oldIds is a hash id -> index.
--            }
--            else
--            {
--                newInfos << info;
--                newExtraValues << extraValues.at(i);
--            }
--        }
--    }
--}
--
--void ImageModelIncrementalUpdater::aboutToBeRemovedInModel(const IntPairList& toRemove)
--{
--    modelRemovals << toRemove;
--}
--
--QList<QPair<int, int> > ImageModelIncrementalUpdater::oldIndexes()
--{
--    // first, apply all changes to indexes by direct removal in model
--    // while the updater was active
--    foreach(const IntPairList& list, modelRemovals)
--    {
--        int removedRows = 0, offset = 0;
--
--        foreach(const IntPair& pair, list)
--        {
--            const int begin = pair.first - offset;
--            const int end   = pair.second - offset; // inclusive
--            removedRows     = end - begin + 1;
--
--            // when removing from the list, all subsequent indexes are affected
--            offset += removedRows;
--
--            // update idHash - which points to indexes of d->infos, and these change now!
--            QHash<qlonglong, int>::iterator it;
--
--            for (it = oldIds.begin(); it != oldIds.end(); )
--            {
--                if (it.value() >= begin)
--                {
--                    if (it.value() > end)
--                    {
--                        // after the removed interval: adjust index
--                        it.value() -= removedRows;
--                    }
--                    else
--                    {
--                        // in the removed interval
--                        it = oldIds.erase(it);
--                        continue;
--                    }
--                }
--
--                ++it;
--            }
--        }
--    }
--
--    modelRemovals.clear();
--
--    return toContiguousPairs(oldIds.values());
--}
--
--QList<QPair<int, int> > ImageModelIncrementalUpdater::toContiguousPairs(const QList<int>& unsorted)
--{
--    // Take the given indices and return them as contiguous pairs [begin, end]
--
--    QList<QPair<int, int> > pairs;
--
--    if (unsorted.isEmpty())
--    {
--        return pairs;
--    }
--
--    QList<int> indices(unsorted);
--    qSort(indices);
--
--    QPair<int, int> pair(indices.first(), indices.first());
--
--    for (int i=1; i<indices.size(); ++i)
--    {
--        const int &index = indices.at(i);
--
--        if (index == pair.second + 1)
--        {
--            pair.second = index;
--            continue;
--        }
--
--        pairs << pair; // insert last pair
--        pair.first  = index;
--        pair.second = index;
--    }
--
--    pairs << pair;
--
--    return pairs;
--}
--
--// ------------ QAbstractItemModel implementation -------------
--
--QVariant ImageModel::data(const QModelIndex& index, int role) const
--{
--    if (!d->isValid(index))
--    {
--        return QVariant();
--    }
--
--    switch (role)
--    {
--        case Qt::DisplayRole:
--        case Qt::ToolTipRole:
--            return d->infos.at(index.row()).name();
--
--        case ImageModelPointerRole:
--            return QVariant::fromValue(const_cast<ImageModel*>(this));
--
--        case ImageModelInternalId:
--            return index.row();
--
--        case CreationDateRole:
--            return d->infos.at(index.row()).dateTime();
--
--        case ExtraDataRole:
--
--            if (d->extraValueValid(index))
--            {
--                return d->extraValues.at(index.row());
--            }
--            else
--            {
--                return QVariant();
--            }
--
--        case ExtraDataDuplicateCount:
--        {
--            qlonglong id = d->infos.at(index.row()).id();
--            return numberOfIndexesForImageId(id);
--        }
--    }
--
--    return QVariant();
--}
--
--QVariant ImageModel::headerData(int section, Qt::Orientation orientation, int role) const
--{
--    Q_UNUSED(section)
--    Q_UNUSED(orientation)
--    Q_UNUSED(role)
--    return QVariant();
--}
--
--int ImageModel::rowCount(const QModelIndex& parent) const
--{
--    if (parent.isValid())
--    {
--        return 0;
--    }
--
--    return d->infos.size();
--}
--
--Qt::ItemFlags ImageModel::flags(const QModelIndex& index) const
--{
--    if (!d->isValid(index))
--    {
--        return 0;
--    }
--
--    Qt::ItemFlags f = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
--
--    f |= dragDropFlags(index);
--
--    return f;
--}
--
--QModelIndex ImageModel::index(int row, int column, const QModelIndex& parent) const
--{
--    if (column != 0 || row < 0 || parent.isValid() || row >= d->infos.size())
--    {
--        return QModelIndex();
--    }
--
--    return createIndex(row, 0);
--}
--
--// ------------ Database watch -------------
--
--void ImageModel::slotImageChange(const ImageChangeset& changeset)
--{
--    if (d->infos.isEmpty())
--    {
--        return;
--    }
--
--    if (d->watchFlags & changeset.changes())
--    {
--        QItemSelection items;
--
--        foreach(const qlonglong& id, changeset.ids())
--        {
--            QModelIndex index = indexForImageId(id);
--
--            if (index.isValid())
--            {
--                items.select(index, index);
--            }
--        }
--
--        if (!items.isEmpty())
--        {
--            emitDataChangedForSelection(items);
--            emit imageChange(changeset, items);
--        }
--    }
--}
--
--void ImageModel::slotImageTagChange(const ImageTagChangeset& changeset)
--{
--    if (d->infos.isEmpty())
--    {
--        return;
--    }
--
--    QItemSelection items;
--
--    foreach(const qlonglong& id, changeset.ids())
--    {
--        QModelIndex index = indexForImageId(id);
--
--        if (index.isValid())
--        {
--            items.select(index, index);
--        }
--    }
--
--    if (!items.isEmpty())
--    {
--        emitDataChangedForSelection(items);
--        emit imageTagChange(changeset, items);
--    }
--}
--
--} // namespace Digikam
-diff --git a/libs/models/imagemodel.h b/libs/models/imagemodel.h
-deleted file mode 100644
-index dcf94c2..0000000
---- a/libs/models/imagemodel.h
-+++ /dev/null
-@@ -1,364 +0,0 @@
--/* ============================================================
-- *
-- * This file is a part of digiKam project
-- * http://www.digikam.org
-- *
-- * Date        : 2009-03-05
-- * Description : Qt item model for database entries
-- *
-- * Copyright (C) 2009-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
-- *
-- * This program is free software; you can redistribute it
-- * and/or modify it under the terms of the GNU General
-- * Public License as published by the Free Software Foundation;
-- * either version 2, or (at your option)
-- * any later version.
-- *
-- * This program is distributed in the hope that it will be useful,
-- * but WITHOUT ANY WARRANTY; without even the implied warranty of
-- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-- * GNU General Public License for more details.
-- *
-- * ============================================================ */
--
--#ifndef IMAGEMODEL_H
--#define IMAGEMODEL_H
--
--// Qt includes
--
--#include <QAbstractListModel>
--
--// Local includes
--
--#include "dragdropimplementations.h"
--#include "imageinfo.h"
--#include "digikam_export.h"
--
--class QItemSelection;
--
--namespace Digikam
--{
--
--class ImageChangeset;
--class ImageTagChangeset;
--
--namespace DatabaseFields
--{
--class Set;
--}
--
--class DIGIKAM_DATABASE_EXPORT ImageModel : public QAbstractListModel, public DragDropModelImplementation
--{
--    Q_OBJECT
--
--public:
--
--    enum ImageModelRoles
--    {
--        /// An ImageModel* pointer to this model
--        ImageModelPointerRole   = Qt::UserRole,
--        ImageModelInternalId    = Qt::UserRole + 1,
--        /// Returns a thumbnail pixmap. May be implemented by subclasses.
--        /// Returns either a valid pixmap or a null QVariant.
--        ThumbnailRole           = Qt::UserRole + 2,
--        /// Returns a QDateTime with the creation date
--        CreationDateRole        = Qt::UserRole + 3,
--        /// Return (optional) extraData field
--        ExtraDataRole           = Qt::UserRole + 5,
--        /// Returns the number of duplicate indexes for the same image id
--        ExtraDataDuplicateCount = Qt::UserRole + 6,
--
--        // Roles which are defined here but not implemented by ImageModel
--        /// Returns position of item in Left Light Table preview.
--        LTLeftPanelRole         = Qt::UserRole + 50,
--        /// Returns position of item in Right Light Table preview.
--        LTRightPanelRole        = Qt::UserRole + 51,
--
--        // For use by subclasses
--        SubclassRoles           = Qt::UserRole + 100,
--        // For use by filter models
--        FilterModelRoles        = Qt::UserRole + 500
--    };
--
--public:
--
--    explicit ImageModel(QObject* parent = 0);
--    ~ImageModel();
--
--    /** If a cache is kept, lookup by file path is fast,
--     *  without a cache it is O(n). Default is false.
--     */
--    void setKeepsFilePathCache(bool keepCache);
--    bool keepsFilePathCache() const;
--
--    /** Set a set of database fields to watch.
--     *  If either of these is changed, dataChanged() will be emitted.
--     *  Default is no flag (no signal will be emitted).
--     */
--    void setWatchFlags(const DatabaseFields::Set& set);
--
--    /** Returns the ImageInfo object, reference or image id from the underlying data
--     *  pointed to by the index.
--     *  If the index is not valid, imageInfo will return a null ImageInfo, imageId will
--     *  return 0, imageInfoRef must not be called with an invalid index.
--     */
--    ImageInfo        imageInfo(const QModelIndex& index) const;
--    ImageInfo&       imageInfoRef(const QModelIndex& index) const;
--    qlonglong        imageId(const QModelIndex& index) const;
--    QList<ImageInfo> imageInfos(const QList<QModelIndex>& indexes) const;
--    QList<qlonglong> imageIds(const QList<QModelIndex>& indexes) const;
--
--    /** Returns the ImageInfo object, reference or image id from the underlying data
--     *  of the given row (parent is the invalid QModelIndex, column is 0).
--     *  Note that imageInfoRef will crash if index is invalid.
--     */
--    ImageInfo  imageInfo(int row) const;
--    ImageInfo& imageInfoRef(int row) const;
--    qlonglong  imageId(int row) const;
--
--    /** Return the index for the given ImageInfo or id, if contained in this model.
--     */
--    QModelIndex        indexForImageInfo(const ImageInfo& info) const;
--    QModelIndex        indexForImageInfo(const ImageInfo& info, const QVariant& extraValue) const;
--    QModelIndex        indexForImageId(qlonglong id) const;
--    QModelIndex        indexForImageId(qlonglong id, const QVariant& extraValue) const;
--    QList<QModelIndex> indexesForImageInfo(const ImageInfo& info) const;
--    QList<QModelIndex> indexesForImageId(qlonglong id) const;
--
--    int numberOfIndexesForImageInfo(const ImageInfo& info) const;
--    int numberOfIndexesForImageId(qlonglong id) const;
--
--    /** Returns the index or ImageInfo object from the underlying data
--     *  for the given file path. This is fast if keepsFilePathCache is enabled.
--     *  The file path is as returned by ImageInfo.filePath().
--     *  In case of multiple occurrences of the same file, the simpler variants return
--     *  any one found first, use the QList methods to retrieve all occurrences.
--     */
--    QModelIndex        indexForPath(const QString& filePath) const;
--    ImageInfo          imageInfo(const QString& filePath) const;
--    QList<QModelIndex> indexesForPath(const QString& filePath) const;
--    QList<ImageInfo>   imageInfos(const QString& filePath) const;
--
--    /** Main entry point for subclasses adding image infos to the model.
--     *  If you list entries not unique per image id, you must add an extraValue
--     *  so that every entry is unique by imageId and extraValues.
--     *  Please note that these methods do not prevent addition of duplicate entries.
--     */
--    void addImageInfo(const ImageInfo& info);
--    void addImageInfos(const QList<ImageInfo>& infos);
--    void addImageInfos(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues);
--
--    /** Clears image infos and resets model.
--     */
--    void clearImageInfos();
--
--    /** Clears and adds the infos.
--     */
--    void setImageInfos(const QList<ImageInfo>& infos);
--
--    /**
--     * Directly remove the given indexes or infos from the model.
--     */
--    void removeIndex(const QModelIndex& indexes);
--    void removeIndexes(const QList<QModelIndex>& indexes);
--    void removeImageInfo(const ImageInfo& info);
--    void removeImageInfos(const QList<ImageInfo>& infos);
--    void removeImageInfos(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues);
--
--    /**
--     * addImageInfo() is asynchronous if a prepocessor is set.
--     * This method first adds the info, synchronously.
--     * Only afterwards, the preprocessor will have the opportunity to process it.
--     * This method also bypasses any incremental updates.
--     * Please note that these methods do not prevent addition of duplicate entries.
--     */
--    void addImageInfoSynchronously(const ImageInfo& info);
--    void addImageInfosSynchronously(const QList<ImageInfo>& infos);
--    void addImageInfosSynchronously(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues);
--
--    /**
--     * Add the given entries. Method returns immediately, the
--     * addition may happen later asynchronously.
--     * These methods prevent the addition of duplicate entries.
--     */
--    void ensureHasImageInfo(const ImageInfo& info);
--    void ensureHasImageInfos(const QList<ImageInfo>& infos);
--    void ensureHasImageInfos(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues);
--
--    /**
--     * Ensure that all images grouped on the given leader are contained in the model.
--     */
--    void ensureHasGroupedImages(const ImageInfo& groupLeader);
--
--    QList<ImageInfo> imageInfos() const;
--    QList<qlonglong> imageIds()    const;
--    QList<ImageInfo> uniqueImageInfos() const;
--
--    bool hasImage(qlonglong id) const;
--    bool hasImage(const ImageInfo& info) const;
--    bool hasImage(const ImageInfo& info, const QVariant& extraValue) const;
--    bool hasImage(qlonglong id, const QVariant& extraValue) const;
--
--    bool isEmpty() const;
--
--    // Drag and Drop
--    DECLARE_MODEL_DRAG_DROP_METHODS
--
--    /**
--     * Install an object as a preprocessor for ImageInfos added to this model.
--     * For every QList of ImageInfos added to addImageInfo, the signal preprocess()
--     * will be emitted. The preprocessor may process the items and shall then readd
--     * them by calling reAddImageInfos(). It may take some time to process.
--     * It shall discard any held infos when the modelReset() signal is sent.
--     * It shall call readdFinished() when no reset occurred and all infos on the way have been readded.
--     * This means that only after calling this method, you shall make three connections
--     * (preprocess -> your slot, your signal -> reAddImageInfos, your signal -> reAddingFinished)
--     * and make or already hold a connection modelReset() -> your slot.
--     * There is only one preprocessor at a time, a previously set object will be disconnected.
--     */
--    void setPreprocessor(QObject* processor);
--    void unsetPreprocessor(QObject* processor);
--
--    /**
--     * Returns true if this model is currently refreshing.
--     * For a preprocessor this means that, although the preprocessor may currently have
--     * processed all it got, more batches are to be expected.
--     */
--    bool isRefreshing() const;
--
--    /**
--     * Enable sending of imageInfosAboutToBeRemoved and imageInfosRemoved signals.
--     * Default: false
--     */
--    void setSendRemovalSignals(bool send);
--
--    virtual QVariant      data(const QModelIndex& index, int role = Qt::DisplayRole) const;
--    virtual QVariant      headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
--    virtual int           rowCount(const QModelIndex& parent = QModelIndex()) const;
--    virtual Qt::ItemFlags flags(const QModelIndex& index) const;
--    virtual QModelIndex   index(int row, int column = 0, const QModelIndex& parent = QModelIndex()) const;
--
--    /** Retrieves the imageInfo object from the data() method of the given index.
--     *  The index may be from a QSortFilterProxyModel as long as an ImageModel is at the end. */
--    static ImageInfo retrieveImageInfo(const QModelIndex& index);
--    static qlonglong retrieveImageId(const QModelIndex& index);
--
--Q_SIGNALS:
--
--    /** Informs that ImageInfos will be added to the model.
--     *  This signal is sent before the model data is changed and views are informed.
--     */
--    void imageInfosAboutToBeAdded(const QList<ImageInfo>& infos);
--
--    /** Informs that ImageInfos have been added to the model.
--     *  This signal is sent after the model data is changed and views are informed.
--     */
--    void imageInfosAdded(const QList<ImageInfo>& infos);
--
--    /** Informs that ImageInfos will be removed from the model.
--     *  This signal is sent before the model data is changed and views are informed.
--     *  Note: You need to explicitly enable sending of this signal. It is not sent
--     *  in clearImageInfos().
--     */
--    void imageInfosAboutToBeRemoved(const QList<ImageInfo>& infos);
--
--    /** Informs that ImageInfos have been removed from the model.
--     *  This signal is sent after the model data is changed and views are informed. *
--     *  Note: You need to explicitly enable sending of this signal. It is not sent
--     *  in clearImageInfos().
--     */
--    void imageInfosRemoved(const QList<ImageInfo>& infos);
--
--    /** Connect to this signal only if you are the current preprocessor.
--     */
--    void preprocess(const QList<ImageInfo>& infos, const QList<QVariant>&);
--    void processAdded(const QList<ImageInfo>& infos, const QList<QVariant>&);
--
--    /** If an ImageChangeset affected indexes of this model with changes as set in watchFlags(),
--     *  this signal contains the changeset and the affected indexes.
--     */
--    void imageChange(const ImageChangeset&, const QItemSelection&);
--
--    /** If an ImageTagChangeset affected indexes of this model,
--     *  this signal contains the changeset and the affected indexes.
--     */
--    void imageTagChange(const ImageTagChangeset&, const QItemSelection&);
--
--    /** Signals that the model is right now ready to start an incremental refresh.
--     *  This is guaranteed only for the scope of emitting this signal.
--     */
--    void readyForIncrementalRefresh();
--
--    /** Signals that the model has finished currently with all scheduled
--     *  refreshing, full or incremental, and all preprocessing.
--     *  The model is in polished, clean situation right now.
--     */
--    void allRefreshingFinished();
--
--public Q_SLOTS:
--
--    void reAddImageInfos(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues);
--    void reAddingFinished();
--
--protected:
--
--    /** Subclasses that add ImageInfos in batches shall call startRefresh()
--     *  when they start sending batches and finishRefresh() when they have finished.
--     *  No incremental refreshes will be started while listing.
--     *  A clearImageInfos() always stops listing, calling finishRefresh() is then not necessary.
--     */
--    void startRefresh();
--    void finishRefresh();
--
--    /** As soon as the model is ready to start an incremental refresh, the signal
--     *  readyForIncrementalRefresh() will be emitted. The signal will be emitted inline
--     *  if the model is ready right now.
--     */
--    void requestIncrementalRefresh();
--    bool hasIncrementalRefreshPending() const;
--
--    /** Starts an incremental refresh operation. You shall only call this method from a slot
--     *  connected to readyForIncrementalRefresh(). To initiate an incremental refresh,
--     *  call requestIncrementalRefresh().
--     */
--    void startIncrementalRefresh();
--    void finishIncrementalRefresh();
--
--    void emitDataChangedForAll();
--    void emitDataChangedForSelection(const QItemSelection& selection);
--
--    // Called when the internal storage is cleared
--    virtual void imageInfosCleared() {};
--
--    // Called before rowsAboutToBeRemoved
--    virtual void imageInfosAboutToBeRemoved(int /*begin*/, int /*end*/) {};
--
--protected Q_SLOTS:
--
--    virtual void slotImageChange(const ImageChangeset& changeset);
--    virtual void slotImageTagChange(const ImageTagChangeset& changeset);
--
--private:
--
--    void appendInfos(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues);
--    void appendInfosChecked(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues);
--    void publiciseInfos(const QList<ImageInfo>& infos, const QList<QVariant>& extraValues);
--    void cleanSituationChecks();
--    void removeRowPairsWithCheck(const QList<QPair<int, int> >& toRemove);
--    void removeRowPairs(const QList<QPair<int, int> >& toRemove);
--
--public:
--
--    // Declared public because it's used in ImageModelIncrementalUpdater class
--    class Private;
--
--private:
--
--    Private* const d;
--};
--
--} // namespace Digikam
--
--Q_DECLARE_METATYPE(Digikam::ImageModel*)
--
--#endif // IMAGEMODEL_H
-diff --git a/libs/models/imagesortsettings.cpp b/libs/models/imagesortsettings.cpp
-deleted file mode 100644
-index 39ee6e1..0000000
---- a/libs/models/imagesortsettings.cpp
-+++ /dev/null
-@@ -1,400 +0,0 @@
--/* ============================================================
-- *
-- * This file is a part of digiKam project
-- * http://www.digikam.org
-- *
-- * Date        : 2009-03-05
-- * Description : Filter values for use with ImageFilterModel
-- *
-- * Copyright (C) 2009 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
-- * Copyright (C) 2014 by Mohamed Anwer <m dot anwer at gmx dot com>
-- *
-- * This program is free software; you can redistribute it
-- * and/or modify it under the terms of the GNU General
-- * Public License as published by the Free Software Foundation;
-- * either version 2, or (at your option)
-- * any later version.
-- *
-- * This program is distributed in the hope that it will be useful,
-- * but WITHOUT ANY WARRANTY; without even the implied warranty of
-- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-- * GNU General Public License for more details.
-- *
-- * ============================================================ */
--
--#include "imagesortsettings.h"
--
--// Qt includes
--
--#include <QDateTime>
--#include <QRectF>
--
--// Local includes
--
--#include "coredbfields.h"
--#include "imageinfo.h"
--
--namespace Digikam
--{
--
--ImageSortSettings::ImageSortSettings()
--{
--    categorizationMode             = NoCategories;
--    categorizationSortOrder        = DefaultOrder;
--    categorizationCaseSensitivity  = Qt::CaseSensitive;
--    sortRole                       = SortByFileName;
--    sortOrder                      = DefaultOrder;
--    strTypeNatural                 = true;
--    sortCaseSensitivity            = Qt::CaseSensitive;
--    currentCategorizationSortOrder = Qt::AscendingOrder;
--    currentSortOrder               = Qt::AscendingOrder;
--}
--
--bool ImageSortSettings::operator==(const ImageSortSettings& other) const
--{
--    return
--        categorizationMode            == other.categorizationMode            &&
--        categorizationSortOrder       == other.categorizationSortOrder       &&
--        categorizationCaseSensitivity == other.categorizationCaseSensitivity &&
--        sortRole                      == other.sortRole                      &&
--        sortOrder                     == other.sortOrder                     &&
--        sortCaseSensitivity           == other.sortCaseSensitivity;
--}
--
--void ImageSortSettings::setCategorizationMode(CategorizationMode mode)
--{
--    categorizationMode = mode;
--
--    if (categorizationSortOrder == DefaultOrder)
--    {
--        currentCategorizationSortOrder = defaultSortOrderForCategorizationMode(categorizationMode);
--    }
--}
--
--void ImageSortSettings::setCategorizationSortOrder(SortOrder order)
--{
--    categorizationSortOrder = order;
--
--    if (categorizationSortOrder == DefaultOrder)
--    {
--        currentCategorizationSortOrder = defaultSortOrderForCategorizationMode(categorizationMode);
--    }
--    else
--    {
--        currentCategorizationSortOrder = (Qt::SortOrder)categorizationSortOrder;
--    }
--}
--
--void ImageSortSettings::setSortRole(SortRole role)
--{
--    sortRole = role;
--
--    if (sortOrder == DefaultOrder)
--    {
--        currentSortOrder = defaultSortOrderForSortRole(sortRole);
--    }
--}
--
--void ImageSortSettings::setSortOrder(SortOrder order)
--{
--    sortOrder = order;
--
--    if (sortOrder == DefaultOrder)
--    {
--        currentSortOrder = defaultSortOrderForSortRole(sortRole);
--    }
--    else
--    {
--        currentSortOrder = (Qt::SortOrder)order;
--    }
--}
--
--void ImageSortSettings::setStringTypeNatural(bool natural)
--{
--    strTypeNatural = natural;
--}
--
--Qt::SortOrder ImageSortSettings::defaultSortOrderForCategorizationMode(CategorizationMode mode)
--{
--    switch (mode)
--    {
--        case NoCategories:
--        case OneCategory:
--        case CategoryByAlbum:
--        case CategoryByFormat:
--        default:
--            return Qt::AscendingOrder;
--    }
--}
--
--Qt::SortOrder ImageSortSettings::defaultSortOrderForSortRole(SortRole role)
--{
--    switch (role)
--    {
--        case SortByFileName:
--        case SortByFilePath:
--            return Qt::AscendingOrder;
--        case SortByFileSize:
--            return Qt::DescendingOrder;
--        case SortByModificationDate:
--        case SortByCreationDate:
--            return Qt::AscendingOrder;
--        case SortByRating:
--        case SortByImageSize:
--            return Qt::DescendingOrder;
--        case SortByAspectRatio:
--            return Qt::DescendingOrder;
--        case SortBySimilarity:
--            return Qt::DescendingOrder;
--        default:
--            return Qt::AscendingOrder;
--    }
--}
--
--int ImageSortSettings::compareCategories(const ImageInfo& left, const ImageInfo& right) const
--{
--    switch (categorizationMode)
--    {
--        case NoCategories:
--        case OneCategory:
--            return 0;
--        case CategoryByAlbum:
--        {
--            int leftAlbum = left.albumId();
--            int rightAlbum = right.albumId();
--
--            // return comparation result
--            if (leftAlbum == rightAlbum)
--            {
--                return 0;
--            }
--            else if (lessThanByOrder(leftAlbum, rightAlbum, currentCategorizationSortOrder))
--            {
--                return -1;
--            }
--            else
--            {
--                return 1;
--            }
--        }
--        case CategoryByFormat:
--        {
--            return naturalCompare(left.format(), right.format(),
--                                  currentCategorizationSortOrder, categorizationCaseSensitivity, strTypeNatural);
--        }
--        default:
--            return 0;
--    }
--}
--
--bool ImageSortSettings::lessThan(const ImageInfo& left, const ImageInfo& right) const
--{
--    int result = compare(left, right, sortRole);
--
--    if (result != 0)
--    {
--        return result < 0;
--    }
--
--    // are they identical?
--    if (left == right)
--    {
--        return false;
--    }
--
--    // If left and right equal for first sort order, use a hierarchy of all sort orders
--    if ( (result = compare(left, right, SortByFileName)) != 0)
--    {
--        return result < 0;
--    }
--
--    if ( (result = compare(left, right, SortByCreationDate)) != 0)
--    {
--        return result < 0;
--    }
--
--    if ( (result = compare(left, right, SortByModificationDate)) != 0)
--    {
--        return result < 0;
--    }
--
--    if ( (result = compare(left, right, SortByFilePath)) != 0)
--    {
--        return result < 0;
--    }
--
--    if ( (result = compare(left, right, SortByFileSize)) != 0)
--    {
--        return result < 0;
--    }
--
--    if ( (result = compare(left, right, SortBySimilarity)) != 0)
--    {
--        return result < 0;
--    }
--
--    return false;
--}
--
--int ImageSortSettings::compare(const ImageInfo& left, const ImageInfo& right) const
--{
--    return compare(left, right, sortRole);
--}
--
--int ImageSortSettings::compare(const ImageInfo& left, const ImageInfo& right, SortRole role) const
--{
--    switch (role)
--    {
--        case SortByFileName:
--        {
--            bool versioning = (left.name().contains(QLatin1String("_v"), Qt::CaseInsensitive) ||
--                               right.name().contains(QLatin1String("_v"), Qt::CaseInsensitive));
--            return naturalCompare(left.name(), right.name(), currentSortOrder, sortCaseSensitivity, strTypeNatural, versioning);
--        }
--        case SortByFilePath:
--            return naturalCompare(left.filePath(), right.filePath(), currentSortOrder, sortCaseSensitivity, strTypeNatural);
--        case SortByFileSize:
--            return compareByOrder(left.fileSize(), right.fileSize(), currentSortOrder);
--        case SortByModificationDate:
--            return compareByOrder(left.modDateTime(), right.modDateTime(), currentSortOrder);
--        case SortByCreationDate:
--            return compareByOrder(left.dateTime(), right.dateTime(), currentSortOrder);
--        case SortByRating:
--            // I have the feeling that inverting the sort order for rating is the natural order
--            return - compareByOrder(left.rating(), right.rating(), currentSortOrder);
--        case SortByImageSize:
--        {
--            QSize leftSize  = left.dimensions();
--            QSize rightSize = right.dimensions();
--            int leftPixels  = leftSize.width() * leftSize.height();
--            int rightPixels = rightSize.width() * rightSize.height();
--            return compareByOrder(leftPixels, rightPixels, currentSortOrder);
--        }
--        case SortByAspectRatio:
--        {
--            QSize leftSize = left.dimensions();
--            QSize rightSize = right.dimensions();
--            int leftAR = (double(leftSize.width()) / double(leftSize.height())) * 1000000;
--            int rightAR = (double(rightSize.width()) / double(rightSize.height())) * 1000000;
--            return compareByOrder(leftAR, rightAR, currentSortOrder);
--        }
--        case SortBySimilarity:
--        {
--            qlonglong leftReferenceImageId  = left.currentReferenceImage();
--            qlonglong rightReferenceImageId = right.currentReferenceImage();
--            // make sure that the original image has always the highest similarity.
--            double leftSimilarity  = left.id() == leftReferenceImageId ? 1.1 : left.currentSimilarity();
--            double rightSimilarity = right.id() == rightReferenceImageId ? 1.1 : right.currentSimilarity();
--            return compareByOrder(leftSimilarity, rightSimilarity, currentSortOrder);
--        }
--        default:
--            return 1;
--    }
--}
--
--bool ImageSortSettings::lessThan(const QVariant& left, const QVariant& right) const
--{
--    if (left.type() != right.type())
--    {
--        return false;
--    }
--
--    switch (left.type())
--    {
--        case QVariant::Int:
--            return compareByOrder(left.toInt(), right.toInt(), currentSortOrder);
--        case QVariant::UInt:
--            return compareByOrder(left.toUInt(), right.toUInt(), currentSortOrder);
--        case QVariant::LongLong:
--            return compareByOrder(left.toLongLong(), right.toLongLong(), currentSortOrder);
--        case QVariant::ULongLong:
--            return compareByOrder(left.toULongLong(), right.toULongLong(), currentSortOrder);
--        case QVariant::Double:
--            return compareByOrder(left.toDouble(), right.toDouble(), currentSortOrder);
--        case QVariant::Date:
--            return compareByOrder(left.toDate(), right.toDate(), currentSortOrder);
--        case QVariant::DateTime:
--            return compareByOrder(left.toDateTime(), right.toDateTime(), currentSortOrder);
--        case QVariant::Time:
--            return compareByOrder(left.toTime(), right.toTime(), currentSortOrder);
--        case QVariant::Rect:
--        case QVariant::RectF:
--        {
--            QRectF rectLeft  = left.toRectF();
--            QRectF rectRight = right.toRectF();
--            int result;
--
--            if ((result = compareByOrder(rectLeft.top(), rectRight.top(), currentSortOrder)) != 0)
--            {
--                return result < 0;
--            }
--
--            if ((result = compareByOrder(rectLeft.left(), rectRight.left(), currentSortOrder)) != 0)
--            {
--                return result < 0;
--            }
--
--            QSizeF sizeLeft = rectLeft.size(), sizeRight = rectRight.size();
--
--            if ((result = compareByOrder(sizeLeft.width()*sizeLeft.height(), sizeRight.width()*sizeRight.height(), currentSortOrder)) != 0)
--            {
--                return result < 0;
--            }
--            // FIXME: fall through?? If not, add "break" here
--        }
--        default:
--            return naturalCompare(left.toString(), right.toString(), currentSortOrder, sortCaseSensitivity, strTypeNatural);
--    }
--}
--
--DatabaseFields::Set ImageSortSettings::watchFlags() const
--{
--    DatabaseFields::Set set;
--
--    switch (sortRole)
--    {
--        case SortByFileName:
--            set |= DatabaseFields::Name;
--            break;
--        case SortByFilePath:
--            set |= DatabaseFields::Name;
--            break;
--        case SortByFileSize:
--            set |= DatabaseFields::FileSize;
--            break;
--        case SortByModificationDate:
--            set |= DatabaseFields::ModificationDate;
--            break;
--        case SortByCreationDate:
--            set |= DatabaseFields::CreationDate;
--            break;
--        case SortByRating:
--            set |= DatabaseFields::Rating;
--            break;
--        case SortByImageSize:
--            set |= DatabaseFields::Width | DatabaseFields::Height;
--            break;
--        case SortByAspectRatio:
--            set |= DatabaseFields::Width | DatabaseFields::Height;
--            break;
--        case SortBySimilarity:
--            // TODO: Not sure what to do here....
--            set |= DatabaseFields::Name;
--            break;
--    }
--
--    switch (categorizationMode)
--    {
--        case NoCategories:
--        case OneCategory:
--        case CategoryByAlbum:
--            break;
--        case CategoryByFormat:
--            set |= DatabaseFields::Format;
--            break;
--    }
--
--    return set;
--}
--
--} // namespace Digikam
-diff --git a/libs/models/imagesortsettings.h b/libs/models/imagesortsettings.h
-deleted file mode 100644
-index 2a5fd8c..0000000
---- a/libs/models/imagesortsettings.h
-+++ /dev/null
-@@ -1,225 +0,0 @@
--/* ============================================================
-- *
-- * This file is a part of digiKam project
-- * http://www.digikam.org
-- *
-- * Date        : 2009-05-31
-- * Description : Sort settings for use with ImageFilterModel
-- *
-- * Copyright (C) 2009 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
-- *
-- * This program is free software; you can redistribute it
-- * and/or modify it under the terms of the GNU General
-- * Public License as published by the Free Software Foundation;
-- * either version 2, or (at your option)
-- * any later version.
-- *
-- * This program is distributed in the hope that it will be useful,
-- * but WITHOUT ANY WARRANTY; without even the implied warranty of
-- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-- * GNU General Public License for more details.
-- *
-- * ============================================================ */
--
--#ifndef IMAGESORTSETTINGS_H
--#define IMAGESORTSETTINGS_H
--
--// Qt includes
--
--#include <QHash>
--#include <QList>
--#include <QMap>
--#include <QString>
--#include <QCollator>
--
--// Local includes
--
--#include "digikam_export.h"
--
--namespace Digikam
--{
--
--class ImageInfo;
--
--namespace DatabaseFields
--{
--    class Set;
--}
--
--class DIGIKAM_DATABASE_EXPORT ImageSortSettings
--{
--public:
--
--    ImageSortSettings();
--
--    bool operator==(const ImageSortSettings& other) const;
--
--    /** Compares the categories of left and right.
--     *  Return -1 if left is less than right, 0 if both fall in the same category,
--     *  and 1 if left is greater than right.
--     *  Adheres to set categorization mode and current category sort order.
--     */
--    int compareCategories(const ImageInfo& left, const ImageInfo& right) const;
--
--    /** Returns true if left is less than right.
--     *  Adheres to current sort role and sort order.
--     */
--    bool lessThan(const ImageInfo& left, const ImageInfo& right) const;
--
--    /** Compares the ImageInfos left and right.
--     *  Return -1 if left is less than right, 1 if left is greater than right,
--     *  and 0 if left equals right comparing the current sort role's value.
--     *  Adheres to set sort role and sort order.
--     */
--    int compare(const ImageInfo& left, const ImageInfo& right) const;
--
--    /** Returns true if left QVariant is less than right.
--     *  Adheres to current sort role and sort order.
--     *  Use for extraValue, if necessary.
--     */
--    bool lessThan(const QVariant& left, const QVariant& right) const;
--
--    enum SortOrder
--    {
--        AscendingOrder  = Qt::AscendingOrder,
--        DescendingOrder = Qt::DescendingOrder,
--        DefaultOrder /// sort order depends on the chosen sort role
--    };
--
--    /// --- Categories ---
--
--    enum CategorizationMode
--    {
--        NoCategories, /// categorization switched off
--        OneCategory, /// all items in one global category
--        CategoryByAlbum,
--        CategoryByFormat
--    };
--
--    CategorizationMode      categorizationMode;
--    SortOrder               categorizationSortOrder;
--
--    void setCategorizationMode(CategorizationMode mode);
--    void setCategorizationSortOrder(SortOrder order);
--
--    /// Only Ascending or Descending, never DefaultOrder
--    Qt::SortOrder           currentCategorizationSortOrder;
--    Qt::CaseSensitivity     categorizationCaseSensitivity;
--
--    bool isCategorized() const { return categorizationMode >= CategoryByAlbum; }
--
--    /// --- Image Sorting ---
--
--    enum SortRole
--    {
--        // Note: For legacy reasons, the order of the first five entries must remain unchanged
--        SortByFileName,
--        SortByFilePath,
--        SortByCreationDate,
--        SortByFileSize,
--        SortByRating,
--        SortByModificationDate,
--        SortByImageSize,            // pixel number
--        SortByAspectRatio,          // width / height * 100000
--        SortBySimilarity
--    };
--
--    SortRole                sortRole;
--    SortOrder               sortOrder;
--    bool                    strTypeNatural;
--
--    void setSortRole(SortRole role);
--    void setSortOrder(SortOrder order);
--    void setStringTypeNatural(bool natural);
--
--    Qt::SortOrder           currentSortOrder;
--    Qt::CaseSensitivity     sortCaseSensitivity;
--
--    int compare(const ImageInfo& left, const ImageInfo& right, SortRole sortRole) const;
--
--    // --- ---
--
--    static Qt::SortOrder defaultSortOrderForCategorizationMode(CategorizationMode mode);
--    static Qt::SortOrder defaultSortOrderForSortRole(SortRole role);
--
--    /// --- Change notification ---
--
--    /** Returns database fields a change in which would affect the current sorting.
--     */
--    DatabaseFields::Set watchFlags() const;
--
--    /// --- Utilities ---
--
--    /** Returns a < b if sortOrder is Ascending, or b < a if order is descending.
--     */
--    template <typename T>
--    static inline bool lessThanByOrder(const T& a, const T& b, Qt::SortOrder sortOrder)
--    {
--        if (sortOrder == Qt::AscendingOrder)
--        {
--            return a < b;
--        }
--        else
--        {
--            return b < a;
--        }
--    }
--
--    /** Returns the usual compare result of -1, 0, or 1 for lessThan, equals and greaterThan.
--     */
--    template <typename T>
--    static inline int compareValue(const T& a, const T& b)
--    {
--        if (a == b)
--        {
--            return 0;
--        }
--
--        if (a < b)
--        {
--            return -1;
--        }
--        else
--        {
--            return 1;
--        }
--    }
--
--    /** Takes a typical result from a compare method (0 is equal, -1 is less than, 1 is greater than)
--     *  and applies the given sort order to it.
--     */
--    static inline int compareByOrder(int compareResult,  Qt::SortOrder sortOrder)
--    {
--        if (sortOrder == Qt::AscendingOrder)
--        {
--            return compareResult;
--        }
--        else
--        {
--            return - compareResult;
--        }
--    }
--
--    template <typename T>
--    static inline int compareByOrder(const T& a, const T& b, Qt::SortOrder sortOrder)
--    {
--        return compareByOrder(compareValue(a, b), sortOrder);
--    }
--
--    /** Compares the two string by natural comparison and adheres to given sort order
--     */
--    static inline int naturalCompare(const QString& a, const QString& b, Qt::SortOrder sortOrder,
--                                     Qt::CaseSensitivity caseSensitive = Qt::CaseSensitive,
--                                     bool natural = true, bool versioning = false)
--    {
--        QCollator collator;
--        collator.setNumericMode(natural);
--        collator.setIgnorePunctuation(versioning);
--        collator.setCaseSensitivity(caseSensitive);
--        return (compareByOrder(collator.compare(a, b), sortOrder));
--    }
--};
--
--} // namespace Digikam
--
--#endif // IMAGESORTSETTINGS_H
-diff --git a/libs/models/imagethumbnailmodel.cpp b/libs/models/imagethumbnailmodel.cpp
-deleted file mode 100644
-index b7f5661..0000000
---- a/libs/models/imagethumbnailmodel.cpp
-+++ /dev/null
-@@ -1,323 +0,0 @@
--/* ============================================================
-- *
-- * This file is a part of digiKam project
-- * http://www.digikam.org
-- *
-- * Date        : 2009-03-05
-- * Description : Qt item model for database entries with support for thumbnail loading
-- *
-- * Copyright (C) 2009-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
-- * Copyright (C) 2011-2017 by Gilles Caulier <caulier dot gilles at gmail dot com>
-- *
-- * This program is free software; you can redistribute it
-- * and/or modify it under the terms of the GNU General
-- * Public License as published by the Free Software Foundation;
-- * either version 2, or (at your option)
-- * any later version.
-- *
-- * This program is distributed in the hope that it will be useful,
-- * but WITHOUT ANY WARRANTY; without even the implied warranty of
-- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-- * GNU General Public License for more details.
-- *
-- * ============================================================ */
--
--#include "imagethumbnailmodel.h"
--
--// Qt includes
--
--#include <QHash>
--
--// Local includes
--
--#include "digikam_debug.h"
--#include "thumbnailloadthread.h"
--#include "digikam_export.h"
--#include "digikam_globals.h"
--
--namespace Digikam
--{
--
--class ImageThumbnailModel::ImageThumbnailModelPriv
--{
--public:
--
--    ImageThumbnailModelPriv() :
--        thread(0),
--        preloadThread(0),
--        thumbSize(0),
--        lastGlobalThumbSize(0),
--        preloadThumbSize(0),
--        emitDataChanged(true)
--    {
--        staticListContainingThumbnailRole << ImageModel::ThumbnailRole;
--    }
--
--    ThumbnailLoadThread*   thread;
--    ThumbnailLoadThread*   preloadThread;
--    ThumbnailSize          thumbSize;
--    ThumbnailSize          lastGlobalThumbSize;
--    ThumbnailSize          preloadThumbSize;
--    QRect                  detailRect;
--    QVector<int>           staticListContainingThumbnailRole;
--
--    bool                   emitDataChanged;
--
--    int preloadThumbnailSize() const
--    {
--        if (preloadThumbSize.size())
--        {
--            return preloadThumbSize.size();
--        }
--
--        return thumbSize.size();
--    }
--};
--
--ImageThumbnailModel::ImageThumbnailModel(QObject* parent)
--    : ImageModel(parent), d(new ImageThumbnailModelPriv)
--{
--    setKeepsFilePathCache(true);
--}
--
--ImageThumbnailModel::~ImageThumbnailModel()
--{
--    delete d->preloadThread;
--    delete d;
--}
--
--void ImageThumbnailModel::setThumbnailLoadThread(ThumbnailLoadThread* thread)
--{
--    d->thread = thread;
--
--    connect(d->thread, SIGNAL(signalThumbnailLoaded(LoadingDescription,QPixmap)),
--            this, SLOT(slotThumbnailLoaded(LoadingDescription,QPixmap)));
--}
--
--ThumbnailLoadThread* ImageThumbnailModel::thumbnailLoadThread() const
--{
--    return d->thread;
--}
--
--ThumbnailSize ImageThumbnailModel::thumbnailSize() const
--{
--    return d->thumbSize;
--}
--
--void ImageThumbnailModel::setThumbnailSize(const ThumbnailSize& size)
--{
--    d->lastGlobalThumbSize = size;
--    d->thumbSize = size;
--}
--
--void ImageThumbnailModel::setPreloadThumbnailSize(const ThumbnailSize& size)
--{
--    d->preloadThumbSize = size;
--}
--
--void ImageThumbnailModel::setEmitDataChanged(bool emitSignal)
--{
--    d->emitDataChanged = emitSignal;
--}
--
--void ImageThumbnailModel::setPreloadThumbnails(bool preload)
--{
--    if (preload)
--    {
--        if (!d->preloadThread)
--        {
--            d->preloadThread = new ThumbnailLoadThread;
--            d->preloadThread->setPixmapRequested(false);
--            d->preloadThread->setPriority(QThread::LowestPriority);
--        }
--
--        connect(this, SIGNAL(allRefreshingFinished()),
--                this, SLOT(preloadAllThumbnails()));
--    }
--    else
--    {
--        delete d->preloadThread;
--        d->preloadThread = 0;
--        disconnect(this, SIGNAL(allRefreshingFinished()),
--                   this, SLOT(preloadAllThumbnails()));
--    }
--}
--
--void ImageThumbnailModel::prepareThumbnails(const QList<QModelIndex>& indexesToPrepare)
--{
--    prepareThumbnails(indexesToPrepare, d->thumbSize);
--}
--
--void ImageThumbnailModel::prepareThumbnails(const QList<QModelIndex>& indexesToPrepare, const ThumbnailSize& thumbSize)
--{
--    if (!d->thread)
--    {
--        return;
--    }
--
--    QList<ThumbnailIdentifier> ids;
--    foreach(const QModelIndex& index, indexesToPrepare)
--    {
--        ids << imageInfoRef(index).thumbnailIdentifier();
--    }
--    d->thread->findGroup(ids, thumbSize.size());
--}
--
--void ImageThumbnailModel::preloadThumbnails(const QList<ImageInfo>& infos)
--{
--    if (!d->preloadThread)
--    {
--        return;
--    }
--
--    QList<ThumbnailIdentifier> ids;
--    foreach(const ImageInfo& info, infos)
--    {
--        ids << info.thumbnailIdentifier();
--    }
--    d->preloadThread->pregenerateGroup(ids, d->preloadThumbnailSize());
--}
--
--void ImageThumbnailModel::preloadThumbnails(const QList<QModelIndex>& indexesToPreload)
--{
--    if (!d->preloadThread)
--    {
--        return;
--    }
--
--    QList<ThumbnailIdentifier> ids;
--    foreach(const QModelIndex& index, indexesToPreload)
--    {
--        ids << imageInfoRef(index).thumbnailIdentifier();
--    }
--    d->preloadThread->stopAllTasks();
--    d->preloadThread->pregenerateGroup(ids, d->preloadThumbnailSize());
--}
--
--void ImageThumbnailModel::preloadAllThumbnails()
--{
--    preloadThumbnails(imageInfos());
--}
--
--void ImageThumbnailModel::imageInfosCleared()
--{
--    if (d->preloadThread)
--    {
--        d->preloadThread->stopAllTasks();
--    }
--}
--
--QVariant ImageThumbnailModel::data(const QModelIndex& index, int role) const
--{
--    if (role == ThumbnailRole && d->thread && index.isValid())
--    {
--        QPixmap   thumbnail;
--        ImageInfo info = imageInfo(index);
--        QString   path = info.filePath();
--
--        if (info.isNull())
--        {
--            return QVariant(QVariant::Pixmap);
--        }
--
--        if (!d->detailRect.isNull())
--        {
--            if (d->thread->find(info.thumbnailIdentifier(), d->detailRect, thumbnail, d->thumbSize.size()))
--            {
--                return thumbnail;
--            }
--        }
--        else
--        {
--            if (d->thread->find(info.thumbnailIdentifier(), thumbnail, d->thumbSize.size()))
--            {
--                return thumbnail;
--            }
--        }
--
--        return QVariant(QVariant::Pixmap);
--    }
--
--    return ImageModel::data(index, role);
--}
--
--bool ImageThumbnailModel::setData(const QModelIndex& index, const QVariant& value, int role)
--{
--    if (role == ThumbnailRole)
--    {
--        switch (value.type())
--        {
--            case QVariant::Invalid:
--                d->thumbSize  = d->lastGlobalThumbSize;
--                d->detailRect = QRect();
--                break;
--
--            case QVariant::Int:
--
--                if (value.isNull())
--                {
--                    d->thumbSize = d->lastGlobalThumbSize;
--                }
--                else
--                {
--                    d->thumbSize = value.toInt();
--                }
--                break;
--
--            case QVariant::Rect:
--
--                if (value.isNull())
--                {
--                    d->detailRect = QRect();
--                }
--                else
--                {
--                    d->detailRect = value.toRect();
--                }
--                break;
--
--            default:
--                break;
--        }
--    }
--
--    return ImageModel::setData(index, value, role);
--}
--
--void ImageThumbnailModel::slotThumbnailLoaded(const LoadingDescription& loadingDescription, const QPixmap& thumb)
--{
--    if (thumb.isNull())
--    {
--        return;
--    }
--
--    // In case of multiple occurrence, we currently do not know which thumbnail is this. Signal change on all.
--    QModelIndexList indexes;
--    ThumbnailIdentifier thumbId = loadingDescription.thumbnailIdentifier();
--    if (thumbId.filePath.isEmpty())
--    {
--        indexes = indexesForImageId(thumbId.id);
--    }
--    else
--    {
--        indexes = indexesForPath(thumbId.filePath);
--    }
--    foreach(const QModelIndex& index, indexes)
--    {
--        if (thumb.isNull())
--        {
--            emit thumbnailFailed(index, loadingDescription.previewParameters.size);
--        }
--        else
--        {
--            emit thumbnailAvailable(index, loadingDescription.previewParameters.size);
--
--            if (d->emitDataChanged)
--            {
--                emit dataChanged(index, index, d->staticListContainingThumbnailRole);
--            }
--        }
--    }
--}
--
--} // namespace Digikam
-diff --git a/libs/models/imagethumbnailmodel.h b/libs/models/imagethumbnailmodel.h
-deleted file mode 100644
-index 366ca65..0000000
---- a/libs/models/imagethumbnailmodel.h
-+++ /dev/null
-@@ -1,140 +0,0 @@
--/* ============================================================
-- *
-- * This file is a part of digiKam project
-- * http://www.digikam.org
-- *
-- * Date        : 2009-03-05
-- * Description : Qt item model for database entries with support for thumbnail loading
-- *
-- * Copyright (C) 2009-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
-- * Copyright (C)      2011 by Gilles Caulier <caulier dot gilles at gmail dot com>
-- *
-- * This program is free software; you can redistribute it
-- * and/or modify it under the terms of the GNU General
-- * Public License as published by the Free Software Foundation;
-- * either version 2, or (at your option)
-- * any later version.
-- *
-- * This program is distributed in the hope that it will be useful,
-- * but WITHOUT ANY WARRANTY; without even the implied warranty of
-- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-- * GNU General Public License for more details.
-- *
-- * ============================================================ */
--
--#ifndef IMAGETHUMBNAILMODEL_H
--#define IMAGETHUMBNAILMODEL_H
--
--// Local includes
--
--#include "imagemodel.h"
--#include "thumbnailsize.h"
--#include "digikam_export.h"
--
--namespace Digikam
--{
--
--class LoadingDescription;
--class ThumbnailLoadThread;
--
--class DIGIKAM_DATABASE_EXPORT ImageThumbnailModel : public ImageModel
--{
--    Q_OBJECT
--
--public:
--
--    /**
--     *  An ImageModel that supports thumbnail loading.
--     *  You need to set a ThumbnailLoadThread to enable thumbnail loading.
--     *  Adjust the thumbnail size to your needs.
--     *  Note that setKeepsFilePathCache is enabled per default.
--     */
--    explicit ImageThumbnailModel(QObject* parent);
--    ~ImageThumbnailModel();
--
--    /** Enable thumbnail loading and set the thread that shall be used.
--     *  The thumbnail size of this thread will be adjusted.
--     */
--    void setThumbnailLoadThread(ThumbnailLoadThread* thread);
--    ThumbnailLoadThread* thumbnailLoadThread() const;
--
--    /// Set the thumbnail size to use
--    void setThumbnailSize(const ThumbnailSize& thumbSize);
--
--    /// If you want to fix a size for preloading, do it here.
--    void setPreloadThumbnailSize(const ThumbnailSize& thumbSize);
--
--    void setExifRotate(bool rotate);
--
--    /**
--     *  Enable emitting dataChanged() when a thumbnail becomes available.
--     *  The thumbnailAvailable() signal will be emitted in any case.
--     *  Default is true.
--     */
--    void setEmitDataChanged(bool emitSignal);
--
--    /**
--     * Enable preloading of thumbnails:
--     * If preloading is enabled, for every entry in the model a thumbnail generation is started.
--     * Default: false.
--     */
--    void setPreloadThumbnails(bool preload);
--
--    ThumbnailSize thumbnailSize() const;
--
--    /**
--     *  Handles the ThumbnailRole.
--     *  If the pixmap is available, returns it in the QVariant.
--     *  If it still needs to be loaded, returns a null QVariant and emits
--     *  thumbnailAvailable() as soon as it is available.
--     */
--    virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
--
--    /**
--     * You can override the current thumbnail size by giving an integer value for ThumbnailRole.
--     * Set a null QVariant to use the thumbnail size set by setThumbnailSize() again.
--     * The index given here is ignored for this purpose.
--     */
--    virtual bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::DisplayRole);
--
--public Q_SLOTS:
--
--    /** Prepare the thumbnail loading for the given indexes
--     */
--    void prepareThumbnails(const QList<QModelIndex>& indexesToPrepare);
--    void prepareThumbnails(const QList<QModelIndex>& indexesToPrepare, const ThumbnailSize& thumbSize);
--
--    /**
--     *  Preload thumbnail for the given infos resp. indexes.
--     *  Note: Use setPreloadThumbnails to automatically preload all entries in the model.
--     *  Note: This only ensures thumbnail generation. It is not guaranteed that pixmaps
--     *  are stored in the cache. For thumbnails that are expect to be drawn immediately,
--     *  include them in prepareThumbnails().
--     *  Note: Stops preloading of previously added thumbnails.
--     */
--    void preloadThumbnails(const QList<ImageInfo>&);
--    void preloadThumbnails(const QList<QModelIndex>&);
--    void preloadAllThumbnails();
--
--Q_SIGNALS:
--
--    void thumbnailAvailable(const QModelIndex& index, int requestedSize);
--    void thumbnailFailed(const QModelIndex& index, int requestedSize);
--
--protected:
--
--    virtual void imageInfosCleared();
--
--protected Q_SLOTS:
--
--    void slotThumbnailLoaded(const LoadingDescription& loadingDescription, const QPixmap& thumb);
--
--private:
--
--    class ImageThumbnailModelPriv;
--    ImageThumbnailModelPriv* const d;
--};
--
--} // namespace Digikam
--
--#endif /* IMAGETHUMBNAILMODEL_H */
-diff --git a/libs/models/imageversionsmodel.cpp b/libs/models/imageversionsmodel.cpp
-deleted file mode 100644
-index e6ba582..0000000
---- a/libs/models/imageversionsmodel.cpp
-+++ /dev/null
-@@ -1,183 +0,0 @@
--/* ============================================================
-- *
-- * This file is a part of digiKam project
-- * http://www.digikam.org
-- *
-- * Date        : 2010-07-13
-- * Description : Model for image versions
-- *
-- * Copyright (C) 2010 by Martin Klapetek <martin dot klapetek at gmail dot com>
-- *
-- * This program is free software; you can redistribute it
-- * and/or modify it under the terms of the GNU General
-- * Public License as published by the Free Software Foundation;
-- * either version 2, or (at your option)
-- * any later version.
-- *
-- * This program is distributed in the hope that it will be useful,
-- * but WITHOUT ANY WARRANTY; without even the implied warranty of
-- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-- * GNU General Public License for more details.
-- *
-- * ============================================================ */
--
--#include "imageversionsmodel.h"
--
--// KDE includes
--
--#include <klocalizedstring.h>
--
--// Local includes
--
--#include "digikam_debug.h"
--#include "workingwidget.h"
--
--namespace Digikam
--{
--
--class ImageVersionsModel::Private
--{
--public:
--
--    Private()
--    {
--        data      = 0;
--        paintTree = false;
--    }
--
--    ///Complete paths with filenames and tree level
--    QList<QPair<QString, int> >* data;
--    ///This is for delegate to paint it as selected
--    QString                      currentSelectedImage;
--    ///If true, the delegate will paint items as a tree
--    ///if false, it will be painted as a list
--    bool                         paintTree;
--};
--
--ImageVersionsModel::ImageVersionsModel(QObject* parent)
--    : QAbstractListModel(parent),
--      d(new Private)
--{
--    d->data = new QList<QPair<QString, int> >;
--}
--
--ImageVersionsModel::~ImageVersionsModel()
--{
--    //qDeleteAll(d->data);
--    delete d;
--}
--
--Qt::ItemFlags ImageVersionsModel::flags(const QModelIndex& index) const
--{
--    if (!index.isValid())
--    {
--        return 0;
--    }
--
--    return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
--}
--
--QVariant ImageVersionsModel::data(const QModelIndex& index, int role) const
--{
--    if (!index.isValid())
--    {
--        return QVariant();
--    }
--
--    if (role == Qt::DisplayRole && !d->data->isEmpty())
--    {
--        return d->data->at(index.row()).first;
--    }
--    else if (role == Qt::UserRole && !d->data->isEmpty())
--    {
--        return d->data->at(index.row()).second;
--    }
--    else if (role == Qt::DisplayRole && d->data->isEmpty())
--    {
--        //TODO: make this text Italic
--        return QVariant(QString(i18n("No image selected")));
--    }
--
--    return QVariant();
--}
--
--int ImageVersionsModel::rowCount(const QModelIndex& parent) const
--{
--    Q_UNUSED(parent)
--    return d->data->count();
--}
--
--void ImageVersionsModel::setupModelData(QList<QPair<QString, int> >& data)
--{
--    beginResetModel();
--
--    d->data->clear();
--
--    if (!data.isEmpty())
--    {
--        d->data->append(data);
--    }
--    else
--    {
--        d->data->append(qMakePair(QString(i18n("This is the original image")), 0));
--    }
--
--    endResetModel();
--}
--
--void ImageVersionsModel::clearModelData()
--{
--    beginResetModel();
--
--    if (!d->data->isEmpty())
--    {
--        d->data->clear();
--    }
--
--    endResetModel();
--}
--
--void ImageVersionsModel::slotAnimationStep()
--{
--    emit dataChanged(createIndex(0, 0), createIndex(rowCount()-1, 1));
--}
--
--QString ImageVersionsModel::currentSelectedImage() const
--{
--    return d->currentSelectedImage;
--}
--
--void ImageVersionsModel::setCurrentSelectedImage(const QString& path)
--{
--    d->currentSelectedImage = path;
--}
--
--QModelIndex ImageVersionsModel::currentSelectedImageIndex() const
--{
--    return index(listIndexOf(d->currentSelectedImage), 0);
--}
--
--bool ImageVersionsModel::paintTree() const
--{
--    return d->paintTree;
--}
--
--void ImageVersionsModel::setPaintTree(bool paint)
--{
--    d->paintTree = paint;
--}
--
--int ImageVersionsModel::listIndexOf(const QString& item) const
--{
--    for (int i = 0; i < d->data->size(); ++i)
--    {
--        if (d->data->at(i).first == item)
--        {
--            return i;
--        }
--    }
--
--    return -1;
--}
--
--} // namespace Digikam
-diff --git a/libs/models/imageversionsmodel.h b/libs/models/imageversionsmodel.h
-deleted file mode 100644
-index ed08529..0000000
---- a/libs/models/imageversionsmodel.h
-+++ /dev/null
-@@ -1,75 +0,0 @@
--/* ============================================================
-- *
-- * This file is a part of digiKam project
-- * http://www.digikam.org
-- *
-- * Date        : 2010-07-13
-- * Description : Model for image versions
-- *
-- * Copyright (C) 2010 by Martin Klapetek <martin dot klapetek at gmail dot com>
-- *
-- * This program is free software; you can redistribute it
-- * and/or modify it under the terms of the GNU General
-- * Public License as published by the Free Software Foundation;
-- * either version 2, or (at your option)
-- * any later version.
-- *
-- * This program is distributed in the hope that it will be useful,
-- * but WITHOUT ANY WARRANTY; without even the implied warranty of
-- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-- * GNU General Public License for more details.
-- *
-- * ============================================================ */
--
--#ifndef IMAGEVERSIONSMODEL_H
--#define IMAGEVERSIONSMODEL_H
--
--// Qt includes
--
--#include <QModelIndex>
--#include <QPixmap>
--
--// Local includes
--
--#include "digikam_export.h"
--
--namespace Digikam
--{
--
--class DIGIKAM_DATABASE_EXPORT ImageVersionsModel : public QAbstractListModel
--{
--    Q_OBJECT
--
--public:
--
--    explicit ImageVersionsModel(QObject* parent = 0);
--    ~ImageVersionsModel();
--
--    Qt::ItemFlags flags(const QModelIndex& index) const;
--    QVariant      data(const QModelIndex& index, int role = Qt::DisplayRole) const;
--    int           rowCount(const QModelIndex& parent = QModelIndex()) const;
--
--    void setupModelData(QList<QPair<QString, int> >& data);
--    void clearModelData();
--
--    QString     currentSelectedImage() const;
--    void        setCurrentSelectedImage(const QString& path);
--    QModelIndex currentSelectedImageIndex() const;
--
--    bool paintTree() const;
--    int  listIndexOf(const QString& item) const;
--
--public Q_SLOTS:
--
--    void slotAnimationStep();
--    void setPaintTree(bool paint);
--
--private:
--
--    class Private;
--    Private* const d;
--};
--
--} // namespace Digikam
--
--#endif // IMAGEVERSIONSMODEL_H
--- 
-cgit v0.11.2
-