diff options
author | Eric Hameleers <alien@slackware.com> | 2018-06-13 14:50:49 +0200 |
---|---|---|
committer | Eric Hameleers <alien@slackware.com> | 2018-06-13 14:50:49 +0200 |
commit | c779e019ac53019baa07eb843aba59bc55ffec20 (patch) | |
tree | f4dd959f71cea6ac6a8741a1a8f9933a389f5bc7 /kde/patch | |
parent | e0d005c66dbed44be15070995d9d9e9200c989fa (diff) | |
download | ktown-c779e019ac53019baa07eb843aba59bc55ffec20.tar.gz ktown-c779e019ac53019baa07eb843aba59bc55ffec20.tar.xz |
Updated 'testing' in preparation for June '18 release of Plasma 5.13
Diffstat (limited to 'kde/patch')
73 files changed, 18708 insertions, 15 deletions
diff --git a/kde/patch/akonadi.patch b/kde/patch/akonadi.patch new file mode 100644 index 0000000..ddab2a8 --- /dev/null +++ b/kde/patch/akonadi.patch @@ -0,0 +1,12 @@ +# Remove hardcoded absolute path to stdlib header. +# This is Windows-centric and breaks on any linux GCC upgrade. +# Thanks to Gentoo where I found the following two patches at +# https://packages.gentoo.org/packages/kde-apps/akonadi +# No longer needed since 17.04.0. +#cat $CWD/patch/akonadi/akonadi_revert-abs-path.patch | patch -p1 --verbose || { touch ${SLACK_KDE_BUILD_DIR}/${PKGNAME}.failed ; continue ; } +#cat $CWD/patch/akonadi/akonadi_rename-header.patch | patch -p1 --verbose || { touch ${SLACK_KDE_BUILD_DIR}/${PKGNAME}.failed ; continue ; } + +# Temporary fix for non-working Akonadi in combination with mariadb 10.2.8 +# (actually this is a bug in qtsql, not in akonadi or mariadb): +#cat $CWD/patch/akonadi/akonadi_mariadb_qtsql.patch | patch -p1 --verbose || { touch ${SLACK_KDE_BUILD_DIR}/${PKGNAME}.failed ; continue ; } + diff --git a/kde/patch/akonadi/akonadi_mariadb_qtsql.patch b/kde/patch/akonadi/akonadi_mariadb_qtsql.patch new file mode 100644 index 0000000..7ec6d7c --- /dev/null +++ b/kde/patch/akonadi/akonadi_mariadb_qtsql.patch @@ -0,0 +1,91 @@ +Patch taken from https://cgit.kde.org/akonadi.git/patch/?id=b145f47f000978b9d39edc1882849ec7f6b3ef79 + +Upstream bug reports: +https://bugs.kde.org/show_bug.cgi?id=383991 +https://bugreports.qt.io/browse/QTBUG-63108 + +From b145f47f000978b9d39edc1882849ec7f6b3ef79 Mon Sep 17 00:00:00 2001 +From: Heinz Wiesinger <pprkut@liwjatan.at> +Date: Sun, 17 Sep 2017 10:56:44 +0200 +Subject: Only remove init connections to the database on server shutdown. + +Summary: +With MariaDB 10.2 libmysqlclient was replaced with libmariadb that +changed how establishing database connections behaves. The MySQL +QSQL driver calls mysql_server_end() on QSqlDatabase::removeDatabase() +if the overall connection count dropped to 0 (which it does when +the init connection is removed). +A future QSqlDatabase:addDatabase() would call mysql_server_init() +again, but this no longer works with libmariadb as that one only +allows calling mysql_server_init() once. Future calls are simply +ignored. + +In order to prevent this from happening we have to keep the +init connection open until the server shuts down, so the connection +count only drops to 0 at shutdown and mysql_server_end() isn't +called before. + +This is a workaround for QTBUG-63108 + +CCBUG: 383991 + +Reviewers: dvratil, mlaurent + +Reviewed By: dvratil + +Subscribers: #kde_pim + +Tags: #kde_pim + +Differential Revision: https://phabricator.kde.org/D7858 +--- + src/server/akonadi.cpp | 3 ++- + src/server/storage/dbconfigmysql.cpp | 4 +++- + 2 files changed, 5 insertions(+), 2 deletions(-) + +diff --git a/src/server/akonadi.cpp b/src/server/akonadi.cpp +index 4364e63..bcb7e88 100644 +--- a/src/server/akonadi.cpp ++++ b/src/server/akonadi.cpp +@@ -423,13 +423,14 @@ bool AkonadiServer::createDatabase() + success = false; + } + } +- QSqlDatabase::removeDatabase(initCon); + return success; + } + + void AkonadiServer::stopDatabaseProcess() + { + if (!DbConfig::configuredDatabase()->useInternalServer()) { ++ // closing initConnection this late to work around QTBUG-63108 ++ QSqlDatabase::removeDatabase(QStringLiteral("initConnection")); + return; + } + +diff --git a/src/server/storage/dbconfigmysql.cpp b/src/server/storage/dbconfigmysql.cpp +index 2bd231d..d565706 100644 +--- a/src/server/storage/dbconfigmysql.cpp ++++ b/src/server/storage/dbconfigmysql.cpp +@@ -492,7 +492,6 @@ bool DbConfigMysql::startInternalServer() + } + } + +- QSqlDatabase::removeDatabase(initCon); + return success; + } + +@@ -520,6 +519,9 @@ void DbConfigMysql::stopInternalServer() + return; + } + ++ // closing initConnection this late to work around QTBUG-63108 ++ QSqlDatabase::removeDatabase(QStringLiteral("initConnection")); ++ + disconnect(mDatabaseProcess, static_cast<void(QProcess::*)(int,QProcess::ExitStatus)>(&QProcess::finished), + this, &DbConfigMysql::processFinished); + +-- +cgit v0.11.2 + + diff --git a/kde/patch/akonadi/akonadi_rename-header.patch b/kde/patch/akonadi/akonadi_rename-header.patch new file mode 100644 index 0000000..73347f0 --- /dev/null +++ b/kde/patch/akonadi/akonadi_rename-header.patch @@ -0,0 +1,77 @@ +commit 248671e8200ff0883877b6d0e56700ef99ff3b51 +Author: Andreas Sturmlechner <andreas.sturmlechner@gmail.com> +Date: Sat Jan 7 14:38:17 2017 +0100 + + Rename exception.h to exceptionbase.h + + REVIEW: 129788 + +diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt +index 6ac851e..fa996b9 100644 +--- a/src/core/CMakeLists.txt ++++ b/src/core/CMakeLists.txt +@@ -95,7 +95,7 @@ ecm_generate_headers(AkonadiCore_base_HEADERS + EntityDeletedAttribute + EntityDisplayAttribute + EntityHiddenAttribute +- Exception ++ ExceptionBase + GidExtractorInterface + IndexPolicyAttribute + Item +diff --git a/src/core/exception.cpp b/src/core/exception.cpp +index f229c1a..14f7330 100644 +--- a/src/core/exception.cpp ++++ b/src/core/exception.cpp +@@ -17,7 +17,7 @@ + 02110-1301, USA. + */ + +-#include "exception.h" ++#include "exceptionbase.h" + + #include <QString> + +diff --git a/src/core/exception.h b/src/core/exceptionbase.h +similarity index 100% +rename from src/core/exception.h +rename to src/core/exceptionbase.h +diff --git a/src/core/item.h b/src/core/item.h +index de71cad..5ec62c8 100644 +--- a/src/core/item.h ++++ b/src/core/item.h +@@ -23,7 +23,7 @@ + + #include "akonadicore_export.h" + #include "attribute.h" +-#include "exception.h" ++#include "exceptionbase.h" + #include "tag.h" + #include "collection.h" + #include "relation.h" +diff --git a/src/core/itempayloadinternals_p.h b/src/core/itempayloadinternals_p.h +index 0a4de3c..1626f10 100644 +--- a/src/core/itempayloadinternals_p.h ++++ b/src/core/itempayloadinternals_p.h +@@ -32,7 +32,7 @@ + + #include <boost/shared_ptr.hpp> + +-#include "exception.h" ++#include "exceptionbase.h" + + //@cond PRIVATE Doxygen 1.7.1 hangs processing this file. so skip it. + //for more info, see https://bugzilla.gnome.org/show_bug.cgi?id=531637 +diff --git a/src/core/protocolhelper.cpp b/src/core/protocolhelper.cpp +index f740e9d..c218f0c 100644 +--- a/src/core/protocolhelper.cpp ++++ b/src/core/protocolhelper.cpp +@@ -23,7 +23,7 @@ + #include "collectionstatistics.h" + #include "item_p.h" + #include "collection_p.h" +-#include "exception.h" ++#include "exceptionbase.h" + #include "itemserializer_p.h" + #include "itemserializerplugin.h" + #include "servermanager.h" diff --git a/kde/patch/akonadi/akonadi_revert-abs-path.patch b/kde/patch/akonadi/akonadi_revert-abs-path.patch new file mode 100644 index 0000000..3b48253 --- /dev/null +++ b/kde/patch/akonadi/akonadi_revert-abs-path.patch @@ -0,0 +1,70 @@ +commit d98e29a07f4acc3bf01f06f25b3eef5522397e2e +Author: Andreas Sturmlechner <andreas.sturmlechner@gmail.com> +Date: Thu Jan 5 22:41:02 2017 +0100 + + Revert "Workaround an include loop on case-insensitive systems" + + Do not hardcode absolute patchs to GCC headers. + + This reverts commit 59b9d6b79425c9ec1e5df059a2593580048c4adf. + + REVIEW: 129788 + +diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt +index 72589cd..6ac851e 100644 +--- a/src/core/CMakeLists.txt ++++ b/src/core/CMakeLists.txt +@@ -257,21 +257,6 @@ ecm_generate_headers(AkonadiCore_jobs_HEADERS + RELATIVE jobs + ) + +-# This is a workaround for conflict between our "Exception" fancy header and +-# C++ stdlib's "exception" header which occurs in case-insensitive systems. +-# For that reason we generate std_exception.h file, which contains an absolute +-# path to the stdlib's exception header file, which resolves the ambiguity +-# when including <exception> from within Akonadi. +-include(FindStdlibInclude) +-findStdlibInclude("exception" std_exception_file) +-if (NOT "${std_exception_file}" STREQUAL "") +- configure_file(${CMAKE_CURRENT_SOURCE_DIR}/std_exception.h.in +- ${CMAKE_CURRENT_BINARY_DIR}/std_exception.h +- ) +-else() +- message(FATAL_ERROR "stdlib <exception> include absolute path not found") +-endif() +- + set(akonadicore_dbus_xml ${Akonadi_SOURCE_DIR}/src/interfaces/org.freedesktop.Akonadi.NotificationManager.xml) + qt5_add_dbus_interface(akonadicore_dbus_SRCS ${akonadicore_dbus_xml} notificationmanagerinterface) + +@@ -338,7 +323,6 @@ install(TARGETS + + install(FILES + ${CMAKE_CURRENT_BINARY_DIR}/akonadicore_export.h +- ${CMAKE_CURRENT_BINARY_DIR}/std_exception.h + ${AkonadiCore_base_HEADERS} + ${AkonadiCore_models_HEADERS} + ${AkonadiCore_jobs_HEADERS} +diff --git a/src/core/exception.h b/src/core/exception.h +index d07ca71..2a376df 100644 +--- a/src/core/exception.h ++++ b/src/core/exception.h +@@ -20,16 +20,11 @@ + #ifndef AKONADI_EXCEPTION_H + #define AKONADI_EXCEPTION_H + +-// The std_exception.h file is generated at build-time and #includes C++ stdlib +-// header "exception" by aboslute path. This is to workaround an include loop on +-// case-insensitive systems, where #include <exception> includes our "Exception" +-// fancy header instead of stdlib's exception, causing an endless loop of +-// includes between "Exception" and "exception.h". +-#include "std_exception.h" +- + #include "akonadicore_export.h" ++#include <QObject> ++#include <QByteArray> ++#include <exception> + +-class QByteArray; + class QString; + + namespace Akonadi diff --git a/kde/patch/ark.patch b/kde/patch/ark.patch index 5018ca4..35ac2d3 100644 --- a/kde/patch/ark.patch +++ b/kde/patch/ark.patch @@ -2,3 +2,6 @@ # KDEBUG #357057 is fixed in 15.12.1; still needs unrar. #cat $CWD/patch/ark/ark_kdebug357057.patch | patch -p1 --verbose || { touch ${SLACK_KDE_BUILD_DIR}/${PKGNAME}.failed ; continue ; } +# Fix a compilation issue in 18.04.0: +cat $CWD/patch/ark/ark_include_memory.patch | patch -p1 --verbose || { touch ${SLACK_KDE_BUILD_DIR}/${PKGNAME}.failed ; continue ; } + diff --git a/kde/patch/ark/ark_include_memory.patch b/kde/patch/ark/ark_include_memory.patch new file mode 100644 index 0000000..235436e --- /dev/null +++ b/kde/patch/ark/ark_include_memory.patch @@ -0,0 +1,15 @@ +#To overcome compilation issues like: +# error: 'unique_ptr' is not a member of 'std' +# error: expected primary-expression before '[' token + +--- ark-18.04.0/plugins/libzipplugin/libzipplugin.cpp.orig 2018-04-09 22:42:03.000000000 +0200 ++++ ark-18.04.0/plugins/libzipplugin/libzipplugin.cpp 2018-04-15 12:41:56.490025275 +0200 +@@ -39,6 +39,8 @@ + #include <qplatformdefs.h> + #include <QThread> + ++#include <memory> ++ + #include <utime.h> + #include <zlib.h> + diff --git a/kde/patch/cantor.patch b/kde/patch/cantor.patch new file mode 100644 index 0000000..54f2b47 --- /dev/null +++ b/kde/patch/cantor.patch @@ -0,0 +1,4 @@ +# Fix build against KF 5.31.0 (repaired in 16.12.3): +#cat $CWD/patch/cantor/cantor_python.patch | patch -p1 --verbose || { touch ${SLACK_KDE_BUILD_DIR}/${PKGNAME}.failed ; continue ; } +#cat $CWD/patch/cantor/cantor_julia.patch | patch -p1 --verbose || { touch ${SLACK_KDE_BUILD_DIR}/${PKGNAME}.failed ; continue ; } + diff --git a/kde/patch/cantor/cantor_julia.patch b/kde/patch/cantor/cantor_julia.patch new file mode 100644 index 0000000..5c381ca --- /dev/null +++ b/kde/patch/cantor/cantor_julia.patch @@ -0,0 +1,193 @@ +Patch taken from: +https://gitweb.gentoo.org/repo/gentoo.git/plain/kde-apps/cantor/files/cantor-16.12.2-julia-kf-5.31.patch + +From 45322d9f58f50df3d4d5755d4199e579f6fd8646 Mon Sep 17 00:00:00 2001 +From: Andreas Sturmlechner <andreas.sturmlechner@gmail.com> +Date: Sat, 11 Feb 2017 22:46:35 +0100 +Subject: [PATCH] [julia] Fix build with -fno-operator-names + +REVIEW: 129942 +--- + src/backends/julia/juliaexpression.cpp | 6 +++--- + src/backends/julia/juliaextensions.cpp | 4 ++-- + src/backends/julia/juliahighlighter.cpp | 4 ++-- + src/backends/julia/juliakeywords.cpp | 10 +++++----- + src/backends/julia/juliaserver/juliaserver.cpp | 4 ++-- + src/backends/julia/juliaserver/main.cpp | 4 ++-- + src/backends/julia/juliasession.cpp | 4 ++-- + 7 files changed, 18 insertions(+), 18 deletions(-) + +diff --git a/src/backends/julia/juliaexpression.cpp b/src/backends/julia/juliaexpression.cpp +index 27cdd85..618200d 100644 +--- a/src/backends/julia/juliaexpression.cpp ++++ b/src/backends/julia/juliaexpression.cpp +@@ -40,7 +40,7 @@ void JuliaExpression::evaluate() + + // Plots integration + m_plot_filename.clear(); +- if (juliaSession->integratePlots() and checkPlotShowingCommands()) { ++ if (juliaSession->integratePlots() && checkPlotShowingCommands()) { + // Simply add plot saving command to the end of execution + QStringList inlinePlotFormats; + inlinePlotFormats << QLatin1String("svg"); +@@ -73,8 +73,8 @@ void JuliaExpression::finalize() + setResult(new Cantor::TextResult(juliaSession->getOutput())); + setStatus(Cantor::Expression::Error); + } else { +- if (not m_plot_filename.isEmpty() +- and QFileInfo(m_plot_filename).exists()) { ++ if (!m_plot_filename.isEmpty() ++ && QFileInfo(m_plot_filename).exists()) { + // If we have plot in result, show it + setResult( + new Cantor::ImageResult(QUrl::fromLocalFile(m_plot_filename))); +diff --git a/src/backends/julia/juliaextensions.cpp b/src/backends/julia/juliaextensions.cpp +index 4585c6f..ad5e3a9 100644 +--- a/src/backends/julia/juliaextensions.cpp ++++ b/src/backends/julia/juliaextensions.cpp +@@ -138,7 +138,7 @@ QString JuliaPlotExtension::plotFunction2d( + { + auto new_left = left; + auto new_right = right; +- if (new_left.isEmpty() and new_right.isEmpty()) { ++ if (new_left.isEmpty() && new_right.isEmpty()) { + new_left = QLatin1String("-1"); + new_right = QLatin1String("1"); + } else if (new_left.isEmpty()) { +@@ -165,7 +165,7 @@ QString JuliaPlotExtension::plotFunction3d( + { + + auto update_interval = [](Interval &interval) { +- if (interval.first.isEmpty() and interval.second.isEmpty()) { ++ if (interval.first.isEmpty() && interval.second.isEmpty()) { + interval.first = QLatin1String("-1"); + interval.second = QLatin1String("1"); + } else if (interval.first.isEmpty()) { +diff --git a/src/backends/julia/juliahighlighter.cpp b/src/backends/julia/juliahighlighter.cpp +index 4795361..f7d3622 100644 +--- a/src/backends/julia/juliahighlighter.cpp ++++ b/src/backends/julia/juliahighlighter.cpp +@@ -98,7 +98,7 @@ void JuliaHighlighter::highlightBlock(const QString &text) + while (pos < text.length()) { + // Trying to close current environments + bool triggered = false; +- for (int i = 0; i < flags.size() and not triggered; i++) { ++ for (int i = 0; i < flags.size() && !triggered; i++) { + int flag = flags[i]; + QRegExp ®exp = regexps_ends[i]; + QTextCharFormat &format = formats[i]; +@@ -144,7 +144,7 @@ void JuliaHighlighter::highlightBlock(const QString &text) + singleLineCommentStart.indexIn(text, pos); + + if (singleLineCommentStartPos != -1 +- and singleLineCommentStartPos < minPos) { ++ && singleLineCommentStartPos < minPos) { + // single line comment starts earlier + setFormat(pos, text.length() - pos, commentFormat()); + break; +diff --git a/src/backends/julia/juliakeywords.cpp b/src/backends/julia/juliakeywords.cpp +index f0a5846..8a0efec 100644 +--- a/src/backends/julia/juliakeywords.cpp ++++ b/src/backends/julia/juliakeywords.cpp +@@ -62,11 +62,11 @@ void JuliaKeywords::loadFromFile() + const QStringRef name = xml.name(); + + if (name == QLatin1String("keywords") +- or name == QLatin1String("variables") +- or name == QLatin1String("plot_showing_commands")) { ++ || name == QLatin1String("variables") ++ || name == QLatin1String("plot_showing_commands")) { + while (xml.readNextStartElement()) { + Q_ASSERT( +- xml.isStartElement() and xml.name() == QLatin1String("word") ++ xml.isStartElement() && xml.name() == QLatin1String("word") + ); + + const QString text = xml.readElementText(); +@@ -91,7 +91,7 @@ void JuliaKeywords::loadFromFile() + + void JuliaKeywords::addVariable(const QString &variable) + { +- if (not m_variables.contains(variable)) { ++ if (!m_variables.contains(variable)) { + m_variables << variable; + } + } +@@ -104,7 +104,7 @@ void JuliaKeywords::clearVariables() + + void JuliaKeywords::addFunction(const QString &function) + { +- if (not m_functions.contains(function)) { ++ if (!m_functions.contains(function)) { + m_functions << function; + } + } +diff --git a/src/backends/julia/juliaserver/juliaserver.cpp b/src/backends/julia/juliaserver/juliaserver.cpp +index c9beb4c..91585cf 100644 +--- a/src/backends/julia/juliaserver/juliaserver.cpp ++++ b/src/backends/julia/juliaserver/juliaserver.cpp +@@ -47,7 +47,7 @@ void JuliaServer::runJuliaCommand(const QString &command) + { + // Redirect stdout, stderr to temprorary files + QTemporaryFile output, error; +- if (not output.open() or not error.open()) { ++ if (!output.open() || !error.open()) { + qFatal("Unable to create temprorary files for stdout/stderr"); + return; + } +@@ -90,7 +90,7 @@ void JuliaServer::runJuliaCommand(const QString &command) + bool is_nothing = jl_unbox_bool( + static_cast<jl_value_t *>(jl_call2(equality, nothing, val)) + ); +- if (not is_nothing) { ++ if (!is_nothing) { + jl_static_show(JL_STDOUT, val); + } + m_was_exception = false; +diff --git a/src/backends/julia/juliaserver/main.cpp b/src/backends/julia/juliaserver/main.cpp +index ad7e4d9..11687ec 100644 +--- a/src/backends/julia/juliaserver/main.cpp ++++ b/src/backends/julia/juliaserver/main.cpp +@@ -30,7 +30,7 @@ int main(int argc, char *argv[]) + { + QCoreApplication app(argc, argv); + +- if (not QDBusConnection::sessionBus().isConnected()) { ++ if (!QDBusConnection::sessionBus().isConnected()) { + qWarning() << "Can't connect to the D-Bus session bus.\n" + "To start it, run: eval `dbus-launch --auto-syntax`"; + return 1; +@@ -39,7 +39,7 @@ int main(int argc, char *argv[]) + const QString &serviceName = + QString::fromLatin1("org.kde.Cantor.Julia-%1").arg(app.applicationPid()); + +- if (not QDBusConnection::sessionBus().registerService(serviceName)) { ++ if (!QDBusConnection::sessionBus().registerService(serviceName)) { + qWarning() << QDBusConnection::sessionBus().lastError().message(); + return 2; + } +diff --git a/src/backends/julia/juliasession.cpp b/src/backends/julia/juliasession.cpp +index 425e6cb..9183e11 100644 +--- a/src/backends/julia/juliasession.cpp ++++ b/src/backends/julia/juliasession.cpp +@@ -86,7 +86,7 @@ void JuliaSession::login() + QDBusConnection::sessionBus() + ); + +- if (not m_interface->isValid()) { ++ if (!m_interface->isValid()) { + qWarning() << QDBusConnection::sessionBus().lastError().message(); + return; + } +@@ -213,7 +213,7 @@ bool JuliaSession::getWasException() + { + const QDBusReply<bool> &reply = + m_interface->call(QLatin1String("getWasException")); +- return reply.isValid() and reply.value(); ++ return reply.isValid() && reply.value(); + } + + void JuliaSession::listVariables() +-- +2.10.2 + diff --git a/kde/patch/cantor/cantor_python.patch b/kde/patch/cantor/cantor_python.patch new file mode 100644 index 0000000..aa2b398 --- /dev/null +++ b/kde/patch/cantor/cantor_python.patch @@ -0,0 +1,31 @@ +Patch taken from: +https://gitweb.gentoo.org/repo/gentoo.git/plain/kde-apps/cantor/files/cantor-16.12.2-python-kf-5.31.patch + +commit 4b8ef6bed62daced90c7826985650c2a813d2996 +Author: Jonathan Riddell <jr@jriddell.org> +Date: Wed Feb 8 14:56:48 2017 +0000 + + remove modern C++ use to fix compile with current KDE policy + +diff --git a/src/backends/python/pythonhighlighter.cpp b/src/backends/python/pythonhighlighter.cpp +index 4064524..87b10dd 100644 +--- a/src/backends/python/pythonhighlighter.cpp ++++ b/src/backends/python/pythonhighlighter.cpp +@@ -87,7 +87,7 @@ void PythonHighlighter::highlightBlock(const QString &text) + while (pos < text.length()) { + // Trying to close current environments + bool triggered = false; +- for (int i = 0; i < flags.size() and not triggered; i++) { ++ for (int i = 0; i < flags.size() && !triggered; i++) { + int flag = flags[i]; + QRegExp ®exp = regexps[i]; + QTextCharFormat &format = formats[i]; +@@ -126,7 +126,7 @@ void PythonHighlighter::highlightBlock(const QString &text) + singleLineCommentStart.indexIn(text, pos); + + if (singleLineCommentStartPos != -1 +- and singleLineCommentStartPos < minPos) { ++ && singleLineCommentStartPos < minPos) { + setFormat(pos, text.length() - pos, commentFormat()); + break; + } else if (minRegexp) { diff --git a/kde/patch/digikam.patch b/kde/patch/digikam.patch new file mode 100644 index 0000000..ec056c9 --- /dev/null +++ b/kde/patch/digikam.patch @@ -0,0 +1,9 @@ +# Fix compilation with clang. +# Fixed in digikam 5.5.0. +#cat $CWD/patch/digikam/digikam_clang_fix.patch | patch -p1 --verbose || { touch ${SLACK_KDE_BUILD_DIR}/${PKGNAME}.failed ; continue ; } + +# Fix linking issue, backport from git master: +#cd core +# cat $CWD/patch/digikam/digikam_databasemodel.patch | patch -p1 --verbose || { touch ${SLACK_KDE_BUILD_DIR}/${PKGNAME}.failed ; continue ; } +#cd - + diff --git a/kde/patch/digikam/digikam_clang_fix.patch b/kde/patch/digikam/digikam_clang_fix.patch new file mode 100644 index 0000000..a4d77b0 --- /dev/null +++ b/kde/patch/digikam/digikam_clang_fix.patch @@ -0,0 +1,38 @@ +Taken from: +http://pkgs.fedoraproject.org/cgit/rpms/digikam.git/ +And added and extra '/core/' path componenent. + +From 86cd0d1d89c8b4d13f06dc8a353bdd99f13c4758 Mon Sep 17 00:00:00 2001 +From: Gilles Caulier <caulier.gilles@gmail.com> +Date: Wed, 18 Jan 2017 10:13:20 +0100 +Subject: [PATCH 2/2] Fix compilation with clang + +--- + libs/dmetadata/metaengine_p.cpp | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/core/libs/dmetadata/metaengine_p.cpp b/core/libs/dmetadata/metaengine_p.cpp +index 2c83b58..2b44e06 100644 +--- a/core/libs/dmetadata/metaengine_p.cpp ++++ b/core/libs/dmetadata/metaengine_p.cpp +@@ -49,7 +49,7 @@ extern "C" + #include "digikam_debug.h" + + // Pragma directives to reduce warnings from Exiv2. +-#if not defined(__APPLE__) && defined(__GNUC__) ++#if !defined(__APPLE__) && defined(__GNUC__) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wdeprecated-declarations" + #endif +@@ -723,7 +723,7 @@ void MetaEngine::Private::loadSidecarData(Exiv2::Image::AutoPtr xmpsidecar) + } // namespace Digikam + + // Restore warnings +-#if not defined(__APPLE__) && defined(__GNUC__) ++#if !defined(__APPLE__) && defined(__GNUC__) + #pragma GCC diagnostic pop + #endif + +-- +2.9.3 + diff --git a/kde/patch/digikam/digikam_databasemodel.patch b/kde/patch/digikam/digikam_databasemodel.patch new file mode 100644 index 0000000..8b2fff7 --- /dev/null +++ b/kde/patch/digikam/digikam_databasemodel.patch @@ -0,0 +1,13337 @@ +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 + diff --git a/kde/patch/dolphin.patch b/kde/patch/dolphin.patch new file mode 100644 index 0000000..dfe3a2f --- /dev/null +++ b/kde/patch/dolphin.patch @@ -0,0 +1,3 @@ +# Let the user decide whether she wants to run Dolphin as root: +cat $CWD/patch/dolphin/dolphin_revert_noroot.patch | patch -p1 --verbose || { touch ${SLACK_KDE_BUILD_DIR}/${PKGNAME}.failed ; continue ; } + diff --git a/kde/patch/dolphin/dolphin_revert_noroot.patch b/kde/patch/dolphin/dolphin_revert_noroot.patch new file mode 100644 index 0000000..46bb541 --- /dev/null +++ b/kde/patch/dolphin/dolphin_revert_noroot.patch @@ -0,0 +1,44 @@ +Taken from openSUSE: +https://build.opensuse.org/package/view_file/KDE:Applications/dolphin/0001-Revert-Disallow-executing-Dolphin-as-root-on-Linux.patch?expand=1 + +From ba74d639178916221c748b0d5d89f7ac4f5ed669 Mon Sep 17 00:00:00 2001 +From: Fabian Vogt <fabian@ritter-vogt.de> +Date: Sat, 22 Apr 2017 14:00:33 +0200 +Subject: [PATCH] Revert "Disallow executing Dolphin as root on Linux" + +This reverts commit 0bdd8e0b0516555c6233fdc7901e9b417cf89791. +We ship a desktop file to open dolphin as root and we allow YaST on the +desktop. So this patch is absolutely pointless for us. +--- + src/main.cpp | 13 ------------- + 1 file changed, 13 deletions(-) + +diff --git a/src/main.cpp b/src/main.cpp +index 789a52996..acba8daed 100644 +--- a/src/main.cpp ++++ b/src/main.cpp +@@ -35,21 +35,8 @@ + #include <KLocalizedString> + #include <Kdelibs4ConfigMigrator> + +-#ifndef Q_OS_WIN +-#include <unistd.h> +-#endif +-#include <iostream> +- + extern "C" Q_DECL_EXPORT int kdemain(int argc, char **argv) + { +-#ifndef Q_OS_WIN +- // Check whether we are running as root +- if (getuid() == 0) { +- std::cout << "Executing Dolphin as root is not possible." << std::endl; +- return EXIT_FAILURE; +- } +-#endif +- + QApplication app(argc, argv); + app.setAttribute(Qt::AA_UseHighDpiPixmaps, true); + app.setWindowIcon(QIcon::fromTheme(QStringLiteral("system-file-manager"), app.windowIcon())); +-- +2.12.0 + diff --git a/kde/patch/gwenview.patch b/kde/patch/gwenview.patch new file mode 100644 index 0000000..04dfe50 --- /dev/null +++ b/kde/patch/gwenview.patch @@ -0,0 +1,4 @@ +# Prevent dataloss when importing pictures; +# Will be fixed in 17.04.2: +#cat $CWD/patch/gwenview/gwenview-17.04.1_dataloss.patch | patch -p1 --verbose || { touch ${SLACK_KDE_BUILD_DIR}/${PKGNAME}.failed ; continue ; } + diff --git a/kde/patch/gwenview/gwenview-17.04.1_dataloss.patch b/kde/patch/gwenview/gwenview-17.04.1_dataloss.patch new file mode 100644 index 0000000..fa93428 --- /dev/null +++ b/kde/patch/gwenview/gwenview-17.04.1_dataloss.patch @@ -0,0 +1,131 @@ +From 6ce5d2f8d82f83c5a3d726ee5807ebaf7a6e3c6c Mon Sep 17 00:00:00 2001 +From: Henrik Fehlauer <rkflx@lab12.net> +Date: Thu, 11 May 2017 22:40:15 +0200 +Subject: Avoid data loss when importing pictures + +Summary: +Fix porting regressions, which left users of Gwenview Importer with: +- failed import (import destination still empty) +- additionally (when choosing "Delete" instead of "Keep" after import): + pictures also removed from import source, with no way to recover + +Correct additional problems remaining after fixing the import failure: +- hang on duplicate filenames +- identically named files with different content are never imported +- error dialog when deleting pictures from import source + +BUG: 379615 + +In detail: + +1st problem (introduced in 017b4fe5dc7f4b6e876cfd7b108dcebbf609ae94): + + Initially, pictures are copied to a temporary folder + (e.g. "foo/.gwenview_importer-IcQqvo/"), before being moved/renamed + to the final destination (e.g. "foo/"), which is determined by + calling "cd .." on the temporary folder. + + However, mistakenly this path contains a superfluous '/' + (e.g. "foo/.gwenview_importer-IcQqvo//"), resulting in the final + destination reading "foo/.gwenview_importer-IcQqvo/" instead of + "foo/". On cleanup, the temporary folder is removed, simultaneously + deleting all new pictures. + + Fix: Omit '/' where appropriate. + +2nd problem (broken by a3262e9b197ee97323ef8bf3a9dca1e13f61a74c): + + When trying to find a unique filename, the while loop "stat"ing the + file repeats forever. + + Fix: Call "KIO::stat" and "KJobWidgets::setWindow" + also inside the loop. + +3rd problem (introduced in 017b4fe5dc7f4b6e876cfd7b108dcebbf609ae94): + + If the destination directory already contains an identically named + file, we try to find a different name by appending a number. For + this, we need to strip the old filename from the full path. + Unfortunately, only calling "path()" is incorrect, giving + "foo/pict0001.jpg/pict0001_1.jpg", rather than "foo/pict0001_1.jpg". + + Fix: Use "adjusted(QUrl::RemoveFilename)". + +4th problem (broken by a3262e9b197ee97323ef8bf3a9dca1e13f61a74c): + + Although deletion works, we show a warning dialog stating failure. + + Fix: Invert the logic of "job->exec()", as the error handling should + be skipped by "break"ing out of the while loop. + +Test Plan: +Steps to reproduce (see https://bugs.kde.org/show_bug.cgi?id=379615) no longer result in data loss. + +Autotests for importer (separate review request in D5750) pass. Hopefully, this helps catching any future porting regressions. + +Reviewers: ltoscano, sandsmark, gateau + +Reviewed By: ltoscano + +Differential Revision: https://phabricator.kde.org/D5749 +--- + importer/fileutils.cpp | 5 ++++- + importer/importdialog.cpp | 2 +- + importer/importer.cpp | 4 ++-- + 3 files changed, 7 insertions(+), 4 deletions(-) + +diff --git a/importer/fileutils.cpp b/importer/fileutils.cpp +index 5293d56..a51a18c 100644 +--- a/importer/fileutils.cpp ++++ b/importer/fileutils.cpp +@@ -128,7 +128,10 @@ RenameResult rename(const QUrl& src, const QUrl& dst_, QWidget* authWindow) + } + result = RenamedUnderNewName; + +- dst.setPath(dst.path() + '/' + prefix + QString::number(count) + suffix); ++ dst.setPath(dst.adjusted(QUrl::RemoveFilename).path() + prefix + QString::number(count) + suffix); ++ statJob = KIO::stat(dst); ++ KJobWidgets::setWindow(statJob, authWindow); ++ + ++count; + } + +diff --git a/importer/importdialog.cpp b/importer/importdialog.cpp +index ee6f7cd..5e9e847 100644 +--- a/importer/importdialog.cpp ++++ b/importer/importdialog.cpp +@@ -121,7 +121,7 @@ public: + QList<QUrl> urls = importedUrls + skippedUrls; + while (true) { + KIO::Job* job = KIO::del(urls); +- if (!job->exec()) { ++ if (job->exec()) { + break; + } + // Deleting failed +diff --git a/importer/importer.cpp b/importer/importer.cpp +index 51c4b96..a7e1d4e 100644 +--- a/importer/importer.cpp ++++ b/importer/importer.cpp +@@ -98,7 +98,7 @@ struct ImporterPrivate + } + mCurrentUrl = mUrlList.takeFirst(); + QUrl dst = mTempImportDirUrl; +- dst.setPath(dst.path() + '/' + mCurrentUrl.fileName()); ++ dst.setPath(dst.path() + mCurrentUrl.fileName()); + KIO::Job* job = KIO::copy(mCurrentUrl, dst, KIO::HideProgressInfo); + KJobWidgets::setWindow(job, mAuthWindow); + QObject::connect(job, SIGNAL(result(KJob*)), +@@ -122,7 +122,7 @@ struct ImporterPrivate + } else { + fileName = src.fileName(); + } +- dst.setPath(dst.path() + '/' + fileName); ++ dst.setPath(dst.path() + fileName); + + FileUtils::RenameResult result = FileUtils::rename(src, dst, mAuthWindow); + switch (result) { +-- +cgit v0.11.2 + + diff --git a/kde/patch/kalzium.patch b/kde/patch/kalzium.patch new file mode 100644 index 0000000..6593e9d --- /dev/null +++ b/kde/patch/kalzium.patch @@ -0,0 +1,3 @@ +# Fix build against KF 5.31.0 (repaired in 16.12.3): +#cat $CWD/patch/kalzium/kalzium_kf_5.31.patch | patch -p1 --verbose || { touch ${SLACK_KDE_BUILD_DIR}/${PKGNAME}.failed ; continue ; } + diff --git a/kde/patch/kalzium/kalzium_kf_5.31.patch b/kde/patch/kalzium/kalzium_kf_5.31.patch new file mode 100644 index 0000000..a3d4e00 --- /dev/null +++ b/kde/patch/kalzium/kalzium_kf_5.31.patch @@ -0,0 +1,156 @@ +Patch taken from: +https://gitweb.gentoo.org/repo/gentoo.git/plain/kde-apps/kalzium/files/kalzium-16.12.2-kf-5.31.patch + +From f233d458959548ab371e3faeca7313f746625afc Mon Sep 17 00:00:00 2001 +From: Heiko Becker <heirecka@exherbo.org> +Date: Sun, 22 Jan 2017 14:46:24 +0100 +Subject: Fix build with extra-cmake-modules > 5.30 + +Since a5f3a76e14799c68b5e8f74e375baa5f6f6ab4dc in +extra-cmake-modules.git -fno-operator-names is passed to the build +(when supported), causing a build error for kalzium. + +REVIEW: 129873 +--- + src/calculator/titrationCalculator.cpp | 39 +++++++++++++++------------------- + 1 file changed, 17 insertions(+), 22 deletions(-) + +diff --git a/src/calculator/titrationCalculator.cpp b/src/calculator/titrationCalculator.cpp +index 44ea152..6ea9ac9 100644 +--- a/src/calculator/titrationCalculator.cpp ++++ b/src/calculator/titrationCalculator.cpp +@@ -41,11 +41,6 @@ + + using namespace std; + +-#ifdef _MSC_VER +-#define and && +-#define or || +-#endif +- + titrationCalculator::titrationCalculator(QWidget * parent) : QWidget(parent) + { + xmin = 0; +@@ -112,7 +107,7 @@ void titrationCalculator::plot() + } + QString mreporto; + int iter = 0; +- if (uid.xaxis->text() == "" or uid.xaxis->text() == " ") { ++ if (uid.xaxis->text() == "" || uid.xaxis->text() == " ") { + uid.xaxis->setText(i18n("nothing")); + } + if (tmpy == 0) { +@@ -121,11 +116,11 @@ void titrationCalculator::plot() + //now we have to solve the system of equations NOTE:yvalue contains the equation of Y-axis variable + //we iterates the process until you have an equation in one only unknown variable or a numeric expression + mreporto = solve(yvalue); +- while (end == 0 or lettere == 1) { ++ while (end == 0 || lettere == 1) { + QByteArray ba = mreporto.toLatin1(); + char *tmreport = ba.data(); + ++iter; +- if (end == 1 or lettere == 0) { ++ if (end == 1 || lettere == 0) { + break; + } + if (iter > 100) { +@@ -273,13 +268,13 @@ QString titrationCalculator::solve(char *yvalue) + QString tempyval; + QString ptem; + for (int i = 0; strlen(yvalue) + 1; ++i) { +- if (!(yvalue[i]=='q' or yvalue[i]=='w' or yvalue[i]=='e' or yvalue[i]=='r' or yvalue[i]=='t' or yvalue[i]=='y' or yvalue[i]=='u' or yvalue[i]=='i' or yvalue[i]=='o' or yvalue[i]=='p' or yvalue[i]=='a' or yvalue[i]=='s' or yvalue[i]=='d' or yvalue[i]=='f' or yvalue[i]=='g' or yvalue[i]=='h' or yvalue[i]=='j' or yvalue[i]=='k' or yvalue[i]=='l' or yvalue[i]=='z' or yvalue[i]=='x' or yvalue[i]=='c' or yvalue[i]=='v' or yvalue[i]=='b' or yvalue[i]=='n' or yvalue[i]=='m' or yvalue[i]=='+' or yvalue[i]=='-' or yvalue[i]=='^' or yvalue[i]=='*' or yvalue[i]=='/' or yvalue[i]=='(' or yvalue[i]==')' or yvalue[i]=='Q' or yvalue[i]=='W' or yvalue[i]=='E' or yvalue[i]=='R' or yvalue[i]=='T' or yvalue[i]=='Y' or yvalue[i]=='U' or yvalue[i]=='I' or yvalue[i]=='O' or yvalue[i]=='P' or yvalue[i]=='A' or yvalue[i]=='S' or yvalue[i]=='D' or yvalue[i]=='F' or yvalue[i]=='G' or yvalue[i]=='H' or yvalue[i]=='J' or yvalue[i]=='K' or yvalue[i]=='L' or yvalue[i]=='Z' or yvalue[i]=='X' or yvalue[i]=='C' or yvalue[i]=='V' or yvalue[i]=='B' or yvalue[i]=='N' or yvalue[i]=='M' or yvalue[i]=='1' or yvalue[i]=='2' or yvalue[i]=='3' or yvalue[i]=='4' or yvalue[i]=='5' or yvalue[i]=='6' or yvalue[i]=='7' or yvalue[i]=='8' or yvalue[i]=='9' or yvalue[i]=='0' or yvalue[i]=='.' or yvalue[i]==',')) { ++ if (!(yvalue[i]=='q' || yvalue[i]=='w' || yvalue[i]=='e' || yvalue[i]=='r' || yvalue[i]=='t' || yvalue[i]=='y' || yvalue[i]=='u' || yvalue[i]=='i' || yvalue[i]=='o' || yvalue[i]=='p' || yvalue[i]=='a' || yvalue[i]=='s' || yvalue[i]=='d' || yvalue[i]=='f' || yvalue[i]=='g' || yvalue[i]=='h' || yvalue[i]=='j' || yvalue[i]=='k' || yvalue[i]=='l' || yvalue[i]=='z' || yvalue[i]=='x' || yvalue[i]=='c' || yvalue[i]=='v' || yvalue[i]=='b' || yvalue[i]=='n' || yvalue[i]=='m' || yvalue[i]=='+' || yvalue[i]=='-' || yvalue[i]=='^' || yvalue[i]=='*' || yvalue[i]=='/' || yvalue[i]=='(' || yvalue[i]==')' || yvalue[i]=='Q' || yvalue[i]=='W' || yvalue[i]=='E' || yvalue[i]=='R' || yvalue[i]=='T' || yvalue[i]=='Y' || yvalue[i]=='U' || yvalue[i]=='I' || yvalue[i]=='O' || yvalue[i]=='P' || yvalue[i]=='A' || yvalue[i]=='S' || yvalue[i]=='D' || yvalue[i]=='F' || yvalue[i]=='G' || yvalue[i]=='H' || yvalue[i]=='J' || yvalue[i]=='K' || yvalue[i]=='L' || yvalue[i]=='Z' || yvalue[i]=='X' || yvalue[i]=='C' || yvalue[i]=='V' || yvalue[i]=='B' || yvalue[i]=='N' || yvalue[i]=='M' || yvalue[i]=='1' || yvalue[i]=='2' || yvalue[i]=='3' || yvalue[i]=='4' || yvalue[i]=='5' || yvalue[i]=='6' || yvalue[i]=='7' || yvalue[i]=='8' || yvalue[i]=='9' || yvalue[i]=='0' || yvalue[i]=='.' || yvalue[i]==',')) { + break; //if current value is not a permitted value, this means that something is wrong + } +- if (yvalue[i]=='q' or yvalue[i]=='w' or yvalue[i]=='e' or yvalue[i]=='r' or yvalue[i]=='t' or yvalue[i]=='y' or yvalue[i]=='u' or yvalue[i]=='i' or yvalue[i]=='o' or yvalue[i]=='p' or yvalue[i]=='a' or yvalue[i]=='s' or yvalue[i]=='d' or yvalue[i]=='f' or yvalue[i]=='g' or yvalue[i]=='h' or yvalue[i]=='j' or yvalue[i]=='k' or yvalue[i]=='l' or yvalue[i]=='z' or yvalue[i]=='x' or yvalue[i]=='c' or yvalue[i]=='v' or yvalue[i]=='b' or yvalue[i]=='n' or yvalue[i]=='m' or yvalue[i]=='Q' or yvalue[i]=='W' or yvalue[i]=='E' or yvalue[i]=='R' or yvalue[i]=='T' or yvalue[i]=='Y' or yvalue[i]=='U' or yvalue[i]=='I' or yvalue[i]=='O' or yvalue[i]=='P' or yvalue[i]=='A' or yvalue[i]=='S' or yvalue[i]=='D' or yvalue[i]=='F' or yvalue[i]=='G' or yvalue[i]=='H' or yvalue[i]=='J' or yvalue[i]=='K' or yvalue[i]=='L' or yvalue[i]=='Z' or yvalue[i]=='X' or yvalue[i]=='C' or yvalue[i]=='V' or yvalue[i]=='B' or yvalue[i]=='N' or yvalue[i]=='M' or yvalue[i]=='.' or yvalue[i]==',') { ++ if (yvalue[i]=='q' || yvalue[i]=='w' || yvalue[i]=='e' || yvalue[i]=='r' || yvalue[i]=='t' || yvalue[i]=='y' || yvalue[i]=='u' || yvalue[i]=='i' || yvalue[i]=='o' || yvalue[i]=='p' || yvalue[i]=='a' || yvalue[i]=='s' || yvalue[i]=='d' || yvalue[i]=='f' || yvalue[i]=='g' || yvalue[i]=='h' || yvalue[i]=='j' || yvalue[i]=='k' || yvalue[i]=='l' || yvalue[i]=='z' || yvalue[i]=='x' || yvalue[i]=='c' || yvalue[i]=='v' || yvalue[i]=='b' || yvalue[i]=='n' || yvalue[i]=='m' || yvalue[i]=='Q' || yvalue[i]=='W' || yvalue[i]=='E' || yvalue[i]=='R' || yvalue[i]=='T' || yvalue[i]=='Y' || yvalue[i]=='U' || yvalue[i]=='I' || yvalue[i]=='O' || yvalue[i]=='P' || yvalue[i]=='A' || yvalue[i]=='S' || yvalue[i]=='D' || yvalue[i]=='F' || yvalue[i]=='G' || yvalue[i]=='H' || yvalue[i]=='J' || yvalue[i]=='K' || yvalue[i]=='L' || yvalue[i]=='Z' || yvalue[i]=='X' || yvalue[i]=='C' || yvalue[i]=='V' || yvalue[i]=='B' || yvalue[i]=='N' || yvalue[i]=='M' || yvalue[i]=='.' || yvalue[i]==',') { + lettere = 1; //if lettere == 0 then the equation contains only mnumbers + } +- if (yvalue[i]=='+' or yvalue[i]=='-' or yvalue[i]=='^' or yvalue[i]=='*' or yvalue[i]=='/' or yvalue[i]=='(' or yvalue[i]==')' or yvalue[i]=='1' or yvalue[i]=='2' or yvalue[i]=='3' or yvalue[i]=='4' or yvalue[i]=='5' or yvalue[i]=='6' or yvalue[i]=='7' or yvalue[i]=='8' or yvalue[i]=='9' or yvalue[i]=='0' or yvalue[i]=='.' or yvalue[i]==',') { ++ if (yvalue[i]=='+' || yvalue[i]=='-' || yvalue[i]=='^' || yvalue[i]=='*' || yvalue[i]=='/' || yvalue[i]=='(' || yvalue[i]==')' || yvalue[i]=='1' || yvalue[i]=='2' || yvalue[i]=='3' || yvalue[i]=='4' || yvalue[i]=='5' || yvalue[i]=='6' || yvalue[i]=='7' || yvalue[i]=='8' || yvalue[i]=='9' || yvalue[i]=='0' || yvalue[i]=='.' || yvalue[i]==',') { + tempyval = tempyval + QString(yvalue[i]); + } else { + tempy = tempy + QString(yvalue[i]); +@@ -302,7 +297,7 @@ QString titrationCalculator::solve(char *yvalue) + end = 1; + } + if (tempy!=uid.xaxis->text()) { +- if (yvalue[i]=='+' or yvalue[i]=='-' or yvalue[i]=='^' or yvalue[i]=='*' or yvalue[i]=='/' or yvalue[i]=='(' or yvalue[i]==')' or yvalue[i]=='1' or yvalue[i]=='2' or yvalue[i]=='3' or yvalue[i]=='4' or yvalue[i]=='5' or yvalue[i]=='6' or yvalue[i]=='7' or yvalue[i]=='8' or yvalue[i]=='9' or yvalue[i]=='0' or yvalue[i]=='.' or yvalue[i]==',') { ++ if (yvalue[i]=='+' || yvalue[i]=='-' || yvalue[i]=='^' || yvalue[i]=='*' || yvalue[i]=='/' || yvalue[i]=='(' || yvalue[i]==')' || yvalue[i]=='1' || yvalue[i]=='2' || yvalue[i]=='3' || yvalue[i]=='4' || yvalue[i]=='5' || yvalue[i]=='6' || yvalue[i]=='7' || yvalue[i]=='8' || yvalue[i]=='9' || yvalue[i]=='0' || yvalue[i]=='.' || yvalue[i]==',') { + //actually nothing + } else { + end = 0; +@@ -335,13 +330,13 @@ QString titrationCalculator::solvex(char *yvalue, QString dnum) { + QString tempyval; + tempy = ""; + for (int i = 0; strlen(yvalue) + 1; ++i) { +- if (!(yvalue[i]=='q' or yvalue[i]=='w' or yvalue[i]=='e' or yvalue[i]=='r' or yvalue[i]=='t' or yvalue[i]=='y' or yvalue[i]=='u' or yvalue[i]=='i' or yvalue[i]=='o' or yvalue[i]=='p' or yvalue[i]=='a' or yvalue[i]=='s' or yvalue[i]=='d' or yvalue[i]=='f' or yvalue[i]=='g' or yvalue[i]=='h' or yvalue[i]=='j' or yvalue[i]=='k' or yvalue[i]=='l' or yvalue[i]=='z' or yvalue[i]=='x' or yvalue[i]=='c' or yvalue[i]=='v' or yvalue[i]=='b' or yvalue[i]=='n' or yvalue[i]=='m' or yvalue[i]=='+' or yvalue[i]=='-' or yvalue[i]=='^' or yvalue[i]=='*' or yvalue[i]=='/' or yvalue[i]=='(' or yvalue[i]==')' or yvalue[i]=='Q' or yvalue[i]=='W' or yvalue[i]=='E' or yvalue[i]=='R' or yvalue[i]=='T' or yvalue[i]=='Y' or yvalue[i]=='U' or yvalue[i]=='I' or yvalue[i]=='O' or yvalue[i]=='P' or yvalue[i]=='A' or yvalue[i]=='S' or yvalue[i]=='D' or yvalue[i]=='F' or yvalue[i]=='G' or yvalue[i]=='H' or yvalue[i]=='J' or yvalue[i]=='K' or yvalue[i]=='L' or yvalue[i]=='Z' or yvalue[i]=='X' or yvalue[i]=='C' or yvalue[i]=='V' or yvalue[i]=='B' or yvalue[i]=='N' or yvalue[i]=='M' or yvalue[i]=='1' or yvalue[i]=='2' or yvalue[i]=='3' or yvalue[i]=='4' or yvalue[i]=='5' or yvalue[i]=='6' or yvalue[i]=='7' or yvalue[i]=='8' or yvalue[i]=='9' or yvalue[i]=='0' or yvalue[i]=='.' or yvalue[i]==',')) { ++ if (!(yvalue[i]=='q' || yvalue[i]=='w' || yvalue[i]=='e' || yvalue[i]=='r' || yvalue[i]=='t' || yvalue[i]=='y' || yvalue[i]=='u' || yvalue[i]=='i' || yvalue[i]=='o' || yvalue[i]=='p' || yvalue[i]=='a' || yvalue[i]=='s' || yvalue[i]=='d' || yvalue[i]=='f' || yvalue[i]=='g' || yvalue[i]=='h' || yvalue[i]=='j' || yvalue[i]=='k' || yvalue[i]=='l' || yvalue[i]=='z' || yvalue[i]=='x' || yvalue[i]=='c' || yvalue[i]=='v' || yvalue[i]=='b' || yvalue[i]=='n' || yvalue[i]=='m' || yvalue[i]=='+' || yvalue[i]=='-' || yvalue[i]=='^' || yvalue[i]=='*' || yvalue[i]=='/' || yvalue[i]=='(' || yvalue[i]==')' || yvalue[i]=='Q' || yvalue[i]=='W' || yvalue[i]=='E' || yvalue[i]=='R' || yvalue[i]=='T' || yvalue[i]=='Y' || yvalue[i]=='U' || yvalue[i]=='I' || yvalue[i]=='O' || yvalue[i]=='P' || yvalue[i]=='A' || yvalue[i]=='S' || yvalue[i]=='D' || yvalue[i]=='F' || yvalue[i]=='G' || yvalue[i]=='H' || yvalue[i]=='J' || yvalue[i]=='K' || yvalue[i]=='L' || yvalue[i]=='Z' || yvalue[i]=='X' || yvalue[i]=='C' || yvalue[i]=='V' || yvalue[i]=='B' || yvalue[i]=='N' || yvalue[i]=='M' || yvalue[i]=='1' || yvalue[i]=='2' || yvalue[i]=='3' || yvalue[i]=='4' || yvalue[i]=='5' || yvalue[i]=='6' || yvalue[i]=='7' || yvalue[i]=='8' || yvalue[i]=='9' || yvalue[i]=='0' || yvalue[i]=='.' || yvalue[i]==',')) { + break; //if current value is not a permitted value, this means that something is wrong + } +- if (yvalue[i]=='q' or yvalue[i]=='w' or yvalue[i]=='e' or yvalue[i]=='r' or yvalue[i]=='t' or yvalue[i]=='y' or yvalue[i]=='u' or yvalue[i]=='i' or yvalue[i]=='o' or yvalue[i]=='p' or yvalue[i]=='a' or yvalue[i]=='s' or yvalue[i]=='d' or yvalue[i]=='f' or yvalue[i]=='g' or yvalue[i]=='h' or yvalue[i]=='j' or yvalue[i]=='k' or yvalue[i]=='l' or yvalue[i]=='z' or yvalue[i]=='x' or yvalue[i]=='c' or yvalue[i]=='v' or yvalue[i]=='b' or yvalue[i]=='n' or yvalue[i]=='m' or yvalue[i]=='Q' or yvalue[i]=='W' or yvalue[i]=='E' or yvalue[i]=='R' or yvalue[i]=='T' or yvalue[i]=='Y' or yvalue[i]=='U' or yvalue[i]=='I' or yvalue[i]=='O' or yvalue[i]=='P' or yvalue[i]=='A' or yvalue[i]=='S' or yvalue[i]=='D' or yvalue[i]=='F' or yvalue[i]=='G' or yvalue[i]=='H' or yvalue[i]=='J' or yvalue[i]=='K' or yvalue[i]=='L' or yvalue[i]=='Z' or yvalue[i]=='X' or yvalue[i]=='C' or yvalue[i]=='V' or yvalue[i]=='B' or yvalue[i]=='N' or yvalue[i]=='M' or yvalue[i]=='.' or yvalue[i]==',') { ++ if (yvalue[i]=='q' || yvalue[i]=='w' || yvalue[i]=='e' || yvalue[i]=='r' || yvalue[i]=='t' || yvalue[i]=='y' || yvalue[i]=='u' || yvalue[i]=='i' || yvalue[i]=='o' || yvalue[i]=='p' || yvalue[i]=='a' || yvalue[i]=='s' || yvalue[i]=='d' || yvalue[i]=='f' || yvalue[i]=='g' || yvalue[i]=='h' || yvalue[i]=='j' || yvalue[i]=='k' || yvalue[i]=='l' || yvalue[i]=='z' || yvalue[i]=='x' || yvalue[i]=='c' || yvalue[i]=='v' || yvalue[i]=='b' || yvalue[i]=='n' || yvalue[i]=='m' || yvalue[i]=='Q' || yvalue[i]=='W' || yvalue[i]=='E' || yvalue[i]=='R' || yvalue[i]=='T' || yvalue[i]=='Y' || yvalue[i]=='U' || yvalue[i]=='I' || yvalue[i]=='O' || yvalue[i]=='P' || yvalue[i]=='A' || yvalue[i]=='S' || yvalue[i]=='D' || yvalue[i]=='F' || yvalue[i]=='G' || yvalue[i]=='H' || yvalue[i]=='J' || yvalue[i]=='K' || yvalue[i]=='L' || yvalue[i]=='Z' || yvalue[i]=='X' || yvalue[i]=='C' || yvalue[i]=='V' || yvalue[i]=='B' || yvalue[i]=='N' || yvalue[i]=='M' || yvalue[i]=='.' || yvalue[i]==',') { + tempy = tempy + yvalue[i]; //if lettere == 0 then the equation contains only mnumbers + } +- if (yvalue[i]=='+' or yvalue[i]=='-' or yvalue[i]=='^' or yvalue[i]=='*' or yvalue[i]=='/' or yvalue[i]=='(' or yvalue[i]==')' or yvalue[i]=='1' or yvalue[i]=='2' or yvalue[i]=='3' or yvalue[i]=='4' or yvalue[i]=='5' or yvalue[i]=='6' or yvalue[i]=='7' or yvalue[i]=='8' or yvalue[i]=='9' or yvalue[i]=='0' or yvalue[i]=='.' or yvalue[i]==',') { ++ if (yvalue[i]=='+' || yvalue[i]=='-' || yvalue[i]=='^' || yvalue[i]=='*' || yvalue[i]=='/' || yvalue[i]=='(' || yvalue[i]==')' || yvalue[i]=='1' || yvalue[i]=='2' || yvalue[i]=='3' || yvalue[i]=='4' || yvalue[i]=='5' || yvalue[i]=='6' || yvalue[i]=='7' || yvalue[i]=='8' || yvalue[i]=='9' || yvalue[i]=='0' || yvalue[i]=='.' || yvalue[i]==',') { + if (!tempyolda.isEmpty()) { + tempy = tempy + yvalue[i]; + if (tempyolda == uid.xaxis->text()) { +@@ -359,7 +354,7 @@ QString titrationCalculator::solvex(char *yvalue, QString dnum) { + tempyolda = tempyold; + } else { + tempyold = ""; +- if (((olda != 1) and (yvalue[i + 1] != '^')) or (yvalue[i] == '+' or yvalue[i] == '-' or yvalue[i] == '^' or yvalue[i] == '*' or yvalue[i] == '/' or yvalue[i] == '(' or yvalue[i] == ')')) { ++ if (((olda != 1) && (yvalue[i + 1] != '^')) || (yvalue[i] == '+' || yvalue[i] == '-' || yvalue[i] == '^' || yvalue[i] == '*' || yvalue[i] == '/' || yvalue[i] == '(' || yvalue[i] == ')')) { + tempyval = tempyval + QString(yvalue[i]); + } + } +@@ -374,7 +369,7 @@ QString titrationCalculator::solvex(char *yvalue, QString dnum) { + tempyold = ""; + olda = 1; + } +- if ((tempy==uid.xaxis->text()) and (!tempyolda.isEmpty())) { ++ if ((tempy==uid.xaxis->text()) && (!tempyolda.isEmpty())) { + if (yvalue[i + 1] != '^') { + tempyval = tempyval + dnum; + } +@@ -611,7 +606,7 @@ void titrationCalculator::on_actionOpen_triggered() + if (tmpchr != '|') { + tempyval = tempyval + tmpchr; + } else { +- if ((tablea == 1) and (tempyval != QString("table1")) and (tempyval != QString("table2")) and (tempyval != QString("xaxis")) and (tempyval != QString("yaxis")) and (tempyval != QString("note"))) { ++ if ((tablea == 1) && (tempyval != QString("table1")) && (tempyval != QString("table2")) && (tempyval != QString("xaxis")) && (tempyval != QString("yaxis")) && (tempyval != QString("note"))) { + if ((i % 2) != 0) { + QTableWidgetItem *titemo = uid.tableWidget->item((i - 1) / 2, 1); + if (titemo) { +@@ -626,7 +621,7 @@ void titrationCalculator::on_actionOpen_triggered() + ++i; + } + +- if ((tableb == 1) and (tempyval != QString("table1")) and (tempyval != QString("table2")) and (tempyval != QString("xaxis")) and (tempyval != QString("yaxis")) and (tempyval != QString("note"))) { ++ if ((tableb == 1) && (tempyval != QString("table1")) && (tempyval != QString("table2")) && (tempyval != QString("xaxis")) && (tempyval != QString("yaxis")) && (tempyval != QString("note"))) { + if ((i % 2) != 0) { + QTableWidgetItem *titemo = uid.tableWidget_2->item((i - 1) / 2, 1); + if (titemo) { +@@ -641,13 +636,13 @@ void titrationCalculator::on_actionOpen_triggered() + } + ++i; + } +- if ((xax == 1) and (tempyval != QString("table1")) and (tempyval != QString("table2")) and (tempyval != QString("xaxis")) and (tempyval != QString("yaxis")) and (tempyval != QString("note"))) { ++ if ((xax == 1) && (tempyval != QString("table1")) && (tempyval != QString("table2")) && (tempyval != QString("xaxis")) && (tempyval != QString("yaxis")) && (tempyval != QString("note"))) { + uid.xaxis->setText(tempyval); + } +- if ((yax == 1) and (tempyval != QString("table1")) and (tempyval != QString("table2")) and (tempyval != QString("xaxis")) and (tempyval != QString("yaxis")) and (tempyval != QString("note"))) { ++ if ((yax == 1) && (tempyval != QString("table1")) && (tempyval != QString("table2")) && (tempyval != QString("xaxis")) && (tempyval != QString("yaxis")) && (tempyval != QString("note"))) { + uid.yaxis->setText(tempyval); + } +- if ((notea == 1) and (tempyval != QString("table1")) and (tempyval != QString("table2")) and (tempyval != QString("xaxis")) and (tempyval != QString("yaxis")) and (tempyval != QString("note"))) { ++ if ((notea == 1) && (tempyval != QString("table1")) && (tempyval != QString("table2")) && (tempyval != QString("xaxis")) && (tempyval != QString("yaxis")) && (tempyval != QString("note"))) { + uid.note->setText(tempyval); + } + +-- +cgit v0.11.2 + + diff --git a/kde/patch/kate.patch b/kde/patch/kate.patch index d8cd42f..45c2f53 100644 --- a/kde/patch/kate.patch +++ b/kde/patch/kate.patch @@ -2,3 +2,7 @@ # Should be fixed after 15.12.0. #cat $CWD/patch/kate/kate-15.12.0.patch | patch -p1 --verbose || { touch ${SLACK_KDE_BUILD_DIR}/${PKGNAME}.failed ; continue ; } +# Allow Kate to be started by the root user; disallowing this is not +# a decision that a developer should make for the user, it is patronizing: +cat $CWD/patch/kate/kate_runasroot.patch | patch -p1 --verbose || { touch ${SLACK_KDE_BUILD_DIR}/${PKGNAME}.failed ; continue ; } + diff --git a/kde/patch/kate/kate_runasroot.patch b/kde/patch/kate/kate_runasroot.patch new file mode 100644 index 0000000..88dbbe9 --- /dev/null +++ b/kde/patch/kate/kate_runasroot.patch @@ -0,0 +1,48 @@ +From 435ed5853b9451ab8fdfff722545c57a8f154625 Mon Sep 17 00:00:00 2001 +From: Fabian Vogt <fabian@ritter-vogt.de> +Date: Sat, 18 Feb 2017 13:49:14 +0100 +Subject: [PATCH] Defuse root block + +While the main point is correct as any application running in the same +X session (not sandboxed) can use kate's capability to open a console, +we allow (even encourage) running YaST on X11 as root. +That way it's only an impact on usability. +--- + kate/main.cpp | 3 +-- + kwrite/main.cpp | 3 +-- + 2 files changed, 2 insertions(+), 4 deletions(-) + +diff --git a/kate/main.cpp b/kate/main.cpp +index 342cd5db3..4845646aa 100644 +--- a/kate/main.cpp ++++ b/kate/main.cpp +@@ -64,9 +64,8 @@ int main(int argc, char **argv) + * Check whether we are running as root + **/ + if (getuid() == 0) { +- std::cout << "Executing Kate as root is not possible. To edit files as root use:" << std::endl; ++ std::cout << "THIS IS POTENTIALLY INSECURE!\nTo edit files as root please use:" << std::endl; + std::cout << "SUDO_EDITOR=kate sudoedit <file>" << std::endl; +- return 0; + } + #endif + /** +diff --git a/kwrite/main.cpp b/kwrite/main.cpp +index 68a055edb..4937f72d3 100644 +--- a/kwrite/main.cpp ++++ b/kwrite/main.cpp +@@ -54,9 +54,8 @@ extern "C" Q_DECL_EXPORT int main(int argc, char **argv) + * Check whether we are running as root + **/ + if (getuid() == 0) { +- std::cout << "Executing KWrite as root is not possible. To edit files as root use:" << std::endl; ++ std::cout << "THIS IS POTENTIALLY INSECURE!\nTo edit files as root please use:" << std::endl; + std::cout << "SUDO_EDITOR=kwrite sudoedit <file>" << std::endl; +- return 0; + } + #endif + /** +-- +2.12.2 + + diff --git a/kde/patch/kcalcore.patch b/kde/patch/kcalcore.patch new file mode 100644 index 0000000..051150d --- /dev/null +++ b/kde/patch/kcalcore.patch @@ -0,0 +1,4 @@ +# Fix compile error against new libical 3: +# Fixed in Applications 17.12.0: +#cat $CWD/patch/kcalcore/kcalcore_libical3.patch | patch -p1 --verbose || { touch ${SLACK_KDE_BUILD_DIR}/${PKGNAME}.failed ; continue ; } + diff --git a/kde/patch/kcalcore/kcalcore_libical3.patch b/kde/patch/kcalcore/kcalcore_libical3.patch new file mode 100644 index 0000000..0a5155f --- /dev/null +++ b/kde/patch/kcalcore/kcalcore_libical3.patch @@ -0,0 +1,109 @@ +From 27eaa211b23a6bb0bcba5a91cf7cadfc1e888e21 Mon Sep 17 00:00:00 2001 +From: Allen Winter <winter@kde.org> +Date: Fri, 6 Oct 2017 10:39:20 -0400 +Subject: icalformat_p.cpp, icaltimezones.cpp - follow API changes in libical3 + +--- + src/icalformat_p.cpp | 11 ++++++----- + src/icaltimezones.cpp | 10 ++++------ + 2 files changed, 10 insertions(+), 11 deletions(-) + +diff --git a/src/icalformat_p.cpp b/src/icalformat_p.cpp +index bd1d8a3..c2e4548 100644 +--- a/src/icalformat_p.cpp ++++ b/src/icalformat_p.cpp +@@ -2355,7 +2355,6 @@ icaltimetype ICalFormatImpl::writeICalDate(const QDate &date) + t.second = 0; + + t.is_date = 1; +- t.is_utc = 0; + t.zone = nullptr; + + return t; +@@ -2377,7 +2376,9 @@ icaltimetype ICalFormatImpl::writeICalDateTime(const QDateTime &datetime, bool d + t.second = datetime.time().second(); + } + t.zone = nullptr; // zone is NOT set +- t.is_utc = datetime.isUtc() ? 1 : 0; ++ if ( datetime.isUtc() ) { ++ t = icaltime_convert_to_zone(t, icaltimezone_get_utc_timezone()); ++ } + + // _dumpIcaltime( t ); + +@@ -2450,7 +2452,7 @@ icalproperty *ICalFormatImpl::writeICalDateTimeProperty(const icalproperty_kind + } + + KTimeZone ktz; +- if (!t.is_utc) { ++ if (!icaltime_is_utc(t)) { + ktz = dt.timeZone(); + } + +@@ -2483,7 +2484,7 @@ QDateTime ICalFormatImpl::readICalDateTime(icalproperty *p, const icaltimetype & + // _dumpIcaltime( t ); + + KDateTime::Spec timeSpec; +- if (t.is_utc || t.zone == icaltimezone_get_utc_timezone()) { ++ if (icaltime_is_utc(t) || t.zone == icaltimezone_get_utc_timezone()) { + timeSpec = KDateTime::UTC; // the time zone is UTC + utc = false; // no need to convert to UTC + } else { +diff --git a/src/icaltimezones.cpp b/src/icaltimezones.cpp +index 2f6d42f..f8f8d5d 100644 +--- a/src/icaltimezones.cpp ++++ b/src/icaltimezones.cpp +@@ -54,7 +54,7 @@ static QDateTime toQDateTime(const icaltimetype &t) + { + return QDateTime(QDate(t.year, t.month, t.day), + QTime(t.hour, t.minute, t.second), +- (t.is_utc ? Qt::UTC : Qt::LocalTime)); ++ (icaltime_is_utc(t) ? Qt::UTC : Qt::LocalTime)); + } + + // Maximum date for time zone data. +@@ -81,7 +81,6 @@ static icaltimetype writeLocalICalDateTime(const QDateTime &utc, int offset) + t.second = local.time().second(); + t.is_date = 0; + t.zone = nullptr; +- t.is_utc = 0; + return t; + } + +@@ -888,7 +887,7 @@ + } + case ICAL_LASTMODIFIED_PROPERTY: { + const icaltimetype t = icalproperty_get_lastmodified(p); +- if (t.is_utc) { ++ if (icaltime_is_utc(t)) { + data->d->lastModified = toQDateTime(t); + } else { + qCDebug(KCALCORE_LOG) << "LAST-MODIFIED not UTC"; +@@ -1261,7 +1260,7 @@ bool ICalTimeZoneParser::parsePhase(icalcomponent *c, ICalTimeZonePhase &phase) + // Convert DTSTART to QDateTime, and from local time to UTC + const QDateTime localStart = toQDateTime(dtstart); // local time + dtstart.second -= prevOffset; +- dtstart.is_utc = 1; ++ dtstart = icaltime_convert_to_zone(dtstart, icaltimezone_get_utc_timezone()); + const QDateTime utcStart = toQDateTime(icaltime_normalize(dtstart)); // UTC + + transitions += utcStart; +@@ -1286,13 +1285,12 @@ bool ICalTimeZoneParser::parsePhase(icalcomponent *c, ICalTimeZonePhase &phase) + t.minute = dtstart.minute; + t.second = dtstart.second; + t.is_date = 0; +- t.is_utc = 0; // dtstart is in local time + } + // RFC2445 states that RDATE must be in local time, + // but we support UTC as well to be safe. +- if (!t.is_utc) { ++ if (!icaltime_is_utc(t)) { + t.second -= prevOffset; // convert to UTC +- t.is_utc = 1; ++ t = icaltime_convert_to_zone(t, icaltimezone_get_utc_timezone()); + t = icaltime_normalize(t); + } + transitions += toQDateTime(t); +-- +cgit v0.11.2 + diff --git a/kde/patch/kde-gtk-config.patch b/kde/patch/kde-gtk-config.patch new file mode 100644 index 0000000..a62e649 --- /dev/null +++ b/kde/patch/kde-gtk-config.patch @@ -0,0 +1,4 @@ +# Make the kde-gtk-config load the current config first: +# Fixed in 5.11.2 +#cat $CWD/patch/kde-gtk-config/kde-gtk-config_loadcurrentsettings.patch | patch -p1 --verbose || { touch ${SLACK_KDE_BUILD_DIR}/${PKGNAME}.failed ; continue ; } + diff --git a/kde/patch/kde-gtk-config/kde-gtk-config_loadcurrentsettings.patch b/kde/patch/kde-gtk-config/kde-gtk-config_loadcurrentsettings.patch new file mode 100644 index 0000000..b5b074d --- /dev/null +++ b/kde/patch/kde-gtk-config/kde-gtk-config_loadcurrentsettings.patch @@ -0,0 +1,622 @@ +From 0d0f812a1704c62c014bc87162b1280224b64f93 Mon Sep 17 00:00:00 2001 +From: Fabian Vogt <fabian@ritter-vogt.de> +Date: Tue, 24 Oct 2017 13:25:32 +0200 +Subject: Revert "Make the kde-gtk-config kcm better at checking global gtk + settings" + +Summary: +This reverts commit 34357f74ee2d98128ff423b0ec6ddcbf4232c475. + +Reverting this fixes loading of the actually used GTK settings. + +BUG: 382291 + +Test Plan: +Opened kcmshell5 kde-gtk-config with and without this revert. +Without, it shows Adwaita as theme, with it shows breeze. +GTK uses breeze, so the behaviour with the revert is correct. + +Reviewers: #plasma, apol + +Subscribers: plasma-devel + +Tags: #plasma + +Differential Revision: https://phabricator.kde.org/D8443 +--- + CMakeLists.txt | 2 +- + src/abstractappearance.h | 5 +- + src/appearancegtk2.cpp | 103 +++++++++++++--------------------- + src/appearancegtk2.h | 11 +--- + src/appearancegtk3.cpp | 143 +++++++++++++++++++++-------------------------- + src/appearancegtk3.h | 10 +--- + src/appearencegtk.cpp | 4 +- + tests/CMakeLists.txt | 2 +- + tests/configsavetest.cpp | 75 ++++++++++--------------- + tests/configsavetest.h | 8 +-- + 10 files changed, 144 insertions(+), 219 deletions(-) + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 181cfc9..bf1ba29 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -51,7 +51,7 @@ ki18n_wrap_ui(kcm_SRCS + ) + add_library(kcm_kdegtkconfig MODULE ${kcm_SRCS}) + target_compile_definitions(kcm_kdegtkconfig PRIVATE -DPROJECT_VERSION="${PROJECT_VERSION}") +-target_link_libraries(kcm_kdegtkconfig ${X11_Xcursor_LIB} KF5::ConfigCore KF5::I18n KF5::KIOWidgets KF5::NewStuff KF5::Archive KF5::NewStuff KF5::ConfigWidgets KF5::IconThemes) ++target_link_libraries(kcm_kdegtkconfig ${X11_Xcursor_LIB} KF5::I18n KF5::KIOWidgets KF5::NewStuff KF5::Archive KF5::NewStuff KF5::ConfigWidgets KF5::IconThemes) + + kcoreaddons_desktop_to_json(kcm_kdegtkconfig kde-gtk-config.desktop) + +diff --git a/src/abstractappearance.h b/src/abstractappearance.h +index 208342e..2961a09 100644 +--- a/src/abstractappearance.h ++++ b/src/abstractappearance.h +@@ -30,11 +30,10 @@ class AbstractAppearance + { + public: + virtual ~AbstractAppearance() {} +- virtual bool loadSettings() = 0; +- virtual bool saveSettings() const = 0; ++ virtual QString defaultConfigFile() const = 0; + virtual bool loadSettings(const QString& path) = 0; + virtual bool saveSettings(const QString& path) const = 0; +- ++ + /** @returns the installed themes' paths*/ + virtual QStringList installedThemes() const = 0; + +diff --git a/src/appearancegtk2.cpp b/src/appearancegtk2.cpp +index 92cbee3..44a2239 100644 +--- a/src/appearancegtk2.cpp ++++ b/src/appearancegtk2.cpp +@@ -30,38 +30,48 @@ + #include <QStandardPaths> + #include <config.h> + +-bool AppearanceGTK2::loadSettingsPrivate(const QString& path) ++bool AppearanceGTK2::loadSettings(const QString& path) + { + QFile configFile(path); + +- if (!configFile.open(QIODevice::ReadOnly | QIODevice::Text)) +- return false; ++ bool canRead = configFile.open(QIODevice::ReadOnly | QIODevice::Text); + +- const QMap<QString, QString> foundSettings = readSettingsTuples(&configFile); +- +- for(auto it = foundSettings.constBegin(), itEnd = foundSettings.constEnd(); it!=itEnd; ++it) { +- if (it.key() == "gtk-theme-name") +- m_settings["theme"] = *it; +- else if (it.key() == "gtk-icon-theme-name") +- m_settings["icon"] = *it; +- else if (it.key() == "gtk-fallback-icon-theme") +- m_settings["icon_fallback"] = *it; +- else if (it.key() == "gtk-cursor-theme-name") +- m_settings["cursor"] = *it; +- else if (it.key() == "gtk-font-name") +- m_settings["font"] = *it; +- else if (it.key() == "gtk-toolbar-style") +- m_settings["toolbar_style"] = *it; +- else if (it.key() == "gtk-button-images") +- m_settings["show_icons_buttons"] = *it; +- else if(it.key() == "gtk-menu-images") +- m_settings["show_icons_menus"] = *it; +- else if (it.key() == "gtk-primary-button-warps-slider") +- m_settings["primary_button_warps_slider"] = *it; +- else +- qWarning() << "unknown field" << it.key(); ++ if(canRead) { ++// qDebug() << "The gtk2 config file exists..."; ++ const QMap<QString, QString> foundSettings = readSettingsTuples(&configFile); ++ m_settings = QMap<QString, QString> { ++ {"toolbar_style", "GTK_TOOLBAR_ICONS"}, ++ {"show_icons_buttons", "0"}, ++ {"show_icons_menus", "0"}, ++ {"primary_button_warps_slider", "false"} ++ }; ++ ++ for(auto it = foundSettings.constBegin(), itEnd = foundSettings.constEnd(); it!=itEnd; ++it) { ++ if (it.key() == "gtk-theme-name") ++ m_settings["theme"] = *it; ++ else if (it.key() == "gtk-icon-theme-name") ++ m_settings["icon"] = *it; ++ else if (it.key() == "gtk-fallback-icon-theme") ++ m_settings["icon_fallback"] = *it; ++ else if (it.key() == "gtk-cursor-theme-name") ++ m_settings["cursor"] = *it; ++ else if (it.key() == "gtk-font-name") ++ m_settings["font"] = *it; ++ else if (it.key() == "gtk-toolbar-style") ++ m_settings["toolbar_style"] = *it; ++ else if (it.key() == "gtk-button-images") ++ m_settings["show_icons_buttons"] = *it; ++ else if(it.key() == "gtk-menu-images") ++ m_settings["show_icons_menus"] = *it; ++ else if (it.key() == "gtk-primary-button-warps-slider") ++ m_settings["primary_button_warps_slider"] = *it; ++ else ++ qWarning() << "unknown field" << it.key(); ++ } ++ + } +- return true; ++ ++ return canRead; + } + + QString AppearanceGTK2::themesGtkrcFile(const QString& themeName) const +@@ -82,7 +92,7 @@ QString AppearanceGTK2::themesGtkrcFile(const QString& themeName) const + return QString(); + } + +-bool AppearanceGTK2::saveSettingsPrivate(const QString& gtkrcFile) const ++bool AppearanceGTK2::saveSettings(const QString& gtkrcFile) const + { + QFile gtkrc(gtkrcFile); + gtkrc.remove(); +@@ -107,14 +117,14 @@ bool AppearanceGTK2::saveSettingsPrivate(const QString& gtkrcFile) const + flow << "include \"/etc/gtk-2.0/gtkrc\"\n"; //We include the /etc's config file + + int nameEnd = m_settings["font"].lastIndexOf(QRegExp(" ([0-9]+|bold|italic)")); +- const auto fontFamily = m_settings["font"].leftRef(nameEnd); ++ QString fontFamily = m_settings["font"].left(nameEnd); + + //TODO: is this really needed? + flow << "style \"user-font\" \n" + << "{\n" + << "\tfont_name=\""<< fontFamily << "\"\n" + << "}\n"; +- ++ + flow << "widget_class \"*\" style \"user-font\"\n"; + flow << "gtk-font-name=\"" << m_settings["font"] << "\"\n"; + flow << "gtk-theme-name=\"" << m_settings["theme"] << "\"\n"; +@@ -144,16 +154,6 @@ bool AppearanceGTK2::saveSettingsPrivate(const QString& gtkrcFile) const + return true; + } + +-void AppearanceGTK2::reset() +-{ +- m_settings = QMap<QString, QString> { +- {"toolbar_style", "GTK_TOOLBAR_ICONS"}, +- {"show_icons_buttons", "0"}, +- {"show_icons_menus", "0"}, +- {"primary_button_warps_slider", "false"} +- }; +-} +- + QString AppearanceGTK2::defaultConfigFile() const + { + return QDir::homePath()+"/.gtkrc-2.0"; +@@ -183,26 +183,3 @@ QStringList AppearanceGTK2::installedThemes() const + + return paths; + } +- +-bool AppearanceGTK2::loadSettings() +-{ +- reset(); +- +- return loadSettingsPrivate("/etc/gtk-2.0/gtkrc") && loadSettingsPrivate(defaultConfigFile()); +-} +- +-bool AppearanceGTK2::saveSettings() const +-{ +- return saveSettings(defaultConfigFile()); +-} +- +-bool AppearanceGTK2::loadSettings(const QString& gtkrcFile) +-{ +- reset(); +- return loadSettingsPrivate(gtkrcFile); +-} +- +-bool AppearanceGTK2::saveSettings(const QString& gtkrcFile) const +-{ +- return saveSettingsPrivate(gtkrcFile); +-} +diff --git a/src/appearancegtk2.h b/src/appearancegtk2.h +index 7df49bf..8bc28ee 100644 +--- a/src/appearancegtk2.h ++++ b/src/appearancegtk2.h +@@ -29,17 +29,10 @@ class AppearanceGTK2 : public AbstractAppearance + { + bool loadSettings(const QString& path) override; + bool saveSettings(const QString& path) const override; +- bool loadSettings() override; +- bool saveSettings() const override; ++ QString defaultConfigFile() const override; + QStringList installedThemes() const override; +- ++ + QString themesGtkrcFile(const QString& themeName) const; +- +-private: +- void reset(); +- QString defaultConfigFile() const; +- bool loadSettingsPrivate(const QString& path); +- bool saveSettingsPrivate(const QString& path) const; + }; + + #endif // APPEARANCEGTK2_H +diff --git a/src/appearancegtk3.cpp b/src/appearancegtk3.cpp +index 7df48c3..fa1bde5 100644 +--- a/src/appearancegtk3.cpp ++++ b/src/appearancegtk3.cpp +@@ -25,8 +25,6 @@ + #include <QDir> + #include <QDebug> + #include <QStandardPaths> +-#include <KSharedConfig> +-#include <KConfigGroup> + + QStringList AppearanceGTK3::installedThemes() const + { +@@ -53,65 +51,76 @@ QStringList AppearanceGTK3::installedThemes() const + return themes; + } + +-bool AppearanceGTK3::saveSettings(const KSharedConfig::Ptr& file) const +-{ +- KConfigGroup group(file, "Settings"); +- +- group.writeEntry("gtk-font-name", m_settings["font"]); +- group.writeEntry("gtk-theme-name", m_settings["theme"]); +- group.writeEntry("gtk-icon-theme-name", m_settings["icon"]); +- group.writeEntry("gtk-fallback-icon-theme", m_settings["icon_fallback"]); +- group.writeEntry("gtk-cursor-theme-name", m_settings["cursor"]); +- group.writeEntry("gtk-toolbar-style", m_settings["toolbar_style"]); +- group.writeEntry("gtk-menu-images", m_settings["show_icons_menus"]); +- group.writeEntry("gtk-button-images", m_settings["show_icons_buttons"]); +- group.writeEntry("gtk-primary-button-warps-slider", m_settings["primary_button_warps_slider"]); +- group.writeEntry("gtk-application-prefer-dark-theme", m_settings["application_prefer_dark_theme"]); +- +- const bool sync = group.sync(); +- Q_ASSERT(sync); +- return true; +-} +- +-bool AppearanceGTK3::loadSettings(const KSharedConfig::Ptr& file) ++bool AppearanceGTK3::saveSettings(const QString& file) const + { +- KConfigGroup group(file, "Settings"); +- +- if (!file || !group.isValid()) { +- qWarning() << "Cannot open the GTK3 config file" << file; ++ //Opening GTK3 config file $ENV{XDG_CONFIG_HOME}/gtk-3.0/m_settings.ini ++ QDir::home().mkpath(file.left(file.lastIndexOf('/'))); //we make sure the path exists ++ QFile file_gtk3(file); ++ ++ if(!file_gtk3.open(QIODevice::WriteOnly | QIODevice::Text)) { ++ qWarning() << "Couldn't open GTK3 config file for writing at:" << file_gtk3.fileName(); + return false; + } ++ QTextStream flow3(&file_gtk3); ++ flow3 << "[Settings]\n"; ++ flow3 << "gtk-font-name=" << m_settings["font"] << "\n"; ++ flow3 << "gtk-theme-name=" << m_settings["theme"] << "\n"; ++ flow3 << "gtk-icon-theme-name="<< m_settings["icon"] << "\n"; ++ flow3 << "gtk-fallback-icon-theme=" << m_settings["icon_fallback"] << "\n"; ++ flow3 << "gtk-cursor-theme-name=" << m_settings["cursor"] << "\n"; ++ flow3 << "gtk-toolbar-style=" << m_settings["toolbar_style"] << "\n"; ++ flow3 << "gtk-menu-images=" << m_settings["show_icons_menus"] << "\n"; ++ flow3 << "gtk-button-images=" << m_settings["show_icons_buttons"] << "\n"; ++ flow3 << "gtk-primary-button-warps-slider=" << m_settings["primary_button_warps_slider"] << "\n"; ++ flow3 << "gtk-application-prefer-dark-theme=" << m_settings["application_prefer_dark_theme"] << "\n"; + +- m_settings = QMap<QString, QString> { +- {"toolbar_style", "GTK_TOOLBAR_ICONS"}, +- {"show_icons_buttons", "0"}, +- {"show_icons_menus", "0"}, +- {"primary_button_warps_slider", "false"}, +- {"application_prefer_dark_theme", "false"} +- }; +- +- m_settings["theme"] = group.readEntry("gtk-theme-name"); +- m_settings["icon"] = group.readEntry("gtk-icon-theme-name"); +- m_settings["icon_fallback"] = group.readEntry("gtk-fallback-icon-theme"); +- m_settings["cursor"] = group.readEntry("gtk-cursor-theme-name"); +- m_settings["font"] = group.readEntry("gtk-font-name"); +- m_settings["toolbar_style"] = group.readEntry("gtk-toolbar-style"); +- m_settings["show_icons_buttons"] = group.readEntry("gtk-button-images"); +- m_settings["show_icons_menus"] = group.readEntry("gtk-menu-images"); +- m_settings["primary_button_warps_slider"] = group.readEntry("gtk-primary-button-warps-slider"); +- m_settings["application_prefer_dark_theme"] = group.readEntry("gtk-application-prefer-dark-theme"); +- for(auto it = m_settings.begin(); it != m_settings.end(); ) { +- if (it.value().isEmpty()) +- it = m_settings.erase(it); +- else +- ++it; +- } + return true; + } + +-QString AppearanceGTK3::configFileName() const ++bool AppearanceGTK3::loadSettings(const QString& path) + { +- return QStringLiteral("gtk-3.0/settings.ini"); ++ QFile fileGtk3(path); ++ bool canRead=fileGtk3.open(QIODevice::ReadOnly | QIODevice::Text); ++ ++ if(canRead) { ++ const QMap<QString, QString> foundSettings = readSettingsTuples(&fileGtk3); ++ ++ m_settings = QMap<QString, QString> { ++ {"toolbar_style", "GTK_TOOLBAR_ICONS"}, ++ {"show_icons_buttons", "0"}, ++ {"show_icons_menus", "0"}, ++ {"primary_button_warps_slider", "false"}, ++ {"application_prefer_dark_theme", "false"} ++ }; ++ ++ for(auto it = foundSettings.constBegin(), itEnd = foundSettings.constEnd(); it!=itEnd; ++it) { ++ if (it.key() == "gtk-theme-name") ++ m_settings["theme"] = *it; ++ else if (it.key() == "gtk-icon-theme-name") ++ m_settings["icon"] = *it; ++ else if (it.key() == "gtk-fallback-icon-theme") ++ m_settings["icon_fallback"] = *it; ++ else if (it.key() == "gtk-cursor-theme-name") ++ m_settings["cursor"] = *it; ++ else if (it.key() == "gtk-font-name") ++ m_settings["font"] = *it; ++ else if (it.key() == "gtk-toolbar-style") ++ m_settings["toolbar_style"] = *it; ++ else if (it.key() == "gtk-button-images") ++ m_settings["show_icons_buttons"] = *it; ++ else if (it.key() == "gtk-menu-images") ++ m_settings["show_icons_menus"] = *it; ++ else if (it.key() == "gtk-primary-button-warps-slider") ++ m_settings["primary_button_warps_slider"] = *it; ++ else if (it.key() == "gtk-application-prefer-dark-theme") ++ m_settings["application_prefer_dark_theme"] = *it; ++ else ++ qWarning() << "unknown field" << it.key(); ++ } ++ } else ++ qWarning() << "Cannot open the GTK3 config file" << path; ++ ++ return canRead; + } + + QString AppearanceGTK3::defaultConfigFile() const +@@ -120,7 +129,7 @@ QString AppearanceGTK3::defaultConfigFile() const + if(root.isEmpty()) + root = QFileInfo(QDir::home(), ".config").absoluteFilePath(); + +- return root + '/' + configFileName(); ++ return root+"/gtk-3.0/settings.ini"; + } + + bool AppearanceGTK3::getApplicationPreferDarkTheme() const +@@ -132,29 +141,3 @@ void AppearanceGTK3::setApplicationPreferDarkTheme(const bool& enable) + { + m_settings["application_prefer_dark_theme"] = enable ? "true" : "false"; + } +- +-bool AppearanceGTK3::saveSettings(const QString& file) const +-{ +- auto cfg = KSharedConfig::openConfig(file); +- return saveSettings(cfg); +-} +- +-bool AppearanceGTK3::loadSettings(const QString& path) +-{ +- auto cfg = KSharedConfig::openConfig(path); +- return loadSettings(cfg); +-} +- +-bool AppearanceGTK3::loadSettings() +-{ +- auto cfg = KSharedConfig::openConfig(configFileName()); +- cfg->setReadDefaults(true); +- return loadSettings(cfg); +-} +- +-bool AppearanceGTK3::saveSettings() const +-{ +- auto cfg = KSharedConfig::openConfig(configFileName()); +- cfg->setReadDefaults(true); +- return saveSettings(cfg); +-} +diff --git a/src/appearancegtk3.h b/src/appearancegtk3.h +index 3ce5a05..d4562b1 100644 +--- a/src/appearancegtk3.h ++++ b/src/appearancegtk3.h +@@ -23,7 +23,6 @@ + #ifndef APPEARANCEGTK3_H + #define APPEARANCEGTK3_H + +-#include <KSharedConfig> + #include "abstractappearance.h" + + class AppearanceGTK3 : public AbstractAppearance +@@ -31,18 +30,11 @@ class AppearanceGTK3 : public AbstractAppearance + + public: + QStringList installedThemes() const override; +- bool saveSettings() const override; +- bool loadSettings() override; + bool saveSettings(const QString& file) const override; + bool loadSettings(const QString& path) override; ++ QString defaultConfigFile() const override; + bool getApplicationPreferDarkTheme() const; + void setApplicationPreferDarkTheme(const bool& enable); +- +-private: +- QString defaultConfigFile() const; +- QString configFileName() const; +- bool saveSettings(const KSharedConfig::Ptr& file) const; +- bool loadSettings(const KSharedConfig::Ptr& file); + }; + + #endif // APPEARANCEGTK3_H +diff --git a/src/appearencegtk.cpp b/src/appearencegtk.cpp +index 95a6604..2e26a5a 100644 +--- a/src/appearencegtk.cpp ++++ b/src/appearencegtk.cpp +@@ -64,7 +64,7 @@ bool AppearenceGTK::loadFileConfig() + { + bool correct = false; + foreach(AbstractAppearance* app, m_app) { +- bool c = app->loadSettings(); ++ bool c = app->loadSettings(app->defaultConfigFile()); + correct = correct || c; + } + // qDebug() << "loading..." << correct; +@@ -75,7 +75,7 @@ bool AppearenceGTK::saveFileConfig() + { + bool correct = true; + foreach(AbstractAppearance* app, m_app) { +- bool c = app->saveSettings(); ++ bool c = app->saveSettings(app->defaultConfigFile()); + correct = correct && c; + } + // qDebug() << "saving..." << correct; +diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt +index 151725d..05bf8f1 100644 +--- a/tests/CMakeLists.txt ++++ b/tests/CMakeLists.txt +@@ -2,7 +2,7 @@ macro(add_kgc_test name) + add_executable(${name} ${name}.cpp ${ARGV}) + add_test(${name} ${name}) + ecm_mark_as_test(${name}) +- target_link_libraries(${name} Qt5::Core Qt5::Gui Qt5::Test KF5::ConfigCore) ++ target_link_libraries(${name} Qt5::Core Qt5::Gui Qt5::Test) + target_include_directories(${name} PRIVATE ${CMAKE_BINARY_DIR}) + endmacro(add_kgc_test) + +diff --git a/tests/configsavetest.cpp b/tests/configsavetest.cpp +index 1fe8f4f..d5d8460 100644 +--- a/tests/configsavetest.cpp ++++ b/tests/configsavetest.cpp +@@ -9,40 +9,33 @@ + + QTEST_GUILESS_MAIN(ConfigSaveTest); + +-ConfigSaveTest::ConfigSaveTest() +-{ +- QStandardPaths::setTestModeEnabled(true); +-} +- +-static void fillValues(QScopedPointer<AbstractAppearance>& a) ++void ConfigSaveTest::fillValues(AbstractAppearance* a) + { + a->setFont("a"); + a->setIcon("a"); + a->setTheme("a"); + a->setToolbarStyle("a"); + a->setIconFallback("a"); +- a->setCursor("a"); + a->setShowIconsInButtons(true); + a->setShowIconsInMenus(true); + a->setPrimaryButtonWarpsSlider(true); + +- auto a3 = dynamic_cast<AppearanceGTK3*>(a.data()); ++ auto a3 = dynamic_cast<AppearanceGTK3*>(a); + if (a3) { + a3->setApplicationPreferDarkTheme(false); + } + } + +-void compareAppearances(QScopedPointer<AbstractAppearance>& reloaded, QScopedPointer<AbstractAppearance>& instance) ++bool compareAppearances(AbstractAppearance* a, AbstractAppearance* b) + { +- QCOMPARE(reloaded->getFont(), instance->getFont()); +- QCOMPARE(reloaded->getIcon(), instance->getIcon()); +- QCOMPARE(reloaded->getTheme(), instance->getTheme()); +- QCOMPARE(reloaded->getCursor(), instance->getCursor()); +- QCOMPARE(reloaded->getToolbarStyle(), instance->getToolbarStyle()); +- QCOMPARE(reloaded->getIconFallback(), instance->getIconFallback()); +- QCOMPARE(reloaded->getShowIconsInButtons(), instance->getShowIconsInButtons()); +- QCOMPARE(reloaded->getShowIconsInMenus(), instance->getShowIconsInMenus()); +- QCOMPARE(reloaded->getPrimaryButtonWarpsSlider(), instance->getPrimaryButtonWarpsSlider()); ++ return a->getFont() == b->getFont() ++ && a->getIcon() == b->getIcon() ++ && a->getTheme() == b->getTheme() ++ && a->getToolbarStyle() == b->getToolbarStyle() ++ && a->getIconFallback() == b->getIconFallback() ++ && a->getShowIconsInButtons() == b->getShowIconsInButtons() ++ && a->getShowIconsInMenus() == b->getShowIconsInMenus() ++ && a->getPrimaryButtonWarpsSlider() == b->getPrimaryButtonWarpsSlider(); + } + + QByteArray readFile(const QString& path) +@@ -53,35 +46,23 @@ QByteArray readFile(const QString& path) + return f.readAll(); + } + +-void ConfigSaveTest::testGtk2() ++void ConfigSaveTest::testOpen() + { +- const QString pathA = QDir::current().absoluteFilePath("test-gtk2") +- , pathB = QDir::current().absoluteFilePath("testB-gtk2"); +- +- QScopedPointer<AbstractAppearance> instance(new AppearanceGTK2); +- fillValues(instance); +- QVERIFY(instance->saveSettings(pathA)); +- +- QScopedPointer<AbstractAppearance> reloaded(new AppearanceGTK2); +- QVERIFY(reloaded->loadSettings(pathA)); +- compareAppearances(reloaded, instance); +- QVERIFY(reloaded->saveSettings(pathB)); +- QCOMPARE(readFile(pathA), readFile(pathB)); +-} +- +-void ConfigSaveTest::testGtk3() +-{ +- QScopedPointer<AbstractAppearance> instance(new AppearanceGTK3); +- fillValues(instance); +- const QString pathA = QDir::current().absoluteFilePath("test-gtk3") +- , pathB = QDir::current().absoluteFilePath("testB-gtk3"); +- QVERIFY(instance->saveSettings(pathA)); +- +- QScopedPointer<AbstractAppearance> reloaded(new AppearanceGTK3); +- QVERIFY(QFile::exists(pathA)); +- QVERIFY(reloaded->loadSettings(pathA)); +- compareAppearances(reloaded, instance); +- QVERIFY(reloaded->saveSettings(pathB)); ++ QVector<AbstractAppearance*> instances; ++ instances << new AppearanceGTK2 << new AppearanceGTK3; ++ fillValues(instances[0]); ++ fillValues(instances[1]); ++ QVERIFY(instances[0]->saveSettings("test-gtk2")); ++ QVERIFY(instances[1]->saveSettings("test-gtk3")); + +- QCOMPARE(readFile(pathA), readFile(pathB)); ++ QVector<AbstractAppearance*> reloaded; ++ reloaded << new AppearanceGTK2 << new AppearanceGTK3; ++ QVERIFY(reloaded[0]->loadSettings("test-gtk2")); ++ QVERIFY(reloaded[1]->loadSettings("test-gtk3")); ++ QVERIFY(compareAppearances(reloaded[0], instances[0])); ++ QVERIFY(compareAppearances(reloaded[1], instances[1])); ++ QVERIFY(reloaded[0]->saveSettings("testB-gtk2")); ++ QVERIFY(reloaded[1]->saveSettings("testB-gtk3")); ++ QCOMPARE(readFile("test-gtk2"), readFile("testB-gtk2")); ++ QCOMPARE(readFile("test-gtk3"), readFile("testB-gtk3")); + } +diff --git a/tests/configsavetest.h b/tests/configsavetest.h +index 342b408..39fb4c2 100644 +--- a/tests/configsavetest.h ++++ b/tests/configsavetest.h +@@ -11,11 +11,11 @@ class AbstractAppearance; + class ConfigSaveTest : public QObject + { + Q_OBJECT +-public: +- ConfigSaveTest(); + private slots: +- void testGtk2(); +- void testGtk3(); ++ void testOpen(); ++ ++private: ++ void fillValues(AbstractAppearance* a); + }; + + #endif // CONFIGSAVETEST_H +-- +cgit v0.11.2 + diff --git a/kde/patch/kde-runtime.patch b/kde/patch/kde-runtime.patch index d95ef54..c3b6101 100644 --- a/kde/patch/kde-runtime.patch +++ b/kde/patch/kde-runtime.patch @@ -5,3 +5,6 @@ # Fix compilation against NetworkManager 1.0.6: cat $CWD/patch/kde-runtime/kde-runtime_networkmanager.patch | patch -p1 --verbose || { touch ${SLACK_KDE_BUILD_DIR}/${PKGNAME}.failed ; continue ; } +## Fix compilation against gpgme 1.7+: +#cat $CWD/patch/kde-runtime/kde-runtime_gpgme.patch | patch -p1 --verbose || { touch ${SLACK_KDE_BUILD_DIR}/${PKGNAME}.failed ; continue ; } + diff --git a/kde/patch/kde-runtime/kde-runtime_gpgme.patch b/kde/patch/kde-runtime/kde-runtime_gpgme.patch new file mode 100644 index 0000000..b1703ed --- /dev/null +++ b/kde/patch/kde-runtime/kde-runtime_gpgme.patch @@ -0,0 +1,133 @@ +commit 1b80d1d0b961f8e28186928ede2b87af292c3de4 +Author: Antonio Rojas <arojas@archlinux.org> +Date: Thu Nov 10 16:58:10 2016 +0100 + + Allow building kwalletd against gpgme++ from gpgme 1.7 + + REVIEW: 129339 + +diff --git a/kwalletd/CMakeLists.txt b/kwalletd/CMakeLists.txt +index 73aec82..ae8c745 100644 +--- a/kwalletd/CMakeLists.txt ++++ b/kwalletd/CMakeLists.txt +@@ -5,11 +5,18 @@ find_package(Gpgme) # Called by FindQGpgme, but since we call some gpgme + # functions ourselves we need to link against it directly. + find_package(QGpgme) # provided by kdepimlibs + +-if (GPGME_FOUND AND QGPGME_FOUND) ++if (NOT QGPGME_FOUND) ++find_package(Gpgmepp) # provided by gpgme 1.7 ++endif (NOT QGPGME_FOUND) ++ ++if ((GPGME_FOUND AND QGPGME_FOUND) OR Gpgmepp_FOUND) + add_definitions(-DHAVE_QGPGME) +- include_directories(${GPGME_INCLUDES} ${QGPGME_INCLUDE_DIR}) ++ include_directories(${GPGME_INCLUDES}) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${KDE4_ENABLE_EXCEPTIONS}") +-endif(GPGME_FOUND AND QGPGME_FOUND) ++endif((GPGME_FOUND AND QGPGME_FOUND) OR Gpgmepp_FOUND) ++if (GPGME_FOUND AND QGPGME_FOUND) ++ include_directories(${QGPGME_INCLUDE_DIR}) ++endif (GPGME_FOUND AND QGPGME_FOUND) + + ########### build backends ######### + add_subdirectory(backend) +@@ -37,7 +44,7 @@ kde4_add_ui_files(kwalletd_KDEINIT_SRCS + kwalletwizardpagepassword.ui + ) + +-if (GPGME_FOUND AND QGPGME_FOUND) ++if ((GPGME_FOUND AND QGPGME_FOUND) OR Gpgmepp_FOUND) + set(kwalletd_KDEINIT_SRCS + ${kwalletd_KDEINIT_SRCS} + knewwalletdialog.cpp +@@ -48,7 +55,7 @@ if (GPGME_FOUND AND QGPGME_FOUND) + knewwalletdialogintro.ui + knewwalletdialoggpg.ui + ) +-endif(GPGME_FOUND AND QGPGME_FOUND) ++endif((GPGME_FOUND AND QGPGME_FOUND) OR Gpgmepp_FOUND) + + find_file(kwallet_xml org.kde.KWallet.xml HINTS ${KDE4_DBUS_INTERFACES_DIR} ) + +@@ -57,8 +64,12 @@ qt4_add_dbus_adaptor( kwalletd_KDEINIT_SRCS ${kwallet_xml} kwalletd.h KWalletD ) + kde4_add_kdeinit_executable( kwalletd NOGUI ${kwalletd_KDEINIT_SRCS} ) + + target_link_libraries(kdeinit_kwalletd ${KDE4_KDEUI_LIBS} kwalletbackend ) +-if (GPGME_FOUND AND QGPGME_FOUND) +- target_link_libraries(kdeinit_kwalletd ${QGPGME_LIBRARIES} ) ++if(GPGME_FOUND AND QGPGME_FOUND) ++target_link_libraries(kdeinit_kwalletd ${QGPGME_LIBRARIES} ) ++else(GPGME_FOUND AND QGPGME_FOUND) ++if(Gpgmepp_FOUND) ++target_link_libraries(kdeinit_kwalletd Gpgmepp) ++endif(Gpgmepp_FOUND) + endif(GPGME_FOUND AND QGPGME_FOUND) + + install(TARGETS kdeinit_kwalletd ${INSTALL_TARGETS_DEFAULT_ARGS}) +@@ -73,4 +84,4 @@ install( FILES kwalletd.notifyrc DESTINATION ${DATA_INSTALL_DIR}/kwalletd ) + install( FILES kwallet-4.13.upd DESTINATION ${DATA_INSTALL_DIR}/kconf_update) + + add_subdirectory(tests) +-add_subdirectory(autotests) +\ No newline at end of file ++add_subdirectory(autotests) +diff --git a/kwalletd/backend/CMakeLists.txt b/kwalletd/backend/CMakeLists.txt +index 4db348f..7347b12 100644 +--- a/kwalletd/backend/CMakeLists.txt ++++ b/kwalletd/backend/CMakeLists.txt +@@ -22,6 +22,10 @@ kde4_add_library(kwalletbackend SHARED ${kwalletbackend_LIB_SRCS}) + target_link_libraries(kwalletbackend ${KDE4_KDEUI_LIBS} ${LIBGCRYPT_LIBRARIES}) + if(QGPGME_FOUND) + target_link_libraries(kwalletbackend ${QGPGME_LIBRARIES} ) ++else(QGPGME_FOUND) ++if(Gpgmepp_FOUND) ++target_link_libraries(kwalletbackend Gpgmepp) ++endif(Gpgmepp_FOUND) + endif(QGPGME_FOUND) + + # link with advapi32 on windows +diff --git a/kwalletd/backend/backendpersisthandler.cpp b/kwalletd/backend/backendpersisthandler.cpp +index b7f63f8..9608af0 100644 +--- a/kwalletd/backend/backendpersisthandler.cpp ++++ b/kwalletd/backend/backendpersisthandler.cpp +@@ -33,6 +33,7 @@ + #include <gpgme++/data.h> + #include <gpgme++/encryptionresult.h> + #include <gpgme++/decryptionresult.h> ++#include <boost/shared_ptr.hpp> + #endif + #include "backendpersisthandler.h" + #include "kwalletbackend.h" +diff --git a/kwalletd/kwalletwizard.cpp b/kwalletd/kwalletwizard.cpp +index 78de78d..821b666 100644 +--- a/kwalletd/kwalletwizard.cpp ++++ b/kwalletd/kwalletwizard.cpp +@@ -40,6 +40,7 @@ + #include <kdebug.h> + #include <kmessagebox.h> + #include <gpgme.h> ++#include <boost/shared_ptr.hpp> + #endif + + class PageIntro : public QWizardPage +commit cf28801cd34730da07a2c01704ca3114630f4fe7 +Author: Antonio Rojas <arojas@archlinux.org> +Date: Thu Nov 10 18:54:41 2016 +0100 + + Compiling against gmgpe 1.7 requires c++11 + +diff --git a/kwalletd/CMakeLists.txt b/kwalletd/CMakeLists.txt +index ae8c745..88d944e 100644 +--- a/kwalletd/CMakeLists.txt ++++ b/kwalletd/CMakeLists.txt +@@ -17,6 +17,9 @@ endif((GPGME_FOUND AND QGPGME_FOUND) OR Gpgmepp_FOUND) + if (GPGME_FOUND AND QGPGME_FOUND) + include_directories(${QGPGME_INCLUDE_DIR}) + endif (GPGME_FOUND AND QGPGME_FOUND) ++if (Gpgmepp_FOUND) ++ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x") ++endif (Gpgmepp_FOUND) + + ########### build backends ######### + add_subdirectory(backend) diff --git a/kde/patch/kdenlive.patch b/kde/patch/kdenlive.patch new file mode 100644 index 0000000..9065c4d --- /dev/null +++ b/kde/patch/kdenlive.patch @@ -0,0 +1,4 @@ +# Fix compilation with gcc7. +# Should have been fixed in 17.04.2... +#cat $CWD/patch/kdenlive/kdenlive_gcc7.patch | patch -p1 --verbose || { touch ${SLACK_KDE_BUILD_DIR}/${PKGNAME}.failed ; continue ; } + diff --git a/kde/patch/kdenlive/kdenlive_gcc7.patch b/kde/patch/kdenlive/kdenlive_gcc7.patch new file mode 100644 index 0000000..a7ddb90 --- /dev/null +++ b/kde/patch/kdenlive/kdenlive_gcc7.patch @@ -0,0 +1,32 @@ +# Make kdenlive compile with gcc7 + +--- kdenlive-17.04.1/src/profiles/tree/profiletreemodel.cpp.orig 2017-05-08 19:52:35.000000000 +0200 ++++ kdenlive-17.04.1/src/profiles/tree/profiletreemodel.cpp 2017-05-19 08:09:04.986909338 +0200 +@@ -27,6 +27,7 @@ + #include <QVector> + #include <array> + #include <KLocalizedString> ++#include <functional> + + + class ProfileItem +--- kdenlive-17.04.1/src/scopes/audioscopes/spectrogram.cpp.orig 2017-05-08 19:52:35.000000000 +0200 ++++ kdenlive-17.04.1/src/scopes/audioscopes/spectrogram.cpp 2017-05-19 08:09:04.993910503 +0200 +@@ -241,7 +241,7 @@ + x = leftDist + (m_innerScopeRect.width() - 1) * ((float)hz) / m_freqMax; + + // Hide text if it would overlap with the text drawn at the mouse position +- hideText = m_aTrackMouse->isChecked() && m_mouseWithinWidget && abs(x - (leftDist + mouseX + 20)) < (int) minDistX + 16 ++ hideText = m_aTrackMouse->isChecked() && m_mouseWithinWidget && abs(x - ((int)leftDist + mouseX + 20)) < (int) minDistX + 16 + && mouseX < m_innerScopeRect.width() && mouseX >= 0; + + if (x <= rightBorder) { +@@ -268,7 +268,7 @@ + } + // Draw the line at the very right (maximum frequency) + x = leftDist + m_innerScopeRect.width() - 1; +- hideText = m_aTrackMouse->isChecked() && m_mouseWithinWidget && abs(x - (leftDist + mouseX + 30)) < (int) minDistX ++ hideText = m_aTrackMouse->isChecked() && m_mouseWithinWidget && abs(x - ((int)leftDist + mouseX + 30)) < (int) minDistX + && mouseX < m_innerScopeRect.width() && mouseX >= 0; + davinci.drawLine(x, topDist, x, topDist + m_innerScopeRect.height() + 6); + if (!hideText) { diff --git a/kde/patch/kdepimlibs4.patch b/kde/patch/kdepimlibs4.patch new file mode 100644 index 0000000..91a7d15 --- /dev/null +++ b/kde/patch/kdepimlibs4.patch @@ -0,0 +1,3 @@ +# Fix a compilation issue with new libical: +cat $CWD/patch/kdepimlibs4/kdepimlibs.libical3.diff | patch -p1 --verbose || { touch ${SLACK_KDE_BUILD_DIR}/${PKGNAME}.failed ; continue ; } + diff --git a/kde/patch/kdepimlibs4/kdepimlibs.libical3.diff b/kde/patch/kdepimlibs4/kdepimlibs.libical3.diff new file mode 100644 index 0000000..c3b8caf --- /dev/null +++ b/kde/patch/kdepimlibs4/kdepimlibs.libical3.diff @@ -0,0 +1,184 @@ +--- ./kcalcore/icalformat_p.cpp.orig 2015-06-24 07:43:14.000000000 -0500 ++++ ./kcalcore/icalformat_p.cpp 2017-12-05 23:03:44.742261940 -0600 +@@ -2301,7 +2301,6 @@ + t.second = 0; + + t.is_date = 1; +- t.is_utc = 0; + t.zone = 0; + + return t; +@@ -2322,8 +2321,7 @@ + t.minute = datetime.time().minute(); + t.second = datetime.time().second(); + } +- t.zone = 0; // zone is NOT set +- t.is_utc = datetime.isUtc() ? 1 : 0; ++ t.zone = datetime.isUtc() ? icaltimezone_get_utc_timezone() : 0; // zone is NOT set + + // _dumpIcaltime( t ); + +@@ -2398,7 +2396,7 @@ + } + + KTimeZone ktz; +- if (!t.is_utc) { ++ if (!icaltime_is_utc( t )) { + ktz = dt.timeZone(); + } + +@@ -2431,7 +2429,7 @@ + // _dumpIcaltime( t ); + + KDateTime::Spec timeSpec; +- if (t.is_utc || t.zone == icaltimezone_get_utc_timezone()) { ++ if (icaltime_is_utc( t ) || t.zone == icaltimezone_get_utc_timezone()) { + timeSpec = KDateTime::UTC; // the time zone is UTC + utc = false; // no need to convert to UTC + } else { +--- ./kcalcore/icaltimezones.cpp.orig 2015-06-24 07:43:14.000000000 -0500 ++++ ./kcalcore/icaltimezones.cpp 2017-12-05 23:03:55.482262829 -0600 +@@ -54,7 +54,7 @@ + { + return QDateTime(QDate(t.year, t.month, t.day), + QTime(t.hour, t.minute, t.second), +- (t.is_utc ? Qt::UTC : Qt::LocalTime)); ++ (icaltime_is_utc( t ) ? Qt::UTC : Qt::LocalTime)); + } + + // Maximum date for time zone data. +@@ -81,7 +81,6 @@ + t.second = local.time().second(); + t.is_date = 0; + t.zone = 0; +- t.is_utc = 0; + return t; + } + +@@ -886,7 +885,7 @@ + case ICAL_LASTMODIFIED_PROPERTY: + { + const icaltimetype t = icalproperty_get_lastmodified(p); +- if (t.is_utc) { ++ if (icaltime_is_utc( t )) { + data->d->lastModified = toQDateTime(t); + } else { + kDebug() << "LAST-MODIFIED not UTC"; +@@ -1259,7 +1258,7 @@ + // Convert DTSTART to QDateTime, and from local time to UTC + const QDateTime localStart = toQDateTime(dtstart); // local time + dtstart.second -= prevOffset; +- dtstart.is_utc = 1; ++ dtstart.zone = icaltimezone_get_utc_timezone(); + const QDateTime utcStart = toQDateTime(icaltime_normalize(dtstart)); // UTC + + transitions += utcStart; +@@ -1286,13 +1285,13 @@ + t.minute = dtstart.minute; + t.second = dtstart.second; + t.is_date = 0; +- t.is_utc = 0; // dtstart is in local time ++ t.zone = 0; // dtstart is in local time + } + // RFC2445 states that RDATE must be in local time, + // but we support UTC as well to be safe. +- if (!t.is_utc) { ++ if (!icaltime_is_utc( t )) { + t.second -= prevOffset; // convert to UTC +- t.is_utc = 1; ++ t.zone = icaltimezone_get_utc_timezone(); + t = icaltime_normalize(t); + } + transitions += toQDateTime(t); +--- ./kcal/icalformat_p.cpp.orig 2015-06-24 07:43:14.000000000 -0500 ++++ ./kcal/icalformat_p.cpp 2017-12-05 23:04:01.670263342 -0600 +@@ -2087,7 +2087,6 @@ + t.second = 0; + + t.is_date = 1; +- t.is_utc = 0; + t.zone = 0; + + return t; +@@ -2106,8 +2105,7 @@ + t.second = datetime.time().second(); + + t.is_date = 0; +- t.zone = 0; // zone is NOT set +- t.is_utc = datetime.isUtc() ? 1 : 0; ++ t.zone = datetime.isUtc() ? icaltimezone_get_utc_timezone() : 0; + + // _dumpIcaltime( t ); + +@@ -2174,7 +2172,7 @@ + } + + KTimeZone ktz; +- if ( !t.is_utc ) { ++ if ( !icaltime_is_utc( t ) ) { + ktz = dt.timeZone(); + } + +@@ -2207,7 +2205,7 @@ + // _dumpIcaltime( t ); + + KDateTime::Spec timeSpec; +- if ( t.is_utc || t.zone == icaltimezone_get_utc_timezone() ) { ++ if ( icaltime_is_utc( t ) || t.zone == icaltimezone_get_utc_timezone() ) { + timeSpec = KDateTime::UTC; // the time zone is UTC + utc = false; // no need to convert to UTC + } else { +--- ./kcal/icaltimezones.cpp.orig 2015-06-24 07:43:14.000000000 -0500 ++++ ./kcal/icaltimezones.cpp 2017-12-05 23:04:07.385263815 -0600 +@@ -50,7 +50,7 @@ + { + return QDateTime( QDate( t.year, t.month, t.day ), + QTime( t.hour, t.minute, t.second ), +- ( t.is_utc ? Qt::UTC : Qt::LocalTime ) ); ++ ( icaltime_is_utc( t ) ? Qt::UTC : Qt::LocalTime ) ); + } + + // Maximum date for time zone data. +@@ -77,7 +77,6 @@ + t.second = local.time().second(); + t.is_date = 0; + t.zone = 0; +- t.is_utc = 0; + return t; + } + +@@ -787,7 +786,7 @@ + case ICAL_LASTMODIFIED_PROPERTY: + { + icaltimetype t = icalproperty_get_lastmodified(p); +- if ( t.is_utc ) { ++ if ( icaltime_is_utc( t ) ) { + data->d->lastModified = toQDateTime( t ); + } else { + kDebug() << "LAST-MODIFIED not UTC"; +@@ -972,7 +971,7 @@ + // Convert DTSTART to QDateTime, and from local time to UTC + QDateTime localStart = toQDateTime( dtstart ); // local time + dtstart.second -= prevOffset; +- dtstart.is_utc = 1; ++ dtstart.zone = icaltimezone_get_utc_timezone(); + QDateTime utcStart = toQDateTime( icaltime_normalize( dtstart ) ); // UTC + + transitions += utcStart; +@@ -999,13 +998,13 @@ + t.minute = dtstart.minute; + t.second = dtstart.second; + t.is_date = 0; +- t.is_utc = 0; // dtstart is in local time ++ t.zone = 0; // dtstart is in local time + } + // RFC2445 states that RDATE must be in local time, + // but we support UTC as well to be safe. +- if ( !t.is_utc ) { ++ if ( !icaltime_is_utc( t ) ) { + t.second -= prevOffset; // convert to UTC +- t.is_utc = 1; ++ t.zone = icaltimezone_get_utc_timezone(); + t = icaltime_normalize( t ); + } + transitions += toQDateTime( t ); diff --git a/kde/patch/kdesdk-kioslaves.patch b/kde/patch/kdesdk-kioslaves.patch index dd4f9e2..da2f885 100644 --- a/kde/patch/kdesdk-kioslaves.patch +++ b/kde/patch/kdesdk-kioslaves.patch @@ -1,3 +1,3 @@ # Fix compilation against svn > 1.8. -cat $CWD/patch/kdesdk-kioslaves/svn19.patch | patch -p1 --verbose || { touch ${SLACK_KDE_BUILD_DIR}/${PKGNAME}.failed ; continue ; } +#cat $CWD/patch/kdesdk-kioslaves/svn19.patch | patch -p1 --verbose || { touch ${SLACK_KDE_BUILD_DIR}/${PKGNAME}.failed ; continue ; } diff --git a/kde/patch/kholidays.patch b/kde/patch/kholidays.patch index f1ed4b5..ec8ad80 100644 --- a/kde/patch/kholidays.patch +++ b/kde/patch/kholidays.patch @@ -2,3 +2,7 @@ # Fixed post Plasma 5.5.5. #cat $CWD/patch/kholidays/kholidays_isnan.patch | patch -p1 --verbose || { touch ${SLACK_KDE_BUILD_DIR}/${PKGNAME}.failed ; continue ; } +# Revert depfreeze breaking merges (KF5_VERSION, cmake, PIM_VERSION). +# Should be fixed in Applications > 16.12.3. +#cat $CWD/patch/kholidays/kholidays_depfreeze_revert.patch | patch -p1 --verbose || { touch ${SLACK_KDE_BUILD_DIR}/${PKGNAME}.failed ; continue ; } + diff --git a/kde/patch/kholidays/kholidays_depfreeze_revert.patch b/kde/patch/kholidays/kholidays_depfreeze_revert.patch new file mode 100644 index 0000000..316403a --- /dev/null +++ b/kde/patch/kholidays/kholidays_depfreeze_revert.patch @@ -0,0 +1,61 @@ +From 506bd08a6faf61c776beecb05f1acbe04223827a Mon Sep 17 00:00:00 2001 +From: Andreas Sturmlechner <andreas.sturmlechner@gmail.com> +Date: Fri, 10 Mar 2017 13:33:10 +0100 +Subject: Revert depfreeze breaking merges (KF5_VERSION, cmake, PIM_VERSION) + +--- + CMakeLists.txt | 16 ++++++++-------- + 1 file changed, 8 insertions(+), 8 deletions(-) + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 05164db..e0b67b8 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -1,28 +1,28 @@ +-cmake_minimum_required(VERSION 3.0) +-set(PIM_VERSION "5.4.40") ++cmake_minimum_required(VERSION 2.8.12) + +-project(KHolidays VERSION ${PIM_VERSION}) ++project(KHolidays) + + # ECM setup +-set(KF5_VERSION "5.31.0") ++set(KF5_VERSION "5.28.0") + find_package(ECM ${KF5_VERSION} CONFIG REQUIRED) + set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH}) + + include(GenerateExportHeader) + include(ECMGenerateHeaders) + include(ECMGeneratePriFile) +-include(CMakePackageConfigHelpers) ++include(ECMPackageConfigHelpers) + include(ECMSetupVersion) + include(ECMPoQmTools) + include(FeatureSummary) + include(KDEInstallDirs) + include(KDECMakeSettings) + include(KDEFrameworkCompilerSettings NO_POLICY_SCOPE) +-include(ECMCoverageOption) ++ ++set(PIM_VERSION "5.4.3") + + set(KHOLIDAYS_LIB_VERSION ${PIM_VERSION}) + +-ecm_setup_version(PROJECT VARIABLE_PREFIX KHOLIDAYS ++ecm_setup_version(${KHOLIDAYS_LIB_VERSION} VARIABLE_PREFIX KHOLIDAYS + VERSION_HEADER "${CMAKE_CURRENT_BINARY_DIR}/kholidays_version.h" + PACKAGE_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/KF5HolidaysConfigVersion.cmake" + SOVERSION 5 +@@ -50,7 +50,7 @@ endif() + ########### CMake Config Files ########### + set(CMAKECONFIG_INSTALL_DIR "${KDE_INSTALL_CMAKEPACKAGEDIR}/KF5Holidays") + +-configure_package_config_file( ++ecm_configure_package_config_file( + "${CMAKE_CURRENT_SOURCE_DIR}/KF5HolidaysConfig.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/KF5HolidaysConfig.cmake" + INSTALL_DESTINATION ${CMAKECONFIG_INSTALL_DIR} +-- +cgit v0.11.2 + diff --git a/kde/patch/kio.patch b/kde/patch/kio.patch index 5d87cba..1caed00 100644 --- a/kde/patch/kio.patch +++ b/kde/patch/kio.patch @@ -7,3 +7,8 @@ # to kio (kf5 based) to make service menus visible in dolphin (kf5 based): #cat $CWD/patch/kio/kio_dolphin_servicemenus.patch | patch -p1 --verbose || { touch ${SLACK_KDE_BUILD_DIR}/${PKGNAME}.failed ; continue ; } +# Qt >= 5.9.3 breaks creation of folders in kio, which affects e.g. +# Dolphin and Plasma Folder View. +# Fixed in KIO 5.41: +#cat $CWD/patch/kio/kio_fix_url_setpath.patch | patch -p1 --verbose || { touch ${SLACK_KDE_BUILD_DIR}/${PKGNAME}.failed ; continue ; } + diff --git a/kde/patch/kio/kio_fix_url_setpath.patch b/kde/patch/kio/kio_fix_url_setpath.patch new file mode 100644 index 0000000..d9cf740 --- /dev/null +++ b/kde/patch/kio/kio_fix_url_setpath.patch @@ -0,0 +1,65 @@ +From 2353119aae8f03565bc7779ed1d597d266f5afda Mon Sep 17 00:00:00 2001 +From: Elvis Angelaccio <elvis.angelaccio@kde.org> +Date: Thu, 16 Nov 2017 10:41:19 +0100 +Subject: Fix KIO::mkpath with qtbase 5.10 beta 4 + +Summary: +The latest Qt 5.10 beta includes [1] which breaks a bunch of unit tests, +since `url.setPath("//foo")` will now result in an invalid (empty) QUrl. + +This patch fixes the KIO::mkpath() case. + +[1]: http://code.qt.io/cgit/qt/qtbase.git/commit/?id=f62768d046528636789f901ac79e2cfa1843a7b7 + +Test Plan: + +* I can now create folders from dolphin and plasma. +* fileundomanagertest and mkpathjobtest no longer fail + +Reviewers: #frameworks, dfaure + +Tags: #frameworks + +Differential Revision: https://phabricator.kde.org/D8836 +--- + src/core/mkpathjob.cpp | 17 ++++++++++++++--- + 1 file changed, 14 insertions(+), 3 deletions(-) + +diff --git a/src/core/mkpathjob.cpp b/src/core/mkpathjob.cpp +index bff46ca..a177805 100644 +--- a/src/core/mkpathjob.cpp ++++ b/src/core/mkpathjob.cpp +@@ -43,8 +43,13 @@ public: + m_url.setPath(QStringLiteral("/")); + int i = 0; + for (; i < basePathComponents.count() && i < m_pathComponents.count(); ++i) { +- if (m_pathComponents.at(i) == basePathComponents.at(i)) { +- m_url.setPath(m_url.path() + '/' + m_pathComponents.at(i)); ++ const QString pathComponent = m_pathComponents.at(i); ++ if (pathComponent == basePathComponents.at(i)) { ++ if (m_url.path() == QLatin1Char('/')) { ++ m_url.setPath(m_url.path() + pathComponent); ++ } else { ++ m_url.setPath(m_url.path() + '/' + pathComponent); ++ } + } else { + break; + } +@@ -57,7 +62,13 @@ public: + if (m_url.isLocalFile()) { + i = 0; + for (; i < m_pathComponents.count(); ++i) { +- QString testDir = m_url.toLocalFile() + '/' + m_pathComponents.at(i); ++ const QString localFile = m_url.toLocalFile(); ++ QString testDir; ++ if (localFile == QLatin1Char('/')) { ++ testDir = localFile + m_pathComponents.at(i); ++ } else { ++ testDir = localFile + '/' + m_pathComponents.at(i); ++ } + if (QFileInfo(testDir).isDir()) { + m_url.setPath(testDir); + } else { +-- +cgit v0.11.2 + diff --git a/kde/patch/konsole.patch b/kde/patch/konsole.patch new file mode 100644 index 0000000..ce82120 --- /dev/null +++ b/kde/patch/konsole.patch @@ -0,0 +1,5 @@ +# Set TERM to 'konsole' instead of the default 'xterm-256color' +# to prevent garbled text under certain conditions: +# Reverted to default behaviour in 18.04.0 after Slackware did the same: +#cat $CWD/patch/konsole/konsole.term.is.konsole.patch | patch -p1 --verbose || { touch ${SLACK_KDE_BUILD_DIR}/${PKGNAME}.failed ; continue ; } + diff --git a/kde/patch/konsole/konsole.term.is.konsole.patch b/kde/patch/konsole/konsole.term.is.konsole.patch new file mode 100644 index 0000000..443b9f1 --- /dev/null +++ b/kde/patch/konsole/konsole.term.is.konsole.patch @@ -0,0 +1,24 @@ +diff -uar konsole-17.12.3.orig/src/Profile.cpp konsole-17.12.3/src/Profile.cpp +--- konsole-17.12.3.orig/src/Profile.cpp 2018-03-01 23:54:01.000000000 +0100 ++++ konsole-17.12.3/src/Profile.cpp 2018-04-03 21:17:11.897873304 +0200 +@@ -157,7 +157,7 @@ + // See Pty.cpp on why Arguments is populated + setProperty(Arguments, QStringList() << QString::fromUtf8(qgetenv("SHELL"))); + setProperty(Icon, QStringLiteral("utilities-terminal")); +- setProperty(Environment, QStringList() << QStringLiteral("TERM=xterm-256color") << QStringLiteral("COLORTERM=truecolor")); ++ setProperty(Environment, QStringList() << QStringLiteral("TERM=konsole") << QStringLiteral("COLORTERM=truecolor")); + setProperty(LocalTabTitleFormat, QStringLiteral("%d : %n")); + setProperty(RemoteTabTitleFormat, QStringLiteral("(%u) %H")); + setProperty(ShowTerminalSizeHint, true); +diff -uar konsole-17.12.3.orig/src/Pty.cpp konsole-17.12.3/src/Pty.cpp +--- konsole-17.12.3.orig/src/Pty.cpp 2018-03-01 23:54:01.000000000 +0100 ++++ konsole-17.12.3/src/Pty.cpp 2018-04-03 21:18:18.898007801 +0200 +@@ -229,7 +229,7 @@ + + // extra safeguard to make sure $TERM is always set + if (!isTermEnvAdded) { +- setEnv(QStringLiteral("TERM"), QStringLiteral("xterm-256color")); ++ setEnv(QStringLiteral("TERM"), QStringLiteral("konsole")); + } + } + diff --git a/kde/patch/kopete.patch b/kde/patch/kopete.patch new file mode 100644 index 0000000..64e8238 --- /dev/null +++ b/kde/patch/kopete.patch @@ -0,0 +1,9 @@ +# Fix for jabber protocol vulnerability in Kopete: CVE-2017-5593 +# (User Impersonation Vulnerability) +# Fixed in 16.12.3. +# cat $CWD/patch/kopete/kopete_kdebug376348.patch | patch -p1 --verbose || { touch ${SLACK_KDE_BUILD_DIR}/${PKGNAME}.failed ; continue ; } + +# Make 18.04.0 compile. +# Fixed in 18.04.1. +#cat $CWD/patch/kopete/kopete_kdebug393372.patch | patch -p1 --verbose || { touch ${SLACK_KDE_BUILD_DIR}/${PKGNAME}.failed ; continue ; } + diff --git a/kde/patch/kopete/kopete_kdebug376348.patch b/kde/patch/kopete/kopete_kdebug376348.patch new file mode 100644 index 0000000..d9bb057 --- /dev/null +++ b/kde/patch/kopete/kopete_kdebug376348.patch @@ -0,0 +1,127 @@ +From 6243764c4fd0985320d4a10b48051cc418d584ad Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali.rohar@gmail.com> +Date: Sat, 11 Feb 2017 13:24:59 +0100 +Subject: Fix CVE 2017-5593 (User Impersonation Vulnerability) in jabber + protocol + +BUG: 376348 +FIXED-IN: 16.12.3 +--- + .../jabber/libiris/patches/01_cve_2017-5593.patch | 52 ++++++++++++++++++++++ + .../jabber/libiris/src/xmpp/xmpp-im/xmpp_tasks.cpp | 14 +++--- + 2 files changed, 61 insertions(+), 5 deletions(-) + create mode 100644 protocols/jabber/libiris/patches/01_cve_2017-5593.patch + +diff --git a/protocols/jabber/libiris/patches/01_cve_2017-5593.patch b/protocols/jabber/libiris/patches/01_cve_2017-5593.patch +new file mode 100644 +index 0000000..573ca66 +--- /dev/null ++++ b/protocols/jabber/libiris/patches/01_cve_2017-5593.patch +@@ -0,0 +1,52 @@ ++diff --git a/src/xmpp/xmpp-im/xmpp_tasks.cpp b/src/xmpp/xmpp-im/xmpp_tasks.cpp ++index 0e74b71..0837548 100644 ++--- a/src/xmpp/xmpp-im/xmpp_tasks.cpp +++++ b/src/xmpp/xmpp-im/xmpp_tasks.cpp ++@@ -888,14 +888,18 @@ bool JT_PushMessage::take(const QDomElement &e) ++ QDomElement forward; ++ Message::CarbonDir cd = Message::NoCarbon; ++ +++ Jid fromJid = Jid(e1.attribute(QLatin1String("from"))); ++ // Check for Carbon ++ QDomNodeList list = e1.childNodes(); ++ for (int i = 0; i < list.size(); ++i) { ++ QDomElement el = list.at(i).toElement(); ++ ++- if (el.attribute("xmlns") == QLatin1String("urn:xmpp:carbons:2") && (el.tagName() == QLatin1String("received") || el.tagName() == QLatin1String("sent"))) { +++ if (el.attribute("xmlns") == QLatin1String("urn:xmpp:carbons:2") +++ && (el.tagName() == QLatin1String("received") || el.tagName() == QLatin1String("sent")) +++ && fromJid.compare(Jid(e1.attribute(QLatin1String("to"))), false)) { ++ QDomElement el1 = el.firstChildElement(); ++- if (el1.tagName() == QLatin1String("forwarded") && el1.attribute(QLatin1String("xmlns")) == QLatin1String("urn:xmpp:forward:0")) { +++ if (el1.tagName() == QLatin1String("forwarded") +++ && el1.attribute(QLatin1String("xmlns")) == QLatin1String("urn:xmpp:forward:0")) { ++ QDomElement el2 = el1.firstChildElement(QLatin1String("message")); ++ if (!el2.isNull()) { ++ forward = el2; ++@@ -904,7 +908,8 @@ bool JT_PushMessage::take(const QDomElement &e) ++ } ++ } ++ } ++- else if (el.tagName() == QLatin1String("forwarded") && el.attribute(QLatin1String("xmlns")) == QLatin1String("urn:xmpp:forward:0")) { +++ else if (el.tagName() == QLatin1String("forwarded") +++ && el.attribute(QLatin1String("xmlns")) == QLatin1String("urn:xmpp:forward:0")) { ++ forward = el.firstChildElement(QLatin1String("message")); // currently only messages are supportted ++ // TODO <delay> element support ++ if (!forward.isNull()) { ++@@ -913,7 +918,6 @@ bool JT_PushMessage::take(const QDomElement &e) ++ } ++ } ++ ++- QString from = e1.attribute(QLatin1String("from")); ++ Stanza s = client()->stream().createStanza(addCorrectNS(forward.isNull()? e1 : forward)); ++ if(s.isNull()) { ++ //printf("take: bad stanza??\n"); ++@@ -926,7 +930,7 @@ bool JT_PushMessage::take(const QDomElement &e) ++ return false; ++ } ++ if (!forward.isNull()) { ++- m.setForwardedFrom(Jid(from)); +++ m.setForwardedFrom(fromJid); ++ m.setCarbonDirection(cd); ++ } ++ +diff --git a/protocols/jabber/libiris/src/xmpp/xmpp-im/xmpp_tasks.cpp b/protocols/jabber/libiris/src/xmpp/xmpp-im/xmpp_tasks.cpp +index 0e74b71..0837548 100644 +--- a/protocols/jabber/libiris/src/xmpp/xmpp-im/xmpp_tasks.cpp ++++ b/protocols/jabber/libiris/src/xmpp/xmpp-im/xmpp_tasks.cpp +@@ -888,14 +888,18 @@ bool JT_PushMessage::take(const QDomElement &e) + QDomElement forward; + Message::CarbonDir cd = Message::NoCarbon; + ++ Jid fromJid = Jid(e1.attribute(QLatin1String("from"))); + // Check for Carbon + QDomNodeList list = e1.childNodes(); + for (int i = 0; i < list.size(); ++i) { + QDomElement el = list.at(i).toElement(); + +- if (el.attribute("xmlns") == QLatin1String("urn:xmpp:carbons:2") && (el.tagName() == QLatin1String("received") || el.tagName() == QLatin1String("sent"))) { ++ if (el.attribute("xmlns") == QLatin1String("urn:xmpp:carbons:2") ++ && (el.tagName() == QLatin1String("received") || el.tagName() == QLatin1String("sent")) ++ && fromJid.compare(Jid(e1.attribute(QLatin1String("to"))), false)) { + QDomElement el1 = el.firstChildElement(); +- if (el1.tagName() == QLatin1String("forwarded") && el1.attribute(QLatin1String("xmlns")) == QLatin1String("urn:xmpp:forward:0")) { ++ if (el1.tagName() == QLatin1String("forwarded") ++ && el1.attribute(QLatin1String("xmlns")) == QLatin1String("urn:xmpp:forward:0")) { + QDomElement el2 = el1.firstChildElement(QLatin1String("message")); + if (!el2.isNull()) { + forward = el2; +@@ -904,7 +908,8 @@ bool JT_PushMessage::take(const QDomElement &e) + } + } + } +- else if (el.tagName() == QLatin1String("forwarded") && el.attribute(QLatin1String("xmlns")) == QLatin1String("urn:xmpp:forward:0")) { ++ else if (el.tagName() == QLatin1String("forwarded") ++ && el.attribute(QLatin1String("xmlns")) == QLatin1String("urn:xmpp:forward:0")) { + forward = el.firstChildElement(QLatin1String("message")); // currently only messages are supportted + // TODO <delay> element support + if (!forward.isNull()) { +@@ -913,7 +918,6 @@ bool JT_PushMessage::take(const QDomElement &e) + } + } + +- QString from = e1.attribute(QLatin1String("from")); + Stanza s = client()->stream().createStanza(addCorrectNS(forward.isNull()? e1 : forward)); + if(s.isNull()) { + //printf("take: bad stanza??\n"); +@@ -926,7 +930,7 @@ bool JT_PushMessage::take(const QDomElement &e) + return false; + } + if (!forward.isNull()) { +- m.setForwardedFrom(Jid(from)); ++ m.setForwardedFrom(fromJid); + m.setCarbonDirection(cd); + } + +-- +cgit v0.11.2 + diff --git a/kde/patch/kopete/kopete_kdebug393372.patch b/kde/patch/kopete/kopete_kdebug393372.patch new file mode 100644 index 0000000..2d1b1f0 --- /dev/null +++ b/kde/patch/kopete/kopete_kdebug393372.patch @@ -0,0 +1,30 @@ +From b1f4fa1401cba2e359e5a4b3ea2bafd119fca62b Mon Sep 17 00:00:00 2001 +From: Pino Toscano <pino@kde.org> +Date: Tue, 24 Apr 2018 06:30:19 +0200 +Subject: oscar: include buffer.h + +This header uses Buffer as by-value parameter, so make sure it knows +about it. + +BUG: 393372 +FIXED-IN: 18.04.1 +--- + protocols/oscar/liboscar/tasks/messagereceivertask.h | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/protocols/oscar/liboscar/tasks/messagereceivertask.h b/protocols/oscar/liboscar/tasks/messagereceivertask.h +index 8f52cd7..908e903 100644 +--- a/protocols/oscar/liboscar/tasks/messagereceivertask.h ++++ b/protocols/oscar/liboscar/tasks/messagereceivertask.h +@@ -21,6 +21,7 @@ + #include <QByteArray> + #include "oscarmessage.h" + #include "oscartypeclasses.h" ++#include "buffer.h" + + class QTextCodec; + +-- +cgit v0.11.2 + + diff --git a/kde/patch/kpat.patch b/kde/patch/kpat.patch new file mode 100644 index 0000000..dfc1651 --- /dev/null +++ b/kde/patch/kpat.patch @@ -0,0 +1,5 @@ +# Commit https://cgit.kde.org/kpat.git/patch/?id=fc1d54ced6a727382599d767e55879b6843c3456 +# Introduces a hard dependency on fc-solver which in turn has new dependencies +# So, we revert this commit to avoid dropping kpat altogether +cat $CWD/patch/kpat/kpat_no_freecell_solver_dep.patch | patch -p1 --reverse --verbose || { touch ${SLACK_KDE_BUILD_DIR}/${PKGNAME}.failed ; continue ; } + diff --git a/kde/patch/kpat/kpat_no_freecell_solver_dep.patch b/kde/patch/kpat/kpat_no_freecell_solver_dep.patch new file mode 100644 index 0000000..c2a0e66 --- /dev/null +++ b/kde/patch/kpat/kpat_no_freecell_solver_dep.patch @@ -0,0 +1,1475 @@ +Commit https://cgit.kde.org/kpat.git/patch/?id=fc1d54ced6a727382599d767e55879b6843c3456 +Introduces a hard dependency on fc-solver which in turn has new dependencies +So, we revert this commit to avoid dropping kpat altogether + +From ed0e53e0888da7123f4a0d2097f8da7fb105ca18 Mon Sep 17 00:00:00 2001 +From: Fabian Kosmale <0inkane@googlemail.com> +Date: Sun, 13 May 2018 15:14:53 +0200 +Subject: Use Freecell Solver for FreeCell and Simple Simon + +Summary: This uses http://fc-solve.shlomifish.org/ and prevents the looping in the existing solvers. + +Test Plan: Test that the solvers are working. + +Reviewers: #kde_games, fabiank + +Subscribers: kde-games-devel, aacid, #kde_games + +Tags: #kde_games + +Differential Revision: https://phabricator.kde.org/D12415 +--- + CMakeLists.txt | 6 +- + dealer.cpp | 4 + + freecell.cpp | 35 ++++ + freecell.h | 1 + + patsolve/abstract_fc_solve_solver.cpp | 239 ++++++++++++++++++++++++++ + patsolve/abstract_fc_solve_solver.h | 52 ++++++ + patsolve/freecellsolver.cpp | 310 +++++++++++++++++----------------- + patsolve/freecellsolver.h | 23 ++- + patsolve/patsolve.h | 12 +- + patsolve/simonsolver.cpp | 129 +++++++++++++- + patsolve/simonsolver.h | 20 ++- + patsolve/solverinterface.h | 2 + + pileutils.cpp | 61 +++++++ + pileutils.h | 4 + + simon.cpp | 56 +++++- + simon.h | 1 + + 16 files changed, 779 insertions(+), 176 deletions(-) + create mode 100644 patsolve/abstract_fc_solve_solver.cpp + create mode 100644 patsolve/abstract_fc_solve_solver.h + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 8f738bf..c043c45 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -4,6 +4,8 @@ cmake_minimum_required (VERSION 2.8.12 FATAL_ERROR) + set (QT_MIN_VERSION "5.7.0") + set (KF5_MIN_VERSION "5.30.0") + ++include(FindPkgConfig) ++pkg_check_modules(FC_SOLVE REQUIRED libfreecell-solver) + find_package(ECM ${KF5_MIN_VERSION} REQUIRED CONFIG) + set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR}) + +@@ -45,7 +47,7 @@ add_subdirectory(sounds) + add_subdirectory(themes) + add_subdirectory(doc) + +-set(kpat_SRCS ++set(kpat_SRCS ${libfcs_SRCS} + main.cpp + dealer.cpp + dealerinfo.cpp +@@ -59,6 +61,7 @@ set(kpat_SRCS + soundengine.cpp + statisticsdialog.cpp + view.cpp ++ patsolve/abstract_fc_solve_solver.cpp + patsolve/memory.cpp + patsolve/patsolve.cpp + +@@ -101,6 +104,7 @@ target_link_libraries(kpat + KF5::KIOCore + KF5KDEGames + kcardgame ++ ${FC_SOLVE_LIBRARIES} + ) + + install(TARGETS kpat ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) +diff --git a/dealer.cpp b/dealer.cpp +index 7c03ebf..a2558fc 100644 +--- a/dealer.cpp ++++ b/dealer.cpp +@@ -1724,6 +1724,10 @@ void DealerScene::startSolver() + + bool DealerScene::isGameLost() const + { ++ if (! m_winningMoves.isEmpty()) ++ { ++ return false; ++ } + if ( solver() ) + { + if ( m_solverThread && m_solverThread->isRunning() ) +diff --git a/freecell.cpp b/freecell.cpp +index f870cdb..9a7c278 100644 +--- a/freecell.cpp ++++ b/freecell.cpp +@@ -111,6 +111,41 @@ void Freecell::restart( const QList<KCard*> & cards ) + } + + ++QString Freecell::solverFormat() const ++{ ++ QString output; ++ QString tmp; ++ for (int i = 0; i < 4 ; i++) { ++ if (target[i]->isEmpty()) ++ continue; ++ tmp += suitToString(target[i]->topCard()->suit()) + '-' + rankToString(target[i]->topCard()->rank()) + ' '; ++ } ++ if (!tmp.isEmpty()) ++ output += QString::fromLatin1("Foundations: %1\n").arg(tmp); ++ ++ tmp.truncate(0); ++ for (int i = 0; i < 4 ; i++) { ++ if (freecell[i]->isEmpty()) ++ tmp += "- "; ++ else ++ tmp += rankToString(freecell[i]->topCard()->rank()) + suitToString(freecell[i]->topCard()->suit()) + ' '; ++ } ++ if (!tmp.isEmpty()) ++ { ++ QString a = QString::fromLatin1("Freecells: %1\n"); ++ output += a.arg(tmp); ++ } ++ ++ for (int i = 0; i < 8 ; i++) ++ { ++ QList<KCard*> cards = store[i]->cards(); ++ for (QList<KCard*>::ConstIterator it = cards.begin(); it != cards.end(); ++it) ++ output += rankToString((*it)->rank()) + suitToString((*it)->suit()) + ' '; ++ output += '\n'; ++ } ++ return output; ++} ++ + void Freecell::cardsDroppedOnPile( const QList<KCard*> & cards, KCardPile * pile ) + { + if ( cards.size() <= 1 ) +diff --git a/freecell.h b/freecell.h +index 7b0b2cb..9f7d84b 100644 +--- a/freecell.h ++++ b/freecell.h +@@ -62,6 +62,7 @@ protected slots: + private: + bool canPutStore( const KCardPile * pile, const QList<KCard*> & cards ) const; + ++ virtual QString solverFormat() const; + PatPile* store[8]; + PatPile* freecell[4]; + PatPile* target[4]; +diff --git a/patsolve/abstract_fc_solve_solver.cpp b/patsolve/abstract_fc_solve_solver.cpp +new file mode 100644 +index 0000000..11e5baa +--- /dev/null ++++ b/patsolve/abstract_fc_solve_solver.cpp +@@ -0,0 +1,239 @@ ++/* ++ * Copyright (C) 2006-2009 Stephan Kulow <coolo@kde.org> ++ * ++ * 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 of ++ * the License, 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. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ */ ++ ++#include <stdlib.h> ++#include <string.h> ++ ++#include "freecell-solver/fcs_user.h" ++#include "freecell-solver/fcs_cl.h" ++ ++#include "abstract_fc_solve_solver.h" ++ ++const int CHUNKSIZE = 100; ++const long int MAX_ITERS_LIMIT = 200000; ++ ++#define PRINT 0 ++ ++/* These two routines make and unmake moves. */ ++ ++void FcSolveSolver::make_move(MOVE *) ++{ ++ return; ++} ++ ++void FcSolveSolver::undo_move(MOVE *) ++{ ++ return; ++} ++ ++/* Get the possible moves from a position, and store them in Possible[]. */ ++SolverInterface::ExitStatus FcSolveSolver::patsolve( int _max_positions ) ++{ ++ int current_iters_count; ++ max_positions = (_max_positions < 0) ? MAX_ITERS_LIMIT : _max_positions; ++ ++ init(); ++ ++ if (!solver_instance) ++ { ++ { ++ solver_instance = freecell_solver_user_alloc(); ++ ++ solver_ret = FCS_STATE_NOT_BEGAN_YET; ++ ++ char * error_string; ++ int error_arg; ++ const char * known_parameters[1] = {NULL}; ++ /* A "char *" copy instead of "const char *". */ ++ ++ int parse_args_ret_code = freecell_solver_user_cmd_line_parse_args( ++ solver_instance, ++ get_cmd_line_arg_count() , ++ get_cmd_line_args(), ++ 0, ++ known_parameters, ++ NULL, ++ NULL, ++ &error_string, ++ &error_arg ++ ); ++ ++ Q_ASSERT(!parse_args_ret_code); ++ } ++ ++ /* Not needed for Simple Simon because it's already specified in ++ * freecell_solver_cmd_line_args. TODO : abstract . ++ * ++ * Shlomi Fish ++ * */ ++ setFcSolverGameParams(); ++ ++ current_iters_count = CHUNKSIZE; ++ freecell_solver_user_limit_iterations(solver_instance, current_iters_count); ++ } ++ ++ if (solver_instance) ++ { ++ bool continue_loop = true; ++ while (continue_loop && ++ ( (solver_ret == FCS_STATE_NOT_BEGAN_YET) ++ || (solver_ret == FCS_STATE_SUSPEND_PROCESS)) ++ && ++ (current_iters_count < MAX_ITERS_LIMIT) ++ ) ++ { ++ current_iters_count += CHUNKSIZE; ++ freecell_solver_user_limit_iterations(solver_instance, current_iters_count); ++ ++ if (solver_ret == FCS_STATE_NOT_BEGAN_YET) ++ { ++ solver_ret = ++ freecell_solver_user_solve_board( ++ solver_instance, ++ board_as_string ++ ); ++ } ++ else ++ { ++ solver_ret = freecell_solver_user_resume_solution(solver_instance); ++ } ++ { ++ // QMutexLocker lock( &endMutex ); ++ if ( m_shouldEnd ) ++ { ++ continue_loop = false; ++ } ++ } ++ } ++ } ++ ++ switch (solver_ret) ++ { ++ case FCS_STATE_IS_NOT_SOLVEABLE: ++ if (solver_instance) ++ { ++ freecell_solver_user_free(solver_instance); ++ solver_instance = NULL; ++ } ++ return Solver::NoSolutionExists; ++ ++ case FCS_STATE_WAS_SOLVED: ++ { ++ if (solver_instance) ++ { ++ m_winMoves.clear(); ++ while (freecell_solver_user_get_moves_left(solver_instance)) ++ { ++ fcs_move_t move; ++ MOVE new_move; ++ const int verdict = !freecell_solver_user_get_next_move( ++ solver_instance, &move) ++ ; ++ ++ Q_ASSERT (verdict); ++ ++ new_move.fcs = move; ++ ++ m_winMoves.append( new_move ); ++ } ++ ++ freecell_solver_user_free(solver_instance); ++ solver_instance = NULL; ++ } ++ return Solver::SolutionExists; ++ } ++ ++ case FCS_STATE_SUSPEND_PROCESS: ++ return Solver::UnableToDetermineSolvability; ++ ++ default: ++ if (solver_instance) ++ { ++ freecell_solver_user_free(solver_instance); ++ solver_instance = NULL; ++ } ++ return Solver::NoSolutionExists; ++ } ++} ++ ++/* Get the possible moves from a position, and store them in Possible[]. */ ++ ++int FcSolveSolver::get_possible_moves(int *, int *) ++{ ++ return 0; ++} ++ ++bool FcSolveSolver::isWon() ++{ ++ return true; ++} ++ ++int FcSolveSolver::getOuts() ++{ ++ return 0; ++} ++ ++FcSolveSolver::FcSolveSolver() ++ : Solver() ++ , solver_instance(NULL) ++ , solver_ret(FCS_STATE_NOT_BEGAN_YET) ++ , board_as_string("") ++{ ++} ++ ++unsigned int FcSolveSolver::getClusterNumber() ++{ ++ return 0; ++} ++ ++void FcSolveSolver::print_layout() ++{ ++#if 0 ++ int i, w, o; ++ ++ fprintf(stderr, "print-layout-begin\n"); ++ for (w = 0; w < 10; ++w) { ++ Q_ASSERT( Wp[w] == &W[w][Wlen[w]-1] ); ++ fprintf( stderr, "Play%d: ", w ); ++ for (i = 0; i < Wlen[w]; ++i) { ++ printcard(W[w][i], stderr); ++ } ++ fputc('\n', stderr); ++ } ++ fprintf( stderr, "Off: " ); ++ for (o = 0; o < 4; ++o) { ++ if ( O[o] != -1 ) ++ printcard( O[o] + PS_KING, stderr); ++ } ++ fprintf(stderr, "\nprint-layout-end\n"); ++#endif ++} ++ ++void FcSolveSolver::unpack_cluster( unsigned int) ++{ ++ return; ++} ++ ++FcSolveSolver::~FcSolveSolver() ++{ ++ if (solver_instance) ++ { ++ freecell_solver_user_free(solver_instance); ++ solver_instance = NULL; ++ } ++} ++ +diff --git a/patsolve/abstract_fc_solve_solver.h b/patsolve/abstract_fc_solve_solver.h +new file mode 100644 +index 0000000..d2d072d +--- /dev/null ++++ b/patsolve/abstract_fc_solve_solver.h +@@ -0,0 +1,52 @@ ++/* ++ * Copyright (C) 2006-2009 Stephan Kulow <coolo@kde.org> ++ * ++ * 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 of ++ * the License, 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. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ */ ++ ++#ifndef ABSTRACT_FC_SOLVE_SOLVER_H ++#define ABSTRACT_FC_SOLVE_SOLVER_H ++ ++#include "patsolve.h" ++ ++struct FcSolveSolver : public Solver<10> ++{ ++public: ++ FcSolveSolver(); ++ virtual ~FcSolveSolver(); ++ virtual int get_possible_moves(int *a, int *numout); ++ virtual bool isWon(); ++ virtual void make_move(MOVE *m); ++ virtual void undo_move(MOVE *m); ++ virtual int getOuts(); ++ virtual unsigned int getClusterNumber(); ++ virtual void translate_layout() = 0; ++ virtual void unpack_cluster( unsigned int k ); ++ virtual MoveHint translateMove(const MOVE &m) = 0; ++ virtual SolverInterface::ExitStatus patsolve( int _max_positions = -1); ++ virtual void setFcSolverGameParams() = 0; ++ ++ virtual void print_layout(); ++ ++ virtual int get_cmd_line_arg_count() = 0; ++ virtual const char * * get_cmd_line_args() = 0; ++/* Names of the cards. The ordering is defined in pat.h. */ ++ ++ void * solver_instance; ++ int solver_ret; ++ // More than enough space for two decks. ++ char board_as_string[4 * 13 * 2 * 4 * 3]; ++}; ++ ++#endif // ABSTRACT_FC_SOLVE_SOLVER_H +diff --git a/patsolve/freecellsolver.cpp b/patsolve/freecellsolver.cpp +index 39eff50..e92000f 100644 +--- a/patsolve/freecellsolver.cpp ++++ b/patsolve/freecellsolver.cpp +@@ -16,12 +16,18 @@ + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + ++#include <stdlib.h> ++#include <string.h> ++ ++#include "freecell-solver/fcs_user.h" ++#include "freecell-solver/fcs_cl.h" ++ + #include "freecellsolver.h" + + #include "../freecell.h" + +- +-/* Some macros used in get_possible_moves(). */ ++const int CHUNKSIZE = 100; ++const long int MAX_ITERS_LIMIT = 200000; + + /* The following function implements + (Same_suit ? (suit(a) == suit(b)) : (color(a) != color(b))) +@@ -32,10 +38,13 @@ namespace { + + /* Statistics. */ + ++#if 0 + int FreecellSolver::Xparam[] = { 4, 1, 8, -1, 7, 11, 4, 2, 2, 1, 2 }; ++#endif + + /* These two routines make and unmake moves. */ + ++#if 0 + void FreecellSolver::make_move(MOVE *m) + { + int from, to; +@@ -85,7 +94,9 @@ void FreecellSolver::undo_move(MOVE *m) + Wlen[from]++; + hashpile(from); + } ++#endif + ++#if 0 + /* Move prioritization. Given legal, pruned moves, there are still some + that are a waste of time, especially in the endgame where there are lots of + possible moves, but few productive ones. Note that we also prioritize +@@ -178,9 +189,11 @@ void FreecellSolver::prioritize(MOVE *mp0, int n) + } + } + } ++#endif + + /* Automove logic. Freecell games must avoid certain types of automoves. */ + ++#if 0 + int FreecellSolver::good_automove(int o, int r) + { + int i; +@@ -220,148 +233,43 @@ int FreecellSolver::good_automove(int o, int r) + + return true; + } ++#endif + +-/* Get the possible moves from a position, and store them in Possible[]. */ ++#define CMD_LINE_ARGS_NUM 2 + +-int FreecellSolver::get_possible_moves(int *a, int *numout) ++static const char * freecell_solver_cmd_line_args[CMD_LINE_ARGS_NUM] = + { +- int i, n, t, w, o, empty, emptyw; +- card_t card; +- MOVE *mp; +- +- /* Check for moves from W to O. */ +- +- n = 0; +- mp = Possible; +- for (w = 0; w < Nwpiles + Ntpiles; ++w) { +- if (Wlen[w] > 0) { +- card = *Wp[w]; +- o = SUIT(card); +- empty = (O[o] == NONE); +- if ((empty && (RANK(card) == PS_ACE)) || +- (!empty && (RANK(card) == O[o] + 1))) { +- mp->card_index = 0; +- mp->from = w; +- mp->to = o; +- mp->totype = O_Type; +- mp->turn_index = -1; +- mp->pri = 0; /* unused */ +- n++; +- mp++; +- +- /* If it's an automove, just do it. */ +- +- if (good_automove(o, RANK(card))) { +- *a = true; +- mp[-1].pri = 127; +- if (n != 1) { +- Possible[0] = mp[-1]; +- return 1; +- } +- return n; +- } +- } +- } +- } +- +- /* No more automoves, but remember if there were any moves out. */ +- +- *a = false; +- *numout = n; +- +- /* Check for moves from non-singleton W cells to one of any +- empty W cells. */ ++ "--load-config", "video-editing" ++}; + +- emptyw = -1; +- for (w = 0; w < Nwpiles; ++w) { +- if (Wlen[w] == 0) { +- emptyw = w; +- break; +- } +- } +- if (emptyw >= 0) { +- for (i = 0; i < Nwpiles + Ntpiles; ++i) { +- if (i == emptyw || Wlen[i] == 0) { +- continue; +- } +- bool allowed = false; +- if ( i < Nwpiles) +- allowed = true; +- if ( i >= Nwpiles ) +- allowed = true; +- if ( allowed ) { +- card = *Wp[i]; +- mp->card_index = 0; +- mp->from = i; +- mp->to = emptyw; +- mp->totype = W_Type; +- mp->turn_index = -1; +- if ( i >= Nwpiles ) +- mp->pri = Xparam[6]; +- else +- mp->pri = Xparam[3]; +- n++; +- mp++; +- } +- } +- } +- +- /* Check for moves from W to non-empty W cells. */ +- +- for (i = 0; i < Nwpiles + Ntpiles; ++i) { +- if (Wlen[i] > 0) { +- card = *Wp[i]; +- for (w = 0; w < Nwpiles; ++w) { +- if (i == w) { +- continue; +- } +- if (Wlen[w] > 0 && +- (RANK(card) == RANK(*Wp[w]) - 1 && +- suitable(card, *Wp[w]))) { +- mp->card_index = 0; +- mp->from = i; +- mp->to = w; +- mp->totype = W_Type; +- mp->turn_index = -1; +- if ( i >= Nwpiles ) +- mp->pri = Xparam[5]; +- else +- mp->pri = Xparam[4]; +- n++; +- mp++; +- } +- } +- } +- } +- +- /* Check for moves from W to one of any empty T cells. */ +- +- for (t = 0; t < Ntpiles; ++t) { +- if (!Wlen[t+Nwpiles]) { +- break; +- } +- } ++int FreecellSolver::get_cmd_line_arg_count() ++{ ++ return CMD_LINE_ARGS_NUM; ++} + +- if (t < Ntpiles) { +- for (w = 0; w < Nwpiles; ++w) { +- if (Wlen[w] > 0) { +- card = *Wp[w]; +- mp->card_index = 0; +- mp->from = w; +- mp->turn_index = -1; +- mp->to = t+Nwpiles; +- mp->totype = W_Type; +- mp->pri = Xparam[7]; +- n++; +- mp++; +- } +- } +- } ++const char * * FreecellSolver::get_cmd_line_args() ++{ ++ return freecell_solver_cmd_line_args; ++} + + +- return n; ++void FreecellSolver::setFcSolverGameParams() ++{ ++ /* ++ * I'm using the more standard interface instead of the depracated ++ * user_set_game one. I'd like that each function will have its ++ * own dedicated purpose. ++ * ++ * Shlomi Fish ++ * */ ++ freecell_solver_user_set_num_freecells(solver_instance,4); ++ freecell_solver_user_set_num_stacks(solver_instance,8); ++ freecell_solver_user_set_num_decks(solver_instance,1); ++ freecell_solver_user_set_sequences_are_built_by_type(solver_instance, FCS_SEQ_BUILT_BY_ALTERNATE_COLOR); ++ freecell_solver_user_set_sequence_move(solver_instance, 0); ++ freecell_solver_user_set_empty_stacks_filled_by(solver_instance, FCS_ES_FILLED_BY_ANY_CARD); + } +- ++#if 0 + void FreecellSolver::unpack_cluster( unsigned int k ) + { + /* Get the Out cells from the cluster number. */ +@@ -373,27 +281,13 @@ void FreecellSolver::unpack_cluster( unsigned int k ) + k >>= 4; + O[3] = k & 0xF; + } ++#endif + +-bool FreecellSolver::isWon() +-{ +- // maybe won? +- for (int o = 0; o < 4; ++o) { +- if (O[o] != PS_KING) { +- return false; +- } +- } +- +- return true; +-} +- +-int FreecellSolver::getOuts() +-{ +- return O[0] + O[1] + O[2] + O[3]; +-} + + FreecellSolver::FreecellSolver(const Freecell *dealer) +- : Solver() ++ : FcSolveSolver() + { ++#if 0 + Osuit[0] = PS_DIAMOND; + Osuit[1] = PS_CLUB; + Osuit[2] = PS_HEART; +@@ -402,12 +296,15 @@ FreecellSolver::FreecellSolver(const Freecell *dealer) + Nwpiles = 8; + Ntpiles = 4; + ++#endif ++ + deal = dealer; + } + + /* Read a layout file. Format is one pile per line, bottom to top (visible + card). Temp cells and Out on the last two lines, if any. */ + ++#if 0 + void FreecellSolver::translate_layout() + { + /* Read the workspace. */ +@@ -447,9 +344,78 @@ void FreecellSolver::translate_layout() + } + } + } ++#endif + + MoveHint FreecellSolver::translateMove( const MOVE &m ) + { ++ fcs_move_t move = m.fcs; ++ int cards = fcs_move_get_num_cards_in_seq(move); ++ PatPile *from = 0; ++ PatPile *to = 0; ++ ++ switch(fcs_move_get_type(move)) ++ { ++ case FCS_MOVE_TYPE_STACK_TO_STACK: ++ from = deal->store[fcs_move_get_src_stack(move)]; ++ to = deal->store[fcs_move_get_dest_stack(move)]; ++ break; ++ ++ case FCS_MOVE_TYPE_FREECELL_TO_STACK: ++ from = deal->freecell[fcs_move_get_src_freecell(move)]; ++ to = deal->store[fcs_move_get_dest_stack(move)]; ++ cards = 1; ++ break; ++ ++ case FCS_MOVE_TYPE_FREECELL_TO_FREECELL: ++ from = deal->freecell[fcs_move_get_src_freecell(move)]; ++ to = deal->freecell[fcs_move_get_dest_freecell(move)]; ++ cards = 1; ++ break; ++ ++ case FCS_MOVE_TYPE_STACK_TO_FREECELL: ++ from = deal->store[fcs_move_get_src_stack(move)]; ++ to = deal->freecell[fcs_move_get_dest_freecell(move)]; ++ cards = 1; ++ break; ++ ++ case FCS_MOVE_TYPE_STACK_TO_FOUNDATION: ++ from = deal->store[fcs_move_get_src_stack(move)]; ++ cards = 1; ++ to = 0; ++ break; ++ ++ case FCS_MOVE_TYPE_FREECELL_TO_FOUNDATION: ++ from = deal->freecell[fcs_move_get_src_freecell(move)]; ++ cards = 1; ++ to = 0; ++ } ++ Q_ASSERT(from); ++ Q_ASSERT(cards <= from->cards().count()); ++ Q_ASSERT(to || cards == 1); ++ KCard *card = from->cards()[from->cards().count() - cards]; ++ ++ if (!to) ++ { ++ PatPile *target = 0; ++ PatPile *empty = 0; ++ for (int i = 0; i < 4; ++i) { ++ KCard *c = deal->target[i]->topCard(); ++ if (c) { ++ if ( c->suit() == card->suit() ) ++ { ++ target = deal->target[i]; ++ break; ++ } ++ } else if ( !empty ) ++ empty = deal->target[i]; ++ } ++ to = target ? target : empty; ++ } ++ Q_ASSERT(to); ++ ++ return MoveHint(card, to, 0); ++ ++#if 0 + // this is tricky as we need to want to build the "meta moves" + + PatPile *frompile = nullptr; +@@ -486,8 +452,43 @@ MoveHint FreecellSolver::translateMove( const MOVE &m ) + + return MoveHint( card, target, m.pri ); + } ++#endif + } + ++void FreecellSolver::translate_layout() ++{ ++ strcpy(board_as_string, deal->solverFormat().toLatin1()); ++ ++ if (solver_instance) ++ { ++ freecell_solver_user_recycle(solver_instance); ++ solver_ret = FCS_STATE_NOT_BEGAN_YET; ++ } ++#if 0 ++ /* Read the workspace. */ ++ int total = 0; ++ ++ for ( int w = 0; w < 10; ++w ) { ++ int i = translate_pile(deal->store[w], W[w], 52); ++ Wp[w] = &W[w][i - 1]; ++ Wlen[w] = i; ++ total += i; ++ } ++ ++ for (int i = 0; i < 4; ++i) { ++ O[i] = -1; ++ KCard *c = deal->target[i]->top(); ++ if (c) { ++ total += 13; ++ O[i] = translateSuit( c->suit() ); ++ } ++ } ++#endif ++} ++ ++ ++ ++#if 0 + unsigned int FreecellSolver::getClusterNumber() + { + int i = O[0] + (O[1] << 4); +@@ -496,7 +497,9 @@ unsigned int FreecellSolver::getClusterNumber() + k |= i << 8; + return k; + } ++#endif + ++#if 0 + void FreecellSolver::print_layout() + { + int i, t, w, o; +@@ -519,3 +522,4 @@ void FreecellSolver::print_layout() + } + fprintf(stderr, "\nprint-layout-end\n"); + } ++#endif +diff --git a/patsolve/freecellsolver.h b/patsolve/freecellsolver.h +index 45ca063..99d1dbb 100644 +--- a/patsolve/freecellsolver.h ++++ b/patsolve/freecellsolver.h +@@ -19,16 +19,17 @@ + #ifndef FREECELLSOLVER_H + #define FREECELLSOLVER_H + +-class Freecell; +-#include "patsolve.h" ++#include "abstract_fc_solve_solver.h" + + constexpr auto Nwpiles = 8; + constexpr auto Ntpiles = 4; ++class Freecell; + +-class FreecellSolver : public Solver<Nwpiles + Ntpiles> ++class FreecellSolver : public FcSolveSolver + { + public: + explicit FreecellSolver(const Freecell *dealer); ++#if 0 + int good_automove(int o, int r); + int get_possible_moves(int *a, int *numout) Q_DECL_OVERRIDE; + bool isWon() Q_DECL_OVERRIDE; +@@ -40,8 +41,17 @@ public: + void translate_layout() Q_DECL_OVERRIDE; + void unpack_cluster( unsigned int k ) Q_DECL_OVERRIDE; + MoveHint translateMove(const MOVE &m) Q_DECL_OVERRIDE; +- +- void print_layout() Q_DECL_OVERRIDE; ++#endif ++ virtual void translate_layout(); ++#if 0 ++ virtual void unpack_cluster( unsigned int k ); ++#endif ++ virtual MoveHint translateMove(const MOVE &m); ++ virtual void setFcSolverGameParams(); ++ virtual int get_cmd_line_arg_count(); ++ virtual const char * * get_cmd_line_args(); ++#if 0 ++ virtual void print_layout(); + + int Nwpiles; /* the numbers we're actually using */ + int Ntpiles; +@@ -51,10 +61,11 @@ public: + card_t O[4]; /* output piles store only the rank or NONE */ + card_t Osuit[4]; + +- const Freecell *deal; + + static int Xparam[]; ++#endif + ++ const Freecell *deal; + }; + + #endif // FREECELLSOLVER_H +diff --git a/patsolve/patsolve.h b/patsolve/patsolve.h +index 03285d4..1c3a7c6 100644 +--- a/patsolve/patsolve.h ++++ b/patsolve/patsolve.h +@@ -33,6 +33,10 @@ + + #include <cstdio> + ++/* A card is represented as ( down << 6 ) + (suit << 4) + rank. */ ++ ++typedef quint8 card_t; ++ + struct POSITION { + POSITION *queue; /* next position in the queue */ + POSITION *parent; /* point back up the move stack */ +@@ -48,14 +52,15 @@ class MemoryManager; + template<size_t NumberPiles> + class Solver : public SolverInterface + { ++ + public: + + Solver(); + virtual ~Solver(); +- ExitStatus patsolve( int max_positions = -1) final; ++ virtual ExitStatus patsolve( int max_positions = -1); ++ bool recursive(POSITION *pos = nullptr); + virtual void translate_layout() = 0; + virtual MoveHint translateMove(const MOVE &m ) = 0; +- + void stopExecution() final; + QList<MOVE> firstMoves() const final; + QList<MOVE> winMoves() const final; +@@ -126,8 +131,7 @@ protected: + POSITION *Stack = nullptr; + QMap<qint32,bool> recu_pos; + int max_positions; +- +-private: ++protected: + QList<MOVE> m_firstMoves; + QList<MOVE> m_winMoves; + std::atomic_bool m_shouldEnd; +diff --git a/patsolve/simonsolver.cpp b/patsolve/simonsolver.cpp +index a9d640c..e75dcaa 100644 +--- a/patsolve/simonsolver.cpp ++++ b/patsolve/simonsolver.cpp +@@ -15,17 +15,26 @@ + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + ++#include <stdlib.h> ++#include <string.h> ++ ++#include "freecell-solver/fcs_user.h" ++#include "freecell-solver/fcs_cl.h" ++ + #include "simonsolver.h" + + #include "../simon.h" + + #include <QDebug> + ++const int CHUNKSIZE = 100; ++const long int MAX_ITERS_LIMIT = 200000; + + #define PRINT 0 + + /* These two routines make and unmake moves. */ + ++#if 0 + void SimonSolver::make_move(MOVE *m) + { + #if PRINT +@@ -136,13 +145,62 @@ void SimonSolver::undo_move(MOVE *m) + print_layout(); + #endif + } ++#endif ++ ++#define CMD_LINE_ARGS_NUM 4 ++static const char * freecell_solver_cmd_line_args[CMD_LINE_ARGS_NUM] = ++{ ++ "-g", "simple_simon", "--load-config", "the-last-mohican" ++}; ++ ++int SimonSolver::get_cmd_line_arg_count() ++{ ++ return CMD_LINE_ARGS_NUM; ++} ++ ++const char * * SimonSolver::get_cmd_line_args() ++{ ++ return freecell_solver_cmd_line_args; ++} ++ ++void SimonSolver::setFcSolverGameParams() ++{ ++ freecell_solver_user_apply_preset(solver_instance, "simple_simon"); ++} + ++#if 0 + /* Get the possible moves from a position, and store them in Possible[]. */ + + int SimonSolver::get_possible_moves(int *a, int *numout) + { + MOVE *mp; ++ int n; ++ ++ mp = Possible; ++ n = 0; ++ *a = 1; ++ ++ while (freecell_solver_user_get_moves_left(solver_instance)) ++ { ++ fcs_move_t move; ++ fcs_move_t * move_ptr; ++ if (!freecell_solver_user_get_next_move(solver_instance, &move)) { ++ move_ptr = new fcs_move_t; ++ *move_ptr = move; ++ mp->ptr = (void *)move_ptr; ++ mp++; ++ n++; ++ } ++ else ++ { ++ Q_ASSERT(0); ++ } ++ } ++ ++ *numout = n; ++ return n; + ++#if 0 + /* Check for moves from W to O. */ + + int n = 0; +@@ -301,8 +359,11 @@ int SimonSolver::get_possible_moves(int *a, int *numout) + } + + return n; ++#endif + } ++#endif + ++#if 0 + void SimonSolver::unpack_cluster( unsigned int k ) + { + // TODO: this only works for easy +@@ -314,7 +375,9 @@ void SimonSolver::unpack_cluster( unsigned int k ) + O[i] = -1; + } + } ++#endif + ++#if 0 + bool SimonSolver::isWon() + { + // maybe won? +@@ -324,7 +387,9 @@ bool SimonSolver::isWon() + + return true; + } ++#endif + ++#if 0 + int SimonSolver::getOuts() + { + int k = 0; +@@ -334,9 +399,10 @@ int SimonSolver::getOuts() + + return k; + } ++#endif + + SimonSolver::SimonSolver(const Simon *dealer) +- : Solver() ++ : FcSolveSolver() + { + deal = dealer; + } +@@ -346,6 +412,14 @@ card). Temp cells and Out on the last two lines, if any. */ + + void SimonSolver::translate_layout() + { ++ strcpy(board_as_string, deal->solverFormat().toLatin1()); ++ ++ if (solver_instance) ++ { ++ freecell_solver_user_recycle(solver_instance); ++ solver_ret = FCS_STATE_NOT_BEGAN_YET; ++ } ++#if 0 + /* Read the workspace. */ + int total = 0; + +@@ -364,8 +438,10 @@ void SimonSolver::translate_layout() + O[i] = translateSuit( c->suit() ); + } + } ++#endif + } + ++#if 0 + unsigned int SimonSolver::getClusterNumber() + { + unsigned int k = 0; +@@ -376,7 +452,9 @@ unsigned int SimonSolver::getClusterNumber() + } + return k; + } ++#endif + ++#if 0 + void SimonSolver::print_layout() + { + int i, w, o; +@@ -397,9 +475,57 @@ void SimonSolver::print_layout() + } + fprintf(stderr, "\nprint-layout-end\n"); + } ++#endif + + MoveHint SimonSolver::translateMove( const MOVE &m ) + { ++ fcs_move_t move = m.fcs; ++ int cards = fcs_move_get_num_cards_in_seq(move); ++ PatPile *from = 0; ++ PatPile *to = 0; ++ ++ switch(fcs_move_get_type(move)) ++ { ++ case FCS_MOVE_TYPE_STACK_TO_STACK: ++ from = deal->store[fcs_move_get_src_stack(move)]; ++ to = deal->store[fcs_move_get_dest_stack(move)]; ++ break; ++ ++ case FCS_MOVE_TYPE_SEQ_TO_FOUNDATION: ++ from = deal->store[fcs_move_get_src_stack(move)]; ++ cards = 13; ++ to = deal->target[fcs_move_get_foundation(move)]; ++ break; ++ ++ } ++ Q_ASSERT(from); ++ Q_ASSERT(cards <= from->cards().count()); ++ Q_ASSERT(to || cards == 1); ++ KCard *card = from->cards()[from->cards().count() - cards]; ++ ++ if (!to) ++ { ++ PatPile *target = 0; ++ PatPile *empty = 0; ++ for (int i = 0; i < 4; ++i) { ++ KCard *c = deal->target[i]->topCard(); ++ if (c) { ++ if ( c->suit() == card->suit() ) ++ { ++ target = deal->target[i]; ++ break; ++ } ++ } else if ( !empty ) ++ empty = deal->target[i]; ++ } ++ to = target ? target : empty; ++ } ++ ++ Q_ASSERT(to); ++ ++ return MoveHint(card, to, 0); ++ ++#if 0 + Q_ASSERT( m.from < 10 && m.to < 10 ); + + PatPile *frompile = deal->store[m.from]; +@@ -414,4 +540,5 @@ MoveHint SimonSolver::translateMove( const MOVE &m ) + + Q_ASSERT( m.to < 10 ); + return MoveHint( card, deal->store[m.to], m.pri ); ++#endif + } +diff --git a/patsolve/simonsolver.h b/patsolve/simonsolver.h +index 2d57dda..4a417b1 100644 +--- a/patsolve/simonsolver.h ++++ b/patsolve/simonsolver.h +@@ -18,29 +18,37 @@ + #ifndef SIMONSOLVER_H + #define SIMONSOLVER_H + +-#include "patsolve.h" ++#include "abstract_fc_solve_solver.h" ++#include "simon.h" + class Simon; + + +-class SimonSolver : public Solver<10> ++class SimonSolver : public FcSolveSolver + { + public: + explicit SimonSolver(const Simon *dealer); ++#if 0 + int get_possible_moves(int *a, int *numout) Q_DECL_OVERRIDE; + bool isWon() Q_DECL_OVERRIDE; + void make_move(MOVE *m) Q_DECL_OVERRIDE; + void undo_move(MOVE *m) Q_DECL_OVERRIDE; + int getOuts() Q_DECL_OVERRIDE; + unsigned int getClusterNumber() Q_DECL_OVERRIDE; +- void translate_layout() Q_DECL_OVERRIDE; ++#endif ++ virtual void translate_layout() Q_DECL_OVERRIDE; ++ virtual MoveHint translateMove(const MOVE &m) Q_DECL_OVERRIDE; ++#if 0 + void unpack_cluster( unsigned int k ) Q_DECL_OVERRIDE; +- MoveHint translateMove(const MOVE &m) Q_DECL_OVERRIDE; +- + void print_layout() Q_DECL_OVERRIDE; ++#endif ++ virtual void setFcSolverGameParams(); + ++ virtual int get_cmd_line_arg_count(); ++ virtual const char * * get_cmd_line_args(); ++#if 0 + /* Names of the cards. The ordering is defined in pat.h. */ +- + int O[4]; ++#endif + const Simon *deal; + }; + +diff --git a/patsolve/solverinterface.h b/patsolve/solverinterface.h +index d99d3b8..77fd410 100644 +--- a/patsolve/solverinterface.h ++++ b/patsolve/solverinterface.h +@@ -4,6 +4,7 @@ + #include <QList> + + #include "../hint.h" ++#include "freecell-solver/fcs_user.h" + + + /* A card is represented as ( down << 6 ) + (suit << 4) + rank. */ +@@ -22,6 +23,7 @@ public: + PileType totype; + signed char pri; /* move priority (low priority == low value) */ + int turn_index; /* turn the card index */ ++ fcs_move_t fcs; /* A Freecell Solver move. */ + + bool operator<( const MOVE &m) const + { +diff --git a/pileutils.cpp b/pileutils.cpp +index 1e3da3e..609c716 100644 +--- a/pileutils.cpp ++++ b/pileutils.cpp +@@ -48,6 +48,33 @@ bool isSameSuitAscending( const QList<KCard*> & cards ) + return true; + } + ++int countSameSuitDescendingSequences( const QList<KCard*> & cards ) ++{ ++ if ( cards.size() <= 1 ) ++ return 0; ++ ++ int suit = cards.first()->suit(); ++ int lastRank = cards.first()->rank(); ++ ++ int count = 1; ++ ++ for( int i = 1; i < cards.size(); ++i ) ++ { ++ --lastRank; ++ ++ if ( cards[i]->rank() != lastRank ) ++ return -1; ++ ++ if ( cards[i]->suit() != suit ) ++ { ++ count++; ++ suit = cards[i]->suit(); ++ } ++ } ++ return count; ++} ++ ++ + + bool isSameSuitDescending( const QList<KCard*> & cards ) + { +@@ -121,3 +148,37 @@ bool checkAddAlternateColorDescendingFromKing( const QList<KCard*> & oldCards, c + && newCards.first()->rank() == oldCards.last()->rank() - 1; + } + ++QString suitToString(int s) { ++ switch (s) { ++ case KCardDeck::Clubs: ++ return "C"; ++ case KCardDeck::Hearts: ++ return "H"; ++ case KCardDeck::Diamonds: ++ return "D"; ++ case KCardDeck::Spades: ++ return "S"; ++ default: ++ exit(-1); ++ } ++ return QString(); ++} ++ ++QString rankToString(int r) ++{ ++ switch (r) { ++ case KCardDeck::King: ++ return "K"; ++ case KCardDeck::Ace: ++ return "A"; ++ case KCardDeck::Jack: ++ return "J"; ++ case KCardDeck::Queen: ++ return "Q"; ++ case KCardDeck::Ten: ++ return "T"; ++ default: ++ return QString::number(r); ++ } ++} ++ +diff --git a/pileutils.h b/pileutils.h +index 2fa1657..faa8c40 100644 +--- a/pileutils.h ++++ b/pileutils.h +@@ -26,9 +26,13 @@ class KCard; + bool isSameSuitAscending( const QList<KCard*> & cards ); + bool isSameSuitDescending( const QList<KCard*> & cards ); + bool isAlternateColorDescending( const QList<KCard*> & cards ); ++int countSameSuitDescendingSequences( const QList<KCard*> & cards ); + + bool checkAddSameSuitAscendingFromAce( const QList<KCard*> & oldCards, const QList<KCard*> & newCards ); + bool checkAddAlternateColorDescending( const QList<KCard*> & oldCards, const QList<KCard*> & newCards ); + bool checkAddAlternateColorDescendingFromKing( const QList<KCard*> & oldCards, const QList<KCard*> & newCards ); + ++extern QString suitToString(int s); ++extern QString rankToString(int r); ++ + #endif +diff --git a/simon.cpp b/simon.cpp +index 8e3ef10..834dd55 100644 +--- a/simon.cpp ++++ b/simon.cpp +@@ -111,24 +111,70 @@ bool Simon::checkAdd(const PatPile * pile, const QList<KCard*> & oldCards, const + { + if (pile->pileRole() == PatPile::Tableau) + { +- return oldCards.isEmpty() +- || oldCards.last()->rank() == newCards.first()->rank() + 1; ++ if (! (oldCards.isEmpty() ++ || oldCards.last()->rank() == newCards.first()->rank() + 1 )) ++ { ++ return false; ++ } ++ ++ int seqs_count = countSameSuitDescendingSequences(newCards); ++ ++ if (seqs_count < 0) ++ return false; ++ ++ // This is similar to the supermoves of Freecell - we can use empty ++ // columns to temporarily hold intermediate sub-sequences which are ++ // not the same suit - only a "false" parent. ++ // Shlomi Fish ++ ++ int empty_piles_count = 0; ++ ++ for (int i = 0; i < 10; ++i ) ++ if (store[i]->isEmpty() && ( store[i]->index() != pile->index() )) ++ empty_piles_count++; ++ ++ return (seqs_count <= (1 << empty_piles_count)); + } + else + { + return oldCards.isEmpty() + && newCards.first()->rank() == KCardDeck::King +- && newCards.last()->rank() == KCardDeck::Ace; ++ && newCards.last()->rank() == KCardDeck::Ace ++ && isSameSuitDescending(newCards); + } + } + + bool Simon::checkRemove(const PatPile * pile, const QList<KCard*> & cards) const + { +- return pile->pileRole() == PatPile::Tableau +- && isSameSuitDescending(cards); ++ if (pile->pileRole() != PatPile::Tableau) ++ return false; ++ ++ int seqs_count = countSameSuitDescendingSequences(cards); ++ ++ return (seqs_count >= 0); + } + ++QString Simon::solverFormat() const ++{ ++ QString output; ++ QString tmp; ++ for (int i = 0; i < 4 ; i++) { ++ if (target[i]->isEmpty()) ++ continue; ++ tmp += suitToString(target[i]->topCard()->suit()) + "-K "; ++ } ++ if (!tmp.isEmpty()) ++ output += QString::fromLatin1("Foundations: %1\n").arg(tmp); + ++ for (int i = 0; i < 10 ; i++) ++ { ++ QList<KCard*> cards = store[i]->cards(); ++ for (QList<KCard*>::ConstIterator it = cards.begin(); it != cards.end(); ++it) ++ output += rankToString((*it)->rank()) + suitToString((*it)->suit()) + ' '; ++ output += '\n'; ++ } ++ return output; ++} + + static class SimonDealerInfo : public DealerInfo + { +diff --git a/simon.h b/simon.h +index 83d10ab..d816f27 100644 +--- a/simon.h ++++ b/simon.h +@@ -57,6 +57,7 @@ private: + PatPile* store[10]; + PatPile* target[4]; + ++ virtual QString solverFormat() const; + friend class SimonSolver; + }; + +-- +cgit v0.11.2 + diff --git a/kde/patch/krita.patch b/kde/patch/krita.patch new file mode 100644 index 0000000..e1604ab --- /dev/null +++ b/kde/patch/krita.patch @@ -0,0 +1,3 @@ +# Fix compilation of Krita against Qt 5.9: +#cat $CWD/patch/krita/krita_qt59.patch | patch -p1 --verbose || { touch ${SLACK_KDE_BUILD_DIR}/${PKGNAME}.failed ; continue ; } + diff --git a/kde/patch/krita/krita_qt59.patch b/kde/patch/krita/krita_qt59.patch new file mode 100644 index 0000000..f517995 --- /dev/null +++ b/kde/patch/krita/krita_qt59.patch @@ -0,0 +1,26 @@ +Source: https://github.com/KDE/krita/commit/2f59d0d1.patch + +From 2f59d0d1d91e3f79342c20d0df68aa9a51817e8d Mon Sep 17 00:00:00 2001 +From: Luca Beltrame <lbeltrame@kde.org> +Date: Sat, 6 May 2017 16:00:21 +0200 +Subject: [PATCH] Drop QForeachContainer include and fix Qt 5.9 build + +It's not used anywhere. + +Acked by boud. +--- + libs/ui/KisResourceBundleManifest.cpp | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/libs/ui/KisResourceBundleManifest.cpp b/libs/ui/KisResourceBundleManifest.cpp +index e4f94ca788..83761657d8 100644 +--- a/libs/ui/KisResourceBundleManifest.cpp ++++ b/libs/ui/KisResourceBundleManifest.cpp +@@ -22,7 +22,6 @@ + #include <QDomElement> + #include <QDomNode> + #include <QDomNodeList> +-#include <QForeachContainer> + + #include <KoXmlNS.h> + #include <KoXmlReader.h> diff --git a/kde/patch/ksudoku.patch b/kde/patch/ksudoku.patch new file mode 100644 index 0000000..d379585 --- /dev/null +++ b/kde/patch/ksudoku.patch @@ -0,0 +1,4 @@ +# Qt5 apps accept qwindowtitle, not caption. KDEBUG 381087, +# however ksudoku-17.04.x is still kdelibs4 based: +#cat $CWD/patch/ksudoku/ksudoku_qwindowtitle.patch | patch -p1 --verbose || { touch ${SLACK_KDE_BUILD_DIR}/${PKGNAME}.failed ; continue ; } + diff --git a/kde/patch/ksudoku/ksudoku_qwindowtitle.patch b/kde/patch/ksudoku/ksudoku_qwindowtitle.patch new file mode 100644 index 0000000..3417b34 --- /dev/null +++ b/kde/patch/ksudoku/ksudoku_qwindowtitle.patch @@ -0,0 +1,15 @@ +Qt5 .desktop files for apps accept qwindowtitle, not caption. +However, the 17.04 branch of ksudoku is still kdelibs4 based, and the +patch applied to KDE BUG 381087 broke the launch of ksudoku here: + +--- a/src/gui/org.kde.ksudoku.desktop 2017-06-13 02:44:11.000000000 +0200 ++++ b/src/gui/org.kde.ksudoku.desktop 2017-07-15 11:58:32.027477614 +0200 +@@ -50,7 +50,7 @@ + Name[x-test]=xxKSudokuxx + Name[zh_CN]=KSudoku + Name[zh_TW]=KSudoku +-Exec=ksudoku %i -qwindowtitle %c ++Exec=ksudoku %i -caption %c + Icon=ksudoku + Type=Application + X-DocPath=ksudoku/index.html diff --git a/kde/patch/ktexteditor.patch b/kde/patch/ktexteditor.patch new file mode 100644 index 0000000..a4f0aad --- /dev/null +++ b/kde/patch/ktexteditor.patch @@ -0,0 +1,3 @@ +# Fix indentation for some languages (e.g. Python) in Kate and KDevelop: +#cat $CWD/patch/ktexteditor/ktexteditor_fix_indentation.patch | patch -p1 --verbose || { touch ${SLACK_KDE_BUILD_DIR}/${PKGNAME}.failed ; continue ; } + diff --git a/kde/patch/ktexteditor/ktexteditor_fix_indentation.patch b/kde/patch/ktexteditor/ktexteditor_fix_indentation.patch new file mode 100644 index 0000000..fc5d9d9 --- /dev/null +++ b/kde/patch/ktexteditor/ktexteditor_fix_indentation.patch @@ -0,0 +1,32 @@ +From aeebeadb5f5955995c17de56cf83ba7166a132dd Mon Sep 17 00:00:00 2001 +From: Sven Brauch <mail@svenbrauch.de> +Date: Mon, 16 Oct 2017 18:35:50 +0200 +Subject: fix some indenters from indenting on random characters + +If triggerCharacters was not set, toString() would return "undefined", +making indenters trigger on u, n, d, e, f, i and n. + +Differential Revision: https://phabricator.kde.org/D8333 +--- + src/script/kateindentscript.cpp | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/src/script/kateindentscript.cpp b/src/script/kateindentscript.cpp +index 15ce387..380bd45 100644 +--- a/src/script/kateindentscript.cpp ++++ b/src/script/kateindentscript.cpp +@@ -46,7 +46,10 @@ const QString &KateIndentScript::triggerCharacters() + + m_triggerCharactersSet = true; + +- m_triggerCharacters = global(QStringLiteral("triggerCharacters")).toString(); ++ auto triggerCharacters = global(QStringLiteral("triggerCharacters")); ++ if ( !triggerCharacters.isUndefined() ) { ++ m_triggerCharacters = triggerCharacters.toString(); ++ } + + //qCDebug(LOG_KTE) << "trigger chars: '" << m_triggerCharacters << "'"; + +-- +cgit v0.11.2 + diff --git a/kde/patch/kwin.patch b/kde/patch/kwin.patch new file mode 100644 index 0000000..9f05f30 --- /dev/null +++ b/kde/patch/kwin.patch @@ -0,0 +1,11 @@ +# Trivial patch for testing the CK2 session controller interface +# as a replacement for systemd-logind; +#cat $CWD/patch/kwin/kwin_replace_logind_with_ck2.patch | patch -p1 --verbose || { touch ${SLACK_KDE_BUILD_DIR}/${PKGNAME}.failed ; continue ; } + +# Workaround Qt regression no longer delivering events for the root window. +# Fixed in kwin 5.10.3. +#cat $CWD/patch/kwin/kwin_qt59_rootwindow_events.patch | patch -p1 --verbose || { touch ${SLACK_KDE_BUILD_DIR}/${PKGNAME}.failed ; continue ; } + +# Fix compilation with cmake 3.10 (fixed in 5.12.0): +#cat $CWD/patch/kwin/kwin_cmake310.patch | patch -p1 --verbose || { touch ${SLACK_KDE_BUILD_DIR}/${PKGNAME}.failed ; continue ; } + diff --git a/kde/patch/kwin/kwin_cmake310.patch b/kde/patch/kwin/kwin_cmake310.patch new file mode 100644 index 0000000..5675ba5 --- /dev/null +++ b/kde/patch/kwin/kwin_cmake310.patch @@ -0,0 +1,52 @@ +Taken from: +https://gitweb.gentoo.org/repo/gentoo.git/tree/kde-plasma/kwin/files/kwin-5.11.5-cmake-3.10.patch + +From cd544890ced4192d07467c89e23adbb62d8cea5c Mon Sep 17 00:00:00 2001 +From: Milian Wolff <mail@milianw.de> +Date: Mon, 18 Dec 2017 11:40:35 +0100 +Subject: Fix build with CMake 3.10 + +Looks like a classic false-positive, but this makes the compile +pass for me without making the code harder to read: + +AutoMoc error +------------- + "/ssd/milian/projects/kf5/src/kde/workspace/kwin/kcmkwin/kwinscripts/main.cpp" +The file contains a K_PLUGIN_FACTORY macro, but does not include "main.moc"! +Consider to + - add #include "main.moc" + - enable SKIP_AUTOMOC for this file + +So we just add the include and then get rid of the duplicate +definition of the plugin factory and the problem is resolved. +--- + kcmkwin/kwinscripts/main.cpp | 2 ++ + kcmkwin/kwinscripts/module.cpp | 2 -- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/kcmkwin/kwinscripts/main.cpp b/kcmkwin/kwinscripts/main.cpp +index f5ee04b..baa5175 100644 +--- a/kcmkwin/kwinscripts/main.cpp ++++ b/kcmkwin/kwinscripts/main.cpp +@@ -22,3 +22,5 @@ + + K_PLUGIN_FACTORY(KcmKWinScriptsFactory, + registerPlugin<Module>("kwin-scripts");) ++ ++#include "main.moc" +diff --git a/kcmkwin/kwinscripts/module.cpp b/kcmkwin/kwinscripts/module.cpp +index a0d698e..ccf7d41 100644 +--- a/kcmkwin/kwinscripts/module.cpp ++++ b/kcmkwin/kwinscripts/module.cpp +@@ -40,8 +40,6 @@ + + #include "version.h" + +-K_PLUGIN_FACTORY_DECLARATION(KcmKWinScriptsFactory) +- + Module::Module(QWidget *parent, const QVariantList &args) : + KCModule(parent, args), + ui(new Ui::Module), +-- +cgit v0.11.2 + diff --git a/kde/patch/kwin/kwin_qt59_rootwindow_events.patch b/kde/patch/kwin/kwin_qt59_rootwindow_events.patch new file mode 100644 index 0000000..178d081 --- /dev/null +++ b/kde/patch/kwin/kwin_qt59_rootwindow_events.patch @@ -0,0 +1,63 @@ +From a6dee74ee455d1da47dd5c9d55a84adbb5e1426a Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Martin=20Fl=C3=B6ser?= <mgraesslin@kde.org> +Date: Sun, 18 Jun 2017 14:23:33 +0200 +Subject: Workaround Qt regression of no longer delivering events for the root + window + +Summary: +With qtbase 2b34aefcf02f09253473b096eb4faffd3e62b5f4 we do no longer get +events reported for the X11 root window. Our keyboard handling in effects +like PresentWindows and DesktopGrid relied on that. + +This change works around the regression by calling winId() on +qApp->desktop() as suggested in the change. This is a short term solution +for the 5.10 branch. + +This needs to be addressed properly by no longer relying on Qt in this +area. KWin already does not rely on Qt for Wayland in that area and is +able to compose the QKeyEvents. This should also be done on X11. It just +needs some more hook up code for xkb, but that's needed anyway to improve +modifier only shortcuts and friends. + +BUG: 360841 +FIXED-IN: 5.10.3 + +Reviewers: #kwin, #plasma + +Subscribers: plasma-devel, kwin + +Tags: #kwin + +Differential Revision: https://phabricator.kde.org/D6258 +--- + effects.cpp | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/effects.cpp b/effects.cpp +index d2c4768..8155de6 100644 +--- a/effects.cpp ++++ b/effects.cpp +@@ -48,6 +48,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. + #include "kwinglutils.h" + + #include <QDebug> ++#include <QDesktopWidget> + + #include <Plasma/Theme> + +@@ -599,6 +600,11 @@ bool EffectsHandlerImpl::grabKeyboard(Effect* effect) + bool ret = grabXKeyboard(); + if (!ret) + return false; ++ // Workaround for Qt 5.9 regression introduced with 2b34aefcf02f09253473b096eb4faffd3e62b5f4 ++ // we no longer get any events for the root window, one needs to call winId() on the desktop window ++ // TODO: change effects event handling to create the appropriate QKeyEvent without relying on Qt ++ // as it's done already in the Wayland case. ++ qApp->desktop()->winId(); + } + keyboard_grab_effect = effect; + return true; +-- +cgit v0.11.2 + + diff --git a/kde/patch/kwin/kwin_replace_logind_with_ck2.patch b/kde/patch/kwin/kwin_replace_logind_with_ck2.patch new file mode 100644 index 0000000..294a691 --- /dev/null +++ b/kde/patch/kwin/kwin_replace_logind_with_ck2.patch @@ -0,0 +1,85 @@ +From: Eric Koegel <eric.koegel@gmail.com> +Date: Sun, 24 Jul 2016 14:37:26 +0300 + +Trivial patch for testing the CK2 session controller interface +as a replacement for systemd-logind; +Eric Koegel's original patch was rebased to kwin-5.10.2 by Eric Hameleers + +diff -uar kwin-5.10.2.orig/logind.cpp kwin-5.10.2/logind.cpp +--- kwin-5.10.2.orig/logind.cpp 2017-06-13 20:19:37.000000000 +0200 ++++ kwin-5.10.2/logind.cpp 2017-06-18 20:49:56.018661568 +0200 +@@ -58,10 +58,10 @@ + namespace KWin + { + +-const static QString s_login1Service = QStringLiteral("org.freedesktop.login1"); +-const static QString s_login1Path = QStringLiteral("/org/freedesktop/login1"); +-const static QString s_login1ManagerInterface = QStringLiteral("org.freedesktop.login1.Manager"); +-const static QString s_login1SessionInterface = QStringLiteral("org.freedesktop.login1.Session"); ++const static QString s_login1Service = QStringLiteral("org.freedesktop.ConsoleKit"); ++const static QString s_login1Path = QStringLiteral("/org/freedesktop/ConsoleKit/Manager"); ++const static QString s_login1ManagerInterface = QStringLiteral("org.freedesktop.ConsoleKit.Manager"); ++const static QString s_login1SessionInterface = QStringLiteral("org.freedesktop.ConsoleKit.Session"); + const static QString s_dbusPropertiesInterface = QStringLiteral("org.freedesktop.DBus.Properties"); + + LogindIntegration *LogindIntegration::s_self = nullptr; +@@ -151,7 +151,7 @@ + return; + } + if (!reply.isValid()) { +- qCDebug(KWIN_CORE) << "The session is not registered with logind" << reply.error().message(); ++ qCDebug(KWIN_CORE) << "The session is not registered with ConsoleKit2" << reply.error().message(); + return; + } + m_sessionPath = reply.value().path(); +@@ -199,7 +199,7 @@ + m_sessionPath, + s_dbusPropertiesInterface, + QStringLiteral("Get")); +- message.setArguments(QVariantList({s_login1SessionInterface, QStringLiteral("Active")})); ++ message.setArguments(QVariantList({s_login1SessionInterface, QStringLiteral("active")})); + QDBusPendingReply<QVariant> reply = m_bus.asyncCall(message); + QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(reply, this); + connect(watcher, &QDBusPendingCallWatcher::finished, this, +@@ -207,7 +207,7 @@ + QDBusPendingReply<QVariant> reply = *self; + self->deleteLater(); + if (!reply.isValid()) { +- qCDebug(KWIN_CORE) << "Failed to get Active Property of logind session:" << reply.error().message(); ++ qCDebug(KWIN_CORE) << "Failed to get Active Property of ConsoleKit2 session:" << reply.error().message(); + return; + } + const bool active = reply.value().toBool(); +@@ -236,7 +236,7 @@ + QDBusPendingReply<QVariant> reply = *self; + self->deleteLater(); + if (!reply.isValid()) { +- qCDebug(KWIN_CORE) << "Failed to get VTNr Property of logind session:" << reply.error().message(); ++ qCDebug(KWIN_CORE) << "Failed to get VTNr Property of ConsoleKit2 session:" << reply.error().message(); + return; + } + const int vt = reply.value().toUInt(); +@@ -365,12 +365,12 @@ + QDBusPendingReply<QVariant> reply = *self; + self->deleteLater(); + if (!reply.isValid()) { +- qCDebug(KWIN_CORE) << "Failed to get Seat Property of logind session:" << reply.error().message(); ++ qCDebug(KWIN_CORE) << "Failed to get Seat Property of ConsoleKit2 session:" << reply.error().message(); + return; + } + DBusLogindSeat seat = qdbus_cast<DBusLogindSeat>(reply.value().value<QDBusArgument>()); + const QString seatPath = seat.path.path(); +- qCDebug(KWIN_CORE) << "Logind seat:" << seat.name << "/" << seatPath; ++ qCDebug(KWIN_CORE) << "ConsoleKit2 seat:" << seat.name << "/" << seatPath; + if (m_seatPath != seatPath) { + m_seatPath = seatPath; + } +@@ -385,7 +385,7 @@ + } + QDBusMessage message = QDBusMessage::createMethodCall(s_login1Service, + m_seatPath, +- QStringLiteral("org.freedesktop.login1.Seat"), ++ QStringLiteral("org.freedesktop.ConsoleKit.Seat"), + QStringLiteral("SwitchTo")); + message.setArguments(QVariantList{vtNr}); + m_bus.asyncCall(message); diff --git a/kde/patch/libkface.patch b/kde/patch/libkface.patch new file mode 100644 index 0000000..9775e82 --- /dev/null +++ b/kde/patch/libkface.patch @@ -0,0 +1,3 @@ +# Compile libkface against opencv 3.2: +cat $CWD/patch/libkface/libkface_opencv3.patch | patch -p1 --verbose || { touch ${SLACK_KDE_BUILD_DIR}/${PKGNAME}.failed ; continue ; } + diff --git a/kde/patch/libkface/libkface_opencv3.patch b/kde/patch/libkface/libkface_opencv3.patch new file mode 100644 index 0000000..a6804d3 --- /dev/null +++ b/kde/patch/libkface/libkface_opencv3.patch @@ -0,0 +1,61 @@ +Taken from Gentoo: +https://gitweb.gentoo.org/repo/gentoo.git/plain/kde-apps/libkface/files/libkface-16.11.80-opencv3.2-gentoo-3.1.patch + +--- a/src/recognition-opencv-lbph/facerec_borrowed.h 2016-11-26 14:19:01.492645170 +0100 ++++ b/src/recognition-opencv-lbph/facerec_borrowed.h.new 2016-11-26 14:19:17.655835794 +0100 +@@ -141,7 +141,7 @@ + /* + * Predict + */ +- void predict(cv::InputArray src, cv::Ptr<cv::face::PredictCollector> collector, const int state = 0) const override; ++ void predict(cv::InputArray src, cv::Ptr<cv::face::PredictCollector> collector) const override; + #endif + + /** +--- a/src/recognition-opencv-lbph/facerec_borrowed.cpp 2016-11-26 14:19:01.492645170 +0100 ++++ b/src/recognition-opencv-lbph/facerec_borrowed.cpp.new 2016-11-26 14:19:29.184971765 +0100 +@@ -380,7 +380,7 @@ + #if OPENCV_TEST_VERSION(3,1,0) + void LBPHFaceRecognizer::predict(InputArray _src, int &minClass, double &minDist) const + #else +-void LBPHFaceRecognizer::predict(cv::InputArray _src, cv::Ptr<cv::face::PredictCollector> collector, const int state) const ++void LBPHFaceRecognizer::predict(cv::InputArray _src, cv::Ptr<cv::face::PredictCollector> collector) const + #endif + { + if(m_histograms.empty()) +@@ -404,7 +404,7 @@ + minDist = DBL_MAX; + minClass = -1; + #else +- collector->init((int)m_histograms.size(), state); ++ collector->init((int)m_histograms.size()); + #endif + + // This is the standard method +@@ -424,7 +424,7 @@ + } + #else + int label = m_labels.at<int>((int) sampleIdx); +- if (!collector->emit(label, dist, state)) ++ if (!collector->collect(label, dist)) + { + return; + } +@@ -470,7 +470,7 @@ + minClass = it->first; + } + #else +- if (!collector->emit(it->first, mean, state)) ++ if (!collector->collect(it->first, mean)) + { + return; + } +@@ -523,7 +523,7 @@ + } + #else + // large is better thus it is -score. +- if (!collector->emit(it->first, -score, state)) ++ if (!collector->collect(it->first, -score)) + { + return; + } diff --git a/kde/patch/libkleo.patch b/kde/patch/libkleo.patch new file mode 100644 index 0000000..9c1cef3 --- /dev/null +++ b/kde/patch/libkleo.patch @@ -0,0 +1,4 @@ +# Compile libkleo with gcc7: +# Fixed in Applications 17.04.2: +#cat $CWD/patch/libkleo/libkleo_gcc7.patch | patch -p1 --verbose || { touch ${SLACK_KDE_BUILD_DIR}/${PKGNAME}.failed ; continue ; } + diff --git a/kde/patch/libkleo/libkleo_gcc7.patch b/kde/patch/libkleo/libkleo_gcc7.patch new file mode 100644 index 0000000..40e0616 --- /dev/null +++ b/kde/patch/libkleo/libkleo_gcc7.patch @@ -0,0 +1,27 @@ +From 675ce908a33d16f3b78d3fc741b0ff45790e4770 Mon Sep 17 00:00:00 2001 +From: Fabian Vogt <fabian@ritter-vogt.de> +Date: Wed, 17 May 2017 17:05:41 +0200 +Subject: Fix compilation with GCC 7 + +std::bind is part of functional, and GCC 7 requires an explicit include. +--- + src/kleo/enum.cpp | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/src/kleo/enum.cpp b/src/kleo/enum.cpp +index 15ea1fd..829d4fd 100644 +--- a/src/kleo/enum.cpp ++++ b/src/kleo/enum.cpp +@@ -33,6 +33,9 @@ + #include "enum.h" + #include "libkleo_debug.h" + #include "models/keycache.h" ++ ++#include <functional> ++ + #include <KLocalizedString> + + #include <gpgme++/key.h> +-- +cgit v0.11.2 + diff --git a/kde/patch/oxygen-gtk2.patch b/kde/patch/oxygen-gtk2.patch new file mode 100644 index 0000000..4ca11e9 --- /dev/null +++ b/kde/patch/oxygen-gtk2.patch @@ -0,0 +1,3 @@ +# Add more firefox-derived browsers as supported by the theme: +cat $CWD/patch/oxygen-gtk2/oxygen-gtk2_KDEBUG_341181.patch | patch -p1 --verbose || { touch ${SLACK_KDE_BUILD_DIR}/${PKGNAME}.failed ; continue ; } + diff --git a/kde/patch/oxygen-gtk2/oxygen-gtk2_KDEBUG_341181.patch b/kde/patch/oxygen-gtk2/oxygen-gtk2_KDEBUG_341181.patch new file mode 100644 index 0000000..b6a1e55 --- /dev/null +++ b/kde/patch/oxygen-gtk2/oxygen-gtk2_KDEBUG_341181.patch @@ -0,0 +1,115 @@ +From b1ee5fb80c44c6c8a625333af1cfdc997d408805 Mon Sep 17 00:00:00 2001 +From: Hugo Pereira Da Costa <hugo.pereira@free.fr> +Date: Sat, 18 Jul 2015 20:09:28 +0200 +Subject: moved xul application names to dedicated header file, added a number + of xul applications to prevent crash for these CCBUG: 341181 + +--- + src/oxygenapplicationname.cpp | 18 ++----------- + src/oxygenxulapplicationnames.h | 56 +++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 58 insertions(+), 16 deletions(-) + create mode 100644 src/oxygenxulapplicationnames.h + +diff --git a/src/oxygenapplicationname.cpp b/src/oxygenapplicationname.cpp +index feb5a23..17c8a1a 100644 +--- a/src/oxygenapplicationname.cpp ++++ b/src/oxygenapplicationname.cpp +@@ -25,6 +25,7 @@ + + #include "oxygenapplicationname.h" + #include "oxygengtkutils.h" ++#include "oxygenxulapplicationnames.h" + #include "config.h" + + #include <cstdlib> +@@ -79,23 +80,8 @@ namespace Oxygen + gtkAppName == "chromium" || + gtkAppName == "chromium-browser" || + gtkAppName == "google-chrome" ) _name = GoogleChrome; +- else { + +- // tag all mozilla-like applications (XUL) +- static const std::string XulAppNames[] = +- { +- "firefox", +- "thunderbird", +- "seamonkey", +- "iceweasel", +- "icecat", +- "icedove", +- "xulrunner", +- "komodo", +- "aurora", +- "zotero", +- "" +- }; ++ else { + + for( unsigned int index = 0; !XulAppNames[index].empty(); ++index ) + { +diff --git a/src/oxygenxulapplicationnames.h b/src/oxygenxulapplicationnames.h +new file mode 100644 +index 0000000..252a1fc +--- /dev/null ++++ b/src/oxygenxulapplicationnames.h +@@ -0,0 +1,56 @@ ++#ifndef oxygenxulapplicationname_h ++#define oxygenxulapplicationname_h ++/* ++* this file is part of the oxygen gtk engine ++* Copyright (c) 2010 Hugo Pereira Da Costa <hugo.pereira@free.fr> ++* ++* inspired notably from kdelibs/kdeui/color/kcolorutils.h ++* Copyright (C) 2007 Matthew Woehlke <mw_triad@users.sourceforge.net> ++* Copyright (C) 2007 Thomas Zander <zander@kde.org> ++* Copyright (C) 2007 Zack Rusin <zack@kde.org> ++* ++* This library is free software; you can redistribute it and/or ++* modify it under the terms of the GNU Lesser General Public ++* License as published by the Free Software Foundation; either ++* version 2 of the License, or( at your option ) any later version. ++* ++* This library is distributed in the hope that it will be useful, ++* but WITHOUT ANY WARRANTY; without even the implied warranty of ++* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++* Lesser General Public License for more details. ++* ++* You should have received a copy of the GNU Lesser General Public ++* License along with this library; if not, write to the Free ++* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, ++* MA 02110-1301, USA. ++*/ ++ ++#include <string> ++ ++namespace Oxygen ++{ ++ ++ // tag all mozilla-like applications (XUL) ++ static const std::string XulAppNames[] = ++ { ++ "aurora", ++ "earlybird", ++ "icecat", ++ "icedove", ++ "iceweasel", ++ "instantbird", ++ "firefox", ++ "fossamail", ++ "komodo", ++ "newmoon", ++ "palemoon", ++ "seamonkey", ++ "thunderbird", ++ "xulrunner", ++ "zotero", ++ "" ++ }; ++ ++} ++ ++#endif +-- +cgit v0.11.2 + + diff --git a/kde/patch/perlqt.patch b/kde/patch/perlqt.patch new file mode 100644 index 0000000..1cdd7db --- /dev/null +++ b/kde/patch/perlqt.patch @@ -0,0 +1,3 @@ +# Fix build: +cat $CWD/patch/perlqt/perlqt.gcc6.diff | patch -p1 --verbose || { touch ${SLACK_KDE_BUILD_DIR}/${PKGNAME}.failed ; continue ; } + diff --git a/kde/patch/perlqt/perlqt.gcc6.diff b/kde/patch/perlqt/perlqt.gcc6.diff new file mode 100644 index 0000000..9a6e42e --- /dev/null +++ b/kde/patch/perlqt/perlqt.gcc6.diff @@ -0,0 +1,11 @@ +--- ./qtcore/src/util.cpp.orig 2014-11-04 16:59:39.000000000 -0600 ++++ ./qtcore/src/util.cpp 2017-10-04 22:25:36.055839800 -0500 +@@ -2251,7 +2251,7 @@ + methcache.insert(mcid, new Smoke::ModuleIndex(mi)); + } + +- static smokeperl_object nothis = { 0, 0, 0, false }; ++ static smokeperl_object nothis = { 0, 0, 0, NULL }; + smokeperl_object* call_this = 0; + if ( SvOK(sv_this) ) { + call_this = sv_obj_info( sv_this ); diff --git a/kde/patch/plasma-workspace.patch b/kde/patch/plasma-workspace.patch index c1e56fc..8671c65 100644 --- a/kde/patch/plasma-workspace.patch +++ b/kde/patch/plasma-workspace.patch @@ -4,5 +4,14 @@ #cat $CWD/patch/plasma-workspace/plasma-workspace_consolekit2.patch | patch -p1 --verbose || { touch ${SLACK_KDE_BUILD_DIR}/${PKGNAME}.failed ; continue ; } # Apply commit that fixes compilation of 5.6.5: -#cat $CWD/patch/plasma-workspace/plasma-workspace_apply_767aa57.patc | patch -p1 --verbose || { touch ${SLACK_KDE_BUILD_DIR}/${PKGNAME}.failed ; continue ; } +#cat $CWD/patch/plasma-workspace/plasma-workspace_apply_767aa57.patch | patch -p1 --verbose || { touch ${SLACK_KDE_BUILD_DIR}/${PKGNAME}.failed ; continue ; } + +# Systray: Move all icon resolution to dataengine, preventing high CPU usage, +# see https://phabricator.kde.org/D2986 : +# Fixed in 5.9.0 +#cat $CWD/patch/plasma-workspace/plasma-workspace.systray_cpubug.patch | patch -p1 --verbose || { touch ${SLACK_KDE_BUILD_DIR}/${PKGNAME}.failed ; continue ; } + +# Fix vulnerability (CVE-2018-6791 - KDEBUG_389815) +# (already fixed in Plasma 5.12.0): +#cat $CWD/patch/plasma-workspace/plasma-workspace_kdebug389815.patch | patch -p1 --verbose || { touch ${SLACK_KDE_BUILD_DIR}/${PKGNAME}.failed ; continue ; } diff --git a/kde/patch/plasma-workspace/plasma-workspace.systray_cpubug.patch b/kde/patch/plasma-workspace/plasma-workspace.systray_cpubug.patch new file mode 100644 index 0000000..4ad3c07 --- /dev/null +++ b/kde/patch/plasma-workspace/plasma-workspace.systray_cpubug.patch @@ -0,0 +1,152 @@ +https://phabricator.kde.org/D2986 +Systray: Move all icon resolution to dataengine + +diff --git a/applets/systemtray/package/contents/ui/ConfigEntries.qml b/applets/systemtray/package/contents/ui/ConfigEntries.qml +--- a/applets/systemtray/package/contents/ui/ConfigEntries.qml ++++ b/applets/systemtray/package/contents/ui/ConfigEntries.qml +@@ -75,7 +75,7 @@ + "index": i, + "taskId": item.Id, + "name": item.Title, +- "iconName": plasmoid.nativeInterface.resolveIcon(item.IconName, item.IconThemePath), ++ "iconName": item.IconName, + "icon": item.Icon + }); + } +diff --git a/applets/systemtray/package/contents/ui/items/StatusNotifierItem.qml b/applets/systemtray/package/contents/ui/items/StatusNotifierItem.qml +--- a/applets/systemtray/package/contents/ui/items/StatusNotifierItem.qml ++++ b/applets/systemtray/package/contents/ui/items/StatusNotifierItem.qml +@@ -28,7 +28,7 @@ + text: Title + mainText: ToolTipTitle != "" ? ToolTipTitle : Title + subText: ToolTipSubTitle +- icon: ToolTipIcon != "" ? ToolTipIcon : plasmoid.nativeInterface.resolveIcon(IconName != "" ? IconName : Icon, IconThemePath) ++ icon: ToolTipIcon != "" ? ToolTipIcon : Icon ? Icon : IconName + textFormat: Text.AutoText + category: Category + +@@ -48,7 +48,7 @@ + + PlasmaCore.IconItem { + id: iconItem +- source: plasmoid.nativeInterface.resolveIcon(IconName != "" ? IconName : Icon, IconThemePath) ++ source: Icon ? Icon : IconName + width: Math.min(parent.width, parent.height) + height: width + active: taskIcon.containsMouse +diff --git a/applets/systemtray/systemtray.h b/applets/systemtray/systemtray.h +--- a/applets/systemtray/systemtray.h ++++ b/applets/systemtray/systemtray.h +@@ -60,12 +60,6 @@ + + //Invokable utilities + /** +- * returns either a simple icon name or a custom path if the app is +- * using a custom theme +- */ +- Q_INVOKABLE QVariant resolveIcon(const QVariant &variant, const QString &iconThemePath); +- +- /** + * Given an AppletInterface pointer, shows a proper context menu for it + */ + Q_INVOKABLE void showPlasmoidMenu(QQuickItem *appletInterface, int x, int y); +diff --git a/applets/systemtray/systemtray.cpp b/applets/systemtray/systemtray.cpp +--- a/applets/systemtray/systemtray.cpp ++++ b/applets/systemtray/systemtray.cpp +@@ -37,37 +37,11 @@ + #include <Plasma/PluginLoader> + #include <Plasma/ServiceJob> + +-#include <KIconLoader> +-#include <KIconEngine> + #include <KActionCollection> + #include <KLocalizedString> + + #include <plasma_version.h> + +-/* +- * An app may also load icons from their own directories, so we need a new iconloader that takes this into account +- * This is wrapped into a subclass of iconengine so the iconloader lifespan matches the icon object +- */ +-class AppIconEngine : public KIconEngine +-{ +-public: +- AppIconEngine(const QString &variant, const QString &path, const QString &appName); +- ~AppIconEngine(); +-private: +- KIconLoader* m_loader; +-}; +- +-AppIconEngine::AppIconEngine(const QString &variant, const QString &path, const QString &appName) : +- KIconEngine(variant, m_loader = new KIconLoader(appName, QStringList())) +-{ +- m_loader->addAppDir(appName, path); +-} +- +-AppIconEngine::~AppIconEngine() +-{ +- delete m_loader; +-} +- + class PlasmoidModel: public QStandardItemModel + { + public: +@@ -169,32 +143,6 @@ + } + } + +-QVariant SystemTray::resolveIcon(const QVariant &variant, const QString &iconThemePath) +-{ +- if (variant.canConvert<QString>()) { +- if (!iconThemePath.isEmpty()) { +- const QString path = iconThemePath; +- if (!path.isEmpty()) { +- // FIXME: If last part of path is not "icons", this won't work! +- auto tokens = path.splitRef('/', QString::SkipEmptyParts); +- if (tokens.length() >= 3 && tokens.takeLast() == QLatin1String("icons")) { +- const QString appName = tokens.takeLast().toString(); +- +- return QVariant(QIcon(new AppIconEngine(variant.toString(), path, appName))); +- } else { +- qCWarning(SYSTEM_TRAY) << "Wrong IconThemePath" << path << ": too short or does not end with 'icons'"; +- } +- } +- +- //return just the string hoping that IconItem will know how to interpret it anyways as either a normal icon or a SVG from the theme +- return variant; +- } +- } +- +- // Most importantly QIcons. Nothing to do for those. +- return variant; +-} +- + void SystemTray::showPlasmoidMenu(QQuickItem *appletInterface, int x, int y) + { + if (!appletInterface) { +diff --git a/dataengines/statusnotifieritem/statusnotifieritemsource.cpp b/dataengines/statusnotifieritem/statusnotifieritemsource.cpp +--- a/dataengines/statusnotifieritem/statusnotifieritemsource.cpp ++++ b/dataengines/statusnotifieritem/statusnotifieritemsource.cpp +@@ -240,14 +240,19 @@ + if (!m_customIconLoader) { + m_customIconLoader = new KIconLoader(QString(), QStringList(), this); + } ++ // FIXME: If last part of path is not "icons", this won't work! ++ QString appName; ++ auto tokens = path.splitRef('/', QString::SkipEmptyParts); ++ if (tokens.length() >= 3 && tokens.takeLast() == QLatin1String("icons")) ++ appName = tokens.takeLast().toString(); + + //icons may be either in the root directory of the passed path or in a appdir format + //i.e hicolor/32x32/iconname.png + +- m_customIconLoader->reconfigure(QString(), QStringList(path)); ++ m_customIconLoader->reconfigure(appName, QStringList(path)); + + //add app dir requires an app name, though this is completely unused in this context +- m_customIconLoader->addAppDir(QStringLiteral("unused"), path); ++ m_customIconLoader->addAppDir(appName.size() ? appName : QStringLiteral("unused"), path); + } + setData(QStringLiteral("IconThemePath"), path); + + diff --git a/kde/patch/plasma-workspace/plasma-workspace_kdebug389815.patch b/kde/patch/plasma-workspace/plasma-workspace_kdebug389815.patch new file mode 100644 index 0000000..e2f1e48 --- /dev/null +++ b/kde/patch/plasma-workspace/plasma-workspace_kdebug389815.patch @@ -0,0 +1,32 @@ +From f32002ce50edc3891f1fa41173132c820b917d57 Mon Sep 17 00:00:00 2001 +From: Marco Martin <notmart@gmail.com> +Date: Mon, 5 Feb 2018 13:12:51 +0100 +Subject: Make sure device paths are quoted + +in the case a vfat removable device has $() or `` in its label, +such as $(touch foo) the quoted command may get executed, +leaving an attack vector. Use KMacroExpander::expandMacrosShellQuote +to make sure everything is quoted and not interpreted as a command + +BUG:389815 +--- + soliduiserver/deviceserviceaction.cpp | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/soliduiserver/deviceserviceaction.cpp b/soliduiserver/deviceserviceaction.cpp +index f49c967..738b27c 100644 +--- a/soliduiserver/deviceserviceaction.cpp ++++ b/soliduiserver/deviceserviceaction.cpp +@@ -158,7 +158,7 @@ void DelayedExecutor::delayedExecute(const QString &udi) + + QString exec = m_service.exec(); + MacroExpander mx(device); +- mx.expandMacros(exec); ++ mx.expandMacrosShellQuote(exec); + + KRun::runCommand(exec, QString(), m_service.icon(), 0); + deleteLater(); +-- +cgit v0.11.2 + + diff --git a/kde/patch/powerdevil.patch b/kde/patch/powerdevil.patch new file mode 100644 index 0000000..b7e2bae --- /dev/null +++ b/kde/patch/powerdevil.patch @@ -0,0 +1,4 @@ +# PowerDevil fails to setup power settings on a fresh install. +# Fixed in 5.12.5. +#cat $CWD/patch/powerdevil/powerdevil-5.12.4_firstrun.patch | patch -p1 --verbose || { touch ${SLACK_KDE_BUILD_DIR}/${PKGNAME}.failed ; continue ; } + diff --git a/kde/patch/powerdevil/powerdevil-5.12.4_firstrun.patch b/kde/patch/powerdevil/powerdevil-5.12.4_firstrun.patch new file mode 100644 index 0000000..165e67b --- /dev/null +++ b/kde/patch/powerdevil/powerdevil-5.12.4_firstrun.patch @@ -0,0 +1,42 @@ +From be91abe7fc8cc731b57bec4cf2c004c07b0fd79b Mon Sep 17 00:00:00 2001 +From: Kai Uwe Broulik <kde@privat.broulik.de> +Date: Wed, 25 Apr 2018 10:56:16 +0200 +Subject: Ignore "migration" key for determining whether the config is empty + +Otherwise we would never generate the default power management configuration +leading to it not suspending on lid close, not handling power buttons etc etc + +CHANGELOG: Fixed bug that caused power management system to not work on a fresh install + +Reviewed-By: David Edmundson + +BUG: 391782 +FIXED-IN: 5.12.5 +--- + daemon/powerdevilcore.cpp | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +diff --git a/daemon/powerdevilcore.cpp b/daemon/powerdevilcore.cpp +index 2cf936e..53b7521 100644 +--- a/daemon/powerdevilcore.cpp ++++ b/daemon/powerdevilcore.cpp +@@ -108,9 +108,15 @@ void Core::onBackendReady() + + m_profilesConfig = KSharedConfig::openConfig("powermanagementprofilesrc", KConfig::CascadeConfig); + ++ QStringList groups = m_profilesConfig->groupList(); ++ // the "migration" key is for shortcuts migration in added by migratePre512KeyboardShortcuts ++ // and as such our configuration would never be considered empty, ignore it! ++ groups.removeOne(QStringLiteral("migration")); ++ + // Is it brand new? +- if (m_profilesConfig->groupList().isEmpty()) { ++ if (groups.isEmpty()) { + // Generate defaults ++ qCDebug(POWERDEVIL) << "Generating a default configuration"; + bool toRam = m_backend->supportedSuspendMethods() & PowerDevil::BackendInterface::ToRam; + bool toDisk = m_backend->supportedSuspendMethods() & PowerDevil::BackendInterface::ToDisk; + ProfileGenerator::generateProfiles(toRam, toDisk); +-- +cgit v0.11.2 + diff --git a/kde/patch/pykde4.patch b/kde/patch/pykde4.patch new file mode 100644 index 0000000..48073d4 --- /dev/null +++ b/kde/patch/pykde4.patch @@ -0,0 +1,5 @@ +# Fix compilation against sip-4.19: +cat $CWD/patch/pykde4/0001-use-LIB_PYTHON-realpath.patch | patch -p1 --verbose || { touch ${SLACK_KDE_BUILD_DIR}/${PKGNAME}.failed ; continue ; } +cat $CWD/patch/pykde4/0002-Add-some-missing-link-libraries.patch | patch -p1 --verbose || { touch ${SLACK_KDE_BUILD_DIR}/${PKGNAME}.failed ; continue ; } +cat $CWD/patch/pykde4/0003-Fix-build-with-sip-4.19.patch | patch -p1 --verbose || { touch ${SLACK_KDE_BUILD_DIR}/${PKGNAME}.failed ; continue ; } + diff --git a/kde/patch/pykde4/0001-use-LIB_PYTHON-realpath.patch b/kde/patch/pykde4/0001-use-LIB_PYTHON-realpath.patch new file mode 100644 index 0000000..85ad4bc --- /dev/null +++ b/kde/patch/pykde4/0001-use-LIB_PYTHON-realpath.patch @@ -0,0 +1,31 @@ +From 34bed3ceb7cd2bb43e67acce97f4cc3e8bbc1c1d Mon Sep 17 00:00:00 2001 +From: Rex Dieter <rdieter@math.unl.edu> +Date: Tue, 11 Mar 2014 09:51:17 -0500 +Subject: [PATCH 1/3] use LIB_PYTHON realpath + +Use GET_FILENAME_COMPONENT( ... REALPATH). PYTHON_LIBRARY as returned +by cmake, whose target is often a symlink. Some distro packaging +reserves such library symlinks for -devel and not runtime. + +REVIEW: 116719 +--- + kpythonpluginfactory/CMakeLists.txt | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/kpythonpluginfactory/CMakeLists.txt b/kpythonpluginfactory/CMakeLists.txt +index c24160e..a777dac 100644 +--- a/kpythonpluginfactory/CMakeLists.txt ++++ b/kpythonpluginfactory/CMakeLists.txt +@@ -3,7 +3,8 @@ + set(kpythonpluginfactory_SRCS + kpythonpluginfactory.cpp) + +-GET_FILENAME_COMPONENT(LIB_PYTHON ${PYTHON_LIBRARIES} NAME) ++GET_FILENAME_COMPONENT(PYTHON_LIBRARY_REALPATH "${PYTHON_LIBRARY}" REALPATH) ++GET_FILENAME_COMPONENT(LIB_PYTHON ${PYTHON_LIBRARY_REALPATH} NAME) + ADD_DEFINITIONS(-DLIB_PYTHON="${LIB_PYTHON}") + ADD_DEFINITIONS(-DKDE_DEFAULT_DEBUG_AREA=15000) + +-- +2.9.3 + diff --git a/kde/patch/pykde4/0002-Add-some-missing-link-libraries.patch b/kde/patch/pykde4/0002-Add-some-missing-link-libraries.patch new file mode 100644 index 0000000..00283cc --- /dev/null +++ b/kde/patch/pykde4/0002-Add-some-missing-link-libraries.patch @@ -0,0 +1,60 @@ +From b0137f694f946c7f10ac2863a71b4cdeda15eb87 Mon Sep 17 00:00:00 2001 +From: Wolfgang Bauer <wbauer@tmo.at> +Date: Wed, 14 Sep 2016 23:54:40 +0200 +Subject: [PATCH 2/3] Add some missing(?) link libraries + +This fixes the following build errors in openSUSE Factory: + +CMakeFiles/python_module_PyKDE4_dnssd.dir/sip/dnssd/sipdnssdpart2.cpp.o: +In function `meth_DNSSD_ServiceBrowser_resolveHostName': +/home/abuild/rpmbuild/BUILD/pykde4-4.14.3/build/sip/dnssd/sipdnssdpart2.cpp:408: +undefined reference to `QHostAddress::QHostAddress(QHostAddress +const&)' +/home/abuild/rpmbuild/BUILD/pykde4-4.14.3/build/sip/dnssd/sipdnssdpart2.cpp:408: +undefined reference to `QHostAddress::~QHostAddress()' +collect2: error: ld returned 1 exit status +... +CMakeFiles/python_module_PyKDE4_kio.dir/sip/kio/sipkiopart3.cpp.o: In +function `meth_KFilePlacesModel_deviceForIndex': +/home/abuild/rpmbuild/BUILD/pykde4-4.14.3/build/sip/kio/sipkiopart3.cpp:18560: +undefined reference to `Solid::Device::Device(Solid::Device const&)' +/home/abuild/rpmbuild/BUILD/pykde4-4.14.3/build/sip/kio/sipkiopart3.cpp:18560: +undefined reference to `Solid::Device::~Device()' +CMakeFiles/python_module_PyKDE4_kio.dir/sip/kio/sipkiopart4.cpp.o: In +function `meth_KDeviceListModel_deviceForIndex': +/home/abuild/rpmbuild/BUILD/pykde4-4.14.3/build/sip/kio/sipkiopart4.cpp:27090: +undefined reference to `Solid::Device::Device(Solid::Device const&)' +/home/abuild/rpmbuild/BUILD/pykde4-4.14.3/build/sip/kio/sipkiopart4.cpp:27090: +undefined reference to `Solid::Device::~Device()' +collect2: error: ld returned 1 exit status + +REVIEW: 127705 +--- + CMakeLists.txt | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index b0768cf..b919d1b 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -166,7 +166,7 @@ add_sip_python_module(PyKDE4.kdeui sip/kdeui/kdeuimod.sip ${KDE4_KDEUI_LIBS} ${Q + + file(GLOB kio_files_sip sip/kio/*.sip) + set(SIP_EXTRA_FILES_DEPEND ${kio_files_sip}) +-add_sip_python_module(PyKDE4.kio sip/kio/kiomod.sip ${KDE4_KIO_LIBS} ${KDE4_KFILE_LIBS}) ++add_sip_python_module(PyKDE4.kio sip/kio/kiomod.sip ${KDE4_KIO_LIBS} ${KDE4_KFILE_LIBS} ${KDE4_SOLID_LIBS}) + + file(GLOB kutils_files_sip sip/kutils/*.sip) + set(SIP_EXTRA_FILES_DEPEND ${kutils_files_sip}) +@@ -190,7 +190,7 @@ add_sip_python_module(PyKDE4.knewstuff sip/knewstuff/knewstuffmod.sip ${KDE4_KNE + + file(GLOB dnssd_files_sip sip/dnssd/*.sip) + set(SIP_EXTRA_FILES_DEPEND ${dnssd_files_sip}) +-add_sip_python_module(PyKDE4.dnssd sip/dnssd/dnssdmod.sip ${KDE4_KDNSSD_LIBS} ${QT_QTCORE_LIBRARY}) ++add_sip_python_module(PyKDE4.dnssd sip/dnssd/dnssdmod.sip ${KDE4_KDNSSD_LIBS} ${QT_QTCORE_LIBRARY} ${QT_QTNETWORK_LIBRARY}) + + file(GLOB phonon_files_sip sip/phonon/*.sip) + set(SIP_EXTRA_FILES_DEPEND ${phonon_files_sip}) +-- +2.9.3 + diff --git a/kde/patch/pykde4/0003-Fix-build-with-sip-4.19.patch b/kde/patch/pykde4/0003-Fix-build-with-sip-4.19.patch new file mode 100644 index 0000000..61ef78d --- /dev/null +++ b/kde/patch/pykde4/0003-Fix-build-with-sip-4.19.patch @@ -0,0 +1,599 @@ +From 2d1eadf5d0148c88cb4393993f0269e196cbe7b1 Mon Sep 17 00:00:00 2001 +From: Johannes Huber <johu@gentoo.org> +Date: Mon, 9 Jan 2017 11:52:12 +0100 +Subject: [PATCH 3/3] Fix build with sip 4.19 + +REVIEW: 129799 +--- + sip/dnssd/remoteservice.sip | 10 +++++----- + sip/kdecore/kmimetype.sip | 10 +++++----- + sip/kdecore/ksharedconfig.sip | 4 ++-- + sip/kdecore/ksycocaentry.sip | 10 +++++----- + sip/kdecore/typedefs.sip | 30 +++++++++++++++--------------- + sip/kdeui/kcompletion.sip | 10 +++++----- + sip/kdeui/kxmlguibuilder.sip | 4 ++-- + sip/kio/kservicegroup.sip | 10 +++++----- + sip/ktexteditor/markinterface.sip | 10 +++++----- + sip/phonon/objectdescription.sip | 10 +++++----- + sip/soprano/pluginmanager.sip | 30 +++++++++++++++--------------- + 11 files changed, 69 insertions(+), 69 deletions(-) + +diff --git a/sip/dnssd/remoteservice.sip b/sip/dnssd/remoteservice.sip +index 5c5397a..44db887 100644 +--- a/sip/dnssd/remoteservice.sip ++++ b/sip/dnssd/remoteservice.sip +@@ -66,7 +66,7 @@ protected: + DNSSD::RemoteService::Ptr *t = new DNSSD::RemoteService::Ptr (sipCpp->at(i)); + PyObject *tobj; + +- if ((tobj = sipConvertFromNewInstance(t->data(), sipClass_DNSSD_RemoteService, sipTransferObj)) == NULL) ++ if ((tobj = sipConvertFromNewType(t->data(), sipType_DNSSD_RemoteService, sipTransferObj)) == NULL) + { + Py_DECREF(l); + delete t; +@@ -88,7 +88,7 @@ protected: + return 0; + + for (int i = 0; i < PyList_GET_SIZE(sipPy); ++i) +- if (!sipCanConvertToInstance(PyList_GET_ITEM(sipPy, i), sipClass_DNSSD_RemoteService, SIP_NOT_NONE)) ++ if (!sipCanConvertToType(PyList_GET_ITEM(sipPy, i), sipType_DNSSD_RemoteService, SIP_NOT_NONE)) + return 0; + + return 1; +@@ -99,11 +99,11 @@ protected: + for (int i = 0; i < PyList_GET_SIZE(sipPy); ++i) + { + int state; +- DNSSD::RemoteService *t = reinterpret_cast<DNSSD::RemoteService *>(sipConvertToInstance(PyList_GET_ITEM(sipPy, i), sipClass_DNSSD_RemoteService, sipTransferObj, SIP_NOT_NONE, &state, sipIsErr)); ++ DNSSD::RemoteService *t = reinterpret_cast<DNSSD::RemoteService *>(sipConvertToType(PyList_GET_ITEM(sipPy, i), sipType_DNSSD_RemoteService, sipTransferObj, SIP_NOT_NONE, &state, sipIsErr)); + + if (*sipIsErr) + { +- sipReleaseInstance(t, sipClass_DNSSD_RemoteService, state); ++ sipReleaseType(t, sipType_DNSSD_RemoteService, state); + + delete ql; + return 0; +@@ -113,7 +113,7 @@ protected: + + ql->append(*tptr); + +- sipReleaseInstance(t, sipClass_DNSSD_RemoteService, state); ++ sipReleaseType(t, sipType_DNSSD_RemoteService, state); + } + + *sipCppPtr = ql; +diff --git a/sip/kdecore/kmimetype.sip b/sip/kdecore/kmimetype.sip +index b2d21f7..2945210 100644 +--- a/sip/kdecore/kmimetype.sip ++++ b/sip/kdecore/kmimetype.sip +@@ -100,7 +100,7 @@ public: + KMimeType::Ptr *t = new KMimeType::Ptr (sipCpp->at(i)); + PyObject *tobj; + +- if ((tobj = sipConvertFromNewInstance(t->data(), sipClass_KMimeType, sipTransferObj)) == NULL) ++ if ((tobj = sipConvertFromNewType(t->data(), sipType_KMimeType, sipTransferObj)) == NULL) + { + Py_DECREF(l); + delete t; +@@ -122,7 +122,7 @@ public: + return 0; + + for (int i = 0; i < PyList_GET_SIZE(sipPy); ++i) +- if (!sipCanConvertToInstance(PyList_GET_ITEM(sipPy, i), sipClass_KMimeType, SIP_NOT_NONE)) ++ if (!sipCanConvertToType(PyList_GET_ITEM(sipPy, i), sipType_KMimeType, SIP_NOT_NONE)) + return 0; + + return 1; +@@ -133,11 +133,11 @@ public: + for (int i = 0; i < PyList_GET_SIZE(sipPy); ++i) + { + int state; +- KMimeType *t = reinterpret_cast<KMimeType *>(sipConvertToInstance(PyList_GET_ITEM(sipPy, i), sipClass_KMimeType, sipTransferObj, SIP_NOT_NONE, &state, sipIsErr)); ++ KMimeType *t = reinterpret_cast<KMimeType *>(sipConvertToType(PyList_GET_ITEM(sipPy, i), sipType_KMimeType, sipTransferObj, SIP_NOT_NONE, &state, sipIsErr)); + + if (*sipIsErr) + { +- sipReleaseInstance(t, sipClass_KMimeType, state); ++ sipReleaseType(t, sipType_KMimeType, state); + + delete ql; + return 0; +@@ -147,7 +147,7 @@ public: + + ql->append(*tptr); + +- sipReleaseInstance(t, sipClass_KMimeType, state); ++ sipReleaseType(t, sipType_KMimeType, state); + } + + *sipCppPtr = ql; +diff --git a/sip/kdecore/ksharedconfig.sip b/sip/kdecore/ksharedconfig.sip +index 54b1599..9442d80 100644 +--- a/sip/kdecore/ksharedconfig.sip ++++ b/sip/kdecore/ksharedconfig.sip +@@ -65,7 +65,7 @@ typedef KSharedConfig::Ptr KSharedConfigPtr; + KSharedConfigPtr kcpp = *sipCpp; + KSharedConfig *ksc = kcpp.data (); + ksc->ref.ref(); +- PyObject *pyKsc = sipConvertFromInstance(ksc, sipClass_KSharedConfig, sipTransferObj); ++ PyObject *pyKsc = sipConvertFromType(ksc, sipType_KSharedConfig, sipTransferObj); + return pyKsc; + %End + +@@ -74,7 +74,7 @@ typedef KSharedConfig::Ptr KSharedConfigPtr; + return 1; + + int state; +- KSharedConfig* ksc = (KSharedConfig *)sipConvertToInstance(sipPy, sipClass_KSharedConfig, sipTransferObj, SIP_NOT_NONE, &state, sipIsErr); ++ KSharedConfig* ksc = (KSharedConfig *)sipConvertToType(sipPy, sipType_KSharedConfig, sipTransferObj, SIP_NOT_NONE, &state, sipIsErr); + *sipCppPtr = new KSharedConfigPtr (ksc); + ksc->ref.deref(); + return sipGetState(sipTransferObj); +diff --git a/sip/kdecore/ksycocaentry.sip b/sip/kdecore/ksycocaentry.sip +index 4632e4a..ceb85fa 100644 +--- a/sip/kdecore/ksycocaentry.sip ++++ b/sip/kdecore/ksycocaentry.sip +@@ -83,7 +83,7 @@ private: + KSycocaEntry::Ptr *t = new KSycocaEntry::Ptr (sipCpp->at(i)); + PyObject *tobj; + +- if ((tobj = sipConvertFromNewInstance(t->data(), sipClass_KSycocaEntry, sipTransferObj)) == NULL) ++ if ((tobj = sipConvertFromNewType(t->data(), sipType_KSycocaEntry, sipTransferObj)) == NULL) + { + Py_DECREF(l); + delete t; +@@ -105,7 +105,7 @@ private: + return 0; + + for (int i = 0; i < PyList_GET_SIZE(sipPy); ++i) +- if (!sipCanConvertToInstance(PyList_GET_ITEM(sipPy, i), sipClass_KSycocaEntry, SIP_NOT_NONE)) ++ if (!sipCanConvertToType(PyList_GET_ITEM(sipPy, i), sipType_KSycocaEntry, SIP_NOT_NONE)) + return 0; + + return 1; +@@ -116,11 +116,11 @@ private: + for (int i = 0; i < PyList_GET_SIZE(sipPy); ++i) + { + int state; +- KSycocaEntry *t = reinterpret_cast<KSycocaEntry *>(sipConvertToInstance(PyList_GET_ITEM(sipPy, i), sipClass_KSycocaEntry, sipTransferObj, SIP_NOT_NONE, &state, sipIsErr)); ++ KSycocaEntry *t = reinterpret_cast<KSycocaEntry *>(sipConvertToType(PyList_GET_ITEM(sipPy, i), sipType_KSycocaEntry, sipTransferObj, SIP_NOT_NONE, &state, sipIsErr)); + + if (*sipIsErr) + { +- sipReleaseInstance(t, sipClass_KSycocaEntry, state); ++ sipReleaseType(t, sipType_KSycocaEntry, state); + + delete ql; + return 0; +@@ -130,7 +130,7 @@ private: + + ql->append(*tptr); + +- sipReleaseInstance(t, sipClass_KSycocaEntry, state); ++ sipReleaseType(t, sipType_KSycocaEntry, state); + } + + *sipCppPtr = ql; +diff --git a/sip/kdecore/typedefs.sip b/sip/kdecore/typedefs.sip +index af53f85..23956b7 100644 +--- a/sip/kdecore/typedefs.sip ++++ b/sip/kdecore/typedefs.sip +@@ -397,8 +397,8 @@ template <TYPE1,TYPE2> + TYPE1 *t1 = new TYPE1(i.key()); + TYPE2 *t2 = new TYPE2(i.value()); + +- PyObject *t1obj = sipConvertFromNewInstance(t1, sipClass_TYPE1, sipTransferObj); +- PyObject *t2obj = sipConvertFromNewInstance(t2, sipClass_TYPE2, sipTransferObj); ++ PyObject *t1obj = sipConvertFromNewType(t1, sipType_TYPE1, sipTransferObj); ++ PyObject *t2obj = sipConvertFromNewType(t2, sipType_TYPE2, sipTransferObj); + + if (t1obj == NULL || t2obj == NULL || PyDict_SetItem(d, t1obj, t2obj) < 0) + { +@@ -438,10 +438,10 @@ template <TYPE1,TYPE2> + + while (PyDict_Next(sipPy, &i, &t1obj, &t2obj)) + { +- if (!sipCanConvertToInstance(t1obj, sipClass_TYPE1, SIP_NOT_NONE)) ++ if (!sipCanConvertToType(t1obj, sipType_TYPE1, SIP_NOT_NONE)) + return 0; + +- if (!sipCanConvertToInstance(t2obj, sipClass_TYPE2, SIP_NOT_NONE)) ++ if (!sipCanConvertToType(t2obj, sipType_TYPE2, SIP_NOT_NONE)) + return 0; + } + +@@ -454,13 +454,13 @@ template <TYPE1,TYPE2> + { + int state1, state2; + +- TYPE1 *t1 = reinterpret_cast<TYPE1 *>(sipConvertToInstance(t1obj, sipClass_TYPE1, sipTransferObj, SIP_NOT_NONE, &state1, sipIsErr)); +- TYPE2 *t2 = reinterpret_cast<TYPE2 *>(sipConvertToInstance(t2obj, sipClass_TYPE2, sipTransferObj, SIP_NOT_NONE, &state2, sipIsErr)); ++ TYPE1 *t1 = reinterpret_cast<TYPE1 *>(sipConvertToType(t1obj, sipType_TYPE1, sipTransferObj, SIP_NOT_NONE, &state1, sipIsErr)); ++ TYPE2 *t2 = reinterpret_cast<TYPE2 *>(sipConvertToType(t2obj, sipType_TYPE2, sipTransferObj, SIP_NOT_NONE, &state2, sipIsErr)); + + if (*sipIsErr) + { +- sipReleaseInstance(t1, sipClass_TYPE1, state1); +- sipReleaseInstance(t2, sipClass_TYPE2, state2); ++ sipReleaseType(t1, sipType_TYPE1, state1); ++ sipReleaseType(t2, sipType_TYPE2, state2); + + delete qm; + return 0; +@@ -468,8 +468,8 @@ template <TYPE1,TYPE2> + + qm->insert(*t1, *t2); + +- sipReleaseInstance(t1, sipClass_TYPE1, state1); +- sipReleaseInstance(t2, sipClass_TYPE2, state2); ++ sipReleaseType(t1, sipType_TYPE1, state1); ++ sipReleaseType(t2, sipType_TYPE2, state2); + } + + *sipCppPtr = qm; +@@ -669,7 +669,7 @@ template <TYPE*> + TYPE *t = (TYPE *)(sipCpp->at(i)); + PyObject *tobj; + +- if ((tobj = sipConvertFromNewInstance(t, sipClass_TYPE, sipTransferObj)) == NULL) ++ if ((tobj = sipConvertFromNewType(t, sipType_TYPE, sipTransferObj)) == NULL) + { + Py_DECREF(l); + delete t; +@@ -691,7 +691,7 @@ template <TYPE*> + return 0; + + for (int i = 0; i < PyList_GET_SIZE(sipPy); ++i) +- if (!sipCanConvertToInstance(PyList_GET_ITEM(sipPy, i), sipClass_TYPE, SIP_NOT_NONE)) ++ if (!sipCanConvertToType(PyList_GET_ITEM(sipPy, i), sipType_TYPE, SIP_NOT_NONE)) + return 0; + + return 1; +@@ -702,11 +702,11 @@ template <TYPE*> + for (int i = 0; i < PyList_GET_SIZE(sipPy); ++i) + { + int state; +- TYPE *t = reinterpret_cast<TYPE *>(sipConvertToInstance(PyList_GET_ITEM(sipPy, i), sipClass_TYPE, sipTransferObj, SIP_NOT_NONE, &state, sipIsErr)); ++ TYPE *t = reinterpret_cast<TYPE *>(sipConvertToType(PyList_GET_ITEM(sipPy, i), sipType_TYPE, sipTransferObj, SIP_NOT_NONE, &state, sipIsErr)); + + if (*sipIsErr) + { +- sipReleaseInstance(t, sipClass_TYPE, state); ++ sipReleaseType(t, sipType_TYPE, state); + + delete qv; + return 0; +@@ -714,7 +714,7 @@ template <TYPE*> + + qv->append(t); + +- sipReleaseInstance(t, sipClass_TYPE, state); ++ sipReleaseType(t, sipType_TYPE, state); + } + + *sipCppPtr = qv; +diff --git a/sip/kdeui/kcompletion.sip b/sip/kdeui/kcompletion.sip +index f1d327f..938506a 100644 +--- a/sip/kdeui/kcompletion.sip ++++ b/sip/kdeui/kcompletion.sip +@@ -176,7 +176,7 @@ public: + #else + PyObject *kobj = PyInt_FromLong((int)i.key()); + #endif +- PyObject *tobj = sipConvertFromNewInstance(t, sipClass_KShortcut, sipTransferObj); ++ PyObject *tobj = sipConvertFromNewType(t, sipType_KShortcut, sipTransferObj); + + if (kobj == NULL || tobj == NULL || PyDict_SetItem(d, kobj, tobj) < 0) + { +@@ -213,7 +213,7 @@ public: + return 0; + + while (PyDict_Next(sipPy, &i, &kobj, &tobj)) +- if (!sipCanConvertToInstance(tobj, sipClass_KShortcut, SIP_NOT_NONE)) ++ if (!sipCanConvertToType(tobj, sipType_KShortcut, SIP_NOT_NONE)) + return 0; + + return 1; +@@ -229,11 +229,11 @@ public: + #else + int k = PyInt_AsLong(kobj); + #endif +- KShortcut *t = reinterpret_cast<KShortcut *>(sipConvertToInstance(tobj, sipClass_KShortcut, sipTransferObj, SIP_NOT_NONE, &state, sipIsErr)); ++ KShortcut *t = reinterpret_cast<KShortcut *>(sipConvertToType(tobj, sipType_KShortcut, sipTransferObj, SIP_NOT_NONE, &state, sipIsErr)); + + if (*sipIsErr) + { +- sipReleaseInstance(t, sipClass_KShortcut, state); ++ sipReleaseType(t, sipType_KShortcut, state); + + delete qm; + return 0; +@@ -241,7 +241,7 @@ public: + + qm->insert((KCompletionBase::KeyBindingType)k, *t); + +- sipReleaseInstance(t, sipClass_KShortcut, state); ++ sipReleaseType(t, sipType_KShortcut, state); + } + + *sipCppPtr = qm; +diff --git a/sip/kdeui/kxmlguibuilder.sip b/sip/kdeui/kxmlguibuilder.sip +index 41ae2aa..e4cf187 100644 +--- a/sip/kdeui/kxmlguibuilder.sip ++++ b/sip/kdeui/kxmlguibuilder.sip +@@ -49,10 +49,10 @@ QAction *containerAction; + PyObject *pyWidget; + PyObject *pyContainerAction; + +- if ((pyWidget = sipConvertFromNewInstance(res, sipClass_QWidget, NULL)) == NULL) ++ if ((pyWidget = sipConvertFromNewType(res, sipType_QWidget, NULL)) == NULL) + return NULL; + +- if ((pyContainerAction = sipConvertFromNewInstance(containerAction, sipClass_QAction, NULL)) == NULL) ++ if ((pyContainerAction = sipConvertFromNewType(containerAction, sipType_QAction, NULL)) == NULL) + return NULL; + + sipRes = Py_BuildValue ("NN", pyWidget, pyContainerAction); +diff --git a/sip/kio/kservicegroup.sip b/sip/kio/kservicegroup.sip +index a1ef981..1ddce37 100644 +--- a/sip/kio/kservicegroup.sip ++++ b/sip/kio/kservicegroup.sip +@@ -151,7 +151,7 @@ public: + KServiceGroup::SPtr *t = new KServiceGroup::SPtr (sipCpp->at(i)); + PyObject *tobj; + +- if ((tobj = sipConvertFromNewInstance(t->data(), sipClass_KServiceGroup, sipTransferObj)) == NULL) ++ if ((tobj = sipConvertFromNewType(t->data(), sipType_KServiceGroup, sipTransferObj)) == NULL) + { + Py_DECREF(l); + delete t; +@@ -173,7 +173,7 @@ public: + return 0; + + for (int i = 0; i < PyList_GET_SIZE(sipPy); ++i) +- if (!sipCanConvertToInstance(PyList_GET_ITEM(sipPy, i), sipClass_KServiceGroup, SIP_NOT_NONE)) ++ if (!sipCanConvertToType(PyList_GET_ITEM(sipPy, i), sipType_KServiceGroup, SIP_NOT_NONE)) + return 0; + + return 1; +@@ -184,11 +184,11 @@ public: + for (int i = 0; i < PyList_GET_SIZE(sipPy); ++i) + { + int state; +- KServiceGroup *t = reinterpret_cast<KServiceGroup *>(sipConvertToInstance(PyList_GET_ITEM(sipPy, i), sipClass_KServiceGroup, sipTransferObj, SIP_NOT_NONE, &state, sipIsErr)); ++ KServiceGroup *t = reinterpret_cast<KServiceGroup *>(sipConvertToType(PyList_GET_ITEM(sipPy, i), sipType_KServiceGroup, sipTransferObj, SIP_NOT_NONE, &state, sipIsErr)); + + if (*sipIsErr) + { +- sipReleaseInstance(t, sipClass_KServiceGroup, state); ++ sipReleaseType(t, sipType_KServiceGroup, state); + + delete ql; + return 0; +@@ -198,7 +198,7 @@ public: + + ql->append(*tptr); + +- sipReleaseInstance(t, sipClass_KServiceGroup, state); ++ sipReleaseType(t, sipType_KServiceGroup, state); + } + + *sipCppPtr = ql; +diff --git a/sip/ktexteditor/markinterface.sip b/sip/ktexteditor/markinterface.sip +index d9b0ec9..888c506 100644 +--- a/sip/ktexteditor/markinterface.sip ++++ b/sip/ktexteditor/markinterface.sip +@@ -158,7 +158,7 @@ signals: + #else + PyObject *t1obj = PyInt_FromLong ((long)t1); + #endif +- PyObject *t2obj = sipConvertFromNewInstance(t2, sipClass_KTextEditor_Mark, sipTransferObj); ++ PyObject *t2obj = sipConvertFromNewType(t2, sipType_KTextEditor_Mark, sipTransferObj); + + if (t2obj == NULL || PyDict_SetItem(d, t1obj, t2obj) < 0) + { +@@ -203,7 +203,7 @@ signals: + #endif + return 0; + +- if (!sipCanConvertToInstance(t2obj, sipClass_KTextEditor_Mark, SIP_NOT_NONE)) ++ if (!sipCanConvertToType(t2obj, sipType_KTextEditor_Mark, SIP_NOT_NONE)) + return 0; + } + +@@ -221,11 +221,11 @@ signals: + #else + int t1 = PyInt_AS_LONG (t1obj); + #endif +- KTextEditor::Mark *t2 = reinterpret_cast<KTextEditor::Mark *>(sipConvertToInstance(t2obj, sipClass_KTextEditor_Mark, sipTransferObj, SIP_NOT_NONE, &state2, sipIsErr)); ++ KTextEditor::Mark *t2 = reinterpret_cast<KTextEditor::Mark *>(sipConvertToType(t2obj, sipType_KTextEditor_Mark, sipTransferObj, SIP_NOT_NONE, &state2, sipIsErr)); + + if (*sipIsErr) + { +- sipReleaseInstance(t2, sipClass_KTextEditor_Mark, state2); ++ sipReleaseType(t2, sipType_KTextEditor_Mark, state2); + + delete qm; + return 0; +@@ -233,7 +233,7 @@ signals: + + qm->insert(t1, t2); + +- sipReleaseInstance(t2, sipClass_KTextEditor_Mark, state2); ++ sipReleaseType(t2, sipType_KTextEditor_Mark, state2); + } + + *sipCppPtr = qm; +diff --git a/sip/phonon/objectdescription.sip b/sip/phonon/objectdescription.sip +index 2b86d5e..015b2ef 100644 +--- a/sip/phonon/objectdescription.sip ++++ b/sip/phonon/objectdescription.sip +@@ -116,7 +116,7 @@ void registerMetaTypes (); + DNSSD::RemoteService::Ptr *t = new Phonon::ObjectDescription (sipCpp->at(i)); + PyObject *tobj; + +- if ((tobj = sipConvertFromNewInstance(t->data(), sipClass_DNSSD_RemoteService, sipTransferObj)) == NULL) ++ if ((tobj = sipConvertFromNewType(t->data(), sipType_DNSSD_RemoteService, sipTransferObj)) == NULL) + { + Py_DECREF(l); + delete t; +@@ -138,7 +138,7 @@ void registerMetaTypes (); + return 0; + + for (int i = 0; i < PyList_GET_SIZE(sipPy); ++i) +- if (!sipCanConvertToInstance(PyList_GET_ITEM(sipPy, i), sipClass_DNSSD_RemoteService, SIP_NOT_NONE)) ++ if (!sipCanConvertToType(PyList_GET_ITEM(sipPy, i), sipType_DNSSD_RemoteService, SIP_NOT_NONE)) + return 0; + + return 1; +@@ -149,11 +149,11 @@ void registerMetaTypes (); + for (int i = 0; i < PyList_GET_SIZE(sipPy); ++i) + { + int state; +- DNSSD::RemoteService *t = reinterpret_cast<DNSSD::RemoteService *>(sipConvertToInstance(PyList_GET_ITEM(sipPy, i), sipClass_DNSSD_RemoteService, sipTransferObj, SIP_NOT_NONE, &state, sipIsErr)); ++ DNSSD::RemoteService *t = reinterpret_cast<DNSSD::RemoteService *>(sipConvertToType(PyList_GET_ITEM(sipPy, i), sipType_DNSSD_RemoteService, sipTransferObj, SIP_NOT_NONE, &state, sipIsErr)); + + if (*sipIsErr) + { +- sipReleaseInstance(t, sipClass_DNSSD_RemoteService, state); ++ sipReleaseType(t, sipType_DNSSD_RemoteService, state); + + delete ql; + return 0; +@@ -163,7 +163,7 @@ void registerMetaTypes (); + + ql->append(*tptr); + +- sipReleaseInstance(t, sipClass_DNSSD_RemoteService, state); ++ sipReleaseType(t, sipType_DNSSD_RemoteService, state); + } + + *sipCppPtr = ql; +diff --git a/sip/soprano/pluginmanager.sip b/sip/soprano/pluginmanager.sip +index c2be1c3..fe990f8 100644 +--- a/sip/soprano/pluginmanager.sip ++++ b/sip/soprano/pluginmanager.sip +@@ -73,7 +73,7 @@ public: + Soprano::Backend* t = const_cast<Soprano::Backend*>(sipCpp->at(i)); + PyObject *tobj; + +- if ((tobj = sipConvertFromInstance(t, sipClass_Soprano_Backend, sipTransferObj)) == NULL) ++ if ((tobj = sipConvertFromType(t, sipType_Soprano_Backend, sipTransferObj)) == NULL) + { + Py_DECREF(l); + return NULL; +@@ -93,7 +93,7 @@ public: + return 0; + + for (int i = 0; i < PyList_GET_SIZE(sipPy); ++i) +- if (!sipCanConvertToInstance(PyList_GET_ITEM(sipPy, i), sipClass_Soprano_Backend, SIP_NOT_NONE)) ++ if (!sipCanConvertToType(PyList_GET_ITEM(sipPy, i), sipType_Soprano_Backend, SIP_NOT_NONE)) + return 0; + + return 1; +@@ -104,18 +104,18 @@ public: + for (int i = 0; i < PyList_GET_SIZE(sipPy); ++i) + { + int state; +- const Soprano::Backend*t = reinterpret_cast<const Soprano::Backend*>(sipConvertToInstance(PyList_GET_ITEM(sipPy, i), sipClass_Soprano_Backend, sipTransferObj, SIP_NOT_NONE, &state, sipIsErr)); ++ const Soprano::Backend*t = reinterpret_cast<const Soprano::Backend*>(sipConvertToType(PyList_GET_ITEM(sipPy, i), sipType_Soprano_Backend, sipTransferObj, SIP_NOT_NONE, &state, sipIsErr)); + + if (*sipIsErr) + { +- sipReleaseInstance(const_cast<Soprano::Backend*>(t), sipClass_Soprano_Backend, state); ++ sipReleaseType(const_cast<Soprano::Backend*>(t), sipType_Soprano_Backend, state); + + delete ql; + return 0; + } + ql->append(t); + +- sipReleaseInstance(const_cast<Soprano::Backend*>(t), sipClass_Soprano_Backend, state); ++ sipReleaseType(const_cast<Soprano::Backend*>(t), sipType_Soprano_Backend, state); + } + + *sipCppPtr = ql; +@@ -144,7 +144,7 @@ public: + Soprano::Parser* t = const_cast<Soprano::Parser*>(sipCpp->at(i)); + PyObject *tobj; + +- if ((tobj = sipConvertFromInstance(t, sipClass_Soprano_Parser, sipTransferObj)) == NULL) ++ if ((tobj = sipConvertFromType(t, sipType_Soprano_Parser, sipTransferObj)) == NULL) + { + Py_DECREF(l); + return NULL; +@@ -164,7 +164,7 @@ public: + return 0; + + for (int i = 0; i < PyList_GET_SIZE(sipPy); ++i) +- if (!sipCanConvertToInstance(PyList_GET_ITEM(sipPy, i), sipClass_Soprano_Parser, SIP_NOT_NONE)) ++ if (!sipCanConvertToType(PyList_GET_ITEM(sipPy, i), sipType_Soprano_Parser, SIP_NOT_NONE)) + return 0; + + return 1; +@@ -175,18 +175,18 @@ public: + for (int i = 0; i < PyList_GET_SIZE(sipPy); ++i) + { + int state; +- const Soprano::Parser*t = reinterpret_cast<const Soprano::Parser*>(sipConvertToInstance(PyList_GET_ITEM(sipPy, i), sipClass_Soprano_Parser, sipTransferObj, SIP_NOT_NONE, &state, sipIsErr)); ++ const Soprano::Parser*t = reinterpret_cast<const Soprano::Parser*>(sipConvertToType(PyList_GET_ITEM(sipPy, i), sipType_Soprano_Parser, sipTransferObj, SIP_NOT_NONE, &state, sipIsErr)); + + if (*sipIsErr) + { +- sipReleaseInstance(const_cast<Soprano::Parser*>(t), sipClass_Soprano_Parser, state); ++ sipReleaseType(const_cast<Soprano::Parser*>(t), sipType_Soprano_Parser, state); + + delete ql; + return 0; + } + ql->append(t); + +- sipReleaseInstance(const_cast<Soprano::Parser*>(t), sipClass_Soprano_Parser, state); ++ sipReleaseType(const_cast<Soprano::Parser*>(t), sipType_Soprano_Parser, state); + } + + *sipCppPtr = ql; +@@ -215,7 +215,7 @@ public: + Soprano::Serializer* t = const_cast<Soprano::Serializer*>(sipCpp->at(i)); + PyObject *tobj; + +- if ((tobj = sipConvertFromInstance(t, sipClass_Soprano_Serializer, sipTransferObj)) == NULL) ++ if ((tobj = sipConvertFromType(t, sipType_Soprano_Serializer, sipTransferObj)) == NULL) + { + Py_DECREF(l); + return NULL; +@@ -235,7 +235,7 @@ public: + return 0; + + for (int i = 0; i < PyList_GET_SIZE(sipPy); ++i) +- if (!sipCanConvertToInstance(PyList_GET_ITEM(sipPy, i), sipClass_Soprano_Serializer, SIP_NOT_NONE)) ++ if (!sipCanConvertToType(PyList_GET_ITEM(sipPy, i), sipType_Soprano_Serializer, SIP_NOT_NONE)) + return 0; + + return 1; +@@ -246,18 +246,18 @@ public: + for (int i = 0; i < PyList_GET_SIZE(sipPy); ++i) + { + int state; +- const Soprano::Serializer*t = reinterpret_cast<const Soprano::Serializer*>(sipConvertToInstance(PyList_GET_ITEM(sipPy, i), sipClass_Soprano_Serializer, sipTransferObj, SIP_NOT_NONE, &state, sipIsErr)); ++ const Soprano::Serializer*t = reinterpret_cast<const Soprano::Serializer*>(sipConvertToType(PyList_GET_ITEM(sipPy, i), sipType_Soprano_Serializer, sipTransferObj, SIP_NOT_NONE, &state, sipIsErr)); + + if (*sipIsErr) + { +- sipReleaseInstance(const_cast<Soprano::Serializer*>(t), sipClass_Soprano_Serializer, state); ++ sipReleaseType(const_cast<Soprano::Serializer*>(t), sipType_Soprano_Serializer, state); + + delete ql; + return 0; + } + ql->append(t); + +- sipReleaseInstance(const_cast<Soprano::Serializer*>(t), sipClass_Soprano_Serializer, state); ++ sipReleaseType(const_cast<Soprano::Serializer*>(t), sipType_Soprano_Serializer, state); + } + + *sipCppPtr = ql; +-- +2.9.3 + diff --git a/kde/patch/sddm-qt5.patch b/kde/patch/sddm-qt5.patch index fa4e1b3..21f2c04 100644 --- a/kde/patch/sddm-qt5.patch +++ b/kde/patch/sddm-qt5.patch @@ -2,9 +2,24 @@ # (brings back the switch_user functionality in KDE): cat $CWD/patch/sddm-qt5/sddm_consolekit.diff | patch -p1 --verbose || { touch ${SLACK_KDE_BUILD_DIR}/${PKGNAME}.failed ; continue ; } +# SDDM 0.14 sources $HOME/.xsession which in Slackware will override the +# session selection you make in SDDM. We fix that unwanted side effect by +# reverting the change: +cat $CWD/patch/sddm-qt5/sddm_userxsession.diff | patch -p1 --verbose || { touch ${SLACK_KDE_BUILD_DIR}/${PKGNAME}.failed ; continue ; } + +# Fix display of user avatars ($HOME/.face.icon file) +# (fixed in sddm-0.15.0). +#cat $CWD//patch/sddm-qt5/sddm_avatars.patch | patch -p1 --verbose || { touch ${SLACK_KDE_BUILD_DIR}/${PKGNAME}.failed ; continue ; } + # Fix a compilation error on passwd backend: #cat $CWD/patch/sddm-qt5/sddm_auth.diff | patch -p1 --verbose || { touch ${SLACK_KDE_BUILD_DIR}/${PKGNAME}.failed ; continue ; } # Fix a compilation error on passwd backend: # (fixed in sddm-0.12.0). #cat $CWD/patch/sddm-qt5/sddm_qstring.patch | patch -p1 --verbose || { touch ${SLACK_KDE_BUILD_DIR}/${PKGNAME}.failed ; continue ; } + +# Add the dutch translation: +if ! grep -q nl.ts data/translations/CMakeLists.txt ; then + sed -e '/set(TRANSLATION_FILES/s/TRANSLATION_FILES/&\n nl.ts/' \ + -i data/translations/CMakeLists.txt +fi diff --git a/kde/patch/sddm-qt5/sddm_avatars.patch b/kde/patch/sddm-qt5/sddm_avatars.patch new file mode 100644 index 0000000..d40f68c --- /dev/null +++ b/kde/patch/sddm-qt5/sddm_avatars.patch @@ -0,0 +1,33 @@ +From ecb903e48822bd90650bdd64fe80754e3e9664cb Mon Sep 17 00:00:00 2001 +From: Bastian Beischer <bastian.beischer@gmail.com> +Date: Fri, 2 Sep 2016 13:05:18 +0200 +Subject: [PATCH] Fix display of user avatars. (#684) + +QFile::exists("...") does not understand file:// URLs, at least in Qt +5.7.0 and Qt 4.8.7. +--- + src/greeter/UserModel.cpp | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/src/greeter/UserModel.cpp b/src/greeter/UserModel.cpp +index 41a9f10..94c492d 100644 +--- a/src/greeter/UserModel.cpp ++++ b/src/greeter/UserModel.cpp +@@ -107,13 +107,13 @@ namespace SDDM { + d->lastIndex = i; + + if (avatarsEnabled) { +- const QString userFace = QStringLiteral("file://%1/.face.icon").arg(user->homeDir); +- const QString systemFace = QStringLiteral("file://%1/%2.face.icon").arg(facesDir).arg(user->name); ++ const QString userFace = QStringLiteral("%1/.face.icon").arg(user->homeDir); ++ const QString systemFace = QStringLiteral("%1/%2.face.icon").arg(facesDir).arg(user->name); + + if (QFile::exists(userFace)) +- user->icon = userFace; ++ user->icon = QStringLiteral("file://%1").arg(userFace); + else if (QFile::exists(systemFace)) +- user->icon = systemFace; ++ user->icon = QStringLiteral("file://%1").arg(systemFace); + } + } + } diff --git a/kde/patch/sddm-qt5/sddm_consolekit.diff b/kde/patch/sddm-qt5/sddm_consolekit.diff index af79f75..9b535bf 100644 --- a/kde/patch/sddm-qt5/sddm_consolekit.diff +++ b/kde/patch/sddm-qt5/sddm_consolekit.diff @@ -1,13 +1,9 @@ -diff --git a/data/scripts/Xsession b/data/scripts/Xsession -index a5d270d..4b48524 100755 ---- a/data/scripts/Xsession -+++ b/data/scripts/Xsession -@@ -74,7 +74,7 @@ case $session in - exec xterm -geometry 80x24-0-0 - ;; - *) -- eval exec "$session" -+ eval exec ck-launch-session dbus-launch --sh-syntax --exit-with-session "$session" - ;; - esac - exec xmessage -center -buttons OK:0 -default OK "Sorry, cannot execute $session. Check $DESKTOP_SESSION.desktop." +--- sddm-0.14.0/data/scripts/Xsession.orig 2016-08-28 13:54:03.000000000 +0200 ++++ sddm-0.14.0/data/scripts/Xsession 2016-11-05 21:47:28.502096600 +0100 +@@ -91,5 +91,5 @@ + if [ -z "$@" ]; then + exec xmessage -center -buttons OK:0 -default OK "Sorry, $DESKTOP_SESSION is no valid session." + else +- exec $@ ++ exec ck-launch-session dbus-launch --sh-syntax --exit-with-session $@ + fi diff --git a/kde/patch/sddm-qt5/sddm_userxsession.diff b/kde/patch/sddm-qt5/sddm_userxsession.diff new file mode 100644 index 0000000..cbfa1ef --- /dev/null +++ b/kde/patch/sddm-qt5/sddm_userxsession.diff @@ -0,0 +1,13 @@ +--- sddm-0.14.0/data/scripts/Xsession.orig 2016-08-28 13:54:03.000000000 +0200 ++++ sddm-0.14.0/data/scripts/Xsession 2016-11-06 21:35:43.183138893 +0100 +@@ -84,10 +84,6 @@ + fi + [ -f $HOME/.Xresources ] && xrdb -merge $HOME/.Xresources + +-if [ -f "$USERXSESSION" ]; then +- . "$USERXSESSION" +-fi +- + if [ -z "$@" ]; then + exec xmessage -center -buttons OK:0 -default OK "Sorry, $DESKTOP_SESSION is no valid session." + else |