┌   ┐
54
└   ┘

summaryrefslogtreecommitdiff
path: root/src/kitemviews/private
diff options
context:
space:
mode:
authorPeter Penz <[email protected]>2012-04-11 16:06:18 +0200
committerPeter Penz <[email protected]>2012-04-11 16:08:32 +0200
commit6c3d9acbc22ea9463ba40ef84c9e8c8419dfacf3 (patch)
treee7ffd63acd5e28eb71a077f816a23534b06fcae2 /src/kitemviews/private
parentd9dbd3398a258d04ec4517fd13e795b437c869d6 (diff)
KItemViews: Internal directory restructuration
- Move all private headers from the kitemviews-directory into the 'private' subdirectory. - Get rid of DolphinDirLister and just use a directory-lister internally in KFileItemModel. - Minor interface-cleanups for signals
Diffstat (limited to 'src/kitemviews/private')
-rw-r--r--src/kitemviews/private/kfileitemclipboard.cpp76
-rw-r--r--src/kitemviews/private/kfileitemclipboard.h62
-rw-r--r--src/kitemviews/private/kfileitemmodeldirlister.cpp44
-rw-r--r--src/kitemviews/private/kfileitemmodeldirlister.h47
-rw-r--r--src/kitemviews/private/kfileitemmodelfilter.cpp71
-rw-r--r--src/kitemviews/private/kfileitemmodelfilter.h70
-rw-r--r--src/kitemviews/private/kfileitemmodelsortalgorithm.cpp148
-rw-r--r--src/kitemviews/private/kfileitemmodelsortalgorithm.h70
-rw-r--r--src/kitemviews/private/kitemlistheaderwidget.cpp535
-rw-r--r--src/kitemviews/private/kitemlistheaderwidget.h171
-rw-r--r--src/kitemviews/private/kitemlistkeyboardsearchmanager.cpp78
-rw-r--r--src/kitemviews/private/kitemlistrubberband.cpp75
-rw-r--r--src/kitemviews/private/kitemlistselectiontoggle.cpp106
-rw-r--r--src/kitemviews/private/kitemlistselectiontoggle.h61
-rw-r--r--src/kitemviews/private/kitemlistsizehintresolver.cpp88
-rw-r--r--src/kitemviews/private/kitemlistsizehintresolver.h53
-rw-r--r--src/kitemviews/private/kitemlistsmoothscroller.cpp207
-rw-r--r--src/kitemviews/private/kitemlistsmoothscroller.h103
-rw-r--r--src/kitemviews/private/kitemlistviewanimation.cpp245
-rw-r--r--src/kitemviews/private/kitemlistviewanimation.h106
-rw-r--r--src/kitemviews/private/kitemlistviewlayouter.cpp630
-rw-r--r--src/kitemviews/private/kitemlistviewlayouter.h235
-rw-r--r--src/kitemviews/private/knepomukdatamanagement_export.h40
-rw-r--r--src/kitemviews/private/knepomukresourcewatcher.h288
-rw-r--r--src/kitemviews/private/knepomukrolesprovider.cpp181
-rw-r--r--src/kitemviews/private/knepomukrolesprovider.h82
-rw-r--r--src/kitemviews/private/kpixmapmodifier.cpp400
-rw-r--r--src/kitemviews/private/kpixmapmodifier.h38
28 files changed, 4310 insertions, 0 deletions
diff --git a/src/kitemviews/private/kfileitemclipboard.cpp b/src/kitemviews/private/kfileitemclipboard.cpp
new file mode 100644
index 000000000..6d6085641
--- /dev/null
+++ b/src/kitemviews/private/kfileitemclipboard.cpp
@@ -0,0 +1,76 @@
+/***************************************************************************
+ * Copyright (C) 2011 by Peter Penz <[email protected]> *
+ * *
+ * 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, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#include "kfileitemclipboard.h"
+
+#include <KGlobal>
+#include <QApplication>
+#include <QClipboard>
+#include <QMimeData>
+
+class KFileItemClipboardSingleton
+{
+public:
+ KFileItemClipboard instance;
+};
+K_GLOBAL_STATIC(KFileItemClipboardSingleton, s_KFileItemClipboard)
+
+
+
+KFileItemClipboard* KFileItemClipboard::instance()
+{
+ return &s_KFileItemClipboard->instance;
+}
+
+bool KFileItemClipboard::isCut(const KUrl& url) const
+{
+ return m_cutItems.contains(url);
+}
+
+QList<KUrl> KFileItemClipboard::cutItems() const
+{
+ return m_cutItems.toList();
+}
+
+KFileItemClipboard::~KFileItemClipboard()
+{
+}
+
+void KFileItemClipboard::updateCutItems()
+{
+ const QMimeData* mimeData = QApplication::clipboard()->mimeData();
+ const QByteArray data = mimeData->data("application/x-kde-cutselection");
+ const bool isCutSelection = (!data.isEmpty() && data.at(0) == QLatin1Char('1'));
+ if (isCutSelection) {
+ m_cutItems = KUrl::List::fromMimeData(mimeData).toSet();
+ emit cutItemsChanged();
+ }
+}
+
+KFileItemClipboard::KFileItemClipboard() :
+ QObject(0),
+ m_cutItems()
+{
+ updateCutItems();
+
+ connect(QApplication::clipboard(), SIGNAL(dataChanged()),
+ this, SLOT(updateCutItems()));
+}
+
+#include "kfileitemclipboard.moc"
diff --git a/src/kitemviews/private/kfileitemclipboard.h b/src/kitemviews/private/kfileitemclipboard.h
new file mode 100644
index 000000000..86eb8e9fc
--- /dev/null
+++ b/src/kitemviews/private/kfileitemclipboard.h
@@ -0,0 +1,62 @@
+/***************************************************************************
+ * Copyright (C) 2011 by Peter Penz <[email protected]> *
+ * *
+ * 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, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#ifndef KFILEITEMCLIPBOARD_H
+#define KFILEITEMCLIPBOARD_H
+
+#include <KUrl>
+#include <QList>
+#include <QSet>
+#include <QObject>
+
+#include "libdolphin_export.h"
+
+/**
+ * @brief Wrapper for QClipboard to provide fast access for checking
+ * whether a KFileItem has been clipped.
+ */
+class LIBDOLPHINPRIVATE_EXPORT KFileItemClipboard : public QObject
+{
+ Q_OBJECT
+
+public:
+ static KFileItemClipboard* instance();
+
+ bool isCut(const KUrl& url) const;
+
+ QList<KUrl> cutItems() const;
+
+signals:
+ void cutItemsChanged();
+
+protected:
+ virtual ~KFileItemClipboard();
+
+private slots:
+ void updateCutItems();
+
+private:
+ KFileItemClipboard();
+
+ QSet<KUrl> m_cutItems;
+
+ friend class KFileItemClipboardSingleton;
+};
+
+#endif
diff --git a/src/kitemviews/private/kfileitemmodeldirlister.cpp b/src/kitemviews/private/kfileitemmodeldirlister.cpp
new file mode 100644
index 000000000..be0f9f77b
--- /dev/null
+++ b/src/kitemviews/private/kfileitemmodeldirlister.cpp
@@ -0,0 +1,44 @@
+/***************************************************************************
+ * Copyright (C) 2006-2012 by Peter Penz <[email protected]> *
+ * *
+ * 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, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#include "kfileitemmodeldirlister.h"
+#include <KLocale>
+#include <KIO/JobClasses>
+
+KFileItemModelDirLister::KFileItemModelDirLister(QObject* parent) :
+ KDirLister(parent)
+{
+ setAutoErrorHandlingEnabled(false, 0);
+}
+
+KFileItemModelDirLister::~KFileItemModelDirLister()
+{
+}
+
+void KFileItemModelDirLister::handleError(KIO::Job* job)
+{
+ const QString errorString = job->errorString();
+ if (errorString.isEmpty()) {
+ emit errorMessage(i18nc("@info:status", "Unknown error."));
+ } else {
+ emit errorMessage(errorString);
+ }
+}
+
+#include "kfileitemmodeldirlister.moc"
diff --git a/src/kitemviews/private/kfileitemmodeldirlister.h b/src/kitemviews/private/kfileitemmodeldirlister.h
new file mode 100644
index 000000000..1d58347f4
--- /dev/null
+++ b/src/kitemviews/private/kfileitemmodeldirlister.h
@@ -0,0 +1,47 @@
+/***************************************************************************
+ * Copyright (C) 2006-2012 by Peter Penz <[email protected]> *
+ * *
+ * 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, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#ifndef KFILEITEMMODELDIRLISTER_H
+#define KFILEITEMMODELDIRLISTER_H
+
+#include <libdolphin_export.h>
+#include <KDirLister>
+
+/**
+ * @brief Extends the class KDirLister by emitting a signal when an
+ * error occurred instead of showing an error dialog.
+ * KDirLister::autoErrorHandlingEnabled() is set to false.
+ */
+class LIBDOLPHINPRIVATE_EXPORT KFileItemModelDirLister : public KDirLister
+{
+ Q_OBJECT
+
+public:
+ KFileItemModelDirLister(QObject* parent = 0);
+ virtual ~KFileItemModelDirLister();
+
+signals:
+ /** Is emitted whenever an error has occurred. */
+ void errorMessage(const QString& msg);
+
+protected:
+ virtual void handleError(KIO::Job* job);
+};
+
+#endif
diff --git a/src/kitemviews/private/kfileitemmodelfilter.cpp b/src/kitemviews/private/kfileitemmodelfilter.cpp
new file mode 100644
index 000000000..816d35634
--- /dev/null
+++ b/src/kitemviews/private/kfileitemmodelfilter.cpp
@@ -0,0 +1,71 @@
+/***************************************************************************
+ * Copyright (C) 2011 by Janardhan Reddy *
+ * *
+ * 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, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#include "kfileitemmodelfilter.h"
+
+#include <KFileItem>
+#include <QRegExp>
+
+KFileItemModelFilter::KFileItemModelFilter() :
+ m_useRegExp(false),
+ m_regExp(0),
+ m_lowerCasePattern(),
+ m_pattern()
+{
+}
+
+KFileItemModelFilter::~KFileItemModelFilter()
+{
+ delete m_regExp;
+ m_regExp = 0;
+}
+
+void KFileItemModelFilter::setPattern(const QString& filter)
+{
+ m_pattern = filter;
+ m_lowerCasePattern = filter.toLower();
+
+ m_useRegExp = filter.contains('*') ||
+ filter.contains('?') ||
+ filter.contains('[');
+ if (m_useRegExp) {
+ if (!m_regExp) {
+ m_regExp = new QRegExp();
+ m_regExp->setCaseSensitivity(Qt::CaseInsensitive);
+ m_regExp->setMinimal(false);
+ m_regExp->setPatternSyntax(QRegExp::WildcardUnix);
+ }
+ m_regExp->setPattern(filter);
+ }
+}
+
+QString KFileItemModelFilter::pattern() const
+{
+ return m_pattern;
+}
+
+bool KFileItemModelFilter::matches(const KFileItem& item) const
+{
+ if (m_useRegExp) {
+ return m_regExp->exactMatch(item.text());
+ } else {
+ return item.text().toLower().contains(m_lowerCasePattern);
+ }
+}
diff --git a/src/kitemviews/private/kfileitemmodelfilter.h b/src/kitemviews/private/kfileitemmodelfilter.h
new file mode 100644
index 000000000..9bdf1fd95
--- /dev/null
+++ b/src/kitemviews/private/kfileitemmodelfilter.h
@@ -0,0 +1,70 @@
+/***************************************************************************
+ * Copyright (C) 2011 by Janardhan Reddy *
+ * *
+ * 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, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#ifndef KFILEITEMMODELFILTER_H
+#define KFILEITEMMODELFILTER_H
+
+#include <libdolphin_export.h>
+#include <QString>
+
+class KFileItem;
+class QRegExp;
+
+/**
+ * @brief Allows to check whether an item of the KFileItemModel
+ * matches with a set filter-string.
+ *
+ * Currently the filter is only checked for the KFileItem::text()
+ * property of the KFileItem, but this might get extended in
+ * future.
+ */
+class LIBDOLPHINPRIVATE_EXPORT KFileItemModelFilter
+{
+
+public:
+ KFileItemModelFilter();
+ virtual ~KFileItemModelFilter();
+
+ /**
+ * Sets the pattern that is used for a comparison with the item
+ * in KFileItemModelFilter::matches(). Per default the pattern
+ * defines a sub-string. As soon as the pattern contains at least
+ * a '*', '?' or '[' the pattern represents a regular expression.
+ */
+ void setPattern(const QString& pattern);
+ QString pattern() const;
+
+ /**
+ * @return True if the item matches with the pattern defined by
+ * KFileItemModelFilter::setPattern().
+ */
+ bool matches(const KFileItem& item) const;
+
+private:
+ bool m_useRegExp; // If true, m_regExp is used for filtering,
+ // otherwise m_lowerCaseFilter is used.
+ QRegExp* m_regExp;
+ QString m_lowerCasePattern; // Lowercase version of m_filter for
+ // faster comparison in matches().
+ QString m_pattern; // Property set by setFilter().
+};
+#endif
+
+
diff --git a/src/kitemviews/private/kfileitemmodelsortalgorithm.cpp b/src/kitemviews/private/kfileitemmodelsortalgorithm.cpp
new file mode 100644
index 000000000..e0aac13de
--- /dev/null
+++ b/src/kitemviews/private/kfileitemmodelsortalgorithm.cpp
@@ -0,0 +1,148 @@
+/***************************************************************************
+ * Copyright (C) 2012 by Peter Penz <[email protected]> *
+ * *
+ * 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, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#include "kfileitemmodelsortalgorithm.h"
+
+void KFileItemModelSortAlgorithm::sort(KFileItemModel* model,
+ QList<KFileItemModel::ItemData*>::iterator begin,
+ QList<KFileItemModel::ItemData*>::iterator end)
+{
+ // The implementation is based on qStableSortHelper() from qalgorithms.h
+ // Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+
+ const int span = end - begin;
+ if (span < 2) {
+ return;
+ }
+
+ const QList<KFileItemModel::ItemData*>::iterator middle = begin + span / 2;
+ sort(model, begin, middle);
+ sort(model, middle, end);
+ merge(model, begin, middle, end);
+}
+
+void KFileItemModelSortAlgorithm::merge(KFileItemModel* model,
+ QList<KFileItemModel::ItemData*>::iterator begin,
+ QList<KFileItemModel::ItemData*>::iterator pivot,
+ QList<KFileItemModel::ItemData*>::iterator end)
+{
+ // The implementation is based on qMerge() from qalgorithms.h
+ // Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+
+ const int len1 = pivot - begin;
+ const int len2 = end - pivot;
+
+ if (len1 == 0 || len2 == 0) {
+ return;
+ }
+
+ if (len1 + len2 == 2) {
+ if (model->lessThan(*(begin + 1), *(begin))) {
+ qSwap(*begin, *(begin + 1));
+ }
+ return;
+ }
+
+ QList<KFileItemModel::ItemData*>::iterator firstCut;
+ QList<KFileItemModel::ItemData*>::iterator secondCut;
+ int len2Half;
+ if (len1 > len2) {
+ const int len1Half = len1 / 2;
+ firstCut = begin + len1Half;
+ secondCut = lowerBound(model, pivot, end, *firstCut);
+ len2Half = secondCut - pivot;
+ } else {
+ len2Half = len2 / 2;
+ secondCut = pivot + len2Half;
+ firstCut = upperBound(model, begin, pivot, *secondCut);
+ }
+
+ reverse(firstCut, pivot);
+ reverse(pivot, secondCut);
+ reverse(firstCut, secondCut);
+
+ const QList<KFileItemModel::ItemData*>::iterator newPivot = firstCut + len2Half;
+ merge(model, begin, firstCut, newPivot);
+ merge(model, newPivot, secondCut, end);
+}
+
+
+QList<KFileItemModel::ItemData*>::iterator
+KFileItemModelSortAlgorithm::lowerBound(KFileItemModel* model,
+ QList<KFileItemModel::ItemData*>::iterator begin,
+ QList<KFileItemModel::ItemData*>::iterator end,
+ const KFileItemModel::ItemData* value)
+{
+ // The implementation is based on qLowerBound() from qalgorithms.h
+ // Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+
+ QList<KFileItemModel::ItemData*>::iterator middle;
+ int n = int(end - begin);
+ int half;
+
+ while (n > 0) {
+ half = n >> 1;
+ middle = begin + half;
+ if (model->lessThan(*middle, value)) {
+ begin = middle + 1;
+ n -= half + 1;
+ } else {
+ n = half;
+ }
+ }
+ return begin;
+}
+
+QList<KFileItemModel::ItemData*>::iterator
+KFileItemModelSortAlgorithm::upperBound(KFileItemModel* model,
+ QList<KFileItemModel::ItemData*>::iterator begin,
+ QList<KFileItemModel::ItemData*>::iterator end,
+ const KFileItemModel::ItemData* value)
+{
+ // The implementation is based on qUpperBound() from qalgorithms.h
+ // Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+
+ QList<KFileItemModel::ItemData*>::iterator middle;
+ int n = end - begin;
+ int half;
+
+ while (n > 0) {
+ half = n >> 1;
+ middle = begin + half;
+ if (model->lessThan(value, *middle)) {
+ n = half;
+ } else {
+ begin = middle + 1;
+ n -= half + 1;
+ }
+ }
+ return begin;
+}
+
+void KFileItemModelSortAlgorithm::reverse(QList<KFileItemModel::ItemData*>::iterator begin,
+ QList<KFileItemModel::ItemData*>::iterator end)
+{
+ // The implementation is based on qReverse() from qalgorithms.h
+ // Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+
+ --end;
+ while (begin < end) {
+ qSwap(*begin++, *end--);
+ }
+}
diff --git a/src/kitemviews/private/kfileitemmodelsortalgorithm.h b/src/kitemviews/private/kfileitemmodelsortalgorithm.h
new file mode 100644
index 000000000..3a596dff5
--- /dev/null
+++ b/src/kitemviews/private/kfileitemmodelsortalgorithm.h
@@ -0,0 +1,70 @@
+/***************************************************************************
+ * Copyright (C) 2012 by Peter Penz <[email protected]> *
+ * *
+ * 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, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#ifndef KFILEITEMMODELSORTALGORITHM_H
+#define KFILEITEMMODELSORTALGORITHM_H
+
+#include <libdolphin_export.h>
+
+#include <kitemviews/kfileitemmodel.h>
+
+/**
+ * @brief Sort algorithm for sorting items of KFileItemModel.
+ *
+ * Sorts the items by using KFileItemModel::lessThan() as comparison criteria.
+ * The merge sort algorithm is used to assure a worst-case
+ * of O(n * log(n)) and to keep the number of comparisons low.
+ *
+ * The implementation is based on qStableSortHelper() from qalgorithms.h
+ * Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+ * The sorting implementations of qAlgorithms could not be used as they
+ * don't support having a member-function as comparison criteria.
+ */
+class LIBDOLPHINPRIVATE_EXPORT KFileItemModelSortAlgorithm
+{
+public:
+ static void sort(KFileItemModel* model,
+ QList<KFileItemModel::ItemData*>::iterator begin,
+ QList<KFileItemModel::ItemData*>::iterator end);
+
+private:
+ static void merge(KFileItemModel* model,
+ QList<KFileItemModel::ItemData*>::iterator begin,
+ QList<KFileItemModel::ItemData*>::iterator pivot,
+ QList<KFileItemModel::ItemData*>::iterator end);
+
+ static QList<KFileItemModel::ItemData*>::iterator
+ lowerBound(KFileItemModel* model,
+ QList<KFileItemModel::ItemData*>::iterator begin,
+ QList<KFileItemModel::ItemData*>::iterator end,
+ const KFileItemModel::ItemData* value);
+
+ static QList<KFileItemModel::ItemData*>::iterator
+ upperBound(KFileItemModel* model,
+ QList<KFileItemModel::ItemData*>::iterator begin,
+ QList<KFileItemModel::ItemData*>::iterator end,
+ const KFileItemModel::ItemData* value);
+
+ static void reverse(QList<KFileItemModel::ItemData*>::iterator begin,
+ QList<KFileItemModel::ItemData*>::iterator end);
+};
+
+#endif
+
+
diff --git a/src/kitemviews/private/kitemlistheaderwidget.cpp b/src/kitemviews/private/kitemlistheaderwidget.cpp
new file mode 100644
index 000000000..576516f25
--- /dev/null
+++ b/src/kitemviews/private/kitemlistheaderwidget.cpp
@@ -0,0 +1,535 @@
+/***************************************************************************
+ * Copyright (C) 2011 by Peter Penz <[email protected]> *
+ * *
+ * 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, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#include "kitemlistheaderwidget.h"
+
+#include <KAction>
+#include <KMenu>
+#include <kitemviews/kitemmodelbase.h>
+
+#include <QApplication>
+#include <QGraphicsSceneHoverEvent>
+#include <QPainter>
+#include <QStyleOptionHeader>
+
+#include <KDebug>
+
+KItemListHeaderWidget::KItemListHeaderWidget(QGraphicsWidget* parent) :
+ QGraphicsWidget(parent),
+ m_automaticColumnResizing(true),
+ m_model(0),
+ m_offset(0),
+ m_columns(),
+ m_columnWidths(),
+ m_preferredColumnWidths(),
+ m_hoveredRoleIndex(-1),
+ m_pressedRoleIndex(-1),
+ m_roleOperation(NoRoleOperation),
+ m_pressedMousePos(),
+ m_movingRole()
+{
+ m_movingRole.x = 0;
+ m_movingRole.xDec = 0;
+ m_movingRole.index = -1;
+
+ setAcceptHoverEvents(true);
+}
+
+KItemListHeaderWidget::~KItemListHeaderWidget()
+{
+}
+
+void KItemListHeaderWidget::setModel(KItemModelBase* model)
+{
+ if (m_model == model) {
+ return;
+ }
+
+ if (m_model) {
+ disconnect(m_model, SIGNAL(sortRoleChanged(QByteArray,QByteArray)),
+ this, SLOT(slotSortRoleChanged(QByteArray,QByteArray)));
+ disconnect(m_model, SIGNAL(sortOrderChanged(Qt::SortOrder,Qt::SortOrder)),
+ this, SLOT(slotSortOrderChanged(Qt::SortOrder,Qt::SortOrder)));
+ }
+
+ m_model = model;
+
+ if (m_model) {
+ connect(m_model, SIGNAL(sortRoleChanged(QByteArray,QByteArray)),
+ this, SLOT(slotSortRoleChanged(QByteArray,QByteArray)));
+ connect(m_model, SIGNAL(sortOrderChanged(Qt::SortOrder,Qt::SortOrder)),
+ this, SLOT(slotSortOrderChanged(Qt::SortOrder,Qt::SortOrder)));
+ }
+}
+
+KItemModelBase* KItemListHeaderWidget::model() const
+{
+ return m_model;
+}
+
+void KItemListHeaderWidget::setAutomaticColumnResizing(bool automatic)
+{
+ m_automaticColumnResizing = automatic;
+}
+
+bool KItemListHeaderWidget::automaticColumnResizing() const
+{
+ return m_automaticColumnResizing;
+}
+
+void KItemListHeaderWidget::setColumns(const QList<QByteArray>& roles)
+{
+ foreach (const QByteArray& role, roles) {
+ if (!m_columnWidths.contains(role)) {
+ m_columnWidths.remove(role);
+ m_preferredColumnWidths.remove(role);
+ }
+ }
+
+ m_columns = roles;
+ update();
+}
+
+QList<QByteArray> KItemListHeaderWidget::columns() const
+{
+ return m_columns;
+}
+
+void KItemListHeaderWidget::setColumnWidth(const QByteArray& role, qreal width)
+{
+ const qreal minWidth = minimumColumnWidth();
+ if (width < minWidth) {
+ width = minWidth;
+ }
+
+ if (m_columnWidths.value(role) != width) {
+ m_columnWidths.insert(role, width);
+ update();
+ }
+}
+
+qreal KItemListHeaderWidget::columnWidth(const QByteArray& role) const
+{
+ return m_columnWidths.value(role);
+}
+
+void KItemListHeaderWidget::setPreferredColumnWidth(const QByteArray& role, qreal width)
+{
+ m_preferredColumnWidths.insert(role, width);
+}
+
+qreal KItemListHeaderWidget::preferredColumnWidth(const QByteArray& role) const
+{
+ return m_preferredColumnWidths.value(role);
+}
+
+void KItemListHeaderWidget::setOffset(qreal offset)
+{
+ if (m_offset != offset) {
+ m_offset = offset;
+ update();
+ }
+}
+
+qreal KItemListHeaderWidget::offset() const
+{
+ return m_offset;
+}
+
+qreal KItemListHeaderWidget::minimumColumnWidth() const
+{
+ QFontMetricsF fontMetrics(font());
+ return fontMetrics.height() * 4;
+}
+
+void KItemListHeaderWidget::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
+{
+ Q_UNUSED(option);
+ Q_UNUSED(widget);
+
+ if (!m_model) {
+ return;
+ }
+
+ // Draw roles
+ painter->setFont(font());
+ painter->setPen(palette().text().color());
+
+ qreal x = -m_offset;
+ int orderIndex = 0;
+ foreach (const QByteArray& role, m_columns) {
+ const qreal roleWidth = m_columnWidths.value(role);
+ const QRectF rect(x, 0, roleWidth, size().height());
+ paintRole(painter, role, rect, orderIndex, widget);
+ x += roleWidth;
+ ++orderIndex;
+ }
+
+ // Draw background without roles
+ QStyleOption opt;
+ opt.init(widget);
+ opt.rect = QRect(x, 0, size().width() - x, size().height());
+ opt.state |= QStyle::State_Horizontal;
+ style()->drawControl(QStyle::CE_HeaderEmptyArea, &opt, painter);
+
+ if (!m_movingRole.pixmap.isNull()) {
+ Q_ASSERT(m_roleOperation == MoveRoleOperation);
+ painter->drawPixmap(m_movingRole.x, 0, m_movingRole.pixmap);
+ }
+}
+
+void KItemListHeaderWidget::mousePressEvent(QGraphicsSceneMouseEvent* event)
+{
+ if (event->button() & Qt::LeftButton) {
+ updatePressedRoleIndex(event->pos());
+ m_pressedMousePos = event->pos();
+ m_roleOperation = isAboveRoleGrip(m_pressedMousePos, m_pressedRoleIndex) ?
+ ResizeRoleOperation : NoRoleOperation;
+ event->accept();
+ } else {
+ event->ignore();
+ }
+}
+
+void KItemListHeaderWidget::mouseReleaseEvent(QGraphicsSceneMouseEvent* event)
+{
+ QGraphicsWidget::mouseReleaseEvent(event);
+
+ if (m_pressedRoleIndex == -1) {
+ return;
+ }
+
+ switch (m_roleOperation) {
+ case NoRoleOperation: {
+ // Only a click has been done and no moving or resizing has been started
+ const QByteArray sortRole = m_model->sortRole();
+ const int sortRoleIndex = m_columns.indexOf(sortRole);
+ if (m_pressedRoleIndex == sortRoleIndex) {
+ // Toggle the sort order
+ const Qt::SortOrder previous = m_model->sortOrder();
+ const Qt::SortOrder current = (m_model->sortOrder() == Qt::AscendingOrder) ?
+ Qt::DescendingOrder : Qt::AscendingOrder;
+ m_model->setSortOrder(current);
+ emit sortOrderChanged(current, previous);
+ } else {
+ // Change the sort role
+ const QByteArray previous = m_model->sortRole();
+ const QByteArray current = m_columns[m_pressedRoleIndex];
+ m_model->setSortRole(current);
+ emit sortRoleChanged(current, previous);
+ }
+ break;
+ }
+
+ case MoveRoleOperation:
+ m_movingRole.pixmap = QPixmap();
+ m_movingRole.x = 0;
+ m_movingRole.xDec = 0;
+ m_movingRole.index = -1;
+ break;
+
+ default:
+ break;
+ }
+
+ m_pressedRoleIndex = -1;
+ m_roleOperation = NoRoleOperation;
+ update();
+
+ QApplication::restoreOverrideCursor();
+}
+
+void KItemListHeaderWidget::mouseMoveEvent(QGraphicsSceneMouseEvent* event)
+{
+ QGraphicsWidget::mouseMoveEvent(event);
+
+ switch (m_roleOperation) {
+ case NoRoleOperation:
+ if ((event->pos() - m_pressedMousePos).manhattanLength() >= QApplication::startDragDistance()) {
+ // A role gets dragged by the user. Create a pixmap of the role that will get
+ // synchronized on each furter mouse-move-event with the mouse-position.
+ m_roleOperation = MoveRoleOperation;
+ const int roleIndex = roleIndexAt(m_pressedMousePos);
+ m_movingRole.index = roleIndex;
+ if (roleIndex == 0) {
+ // TODO: It should be configurable whether moving the first role is allowed.
+ // In the context of Dolphin this is not required, however this should be
+ // changed if KItemViews are used in a more generic way.
+ QApplication::setOverrideCursor(QCursor(Qt::ForbiddenCursor));
+ } else {
+ m_movingRole.pixmap = createRolePixmap(roleIndex);
+
+ qreal roleX = -m_offset;
+ for (int i = 0; i < roleIndex; ++i) {
+ const QByteArray role = m_columns[i];
+ roleX += m_columnWidths.value(role);
+ }
+
+ m_movingRole.xDec = event->pos().x() - roleX;
+ m_movingRole.x = roleX;
+ update();
+ }
+ }
+ break;
+
+ case ResizeRoleOperation: {
+ const QByteArray pressedRole = m_columns[m_pressedRoleIndex];
+
+ qreal previousWidth = m_columnWidths.value(pressedRole);
+ qreal currentWidth = previousWidth;
+ currentWidth += event->pos().x() - event->lastPos().x();
+ currentWidth = qMax(minimumColumnWidth(), currentWidth);
+
+ m_columnWidths.insert(pressedRole, currentWidth);
+ update();
+
+ emit columnWidthChanged(pressedRole, currentWidth, previousWidth);
+ break;
+ }
+
+ case MoveRoleOperation: {
+ // TODO: It should be configurable whether moving the first role is allowed.
+ // In the context of Dolphin this is not required, however this should be
+ // changed if KItemViews are used in a more generic way.
+ if (m_movingRole.index > 0) {
+ m_movingRole.x = event->pos().x() - m_movingRole.xDec;
+ update();
+
+ const int targetIndex = targetOfMovingRole();
+ if (targetIndex > 0 && targetIndex != m_movingRole.index) {
+ const QByteArray role = m_columns[m_movingRole.index];
+ const int previousIndex = m_movingRole.index;
+ m_movingRole.index = targetIndex;
+ emit columnMoved(role, targetIndex, previousIndex);
+
+ m_movingRole.xDec = event->pos().x() - roleXPosition(role);
+ }
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+}
+
+void KItemListHeaderWidget::hoverEnterEvent(QGraphicsSceneHoverEvent* event)
+{
+ QGraphicsWidget::hoverEnterEvent(event);
+ updateHoveredRoleIndex(event->pos());
+}
+
+void KItemListHeaderWidget::hoverLeaveEvent(QGraphicsSceneHoverEvent* event)
+{
+ QGraphicsWidget::hoverLeaveEvent(event);
+ if (m_hoveredRoleIndex != -1) {
+ m_hoveredRoleIndex = -1;
+ update();
+ }
+}
+
+void KItemListHeaderWidget::hoverMoveEvent(QGraphicsSceneHoverEvent* event)
+{
+ QGraphicsWidget::hoverMoveEvent(event);
+
+ const QPointF& pos = event->pos();
+ updateHoveredRoleIndex(pos);
+ if (m_hoveredRoleIndex >= 0 && isAboveRoleGrip(pos, m_hoveredRoleIndex)) {
+ setCursor(Qt::SplitHCursor);
+ } else {
+ unsetCursor();
+ }
+}
+
+void KItemListHeaderWidget::slotSortRoleChanged(const QByteArray& current, const QByteArray& previous)
+{
+ Q_UNUSED(current);
+ Q_UNUSED(previous);
+ update();
+}
+
+void KItemListHeaderWidget::slotSortOrderChanged(Qt::SortOrder current, Qt::SortOrder previous)
+{
+ Q_UNUSED(current);
+ Q_UNUSED(previous);
+ update();
+}
+
+void KItemListHeaderWidget::paintRole(QPainter* painter,
+ const QByteArray& role,
+ const QRectF& rect,
+ int orderIndex,
+ QWidget* widget) const
+{
+ // The following code is based on the code from QHeaderView::paintSection().
+ // Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+ QStyleOptionHeader option;
+ option.section = orderIndex;
+ option.state = QStyle::State_None | QStyle::State_Raised | QStyle::State_Horizontal;
+ if (isEnabled()) {
+ option.state |= QStyle::State_Enabled;
+ }
+ if (window() && window()->isActiveWindow()) {
+ option.state |= QStyle::State_Active;
+ }
+ if (m_hoveredRoleIndex == orderIndex) {
+ option.state |= QStyle::State_MouseOver;
+ }
+ if (m_pressedRoleIndex == orderIndex) {
+ option.state |= QStyle::State_Sunken;
+ }
+ if (m_model->sortRole() == role) {
+ option.sortIndicator = (m_model->sortOrder() == Qt::AscendingOrder) ?
+ QStyleOptionHeader::SortDown : QStyleOptionHeader::SortUp;
+ }
+ option.rect = rect.toRect();
+
+ if (m_columns.count() == 1) {
+ option.position = QStyleOptionHeader::OnlyOneSection;
+ } else if (orderIndex == 0) {
+ option.position = QStyleOptionHeader::Beginning;
+ } else if (orderIndex == m_columns.count() - 1) {
+ option.position = QStyleOptionHeader::End;
+ } else {
+ option.position = QStyleOptionHeader::Middle;
+ }
+
+ option.orientation = Qt::Horizontal;
+ option.selectedPosition = QStyleOptionHeader::NotAdjacent;
+ option.text = m_model->roleDescription(role);
+
+ style()->drawControl(QStyle::CE_Header, &option, painter, widget);
+}
+
+void KItemListHeaderWidget::updatePressedRoleIndex(const QPointF& pos)
+{
+ const int pressedIndex = roleIndexAt(pos);
+ if (m_pressedRoleIndex != pressedIndex) {
+ m_pressedRoleIndex = pressedIndex;
+ update();
+ }
+}
+
+void KItemListHeaderWidget::updateHoveredRoleIndex(const QPointF& pos)
+{
+ const int hoverIndex = roleIndexAt(pos);
+ if (m_hoveredRoleIndex != hoverIndex) {
+ m_hoveredRoleIndex = hoverIndex;
+ update();
+ }
+}
+
+int KItemListHeaderWidget::roleIndexAt(const QPointF& pos) const
+{
+ int index = -1;
+
+ qreal x = -m_offset;
+ foreach (const QByteArray& role, m_columns) {
+ ++index;
+ x += m_columnWidths.value(role);
+ if (pos.x() <= x) {
+ break;
+ }
+ }
+
+ return index;
+}
+
+bool KItemListHeaderWidget::isAboveRoleGrip(const QPointF& pos, int roleIndex) const
+{
+ qreal x = -m_offset;
+ for (int i = 0; i <= roleIndex; ++i) {
+ const QByteArray role = m_columns[i];
+ x += m_columnWidths.value(role);
+ }
+
+ const int grip = style()->pixelMetric(QStyle::PM_HeaderGripMargin);
+ return pos.x() >= (x - grip) && pos.x() <= x;
+}
+
+QPixmap KItemListHeaderWidget::createRolePixmap(int roleIndex) const
+{
+ const QByteArray role = m_columns[roleIndex];
+ const qreal roleWidth = m_columnWidths.value(role);
+ const QRect rect(0, 0, roleWidth, size().height());
+
+ QImage image(rect.size(), QImage::Format_ARGB32_Premultiplied);
+
+ QPainter painter(&image);
+ paintRole(&painter, role, rect, roleIndex);
+
+ // Apply a highlighting-color
+ const QPalette::ColorGroup group = isActiveWindow() ? QPalette::Active : QPalette::Inactive;
+ QColor highlightColor = palette().color(group, QPalette::Highlight);
+ highlightColor.setAlpha(64);
+ painter.fillRect(rect, highlightColor);
+
+ // Make the image transparent
+ painter.setCompositionMode(QPainter::CompositionMode_DestinationIn);
+ painter.fillRect(0, 0, image.width(), image.height(), QColor(0, 0, 0, 192));
+
+ return QPixmap::fromImage(image);
+}
+
+int KItemListHeaderWidget::targetOfMovingRole() const
+{
+ const int movingWidth = m_movingRole.pixmap.width();
+ const int movingLeft = m_movingRole.x;
+ const int movingRight = movingLeft + movingWidth - 1;
+
+ int targetIndex = 0;
+ qreal targetLeft = -m_offset;
+ while (targetIndex < m_columns.count()) {
+ const QByteArray role = m_columns[targetIndex];
+ const qreal targetWidth = m_columnWidths.value(role);
+ const qreal targetRight = targetLeft + targetWidth - 1;
+
+ const bool isInTarget = (targetWidth >= movingWidth &&
+ movingLeft >= targetLeft &&
+ movingRight <= targetRight) ||
+ (targetWidth < movingWidth &&
+ movingLeft <= targetLeft &&
+ movingRight >= targetRight);
+
+ if (isInTarget) {
+ return targetIndex;
+ }
+
+ targetLeft += targetWidth;
+ ++targetIndex;
+ }
+
+ return m_movingRole.index;
+}
+
+qreal KItemListHeaderWidget::roleXPosition(const QByteArray& role) const
+{
+ qreal x = -m_offset;
+ foreach (const QByteArray& visibleRole, m_columns) {
+ if (visibleRole == role) {
+ return x;
+ }
+
+ x += m_columnWidths.value(visibleRole);
+ }
+
+ return -1;
+}
+
+#include "kitemlistheaderwidget.moc"
diff --git a/src/kitemviews/private/kitemlistheaderwidget.h b/src/kitemviews/private/kitemlistheaderwidget.h
new file mode 100644
index 000000000..f8bba977b
--- /dev/null
+++ b/src/kitemviews/private/kitemlistheaderwidget.h
@@ -0,0 +1,171 @@
+/***************************************************************************
+ * Copyright (C) 2011 by Peter Penz <[email protected]> *
+ * *
+ * 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, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#ifndef KITEMLISTHEADERWIDGET_H
+#define KITEMLISTHEADERWIDGET_H
+
+#include <libdolphin_export.h>
+#include <QGraphicsWidget>
+#include <QHash>
+#include <QList>
+
+class KItemModelBase;
+
+/**
+ * @brief Widget the implements the header for KItemListView showing the currently used roles.
+ *
+ * The widget is an internal API, the user of KItemListView may only access the
+ * class KItemListHeader.
+ */
+class LIBDOLPHINPRIVATE_EXPORT KItemListHeaderWidget : public QGraphicsWidget
+{
+ Q_OBJECT
+
+public:
+ KItemListHeaderWidget(QGraphicsWidget* parent = 0);
+ virtual ~KItemListHeaderWidget();
+
+ void setModel(KItemModelBase* model);
+ KItemModelBase* model() const;
+
+ void setAutomaticColumnResizing(bool automatic);
+ bool automaticColumnResizing() const;
+
+ void setColumns(const QList<QByteArray>& roles);
+ QList<QByteArray> columns() const;
+
+ void setColumnWidth(const QByteArray& role, qreal width);
+ qreal columnWidth(const QByteArray& role) const;
+
+ /**
+ * Sets the column-width that is required to show the role unclipped.
+ */
+ void setPreferredColumnWidth(const QByteArray& role, qreal width);
+ qreal preferredColumnWidth(const QByteArray& role) const;
+
+ void setOffset(qreal offset);
+ qreal offset() const;
+
+ qreal minimumColumnWidth() const;
+
+ virtual void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget = 0);
+
+signals:
+ /**
+ * Is emitted if the width of a visible role has been adjusted by the user with the mouse
+ * (no signal is emitted if KItemListHeader::setVisibleRoleWidth() is invoked).
+ */
+ void columnWidthChanged(const QByteArray& role,
+ qreal currentWidth,
+ qreal previousWidth);
+
+ /**
+ * Is emitted if the position of the column has been changed.
+ */
+ void columnMoved(const QByteArray& role, int currentIndex, int previousIndex);
+
+ /**
+ * Is emitted if the user has changed the sort order by clicking on a
+ * header item. The sort order of the model has already been adjusted to
+ * the current sort order. Note that no signal will be emitted if the
+ * sort order of the model has been changed without user interaction.
+ */
+ void sortOrderChanged(Qt::SortOrder current, Qt::SortOrder previous);
+
+ /**
+ * Is emitted if the user has changed the sort role by clicking on a
+ * header item. The sort role of the model has already been adjusted to
+ * the current sort role. Note that no signal will be emitted if the
+ * sort role of the model has been changed without user interaction.
+ */
+ void sortRoleChanged(const QByteArray& current, const QByteArray& previous);
+
+protected:
+ virtual void mousePressEvent(QGraphicsSceneMouseEvent* event);
+ virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent* event);
+ virtual void mouseMoveEvent(QGraphicsSceneMouseEvent* event);
+ virtual void hoverEnterEvent(QGraphicsSceneHoverEvent* event);
+ virtual void hoverLeaveEvent(QGraphicsSceneHoverEvent* event);
+ virtual void hoverMoveEvent(QGraphicsSceneHoverEvent* event);
+
+private slots:
+ void slotSortRoleChanged(const QByteArray& current, const QByteArray& previous);
+ void slotSortOrderChanged(Qt::SortOrder current, Qt::SortOrder previous);
+
+private:
+ void paintRole(QPainter* painter,
+ const QByteArray& role,
+ const QRectF& rect,
+ int orderIndex,
+ QWidget* widget = 0) const;
+
+ void updatePressedRoleIndex(const QPointF& pos);
+ void updateHoveredRoleIndex(const QPointF& pos);
+ int roleIndexAt(const QPointF& pos) const;
+ bool isAboveRoleGrip(const QPointF& pos, int roleIndex) const;
+
+ /**
+ * Creates a pixmap of the role with the index \a roleIndex that is shown
+ * during moving a role.
+ */
+ QPixmap createRolePixmap(int roleIndex) const;
+
+ /**
+ * @return Target index of the currently moving visible role based on the current
+ * state of m_movingRole.
+ */
+ int targetOfMovingRole() const;
+
+ /**
+ * @return x-position of the left border of the role \a role.
+ */
+ qreal roleXPosition(const QByteArray& role) const;
+
+private:
+ enum RoleOperation
+ {
+ NoRoleOperation,
+ ResizeRoleOperation,
+ MoveRoleOperation
+ };
+
+ bool m_automaticColumnResizing;
+ KItemModelBase* m_model;
+ qreal m_offset;
+ QList<QByteArray> m_columns;
+ QHash<QByteArray, qreal> m_columnWidths;
+ QHash<QByteArray, qreal> m_preferredColumnWidths;
+
+ int m_hoveredRoleIndex;
+ int m_pressedRoleIndex;
+ RoleOperation m_roleOperation;
+ QPointF m_pressedMousePos;
+
+ struct MovingRole
+ {
+ QPixmap pixmap;
+ int x;
+ int xDec;
+ int index;
+ } m_movingRole;
+};
+
+#endif
+
+
diff --git a/src/kitemviews/private/kitemlistkeyboardsearchmanager.cpp b/src/kitemviews/private/kitemlistkeyboardsearchmanager.cpp
new file mode 100644
index 000000000..2f4e93b1d
--- /dev/null
+++ b/src/kitemviews/private/kitemlistkeyboardsearchmanager.cpp
@@ -0,0 +1,78 @@
+/***************************************************************************
+ * Copyright (C) 2011 by Tirtha Chatterjee <[email protected]> *
+ * *
+ * Based on the Itemviews NG project from Trolltech Labs: *
+ * http://qt.gitorious.org/qt-labs/itemviews-ng *
+ * *
+ * 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, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#include "kitemlistkeyboardsearchmanager.h"
+
+#include <QApplication>
+#include <QElapsedTimer>
+
+#include <KDebug>
+
+KItemListKeyboardSearchManager::KItemListKeyboardSearchManager(QObject* parent) :
+ QObject(parent),
+ m_timeout(5000)
+{
+ m_keyboardInputTime.invalidate();
+}
+
+KItemListKeyboardSearchManager::~KItemListKeyboardSearchManager()
+{
+}
+
+void KItemListKeyboardSearchManager::addKeys(const QString& keys)
+{
+ const bool keyboardTimeWasValid = m_keyboardInputTime.isValid();
+ const qint64 keyboardInputTimeElapsed = m_keyboardInputTime.restart();
+ if (keyboardInputTimeElapsed > m_timeout || !keyboardTimeWasValid || keys.isEmpty()) {
+ m_searchedString.clear();
+ }
+
+ const bool newSearch = m_searchedString.isEmpty();
+
+ if (!keys.isEmpty()) {
+ m_searchedString.append(keys);
+
+ // Special case:
+ // If the same key is pressed repeatedly, the next item matching that key should be highlighted
+ const QChar firstKey = m_searchedString.length() > 0 ? m_searchedString.at(0) : QChar();
+ const bool sameKey = m_searchedString.length() > 1 && m_searchedString.count(firstKey) == m_searchedString.length();
+
+ // Searching for a matching item should start from the next item if either
+ // 1. a new search is started, or
+ // 2. a 'repeated key' search is done.
+ const bool searchFromNextItem = newSearch || sameKey;
+
+ emit changeCurrentItem(sameKey ? firstKey : m_searchedString, searchFromNextItem);
+ }
+ m_keyboardInputTime.start();
+}
+
+void KItemListKeyboardSearchManager::setTimeout(qint64 milliseconds)
+{
+ m_timeout = milliseconds;
+}
+
+qint64 KItemListKeyboardSearchManager::timeout() const
+{
+ return m_timeout;
+}
+
diff --git a/src/kitemviews/private/kitemlistrubberband.cpp b/src/kitemviews/private/kitemlistrubberband.cpp
new file mode 100644
index 000000000..ae023d2aa
--- /dev/null
+++ b/src/kitemviews/private/kitemlistrubberband.cpp
@@ -0,0 +1,75 @@
+/***************************************************************************
+ * Copyright (C) 2011 by Peter Penz <[email protected]> *
+ * *
+ * 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, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#include "kitemlistrubberband.h"
+
+KItemListRubberBand::KItemListRubberBand(QObject* parent) :
+ QObject(parent),
+ m_active(false),
+ m_startPos(),
+ m_endPos()
+{
+}
+
+KItemListRubberBand::~KItemListRubberBand()
+{
+}
+
+void KItemListRubberBand::setStartPosition(const QPointF& pos)
+{
+ if (m_startPos != pos) {
+ const QPointF previous = m_startPos;
+ m_startPos = pos;
+ emit startPositionChanged(m_startPos, previous);
+ }
+}
+
+QPointF KItemListRubberBand::startPosition() const
+{
+ return m_startPos;
+}
+
+void KItemListRubberBand::setEndPosition(const QPointF& pos)
+{
+ if (m_endPos != pos) {
+ const QPointF previous = m_endPos;
+ m_endPos = pos;
+ emit endPositionChanged(m_endPos, previous);
+ }
+}
+
+QPointF KItemListRubberBand::endPosition() const
+{
+ return m_endPos;
+}
+
+void KItemListRubberBand::setActive(bool active)
+{
+ if (m_active != active) {
+ m_active = active;
+ emit activationChanged(active);
+ }
+}
+
+bool KItemListRubberBand::isActive() const
+{
+ return m_active;
+}
+
+#include "kitemlistrubberband.moc"
diff --git a/src/kitemviews/private/kitemlistselectiontoggle.cpp b/src/kitemviews/private/kitemlistselectiontoggle.cpp
new file mode 100644
index 000000000..66da6a727
--- /dev/null
+++ b/src/kitemviews/private/kitemlistselectiontoggle.cpp
@@ -0,0 +1,106 @@
+/***************************************************************************
+ * Copyright (C) 2011 by Peter Penz <[email protected]> *
+ * *
+ * 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, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#include "kitemlistselectiontoggle.h"
+
+#include <KIconEffect>
+#include <KIconLoader>
+#include <QPainter>
+
+#include <KDebug>
+
+KItemListSelectionToggle::KItemListSelectionToggle(QGraphicsItem* parent) :
+ QGraphicsWidget(parent, 0),
+ m_checked(false),
+ m_hovered(false)
+{
+ setAcceptHoverEvents(true);
+}
+
+KItemListSelectionToggle::~KItemListSelectionToggle()
+{
+}
+
+void KItemListSelectionToggle::setChecked(bool checked)
+{
+ if (m_checked != checked) {
+ m_checked = checked;
+ m_pixmap = QPixmap();
+ update();
+ }
+}
+
+bool KItemListSelectionToggle::isChecked() const
+{
+ return m_checked;
+}
+
+void KItemListSelectionToggle::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
+{
+ Q_UNUSED(option);
+ Q_UNUSED(widget);
+
+ if (m_pixmap.isNull()) {
+ updatePixmap();
+ }
+
+ const qreal x = (size().width() - qreal(m_pixmap.width())) / 2;
+ const qreal y = (size().height() - qreal(m_pixmap.height())) / 2;
+ painter->drawPixmap(x, y, m_pixmap);
+}
+
+void KItemListSelectionToggle::hoverEnterEvent(QGraphicsSceneHoverEvent* event)
+{
+ QGraphicsWidget::hoverEnterEvent(event);
+ m_hovered = true;
+ m_pixmap = QPixmap();
+}
+
+void KItemListSelectionToggle::hoverLeaveEvent(QGraphicsSceneHoverEvent* event)
+{
+ QGraphicsWidget::hoverLeaveEvent(event);
+ m_hovered = false;
+ m_pixmap = QPixmap();
+}
+
+void KItemListSelectionToggle::updatePixmap()
+{
+ const char* icon = m_checked ? "list-remove" : "list-add";
+
+ int iconSize = qMin(size().width(), size().height());
+ if (iconSize < KIconLoader::SizeSmallMedium) {
+ iconSize = KIconLoader::SizeSmall;
+ } else if (iconSize < KIconLoader::SizeMedium) {
+ iconSize = KIconLoader::SizeSmallMedium;
+ } else if (iconSize < KIconLoader::SizeLarge) {
+ iconSize = KIconLoader::SizeMedium;
+ } else if (iconSize < KIconLoader::SizeHuge) {
+ iconSize = KIconLoader::SizeLarge;
+ } else if (iconSize < KIconLoader::SizeEnormous) {
+ iconSize = KIconLoader::SizeHuge;
+ }
+
+ m_pixmap = KIconLoader::global()->loadIcon(QLatin1String(icon), KIconLoader::NoGroup, iconSize);
+
+ if (m_hovered) {
+ KIconLoader::global()->iconEffect()->apply(m_pixmap, KIconLoader::Desktop, KIconLoader::ActiveState);
+ }
+}
+
+#include "kitemlistselectiontoggle.moc"
diff --git a/src/kitemviews/private/kitemlistselectiontoggle.h b/src/kitemviews/private/kitemlistselectiontoggle.h
new file mode 100644
index 000000000..a8050d811
--- /dev/null
+++ b/src/kitemviews/private/kitemlistselectiontoggle.h
@@ -0,0 +1,61 @@
+/***************************************************************************
+ * Copyright (C) 2011 by Peter Penz <[email protected]> *
+ * *
+ * 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, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#ifndef KITEMLISTSELECTIONTOGGLE_H
+#define KITEMLISTSELECTIONTOGGLE_H
+
+#include <libdolphin_export.h>
+
+#include <QGraphicsWidget>
+#include <QPixmap>
+
+class QPropertyAnimation;
+
+/**
+ * @brief Allows to toggle between the selected and unselected state of an item.
+ */
+class LIBDOLPHINPRIVATE_EXPORT KItemListSelectionToggle : public QGraphicsWidget
+{
+ Q_OBJECT
+
+public:
+ KItemListSelectionToggle(QGraphicsItem* parent);
+ virtual ~KItemListSelectionToggle();
+
+ void setChecked(bool checked);
+ bool isChecked() const;
+
+ virtual void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget = 0);
+
+protected:
+ virtual void hoverEnterEvent(QGraphicsSceneHoverEvent* event);
+ virtual void hoverLeaveEvent(QGraphicsSceneHoverEvent* event);
+
+private:
+ void updatePixmap();
+
+private:
+ bool m_checked;
+ bool m_hovered;
+ QPixmap m_pixmap;
+};
+
+#endif
+
+
diff --git a/src/kitemviews/private/kitemlistsizehintresolver.cpp b/src/kitemviews/private/kitemlistsizehintresolver.cpp
new file mode 100644
index 000000000..c76ff0f55
--- /dev/null
+++ b/src/kitemviews/private/kitemlistsizehintresolver.cpp
@@ -0,0 +1,88 @@
+/***************************************************************************
+ * Copyright (C) 2011 by Peter Penz <[email protected]> *
+ * *
+ * 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, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#include "kitemlistsizehintresolver.h"
+
+#include <kitemviews/kitemlistview.h>
+#include <KDebug>
+
+KItemListSizeHintResolver::KItemListSizeHintResolver(const KItemListView* itemListView) :
+ m_itemListView(itemListView),
+ m_sizeHintCache()
+{
+}
+
+KItemListSizeHintResolver::~KItemListSizeHintResolver()
+{
+}
+
+QSizeF KItemListSizeHintResolver::sizeHint(int index) const
+{
+ QSizeF size = m_sizeHintCache.at(index);
+ if (size.isEmpty()) {
+ size = m_itemListView->itemSizeHint(index);
+ m_sizeHintCache[index] = size;
+ }
+ return size;
+}
+
+void KItemListSizeHintResolver::itemsInserted(int index, int count)
+{
+ const int currentCount = m_sizeHintCache.count();
+ m_sizeHintCache.reserve(currentCount + count);
+ while (count > 0) {
+ m_sizeHintCache.insert(index, QSizeF());
+ ++index;
+ --count;
+ }
+}
+
+void KItemListSizeHintResolver::itemsRemoved(int index, int count)
+{
+ const QList<QSizeF>::iterator begin = m_sizeHintCache.begin() + index;
+ const QList<QSizeF>::iterator end = begin + count;
+ m_sizeHintCache.erase(begin, end);
+}
+
+void KItemListSizeHintResolver::itemsMoved(int index, int count)
+{
+ while (count) {
+ m_sizeHintCache[index] = QSizeF();
+ ++index;
+ --count;
+ }
+}
+
+void KItemListSizeHintResolver::itemsChanged(int index, int count, const QSet<QByteArray>& roles)
+{
+ Q_UNUSED(roles);
+ while (count) {
+ m_sizeHintCache[index] = QSizeF();
+ ++index;
+ --count;
+ }
+}
+
+void KItemListSizeHintResolver::clearCache()
+{
+ const int count = m_sizeHintCache.count();
+ for (int i = 0; i < count; ++i) {
+ m_sizeHintCache[i] = QSizeF();
+ }
+}
diff --git a/src/kitemviews/private/kitemlistsizehintresolver.h b/src/kitemviews/private/kitemlistsizehintresolver.h
new file mode 100644
index 000000000..1345e0321
--- /dev/null
+++ b/src/kitemviews/private/kitemlistsizehintresolver.h
@@ -0,0 +1,53 @@
+/***************************************************************************
+ * Copyright (C) 2011 by Peter Penz <[email protected]> *
+ * *
+ * 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, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#ifndef KITEMLISTSIZEHINTRESOLVER_H
+#define KITEMLISTSIZEHINTRESOLVER_H
+
+#include <libdolphin_export.h>
+
+#include <QByteArray>
+#include <QList>
+#include <QSizeF>
+
+class KItemListView;
+
+/**
+ * @brief Calculates and caches the sizehints of items in KItemListView.
+ */
+class LIBDOLPHINPRIVATE_EXPORT KItemListSizeHintResolver
+{
+public:
+ KItemListSizeHintResolver(const KItemListView* itemListView);
+ virtual ~KItemListSizeHintResolver();
+ QSizeF sizeHint(int index) const;
+
+ void itemsInserted(int index, int count);
+ void itemsRemoved(int index, int count);
+ void itemsMoved(int index, int count);
+ void itemsChanged(int index, int count, const QSet<QByteArray>& roles);
+
+ void clearCache();
+
+private:
+ const KItemListView* m_itemListView;
+ mutable QList<QSizeF> m_sizeHintCache;
+};
+
+#endif
diff --git a/src/kitemviews/private/kitemlistsmoothscroller.cpp b/src/kitemviews/private/kitemlistsmoothscroller.cpp
new file mode 100644
index 000000000..6987e1ce1
--- /dev/null
+++ b/src/kitemviews/private/kitemlistsmoothscroller.cpp
@@ -0,0 +1,207 @@
+/***************************************************************************
+ * Copyright (C) 2011 by Peter Penz <[email protected]> *
+ * *
+ * 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, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#include "kitemlistsmoothscroller.h"
+
+#include <KGlobalSettings>
+#include <QEvent>
+#include <QPropertyAnimation>
+#include <QScrollBar>
+#include <QWheelEvent>
+
+#include <KDebug>
+
+KItemListSmoothScroller::KItemListSmoothScroller(QScrollBar* scrollBar,
+ QObject* parent) :
+ QObject(parent),
+ m_scrollBarPressed(false),
+ m_smoothScrolling(true),
+ m_scrollBar(scrollBar),
+ m_animation(0)
+{
+ m_animation = new QPropertyAnimation(this);
+ const int duration = (KGlobalSettings::graphicEffectsLevel() == KGlobalSettings::NoEffects) ? 1 : 100;
+ m_animation->setDuration(duration);
+ connect(m_animation, SIGNAL(stateChanged(QAbstractAnimation::State,QAbstractAnimation::State)),
+ this, SLOT(slotAnimationStateChanged(QAbstractAnimation::State,QAbstractAnimation::State)));
+
+ m_scrollBar->installEventFilter(this);
+}
+
+KItemListSmoothScroller::~KItemListSmoothScroller()
+{
+}
+
+void KItemListSmoothScroller::setScrollBar(QScrollBar *scrollBar)
+{
+ m_scrollBar = scrollBar;
+}
+
+QScrollBar* KItemListSmoothScroller::scrollBar() const
+{
+ return m_scrollBar;
+}
+
+void KItemListSmoothScroller::setTargetObject(QObject* target)
+{
+ m_animation->setTargetObject(target);
+}
+
+QObject* KItemListSmoothScroller::targetObject() const
+{
+ return m_animation->targetObject();
+}
+
+void KItemListSmoothScroller::setPropertyName(const QByteArray& propertyName)
+{
+ m_animation->setPropertyName(propertyName);
+}
+
+QByteArray KItemListSmoothScroller::propertyName() const
+{
+ return m_animation->propertyName();
+}
+
+void KItemListSmoothScroller::scrollContentsBy(qreal distance)
+{
+ QObject* target = targetObject();
+ if (!target) {
+ return;
+ }
+
+ const QByteArray name = propertyName();
+ const qreal currentOffset = target->property(name).toReal();
+ if (static_cast<int>(currentOffset) == m_scrollBar->value()) {
+ // The current offset is already synchronous to the scrollbar
+ return;
+ }
+
+ const bool animRunning = (m_animation->state() == QAbstractAnimation::Running);
+ if (animRunning) {
+ // Stopping a running animation means skipping the range from the current offset
+ // until the target offset. To prevent skipping of the range the difference
+ // is added to the new target offset.
+ const qreal oldEndOffset = m_animation->endValue().toReal();
+ distance += (currentOffset - oldEndOffset);
+ }
+
+ const qreal endOffset = currentOffset - distance;
+ if (m_smoothScrolling || animRunning) {
+ qreal startOffset = currentOffset;
+ if (animRunning) {
+ // If the animation was running and has been interrupted by assigning a new end-offset
+ // one frame must be added to the start-offset to keep the animation smooth. This also
+ // assures that animation proceeds even in cases where new end-offset are triggered
+ // within a very short timeslots.
+ startOffset += (endOffset - currentOffset) * 1000 / (m_animation->duration() * 60);
+ if (currentOffset < endOffset) {
+ startOffset = qMin(startOffset, endOffset);
+ } else {
+ startOffset = qMax(startOffset, endOffset);
+ }
+ }
+
+ m_animation->stop();
+ m_animation->setStartValue(startOffset);
+ m_animation->setEndValue(endOffset);
+ m_animation->setEasingCurve(animRunning ? QEasingCurve::OutQuad : QEasingCurve::InOutQuad);
+ m_animation->start();
+ target->setProperty(name, startOffset);
+ } else {
+ target->setProperty(name, endOffset);
+ }
+}
+
+void KItemListSmoothScroller::scrollTo(qreal position)
+{
+ m_smoothScrolling = true;
+ m_scrollBar->setValue(position);
+}
+
+bool KItemListSmoothScroller::requestScrollBarUpdate(int newMaximum)
+{
+ if (m_animation->state() == QAbstractAnimation::Running) {
+ if (newMaximum == m_scrollBar->maximum()) {
+ // The value has been changed by the animation, no update
+ // of the scrollbars is required as their target state will be
+ // reached with the end of the animation.
+ return false;
+ }
+
+ // The maximum has been changed which indicates that the content
+ // of the view has been changed. Stop the animation in any case and
+ // update the scrollbars immediately.
+ m_animation->stop();
+ }
+ return true;
+}
+
+bool KItemListSmoothScroller::eventFilter(QObject* obj, QEvent* event)
+{
+ Q_ASSERT(obj == m_scrollBar);
+
+ switch (event->type()) {
+ case QEvent::MouseButtonPress:
+ m_scrollBarPressed = true;
+ m_smoothScrolling = true;
+ break;
+
+ case QEvent::MouseButtonRelease:
+ m_scrollBarPressed = false;
+ m_smoothScrolling = false;
+ break;
+
+ case QEvent::Wheel:
+ handleWheelEvent(static_cast<QWheelEvent*>(event));
+ break;
+
+ default:
+ break;
+ }
+
+ return QObject::eventFilter(obj, event);
+}
+
+void KItemListSmoothScroller::slotAnimationStateChanged(QAbstractAnimation::State newState,
+ QAbstractAnimation::State oldState)
+{
+ Q_UNUSED(oldState);
+ if (newState == QAbstractAnimation::Stopped && m_smoothScrolling && !m_scrollBarPressed) {
+ m_smoothScrolling = false;
+ }
+}
+
+void KItemListSmoothScroller::handleWheelEvent(QWheelEvent* event)
+{
+ const int numDegrees = event->delta() / 8;
+ const int numSteps = numDegrees / 15;
+
+ const bool previous = m_smoothScrolling;
+
+ m_smoothScrolling = true;
+ const int value = m_scrollBar->value();
+ const int pageStep = m_scrollBar->pageStep();
+ m_scrollBar->setValue(value - numSteps * pageStep);
+
+ m_smoothScrolling = previous;
+
+ event->accept();
+}
+
+#include "kitemlistsmoothscroller.moc"
diff --git a/src/kitemviews/private/kitemlistsmoothscroller.h b/src/kitemviews/private/kitemlistsmoothscroller.h
new file mode 100644
index 000000000..252c966c7
--- /dev/null
+++ b/src/kitemviews/private/kitemlistsmoothscroller.h
@@ -0,0 +1,103 @@
+/***************************************************************************
+ * Copyright (C) 2011 by Peter Penz <[email protected]> *
+ * *
+ * 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, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#ifndef KITEMLISTSMOOTHSCROLLER_H
+#define KITEMLISTSMOOTHSCROLLER_H
+
+#include <libdolphin_export.h>
+
+#include <QAbstractAnimation>
+#include <QObject>
+
+class QPropertyAnimation;
+class QScrollBar;
+class QWheelEvent;
+
+/**
+ * @brief Helper class for KItemListContainer to have a smooth
+ * scrolling when adjusting the scrollbars.
+ */
+class LIBDOLPHINPRIVATE_EXPORT KItemListSmoothScroller : public QObject
+{
+ Q_OBJECT
+
+public:
+ KItemListSmoothScroller(QScrollBar* scrollBar,
+ QObject* parent = 0);
+ virtual ~KItemListSmoothScroller();
+
+ void setScrollBar(QScrollBar* scrollBar);
+ QScrollBar* scrollBar() const;
+
+ void setTargetObject(QObject* target);
+ QObject* targetObject() const;
+
+ void setPropertyName(const QByteArray& propertyName);
+ QByteArray propertyName() const;
+
+ /**
+ * Adjusts the position of the target by \p distance
+ * pixels. Is invoked in the context of QAbstractScrollArea::scrollContentsBy()
+ * where the scrollbars already have the new position but the content
+ * has not been scrolled yet.
+ */
+ void scrollContentsBy(qreal distance);
+
+ /**
+ * Does a smooth-scrolling to the position \p position
+ * on the target and also adjusts the corresponding scrollbar
+ * to the new position.
+ */
+ void scrollTo(qreal position);
+
+ /**
+ * Must be invoked before the scrollbar should get updated to have a new
+ * maximum. True is returned if the new maximum can be applied. If false
+ * is returned the maximum has already been reached and the value will
+ * be reached at the end of the animation.
+ */
+ // TODO: This interface is tricky to understand. Try to make this more
+ // generic/readable if the corresponding code in KItemListContainer got
+ // stable.
+ bool requestScrollBarUpdate(int newMaximum);
+
+protected:
+ virtual bool eventFilter(QObject* obj, QEvent* event);
+
+private slots:
+ void slotAnimationStateChanged(QAbstractAnimation::State newState,
+ QAbstractAnimation::State oldState);
+
+private:
+ /**
+ * Results into a smooth-scrolling of the target dependent on the direction
+ * of the wheel event.
+ */
+ void handleWheelEvent(QWheelEvent* event);
+
+private:
+ bool m_scrollBarPressed;
+ bool m_smoothScrolling;
+ QScrollBar* m_scrollBar;
+ QPropertyAnimation* m_animation;
+};
+
+#endif
+
+
diff --git a/src/kitemviews/private/kitemlistviewanimation.cpp b/src/kitemviews/private/kitemlistviewanimation.cpp
new file mode 100644
index 000000000..e347c5bb1
--- /dev/null
+++ b/src/kitemviews/private/kitemlistviewanimation.cpp
@@ -0,0 +1,245 @@
+/***************************************************************************
+ * Copyright (C) 2011 by Peter Penz <[email protected]> *
+ * *
+ * 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, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#include "kitemlistviewanimation.h"
+
+#include <kitemviews/kitemlistview.h>
+
+#include <KDebug>
+#include <KGlobalSettings>
+
+#include <QGraphicsWidget>
+#include <QPropertyAnimation>
+
+KItemListViewAnimation::KItemListViewAnimation(QObject* parent) :
+ QObject(parent),
+ m_animationDuration(200),
+ m_scrollOrientation(Qt::Vertical),
+ m_scrollOffset(0),
+ m_animation()
+{
+ if (KGlobalSettings::graphicEffectsLevel() == KGlobalSettings::NoEffects) {
+ m_animationDuration = 1;
+ }
+}
+
+KItemListViewAnimation::~KItemListViewAnimation()
+{
+ for (int type = 0; type < AnimationTypeCount; ++type) {
+ qDeleteAll(m_animation[type]);
+ }
+}
+
+void KItemListViewAnimation::setScrollOrientation(Qt::Orientation orientation)
+{
+ m_scrollOrientation = orientation;
+}
+
+Qt::Orientation KItemListViewAnimation::scrollOrientation() const
+{
+ return m_scrollOrientation;
+}
+
+void KItemListViewAnimation::setScrollOffset(qreal offset)
+{
+ const qreal diff = m_scrollOffset - offset;
+ m_scrollOffset = offset;
+
+ // The change of the offset requires that the position of all
+ // animated QGraphicsWidgets get adjusted. An exception is made
+ // for the delete animation that should just fade away on the
+ // existing position.
+ for (int type = 0; type < AnimationTypeCount; ++type) {
+ if (type == DeleteAnimation) {
+ continue;
+ }
+
+ QHashIterator<QGraphicsWidget*, QPropertyAnimation*> it(m_animation[type]);
+ while (it.hasNext()) {
+ it.next();
+
+ QGraphicsWidget* widget = it.key();
+ QPropertyAnimation* propertyAnim = it.value();
+
+ QPointF currentPos = widget->pos();
+ if (m_scrollOrientation == Qt::Vertical) {
+ currentPos.ry() += diff;
+ } else {
+ currentPos.rx() += diff;
+ }
+
+ if (type == MovingAnimation) {
+ // Stop the animation, calculate the moved start- and end-value
+ // and restart the animation for the remaining duration.
+ const int remainingDuration = propertyAnim->duration()
+ - propertyAnim->currentTime();
+
+ const bool block = propertyAnim->signalsBlocked();
+ propertyAnim->blockSignals(true);
+ propertyAnim->stop();
+
+ QPointF endPos = propertyAnim->endValue().toPointF();
+ if (m_scrollOrientation == Qt::Vertical) {
+ endPos.ry() += diff;
+ } else {
+ endPos.rx() += diff;
+ }
+
+ propertyAnim->setDuration(remainingDuration);
+ propertyAnim->setStartValue(currentPos);
+ propertyAnim->setEndValue(endPos);
+ propertyAnim->start();
+ propertyAnim->blockSignals(block);
+ } else {
+ widget->setPos(currentPos);
+ }
+ }
+ }
+}
+
+qreal KItemListViewAnimation::scrollOffset() const
+{
+ return m_scrollOffset;
+}
+
+void KItemListViewAnimation::start(QGraphicsWidget* widget, AnimationType type, const QVariant& endValue)
+{
+ stop(widget, type);
+
+ QPropertyAnimation* propertyAnim = 0;
+
+ switch (type) {
+ case MovingAnimation: {
+ const QPointF newPos = endValue.toPointF();
+ if (newPos == widget->pos()) {
+ return;
+ }
+
+ propertyAnim = new QPropertyAnimation(widget, "pos");
+ propertyAnim->setDuration(m_animationDuration);
+ propertyAnim->setEndValue(newPos);
+ break;
+ }
+
+ case CreateAnimation: {
+ propertyAnim = new QPropertyAnimation(widget, "opacity");
+ propertyAnim->setEasingCurve(QEasingCurve::InQuart);
+ propertyAnim->setDuration(m_animationDuration);
+ propertyAnim->setStartValue(0.0);
+ propertyAnim->setEndValue(1.0);
+ break;
+ }
+
+ case DeleteAnimation: {
+ propertyAnim = new QPropertyAnimation(widget, "opacity");
+ propertyAnim->setEasingCurve(QEasingCurve::OutQuart);
+ propertyAnim->setDuration(m_animationDuration);
+ propertyAnim->setStartValue(1.0);
+ propertyAnim->setEndValue(0.0);
+ break;
+ }
+
+ case ResizeAnimation: {
+ const QSizeF newSize = endValue.toSizeF();
+ if (newSize == widget->size()) {
+ return;
+ }
+
+ propertyAnim = new QPropertyAnimation(widget, "size");
+ propertyAnim->setDuration(m_animationDuration);
+ propertyAnim->setEndValue(newSize);
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ Q_ASSERT(propertyAnim);
+ connect(propertyAnim, SIGNAL(finished()), this, SLOT(slotFinished()));
+ m_animation[type].insert(widget, propertyAnim);
+
+ propertyAnim->start();
+}
+
+void KItemListViewAnimation::stop(QGraphicsWidget* widget, AnimationType type)
+{
+ QPropertyAnimation* propertyAnim = m_animation[type].value(widget);
+ if (propertyAnim) {
+ propertyAnim->stop();
+
+ switch (type) {
+ case MovingAnimation: break;
+ case CreateAnimation: widget->setOpacity(1.0); break;
+ case DeleteAnimation: widget->setOpacity(0.0); break;
+ case ResizeAnimation: break;
+ default: break;
+ }
+
+ m_animation[type].remove(widget);
+ delete propertyAnim;
+
+ emit finished(widget, type);
+ }
+}
+
+void KItemListViewAnimation::stop(QGraphicsWidget* widget)
+{
+ for (int type = 0; type < AnimationTypeCount; ++type) {
+ stop(widget, static_cast<AnimationType>(type));
+ }
+}
+
+bool KItemListViewAnimation::isStarted(QGraphicsWidget *widget, AnimationType type) const
+{
+ return m_animation[type].value(widget);
+}
+
+bool KItemListViewAnimation::isStarted(QGraphicsWidget* widget) const
+{
+ for (int type = 0; type < AnimationTypeCount; ++type) {
+ if (isStarted(widget, static_cast<AnimationType>(type))) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void KItemListViewAnimation::slotFinished()
+{
+ QPropertyAnimation* finishedAnim = qobject_cast<QPropertyAnimation*>(sender());
+ for (int type = 0; type < AnimationTypeCount; ++type) {
+ QHashIterator<QGraphicsWidget*, QPropertyAnimation*> it(m_animation[type]);
+ while (it.hasNext()) {
+ it.next();
+ QPropertyAnimation* propertyAnim = it.value();
+ if (propertyAnim == finishedAnim) {
+ QGraphicsWidget* widget = it.key();
+ m_animation[type].remove(widget);
+ finishedAnim->deleteLater();
+
+ emit finished(widget, static_cast<AnimationType>(type));
+ return;
+ }
+ }
+ }
+ Q_ASSERT(false);
+}
+
+#include "kitemlistviewanimation.moc"
diff --git a/src/kitemviews/private/kitemlistviewanimation.h b/src/kitemviews/private/kitemlistviewanimation.h
new file mode 100644
index 000000000..a3aceb0f5
--- /dev/null
+++ b/src/kitemviews/private/kitemlistviewanimation.h
@@ -0,0 +1,106 @@
+/***************************************************************************
+ * Copyright (C) 2011 by Peter Penz <[email protected]> *
+ * *
+ * 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, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#ifndef KITEMLISTVIEWANIMATION_H
+#define KITEMLISTVIEWANIMATION_H
+
+#include <libdolphin_export.h>
+
+#include <QHash>
+#include <QObject>
+#include <QVariant>
+
+class KItemListView;
+class QGraphicsWidget;
+class QPointF;
+class QPropertyAnimation;
+
+/**
+ * @brief Internal helper class for KItemListView to animate the items.
+ *
+ * Supports item animations for moving, creating, deleting and resizing
+ * an item. Several applications can be applied to one item in parallel.
+ */
+class LIBDOLPHINPRIVATE_EXPORT KItemListViewAnimation : public QObject
+{
+ Q_OBJECT
+
+public:
+ enum AnimationType {
+ MovingAnimation,
+ CreateAnimation,
+ DeleteAnimation,
+ ResizeAnimation
+ };
+
+ KItemListViewAnimation(QObject* parent = 0);
+ virtual ~KItemListViewAnimation();
+
+ void setScrollOrientation(Qt::Orientation orientation);
+ Qt::Orientation scrollOrientation() const;
+
+ void setScrollOffset(qreal scrollOffset);
+ qreal scrollOffset() const;
+
+ /**
+ * Starts the animation of the type \a type for the widget \a widget. If an animation
+ * of the type is already running, this animation will be stopped before starting
+ * the new animation.
+ */
+ void start(QGraphicsWidget* widget, AnimationType type, const QVariant& endValue = QVariant());
+
+ /**
+ * Stops the animation of the type \a type for the widget \a widget.
+ */
+ void stop(QGraphicsWidget* widget, AnimationType type);
+
+ /**
+ * Stops all animations that have been applied to the widget \a widget.
+ */
+ void stop(QGraphicsWidget* widget);
+
+ /**
+ * @return True if the animation of the type \a type has been started
+ * for the widget \a widget..
+ */
+ bool isStarted(QGraphicsWidget *widget, AnimationType type) const;
+
+ /**
+ * @return True if any animation has been started for the widget.
+ */
+ bool isStarted(QGraphicsWidget* widget) const;
+
+signals:
+ void finished(QGraphicsWidget* widget, KItemListViewAnimation::AnimationType type);
+
+private slots:
+ void slotFinished();
+
+private:
+ enum { AnimationTypeCount = 4 };
+
+ int m_animationDuration;
+ Qt::Orientation m_scrollOrientation;
+ qreal m_scrollOffset;
+ QHash<QGraphicsWidget*, QPropertyAnimation*> m_animation[AnimationTypeCount];
+};
+
+#endif
+
+
diff --git a/src/kitemviews/private/kitemlistviewlayouter.cpp b/src/kitemviews/private/kitemlistviewlayouter.cpp
new file mode 100644
index 000000000..c15b44e13
--- /dev/null
+++ b/src/kitemviews/private/kitemlistviewlayouter.cpp
@@ -0,0 +1,630 @@
+/***************************************************************************
+ * Copyright (C) 2011 by Peter Penz <[email protected]> *
+ * *
+ * 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, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#include "kitemlistviewlayouter.h"
+
+#include <kitemviews/kitemmodelbase.h>
+#include "kitemlistsizehintresolver.h"
+
+#include <KDebug>
+
+// #define KITEMLISTVIEWLAYOUTER_DEBUG
+
+KItemListViewLayouter::KItemListViewLayouter(QObject* parent) :
+ QObject(parent),
+ m_dirty(true),
+ m_visibleIndexesDirty(true),
+ m_scrollOrientation(Qt::Vertical),
+ m_size(),
+ m_itemSize(128, 128),
+ m_itemMargin(),
+ m_headerHeight(0),
+ m_model(0),
+ m_sizeHintResolver(0),
+ m_scrollOffset(0),
+ m_maximumScrollOffset(0),
+ m_itemOffset(0),
+ m_maximumItemOffset(0),
+ m_firstVisibleIndex(-1),
+ m_lastVisibleIndex(-1),
+ m_columnWidth(0),
+ m_xPosInc(0),
+ m_columnCount(0),
+ m_groupItemIndexes(),
+ m_groupHeaderHeight(0),
+ m_groupHeaderMargin(0),
+ m_itemInfos()
+{
+}
+
+KItemListViewLayouter::~KItemListViewLayouter()
+{
+}
+
+void KItemListViewLayouter::setScrollOrientation(Qt::Orientation orientation)
+{
+ if (m_scrollOrientation != orientation) {
+ m_scrollOrientation = orientation;
+ m_dirty = true;
+ }
+}
+
+Qt::Orientation KItemListViewLayouter::scrollOrientation() const
+{
+ return m_scrollOrientation;
+}
+
+void KItemListViewLayouter::setSize(const QSizeF& size)
+{
+ if (m_size != size) {
+ m_size = size;
+ m_dirty = true;
+ }
+}
+
+QSizeF KItemListViewLayouter::size() const
+{
+ return m_size;
+}
+
+void KItemListViewLayouter::setItemSize(const QSizeF& size)
+{
+ if (m_itemSize != size) {
+ m_itemSize = size;
+ m_dirty = true;
+ }
+}
+
+QSizeF KItemListViewLayouter::itemSize() const
+{
+ return m_itemSize;
+}
+
+void KItemListViewLayouter::setItemMargin(const QSizeF& margin)
+{
+ if (m_itemMargin != margin) {
+ m_itemMargin = margin;
+ m_dirty = true;
+ }
+}
+
+QSizeF KItemListViewLayouter::itemMargin() const
+{
+ return m_itemMargin;
+}
+
+void KItemListViewLayouter::setHeaderHeight(qreal height)
+{
+ if (m_headerHeight != height) {
+ m_headerHeight = height;
+ m_dirty = true;
+ }
+}
+
+qreal KItemListViewLayouter::headerHeight() const
+{
+ return m_headerHeight;
+}
+
+void KItemListViewLayouter::setGroupHeaderHeight(qreal height)
+{
+ if (m_groupHeaderHeight != height) {
+ m_groupHeaderHeight = height;
+ m_dirty = true;
+ }
+}
+
+qreal KItemListViewLayouter::groupHeaderHeight() const
+{
+ return m_groupHeaderHeight;
+}
+
+void KItemListViewLayouter::setGroupHeaderMargin(qreal margin)
+{
+ if (m_groupHeaderMargin != margin) {
+ m_groupHeaderMargin = margin;
+ m_dirty = true;
+ }
+}
+
+qreal KItemListViewLayouter::groupHeaderMargin() const
+{
+ return m_groupHeaderMargin;
+}
+
+void KItemListViewLayouter::setScrollOffset(qreal offset)
+{
+ if (m_scrollOffset != offset) {
+ m_scrollOffset = offset;
+ m_visibleIndexesDirty = true;
+ }
+}
+
+qreal KItemListViewLayouter::scrollOffset() const
+{
+ return m_scrollOffset;
+}
+
+qreal KItemListViewLayouter::maximumScrollOffset() const
+{
+ const_cast<KItemListViewLayouter*>(this)->doLayout();
+ return m_maximumScrollOffset;
+}
+
+void KItemListViewLayouter::setItemOffset(qreal offset)
+{
+ if (m_itemOffset != offset) {
+ m_itemOffset = offset;
+ m_visibleIndexesDirty = true;
+ }
+}
+
+qreal KItemListViewLayouter::itemOffset() const
+{
+ return m_itemOffset;
+}
+
+qreal KItemListViewLayouter::maximumItemOffset() const
+{
+ const_cast<KItemListViewLayouter*>(this)->doLayout();
+ return m_maximumItemOffset;
+}
+
+void KItemListViewLayouter::setModel(const KItemModelBase* model)
+{
+ if (m_model != model) {
+ m_model = model;
+ m_dirty = true;
+ }
+}
+
+const KItemModelBase* KItemListViewLayouter::model() const
+{
+ return m_model;
+}
+
+void KItemListViewLayouter::setSizeHintResolver(const KItemListSizeHintResolver* sizeHintResolver)
+{
+ if (m_sizeHintResolver != sizeHintResolver) {
+ m_sizeHintResolver = sizeHintResolver;
+ m_dirty = true;
+ }
+}
+
+const KItemListSizeHintResolver* KItemListViewLayouter::sizeHintResolver() const
+{
+ return m_sizeHintResolver;
+}
+
+int KItemListViewLayouter::firstVisibleIndex() const
+{
+ const_cast<KItemListViewLayouter*>(this)->doLayout();
+ return m_firstVisibleIndex;
+}
+
+int KItemListViewLayouter::lastVisibleIndex() const
+{
+ const_cast<KItemListViewLayouter*>(this)->doLayout();
+ return m_lastVisibleIndex;
+}
+
+QRectF KItemListViewLayouter::itemRect(int index) const
+{
+ const_cast<KItemListViewLayouter*>(this)->doLayout();
+ if (index < 0 || index >= m_itemInfos.count()) {
+ return QRectF();
+ }
+
+ if (m_scrollOrientation == Qt::Horizontal) {
+ // Rotate the logical direction which is always vertical by 90°
+ // to get the physical horizontal direction
+ const QRectF& b = m_itemInfos[index].rect;
+ QRectF bounds(b.y(), b.x(), b.height(), b.width());
+ QPointF pos = bounds.topLeft();
+ pos.rx() -= m_scrollOffset;
+ bounds.moveTo(pos);
+ return bounds;
+ }
+
+ QRectF bounds = m_itemInfos[index].rect;
+ bounds.moveTo(bounds.topLeft() - QPointF(m_itemOffset, m_scrollOffset));
+ return bounds;
+}
+
+QRectF KItemListViewLayouter::groupHeaderRect(int index) const
+{
+ const_cast<KItemListViewLayouter*>(this)->doLayout();
+
+ const QRectF firstItemRect = itemRect(index);
+ QPointF pos = firstItemRect.topLeft();
+ if (pos.isNull()) {
+ return QRectF();
+ }
+
+ QSizeF size;
+ if (m_scrollOrientation == Qt::Vertical) {
+ pos.rx() = 0;
+ pos.ry() -= m_groupHeaderHeight;
+ size = QSizeF(m_size.width(), m_groupHeaderHeight);
+ } else {
+ pos.rx() -= m_itemMargin.width();
+ pos.ry() = 0;
+
+ // Determine the maximum width used in the
+ // current column. As the scroll-direction is
+ // Qt::Horizontal and m_itemRects is accessed directly,
+ // the logical height represents the visual width.
+ qreal width = minimumGroupHeaderWidth();
+ const qreal y = m_itemInfos[index].rect.y();
+ const int maxIndex = m_itemInfos.count() - 1;
+ while (index <= maxIndex) {
+ QRectF bounds = m_itemInfos[index].rect;
+ if (bounds.y() != y) {
+ break;
+ }
+
+ if (bounds.height() > width) {
+ width = bounds.height();
+ }
+
+ ++index;
+ }
+
+ size = QSizeF(width, m_size.height());
+ }
+ return QRectF(pos, size);
+}
+
+int KItemListViewLayouter::itemColumn(int index) const
+{
+ const_cast<KItemListViewLayouter*>(this)->doLayout();
+ if (index < 0 || index >= m_itemInfos.count()) {
+ return -1;
+ }
+
+ return (m_scrollOrientation == Qt::Vertical)
+ ? m_itemInfos[index].column
+ : m_itemInfos[index].row;
+}
+
+int KItemListViewLayouter::itemRow(int index) const
+{
+ const_cast<KItemListViewLayouter*>(this)->doLayout();
+ if (index < 0 || index >= m_itemInfos.count()) {
+ return -1;
+ }
+
+ return (m_scrollOrientation == Qt::Vertical)
+ ? m_itemInfos[index].row
+ : m_itemInfos[index].column;
+}
+
+int KItemListViewLayouter::maximumVisibleItems() const
+{
+ const_cast<KItemListViewLayouter*>(this)->doLayout();
+
+ const int height = static_cast<int>(m_size.height());
+ const int rowHeight = static_cast<int>(m_itemSize.height());
+ int rows = height / rowHeight;
+ if (height % rowHeight != 0) {
+ ++rows;
+ }
+
+ return rows * m_columnCount;
+}
+
+bool KItemListViewLayouter::isFirstGroupItem(int itemIndex) const
+{
+ const_cast<KItemListViewLayouter*>(this)->doLayout();
+ return m_groupItemIndexes.contains(itemIndex);
+}
+
+void KItemListViewLayouter::markAsDirty()
+{
+ m_dirty = true;
+}
+
+
+#ifndef QT_NO_DEBUG
+ bool KItemListViewLayouter::isDirty()
+ {
+ return m_dirty;
+ }
+#endif
+
+void KItemListViewLayouter::doLayout()
+{
+ if (m_dirty) {
+#ifdef KITEMLISTVIEWLAYOUTER_DEBUG
+ QElapsedTimer timer;
+ timer.start();
+#endif
+ m_visibleIndexesDirty = true;
+
+ QSizeF itemSize = m_itemSize;
+ QSizeF itemMargin = m_itemMargin;
+ QSizeF size = m_size;
+
+ const bool grouped = createGroupHeaders();
+
+ const bool horizontalScrolling = (m_scrollOrientation == Qt::Horizontal);
+ if (horizontalScrolling) {
+ // Flip everything so that the layout logically can work like having
+ // a vertical scrolling
+ itemSize.setWidth(m_itemSize.height());
+ itemSize.setHeight(m_itemSize.width());
+ itemMargin.setWidth(m_itemMargin.height());
+ itemMargin.setHeight(m_itemMargin.width());
+ size.setWidth(m_size.height());
+ size.setHeight(m_size.width());
+
+ if (grouped) {
+ // In the horizontal scrolling case all groups are aligned
+ // at the top, which decreases the available height. For the
+ // flipped data this means that the width must be decreased.
+ size.rwidth() -= m_groupHeaderHeight;
+ }
+ }
+
+ m_columnWidth = itemSize.width() + itemMargin.width();
+ const qreal widthForColumns = size.width() - itemMargin.width();
+ m_columnCount = qMax(1, int(widthForColumns / m_columnWidth));
+ m_xPosInc = itemMargin.width();
+
+ const int itemCount = m_model->count();
+ if (itemCount > m_columnCount && m_columnWidth >= 32) {
+ // Apply the unused width equally to each column
+ const qreal unusedWidth = widthForColumns - m_columnCount * m_columnWidth;
+ if (unusedWidth > 0) {
+ const qreal columnInc = unusedWidth / (m_columnCount + 1);
+ m_columnWidth += columnInc;
+ m_xPosInc += columnInc;
+ }
+ }
+
+ int rowCount = itemCount / m_columnCount;
+ if (itemCount % m_columnCount != 0) {
+ ++rowCount;
+ }
+
+ m_itemInfos.reserve(itemCount);
+
+ qreal y = m_headerHeight + itemMargin.height();
+ int row = 0;
+
+ int index = 0;
+ while (index < itemCount) {
+ qreal x = m_xPosInc;
+ qreal maxItemHeight = itemSize.height();
+
+ if (grouped) {
+ if (horizontalScrolling) {
+ // All group headers will always be aligned on the top and not
+ // flipped like the other properties
+ x += m_groupHeaderHeight;
+ }
+
+ if (m_groupItemIndexes.contains(index)) {
+ // The item is the first item of a group.
+ // Increase the y-position to provide space
+ // for the group header.
+ if (index > 0) {
+ // Only add a margin if there has been added another
+ // group already before
+ y += m_groupHeaderMargin;
+ } else if (!horizontalScrolling) {
+ // The first group header should be aligned on top
+ y -= itemMargin.height();
+ }
+
+ if (!horizontalScrolling) {
+ y += m_groupHeaderHeight;
+ }
+ }
+ }
+
+ int column = 0;
+ while (index < itemCount && column < m_columnCount) {
+ qreal requiredItemHeight = itemSize.height();
+ if (m_sizeHintResolver) {
+ const QSizeF sizeHint = m_sizeHintResolver->sizeHint(index);
+ const qreal sizeHintHeight = horizontalScrolling ? sizeHint.width() : sizeHint.height();
+ if (sizeHintHeight > requiredItemHeight) {
+ requiredItemHeight = sizeHintHeight;
+ }
+ }
+
+ const QRectF bounds(x, y, itemSize.width(), requiredItemHeight);
+ if (index < m_itemInfos.count()) {
+ m_itemInfos[index].rect = bounds;
+ m_itemInfos[index].column = column;
+ m_itemInfos[index].row = row;
+ } else {
+ ItemInfo itemInfo;
+ itemInfo.rect = bounds;
+ itemInfo.column = column;
+ itemInfo.row = row;
+ m_itemInfos.append(itemInfo);
+ }
+
+ if (grouped && horizontalScrolling) {
+ // When grouping is enabled in the horizontal mode, the header alignment
+ // looks like this:
+ // Header-1 Header-2 Header-3
+ // Item 1 Item 4 Item 7
+ // Item 2 Item 5 Item 8
+ // Item 3 Item 6 Item 9
+ // In this case 'requiredItemHeight' represents the column-width. We don't
+ // check the content of the header in the layouter to determine the required
+ // width, hence assure that at least a minimal width of 15 characters is given
+ // (in average a character requires the halve width of the font height).
+ //
+ // TODO: Let the group headers provide a minimum width and respect this width here
+ const qreal headerWidth = minimumGroupHeaderWidth();
+ if (requiredItemHeight < headerWidth) {
+ requiredItemHeight = headerWidth;
+ }
+ }
+
+ maxItemHeight = qMax(maxItemHeight, requiredItemHeight);
+ x += m_columnWidth;
+ ++index;
+ ++column;
+
+ if (grouped && m_groupItemIndexes.contains(index)) {
+ // The item represents the first index of a group
+ // and must aligned in the first column
+ break;
+ }
+ }
+
+ y += maxItemHeight + itemMargin.height();
+ ++row;
+ }
+ if (m_itemInfos.count() > itemCount) {
+ m_itemInfos.erase(m_itemInfos.begin() + itemCount,
+ m_itemInfos.end());
+ }
+
+ if (itemCount > 0) {
+ // Calculate the maximum y-range of the last row for m_maximumScrollOffset
+ m_maximumScrollOffset = m_itemInfos.last().rect.bottom();
+ const qreal rowY = m_itemInfos.last().rect.y();
+
+ int index = m_itemInfos.count() - 2;
+ while (index >= 0 && m_itemInfos[index].rect.bottom() >= rowY) {
+ m_maximumScrollOffset = qMax(m_maximumScrollOffset, m_itemInfos[index].rect.bottom());
+ --index;
+ }
+
+ m_maximumScrollOffset += itemMargin.height();
+
+ m_maximumItemOffset = m_columnCount * m_columnWidth;
+ } else {
+ m_maximumScrollOffset = 0;
+ m_maximumItemOffset = 0;
+ }
+
+#ifdef KITEMLISTVIEWLAYOUTER_DEBUG
+ kDebug() << "[TIME] doLayout() for " << m_model->count() << "items:" << timer.elapsed();
+#endif
+ m_dirty = false;
+ }
+
+ updateVisibleIndexes();
+}
+
+void KItemListViewLayouter::updateVisibleIndexes()
+{
+ if (!m_visibleIndexesDirty) {
+ return;
+ }
+
+ Q_ASSERT(!m_dirty);
+
+ if (m_model->count() <= 0) {
+ m_firstVisibleIndex = -1;
+ m_lastVisibleIndex = -1;
+ m_visibleIndexesDirty = false;
+ return;
+ }
+
+ const int maxIndex = m_model->count() - 1;
+
+ // Calculate the first visible index that is fully visible
+ int min = 0;
+ int max = maxIndex;
+ int mid = 0;
+ do {
+ mid = (min + max) / 2;
+ if (m_itemInfos[mid].rect.top() < m_scrollOffset) {
+ min = mid + 1;
+ } else {
+ max = mid - 1;
+ }
+ } while (min <= max);
+
+ if (mid > 0) {
+ // Include the row before the first fully visible index, as it might
+ // be partly visible
+ if (m_itemInfos[mid].rect.top() >= m_scrollOffset) {
+ --mid;
+ Q_ASSERT(m_itemInfos[mid].rect.top() < m_scrollOffset);
+ }
+
+ const qreal rowTop = m_itemInfos[mid].rect.top();
+ while (mid > 0 && m_itemInfos[mid - 1].rect.top() == rowTop) {
+ --mid;
+ }
+ }
+ m_firstVisibleIndex = mid;
+
+ // Calculate the last visible index that is (at least partly) visible
+ const int visibleHeight = (m_scrollOrientation == Qt::Horizontal) ? m_size.width() : m_size.height();
+ qreal bottom = m_scrollOffset + visibleHeight;
+ if (m_model->groupedSorting()) {
+ bottom += m_groupHeaderHeight;
+ }
+
+ min = m_firstVisibleIndex;
+ max = maxIndex;
+ do {
+ mid = (min + max) / 2;
+ if (m_itemInfos[mid].rect.y() <= bottom) {
+ min = mid + 1;
+ } else {
+ max = mid - 1;
+ }
+ } while (min <= max);
+
+ while (mid > 0 && m_itemInfos[mid].rect.y() > bottom) {
+ --mid;
+ }
+ m_lastVisibleIndex = mid;
+
+ m_visibleIndexesDirty = false;
+}
+
+bool KItemListViewLayouter::createGroupHeaders()
+{
+ if (!m_model->groupedSorting()) {
+ return false;
+ }
+
+ m_groupItemIndexes.clear();
+
+ const QList<QPair<int, QVariant> > groups = m_model->groups();
+ if (groups.isEmpty()) {
+ return false;
+ }
+
+ for (int i = 0; i < groups.count(); ++i) {
+ const int firstItemIndex = groups.at(i).first;
+ m_groupItemIndexes.insert(firstItemIndex);
+ }
+
+ return true;
+}
+
+qreal KItemListViewLayouter::minimumGroupHeaderWidth() const
+{
+ return 100;
+}
+
+#include "kitemlistviewlayouter.moc"
diff --git a/src/kitemviews/private/kitemlistviewlayouter.h b/src/kitemviews/private/kitemlistviewlayouter.h
new file mode 100644
index 000000000..da5bd1d7d
--- /dev/null
+++ b/src/kitemviews/private/kitemlistviewlayouter.h
@@ -0,0 +1,235 @@
+/***************************************************************************
+ * Copyright (C) 2011 by Peter Penz <[email protected]> *
+ * *
+ * 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, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#ifndef KITEMLISTVIEWLAYOUTER_H
+#define KITEMLISTVIEWLAYOUTER_H
+
+#include <libdolphin_export.h>
+
+#include <QObject>
+#include <QRectF>
+#include <QSet>
+#include <QSizeF>
+
+class KItemModelBase;
+class KItemListSizeHintResolver;
+
+/**
+ * @brief Internal helper class for KItemListView to layout the items.
+ *
+ * The layouter is capable to align the items within a grid. If the
+ * scroll-direction is horizontal the column-width of the grid can be
+ * variable. If the scroll-direction is vertical the row-height of
+ * the grid can be variable.
+ *
+ * The layouter is implemented in a way that it postpones the expensive
+ * layout operation until a property is read the first time after
+ * marking the layouter as dirty (see markAsDirty()). This means that
+ * changing properties of the layouter is not expensive, only the
+ * first read of a property can get expensive.
+ */
+class LIBDOLPHINPRIVATE_EXPORT KItemListViewLayouter : public QObject
+{
+ Q_OBJECT
+
+public:
+ KItemListViewLayouter(QObject* parent = 0);
+ virtual ~KItemListViewLayouter();
+
+ void setScrollOrientation(Qt::Orientation orientation);
+ Qt::Orientation scrollOrientation() const;
+
+ void setSize(const QSizeF& size);
+ QSizeF size() const;
+
+ void setItemSize(const QSizeF& size);
+ QSizeF itemSize() const;
+
+ /**
+ * Margin between the rows and columns of items.
+ */
+ void setItemMargin(const QSizeF& margin);
+ QSizeF itemMargin() const;
+
+ /**
+ * Sets the height of the header that is always aligned
+ * at the top. A height of <= 0.0 means that no header is
+ * used.
+ */
+ void setHeaderHeight(qreal height);
+ qreal headerHeight() const;
+
+ /**
+ * Sets the height of the group header that is used
+ * to indicate a new item group.
+ */
+ void setGroupHeaderHeight(qreal height);
+ qreal groupHeaderHeight() const;
+
+ /**
+ * Sets the margin between the last items of the group n and
+ * the group header for the group n + 1.
+ */
+ void setGroupHeaderMargin(qreal margin);
+ qreal groupHeaderMargin() const;
+
+ void setScrollOffset(qreal scrollOffset);
+ qreal scrollOffset() const;
+
+ qreal maximumScrollOffset() const;
+
+ void setItemOffset(qreal scrollOffset);
+ qreal itemOffset() const;
+
+ qreal maximumItemOffset() const;
+
+ void setModel(const KItemModelBase* model);
+ const KItemModelBase* model() const;
+
+ void setSizeHintResolver(const KItemListSizeHintResolver* sizeHintResolver);
+ const KItemListSizeHintResolver* sizeHintResolver() const;
+
+ /**
+ * @return The first (at least partly) visible index. -1 is returned
+ * if the item count is 0.
+ */
+ int firstVisibleIndex() const;
+
+ /**
+ * @return The last (at least partly) visible index. -1 is returned
+ * if the item count is 0.
+ */
+ int lastVisibleIndex() const;
+
+ /**
+ * @return Rectangle of the item with the index \a index.
+ * The top/left of the bounding rectangle is related to
+ * the top/left of the KItemListView. An empty rectangle
+ * is returned if an invalid index is given.
+ */
+ QRectF itemRect(int index) const;
+
+ /**
+ * @return Rectangle of the group header for the item with the
+ * index \a index. Note that the layouter does not check
+ * whether the item really has a header: Usually only
+ * the first item of a group gets a header (see
+ * isFirstGroupItem()).
+ */
+ QRectF groupHeaderRect(int index) const;
+
+ /**
+ * @return Column of the item with the index \a index.
+ * -1 is returned if an invalid index is given.
+ */
+ int itemColumn(int index) const;
+
+ /**
+ * @return Row of the item with the index \a index.
+ * -1 is returned if an invalid index is given.
+ */
+ int itemRow(int index) const;
+
+ /**
+ * @return Maximum number of (at least partly) visible items for
+ * the given size.
+ */
+ int maximumVisibleItems() const;
+
+ /**
+ * @return True if the item with the index \p itemIndex
+ * is the first item within a group.
+ */
+ bool isFirstGroupItem(int itemIndex) const;
+
+ /**
+ * Marks the layouter as dirty. This means as soon as a property of
+ * the layouter gets read, an expensive relayout will be done.
+ */
+ void markAsDirty();
+
+#ifndef QT_NO_DEBUG
+ /**
+ * @return True if the layouter has been marked as dirty and hence has
+ * not called yet doLayout(). Is enabled only in the debugging
+ * mode, as it is not useful to check the dirty state otherwise.
+ */
+ bool isDirty();
+#endif
+
+private:
+ void doLayout();
+ void updateVisibleIndexes();
+ bool createGroupHeaders();
+
+ /**
+ * @return Minimum width of group headers when grouping is enabled in the horizontal
+ * alignment mode. The header alignment is done like this:
+ * Header-1 Header-2 Header-3
+ * Item 1 Item 4 Item 7
+ * Item 2 Item 5 Item 8
+ * Item 3 Item 6 Item 9
+ */
+ qreal minimumGroupHeaderWidth() const;
+
+private:
+ bool m_dirty;
+ bool m_visibleIndexesDirty;
+
+ Qt::Orientation m_scrollOrientation;
+ QSizeF m_size;
+
+ QSizeF m_itemSize;
+ QSizeF m_itemMargin;
+ qreal m_headerHeight;
+ const KItemModelBase* m_model;
+ const KItemListSizeHintResolver* m_sizeHintResolver;
+
+ qreal m_scrollOffset;
+ qreal m_maximumScrollOffset;
+
+ qreal m_itemOffset;
+ qreal m_maximumItemOffset;
+
+ int m_firstVisibleIndex;
+ int m_lastVisibleIndex;
+
+ qreal m_columnWidth;
+ qreal m_xPosInc;
+ int m_columnCount;
+
+ // Stores all item indexes that are the first item of a group.
+ // Assures fast access for KItemListViewLayouter::isFirstGroupItem().
+ QSet<int> m_groupItemIndexes;
+ qreal m_groupHeaderHeight;
+ qreal m_groupHeaderMargin;
+
+ struct ItemInfo {
+ QRectF rect;
+ int column;
+ int row;
+ };
+ QList<ItemInfo> m_itemInfos;
+
+ friend class KItemListControllerTest;
+};
+
+#endif
+
+
diff --git a/src/kitemviews/private/knepomukdatamanagement_export.h b/src/kitemviews/private/knepomukdatamanagement_export.h
new file mode 100644
index 000000000..929a737c9
--- /dev/null
+++ b/src/kitemviews/private/knepomukdatamanagement_export.h
@@ -0,0 +1,40 @@
+/* This file is part of the KDE project
+ Copyright (C) 2007 David Faure <[email protected]>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef NEPOMUKDATAMANAGEMENT_EXPORT_H
+#define NEPOMUKDATAMANAGEMENT_EXPORT_H
+
+/* needed for KDE_EXPORT and KDE_IMPORT macros */
+#include <kdemacros.h>
+
+#ifndef NEPOMUK_DATA_MANAGEMENT_EXPORT
+# if defined(MAKE_NEPOMUKDATAMANAGEMENT_LIB)
+ /* We are building this library */
+# define NEPOMUK_DATA_MANAGEMENT_EXPORT KDE_EXPORT
+# else
+ /* We are using this library */
+# define NEPOMUK_DATA_MANAGEMENT_EXPORT KDE_IMPORT
+# endif
+#endif
+
+# ifndef NEPOMUK_DATA_MANAGEMENT_EXPORT_DEPRECATED
+# define NEPOMUK_DATA_MANAGEMENT_EXPORT_DEPRECATED KDE_DEPRECATED NEPOMUK_DATA_MANAGEMENT_EXPORT
+# endif
+
+#endif
diff --git a/src/kitemviews/private/knepomukresourcewatcher.h b/src/kitemviews/private/knepomukresourcewatcher.h
new file mode 100644
index 000000000..3f6643fc8
--- /dev/null
+++ b/src/kitemviews/private/knepomukresourcewatcher.h
@@ -0,0 +1,288 @@
+/*
+ This file is part of the Nepomuk KDE project.
+ Copyright (C) 2011 Vishesh Handa <[email protected]>
+ Copyright (C) 2011 Sebastian Trueg <[email protected]>
+
+ 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.1 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 Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+
+#ifndef RESOURCEWATCHER_H
+#define RESOURCEWATCHER_H
+
+#include <Nepomuk/Types/Class>
+#include <Nepomuk/Types/Property>
+#include <Nepomuk/Resource>
+
+#include <QtDBus/QDBusVariant>
+#include <QtCore/QVariant>
+
+#include "knepomukdatamanagement_export.h"
+
+namespace Nepomuk {
+
+ /**
+ * \class ResourceWatcher resourcewatcher.h
+ *
+ * \brief Selectively monitor the nepomuk repository for changes.
+ *
+ * Resources may be monitored on the basis of types, properties, and uris.
+ *
+ * Changes may be monitored in one of the following ways:
+ * -# By resources -
+ * Specify the exact resources that should be watched. Any changes made to the specified resources
+ * (Excluding \ref nepomuk_dms_metadata) will be notified through the propertyAdded() and propertyRemoved()
+ * signals. Notifications will also be sent if any of the watched resources is deleted.
+ * -# By resources and properties -
+ * Specify the exact resources and their properties. Any changes made to the specified resources
+ * which touch one of the specified properties will be notified through the propertyAdded() and propertyRemoved()
+ * signals.
+ * -# By types -
+ * Specific types may be specified via add/setType. If types are set, then notifications will be
+ * sent for all new resources of that type. This includes property changes and resource creation and removal.
+ * TODO: add flags that allow to only watch for resource creation and removal.
+ * -# By types and properties -
+ * Both the types and properties may be specified. Notifications will be sent for property changes
+ * in resource with the specified types.
+ *
+ * \section nepomuk_rw_examples Resource Watcher Usage Example
+ *
+ * The following code creates a new ResourceWatcher, configures it to listen to changes on the \c nmm:performer
+ * property on one specific resource \c res.
+ *
+ * \code
+ * Nepomuk::ResourceWatcher* watcher = new Nepomuk::ResourceWatcher(this);
+ * watcher->addResource(res);
+ * watcher->addProperty(NMM:performer());
+ * connect(watcher, SIGNAL(propertyAdded(Nepomuk::Resource, Nepomuk::Types::Property, QVariant)),
+ * this, SLOT(slotPropertyChanged()));
+ * connect(watcher, SIGNAL(propertyRemoved(Nepomuk::Resource, Nepomuk::Types::Property, QVariant)),
+ * this, SLOT(slotPropertyChanged()));
+ * rwatcher->start();
+ * \endcode
+ *
+ * \author Vishesh Handa <[email protected]>, Sebastian Trueg <[email protected]>
+ *
+ * \ingroup nepomuk_datamanagement
+ */
+ class NEPOMUK_DATA_MANAGEMENT_EXPORT ResourceWatcher : public QObject
+ {
+ Q_OBJECT
+
+ public:
+ /**
+ * \brief Create a new %ResourceWatcher instance.
+ *
+ * This instance will not emit any signals before it has been configured
+ * and started.
+ */
+ ResourceWatcher( QObject* parent = 0 );
+
+ /**
+ * \brief Destructor.
+ */
+ virtual ~ResourceWatcher();
+
+ /**
+ * \brief Add a type to be watched.
+ *
+ * Every resource of this type will be watched for changes.
+ *
+ * \sa setTypes()
+ */
+ void addType( const Types::Class & type );
+
+ /**
+ * \brief Add a resource to be watched.
+ *
+ * Every change to this resource will be
+ * signalled, depending on the configured properties().
+ *
+ * \sa setResources()
+ */
+ void addResource( const Nepomuk::Resource & res );
+
+ /**
+ * \brief Add a property to be watched.
+ *
+ * Every change to a value of this property
+ * will be signalled, depending on the configured resources() or types().
+ *
+ * \sa setProperties()
+ */
+ void addProperty( const Types::Property & property );
+
+ /**
+ * \brief Set the types to be watched.
+ *
+ * Every resource having one of these types will be watched for changes.
+ *
+ * \sa addType()
+ */
+ void setTypes( const QList<Types::Class> & types_ );
+
+ /**
+ * \brief Set the resources to be watched.
+ *
+ * Every change to one of these resources will be
+ * signalled, depending on the configured properties().
+ *
+ * \sa addResource()
+ */
+ void setResources( const QList<Nepomuk::Resource> & resources_ );
+
+ /**
+ * \brief Set the properties to be watched.
+ *
+ * Every change to a value of any of these properties
+ * will be signalled, depending on the configured resources() or types().
+ *
+ * \sa addProperty()
+ */
+ void setProperties( const QList<Types::Property> & properties_ );
+
+ /**
+ * \brief The types that have been configured via addType() and setTypes().
+ *
+ * Every resource having one of these types will be watched
+ * for changes.
+ */
+ QList<Types::Class> types() const;
+
+ /**
+ * \brief The resources that have been configured via addResource() and setResources().
+ *
+ * Every change to one of these resources will be
+ * signalled, depending on the configured properties().
+ */
+ QList<Nepomuk::Resource> resources() const;
+
+ /**
+ * \brief The properties that have been configured via addProperty() and setProperties().
+ *
+ * Every change to a value of any of these properties
+ * will be signalled, depending on the configured resources() or types().
+ */
+ QList<Types::Property> properties() const;
+
+ public Q_SLOTS:
+ /**
+ * \brief Start the signalling of changes.
+ *
+ * Before calling this method no signal will be emitted. In
+ * combination with stop() this allows to suspend the watching.
+ * Calling start() multiple times has no effect.
+ */
+ bool start();
+
+ /**
+ * \brief Stop the signalling of changes.
+ *
+ * Allows to stop the watcher which has been started
+ * via start(). Calling stop() multiple times has no effect.
+ */
+ void stop();
+
+ Q_SIGNALS:
+ /**
+ * \brief This signal is emitted when a new resource is created.
+ * \param resource The newly created resource.
+ * \param types The types the new resource has. If types() have been configured this list will always
+ * contain one of the configured types.
+ */
+ void resourceCreated( const Nepomuk::Resource & resource, const QList<QUrl>& types ); //FIXME: Use either Resource or uri, not a mix
+
+ /**
+ * \brief This signal is emitted when a resource is deleted.
+ * \param uri The resource URI of the removed resource.
+ * \param types The types the removed resource had. If types() have been configured this list will always
+ * contain one of the configured types.
+ */
+ void resourceRemoved( const QUrl & uri, const QList<QUrl>& types );
+
+ /**
+ * \brief This signal is emitted when a type has been added to a resource. This does not include creation which
+ * is signalled via resourceCreated(). It only applies to changes in a resource's types.
+ * \param res The changed resource.
+ * \param type The newly added type. If types() have been configured it will be one of them.
+ */
+ void resourceTypeAdded( const Nepomuk::Resource & res, const Types::Class & type );
+
+ /**
+ * \brief This signal is emitted when a type has been removed from a resource.
+ *
+ * This does not include removal of entire resources which is signalled via resourceRemoved().
+ * It only applies to changes in a resource's types.
+ * \param res The changed resource.
+ * \param type The removed type. If types() have been configured it will be one of them.
+ */
+ void resourceTypeRemoved( const Nepomuk::Resource & res, const Types::Class & type );
+
+ /**
+ * \brief This signal is emitted when a property value is added.
+ * \param resource The changed resource.
+ * \param property The property which has a new value.
+ * \param value The newly added property value.
+ */
+ void propertyAdded( const Nepomuk::Resource & resource,
+ const Nepomuk::Types::Property & property,
+ const QVariant & value );
+
+ /**
+ * \brief This signal is emitted when a property value is removed.
+ * \param resource The changed resource.
+ * \param property The property which was changed.
+ * \param value The removed property value.
+ */
+ void propertyRemoved( const Nepomuk::Resource & resource,
+ const Nepomuk::Types::Property & property,
+ const QVariant & value );
+
+ /**
+ * \brief This signal is emitted when a property value is changed.
+ *
+ * This signal cannot be emitted for all changes. It doesn't work if a property is first
+ * removed and then set, cause the Data Mangement Service does not maintain an internal
+ * cache for the purpose of emitting the propertyChanged signal.
+ *
+ * Specially, since one could theoretically take forever between the removal and the
+ * setting of the property.
+ *
+ * \param resource The changed resource.
+ * \param property The property which was changed.
+ * \param oldValue The removed property value.
+ */
+ void propertyChanged( const Nepomuk::Resource & resource,
+ const Nepomuk::Types::Property & property,
+ const QVariantList & oldValue,
+ const QVariantList & newValue );
+
+ private Q_SLOTS:
+ void slotResourceCreated(const QString& res, const QStringList& types);
+ void slotResourceRemoved(const QString& res, const QStringList& types);
+ void slotResourceTypeAdded(const QString& res, const QString& type);
+ void slotResourceTypeRemoved(const QString& res, const QString& type);
+ void slotPropertyAdded(const QString& res, const QString& prop, const QDBusVariant& object);
+ void slotPropertyRemoved(const QString& res, const QString& prop, const QDBusVariant& object);
+ void slotPropertyChanged(const QString& res, const QString& prop,
+ const QVariantList & oldObjs,
+ const QVariantList & newObjs);
+ private:
+ class Private;
+ Private * d;
+ };
+}
+
+#endif // RESOURCEWATCHER_H
diff --git a/src/kitemviews/private/knepomukrolesprovider.cpp b/src/kitemviews/private/knepomukrolesprovider.cpp
new file mode 100644
index 000000000..7af887cbf
--- /dev/null
+++ b/src/kitemviews/private/knepomukrolesprovider.cpp
@@ -0,0 +1,181 @@
+/***************************************************************************
+ * Copyright (C) 2012 by Peter Penz <[email protected]> *
+ * *
+ * 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, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#include "knepomukrolesprovider.h"
+
+#include <KDebug>
+#include <KGlobal>
+#include <KLocale>
+
+#include <Nepomuk/Resource>
+#include <Nepomuk/Tag>
+#include <Nepomuk/Types/Property>
+#include <Nepomuk/Variant>
+
+struct KNepomukRolesProviderSingleton
+{
+ KNepomukRolesProvider instance;
+};
+K_GLOBAL_STATIC(KNepomukRolesProviderSingleton, s_nepomukRolesProvider)
+
+
+KNepomukRolesProvider& KNepomukRolesProvider::instance()
+{
+ return s_nepomukRolesProvider->instance;
+}
+
+KNepomukRolesProvider::~KNepomukRolesProvider()
+{
+}
+
+QSet<QByteArray> KNepomukRolesProvider::roles() const
+{
+ return m_roles;
+}
+
+QHash<QByteArray, QVariant> KNepomukRolesProvider::roleValues(const Nepomuk::Resource& resource,
+ const QSet<QByteArray>& roles) const
+{
+ if (!resource.isValid()) {
+ return QHash<QByteArray, QVariant>();
+ }
+
+ QHash<QByteArray, QVariant> values;
+
+ int width = -1;
+ int height = -1;
+
+ QHashIterator<QUrl, Nepomuk::Variant> it(resource.properties());
+ while (it.hasNext()) {
+ it.next();
+
+ const Nepomuk::Types::Property property = it.key();
+ const QByteArray role = m_roleForUri.value(property.uri());
+ if (role.isEmpty() || !roles.contains(role)) {
+ continue;
+ }
+
+ const Nepomuk::Variant value = it.value();
+
+ if (role == "imageSize") {
+ // Merge the two Nepomuk properties for width and height
+ // as one string into the "imageSize" role
+ const QString uri = property.uri().toString();
+ if (uri.endsWith("#width")) {
+ width = value.toInt();
+ } else if (uri.endsWith("#height")) {
+ height = value.toInt();
+ }
+
+ if (width >= 0 && height >= 0) {
+ const QString widthAndHeight = QString::number(width) +
+ QLatin1String(" x ") +
+ QString::number(height);
+ values.insert(role, widthAndHeight);
+ }
+ } else if (role == "tags") {
+ const QString tags = tagsFromValues(value.toStringList());
+ values.insert(role, tags);
+ } else if (role == "orientation") {
+ const QString orientation = orientationFromValue(value.toInt());
+ values.insert(role, orientation);
+ } else {
+ values.insert(role, value.toString());
+ }
+ }
+
+ // Assure that empty values get replaced by "-"
+ foreach (const QByteArray& role, roles) {
+ if (m_roles.contains(role) && values.value(role).toString().isEmpty()) {
+ values.insert(role, QLatin1String("-"));
+ }
+ }
+
+ return values;
+}
+
+KNepomukRolesProvider::KNepomukRolesProvider() :
+ m_roles(),
+ m_roleForUri()
+{
+ struct UriInfo
+ {
+ const char* const uri;
+ const char* const role;
+ };
+
+ // Mapping from the URIs to the KFileItemModel roles. Note that this must not be
+ // a 1:1 mapping: One role may contain several URI-values (e.g. the URIs for height and
+ // width of an image are mapped to the role "imageSize")
+ static const UriInfo uriInfoList[] = {
+ { "http://www.semanticdesktop.org/ontologies/2007/08/15/nao#numericRating", "rating" },
+ { "http://www.semanticdesktop.org/ontologies/2007/08/15/nao#hasTag", "tags" },
+ { "http://www.semanticdesktop.org/ontologies/2007/08/15/nao#description", "comment" },
+ { "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#wordCount", "wordCount" },
+ { "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#lineCount", "lineCount" },
+ { "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#width", "imageSize" },
+ { "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#height", "imageSize" },
+ { "http://www.semanticdesktop.org/ontologies/2007/05/10/nexif#orientation", "orientation", },
+ { "http://www.semanticdesktop.org/ontologies/2009/02/19/nmm#performer", "artist" },
+ { "http://www.semanticdesktop.org/ontologies/2009/02/19/nmm#musicAlbum", "album" },
+ { "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#duration", "duration" },
+ { "http://www.semanticdesktop.org/ontologies/2009/02/19/nmm#trackNumber", "track" },
+ { "http://www.semanticdesktop.org/ontologies/2010/04/30/ndo#copiedFrom", "copiedFrom" }
+ };
+
+ for (unsigned int i = 0; i < sizeof(uriInfoList) / sizeof(UriInfo); ++i) {
+ m_roleForUri.insert(QUrl(uriInfoList[i].uri), uriInfoList[i].role);
+ m_roles.insert(uriInfoList[i].role);
+ }
+}
+
+QString KNepomukRolesProvider::tagsFromValues(const QStringList& values) const
+{
+ QString tags;
+
+ for (int i = 0; i < values.count(); ++i) {
+ if (i > 0) {
+ tags.append(QLatin1String(", "));
+ }
+
+ const Nepomuk::Tag tag(values[i]);
+ tags += tag.genericLabel();
+ }
+
+ return tags;
+}
+
+QString KNepomukRolesProvider::orientationFromValue(int value) const
+{
+ QString string;
+ switch (value) {
+ case 1: string = i18nc("@item:intable Image orientation", "Unchanged"); break;
+ case 2: string = i18nc("@item:intable Image orientation", "Horizontally flipped"); break;
+ case 3: string = i18nc("@item:intable image orientation", "180° rotated"); break;
+ case 4: string = i18nc("@item:intable image orientation", "Vertically flipped"); break;
+ case 5: string = i18nc("@item:intable image orientation", "Transposed"); break;
+ case 6: string = i18nc("@item:intable image orientation", "90° rotated"); break;
+ case 7: string = i18nc("@item:intable image orientation", "Transversed"); break;
+ case 8: string = i18nc("@item:intable image orientation", "270° rotated"); break;
+ default:
+ break;
+ }
+ return string;
+}
+
diff --git a/src/kitemviews/private/knepomukrolesprovider.h b/src/kitemviews/private/knepomukrolesprovider.h
new file mode 100644
index 000000000..46a78d4ee
--- /dev/null
+++ b/src/kitemviews/private/knepomukrolesprovider.h
@@ -0,0 +1,82 @@
+/***************************************************************************
+ * Copyright (C) 2012 by Peter Penz <[email protected]> *
+ * *
+ * 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, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#ifndef KNEPOMUKROLESPROVIDER_H
+#define KNEPOMUKROLESPROVIDER_H
+
+#include <libdolphin_export.h>
+
+#include <QHash>
+#include <QSet>
+#include <QUrl>
+
+namespace Nepomuk
+{
+ class Resource;
+}
+
+/**
+ * @brief Allows accessing metadata of a file by providing KFileItemModel roles.
+ *
+ * Is a helper class for KFileItemModelRolesUpdater to retrieve roles that
+ * are only accessible with Nepomuk.
+ */
+class LIBDOLPHINPRIVATE_EXPORT KNepomukRolesProvider
+{
+public:
+ static KNepomukRolesProvider& instance();
+ virtual ~KNepomukRolesProvider();
+
+ /**
+ * @return Roles that can be provided by KNepomukRolesProvider.
+ */
+ QSet<QByteArray> roles() const;
+
+ /**
+ * @return Values for the roles \a roles that can be determined from the file
+ * with the URL \a url.
+ */
+ QHash<QByteArray, QVariant> roleValues(const Nepomuk::Resource& resource,
+ const QSet<QByteArray>& roles) const;
+
+protected:
+ KNepomukRolesProvider();
+
+private:
+ /**
+ * @return User visible string for the given tag-values.
+ */
+ QString tagsFromValues(const QStringList& values) const;
+
+ /**
+ * @return User visible string for the EXIF-orientation property
+ * which can have the values 0 to 8.
+ * (see http://sylvana.net/jpegcrop/exif_orientation.html)
+ */
+ QString orientationFromValue(int value) const;
+
+private:
+ QSet<QByteArray> m_roles;
+ QHash<QUrl, QByteArray> m_roleForUri;
+
+ friend class KNepomukRolesProviderSingleton;
+};
+
+#endif
+
diff --git a/src/kitemviews/private/kpixmapmodifier.cpp b/src/kitemviews/private/kpixmapmodifier.cpp
new file mode 100644
index 000000000..29aceb66b
--- /dev/null
+++ b/src/kitemviews/private/kpixmapmodifier.cpp
@@ -0,0 +1,400 @@
+// krazy:excludeall=copyright (email of Maxim is missing)
+/*
+ This file is a part of the KDE project
+
+ Copyright © 2006 Zack Rusin <[email protected]>
+ Copyright © 2006-2007, 2008 Fredrik Höglund <[email protected]>
+
+ The stack blur algorithm was invented by Mario Klingemann <[email protected]>
+
+ This implementation is based on the version in Anti-Grain Geometry Version 2.4,
+ Copyright © 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "kpixmapmodifier.h"
+
+#include <QImage>
+#include <QPainter>
+#include <QPixmap>
+#include <QSize>
+
+#include <KDebug>
+
+#include <config-X11.h> // for HAVE_XRENDER
+#if defined(Q_WS_X11) && defined(HAVE_XRENDER)
+# include <QX11Info>
+# include <X11/Xlib.h>
+# include <X11/extensions/Xrender.h>
+#endif
+
+static const quint32 stackBlur8Mul[255] =
+{
+ 512,512,456,512,328,456,335,512,405,328,271,456,388,335,292,512,
+ 454,405,364,328,298,271,496,456,420,388,360,335,312,292,273,512,
+ 482,454,428,405,383,364,345,328,312,298,284,271,259,496,475,456,
+ 437,420,404,388,374,360,347,335,323,312,302,292,282,273,265,512,
+ 497,482,468,454,441,428,417,405,394,383,373,364,354,345,337,328,
+ 320,312,305,298,291,284,278,271,265,259,507,496,485,475,465,456,
+ 446,437,428,420,412,404,396,388,381,374,367,360,354,347,341,335,
+ 329,323,318,312,307,302,297,292,287,282,278,273,269,265,261,512,
+ 505,497,489,482,475,468,461,454,447,441,435,428,422,417,411,405,
+ 399,394,389,383,378,373,368,364,359,354,350,345,341,337,332,328,
+ 324,320,316,312,309,305,301,298,294,291,287,284,281,278,274,271,
+ 268,265,262,259,257,507,501,496,491,485,480,475,470,465,460,456,
+ 451,446,442,437,433,428,424,420,416,412,408,404,400,396,392,388,
+ 385,381,377,374,370,367,363,360,357,354,350,347,344,341,338,335,
+ 332,329,326,323,320,318,315,312,310,307,304,302,299,297,294,292,
+ 289,287,285,282,280,278,275,273,271,269,267,265,263,261,259
+};
+
+static const quint32 stackBlur8Shr[255] =
+{
+ 9, 11, 12, 13, 13, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 17,
+ 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 18, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20,
+ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22,
+ 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
+ 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23,
+ 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
+ 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
+ 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
+ 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24
+};
+
+static void blurHorizontal(QImage& image, unsigned int* stack, int div, int radius)
+{
+ int stackindex;
+ int stackstart;
+
+ quint32 * const pixels = reinterpret_cast<quint32 *>(image.bits());
+ quint32 pixel;
+
+ int w = image.width();
+ int h = image.height();
+ int wm = w - 1;
+
+ unsigned int mulSum = stackBlur8Mul[radius];
+ unsigned int shrSum = stackBlur8Shr[radius];
+
+ unsigned int sum, sumIn, sumOut;
+
+ for (int y = 0; y < h; y++) {
+ sum = 0;
+ sumIn = 0;
+ sumOut = 0;
+
+ const int yw = y * w;
+ pixel = pixels[yw];
+ for (int i = 0; i <= radius; i++) {
+ stack[i] = qAlpha(pixel);
+
+ sum += stack[i] * (i + 1);
+ sumOut += stack[i];
+ }
+
+ for (int i = 1; i <= radius; i++) {
+ pixel = pixels[yw + qMin(i, wm)];
+
+ unsigned int* stackpix = &stack[i + radius];
+ *stackpix = qAlpha(pixel);
+
+ sum += *stackpix * (radius + 1 - i);
+ sumIn += *stackpix;
+ }
+
+ stackindex = radius;
+ for (int x = 0, i = yw; x < w; x++) {
+ pixels[i++] = (((sum * mulSum) >> shrSum) << 24) & 0xff000000;
+
+ sum -= sumOut;
+
+ stackstart = stackindex + div - radius;
+ if (stackstart >= div) {
+ stackstart -= div;
+ }
+
+ unsigned int* stackpix = &stack[stackstart];
+
+ sumOut -= *stackpix;
+
+ pixel = pixels[yw + qMin(x + radius + 1, wm)];
+
+ *stackpix = qAlpha(pixel);
+
+ sumIn += *stackpix;
+ sum += sumIn;
+
+ if (++stackindex >= div) {
+ stackindex = 0;
+ }
+
+ stackpix = &stack[stackindex];
+
+ sumOut += *stackpix;
+ sumIn -= *stackpix;
+ }
+ }
+}
+
+static void blurVertical(QImage& image, unsigned int* stack, int div, int radius)
+{
+ int stackindex;
+ int stackstart;
+
+ quint32 * const pixels = reinterpret_cast<quint32 *>(image.bits());
+ quint32 pixel;
+
+ int w = image.width();
+ int h = image.height();
+ int hm = h - 1;
+
+ int mul_sum = stackBlur8Mul[radius];
+ int shr_sum = stackBlur8Shr[radius];
+
+ unsigned int sum, sumIn, sumOut;
+
+ for (int x = 0; x < w; x++) {
+ sum = 0;
+ sumIn = 0;
+ sumOut = 0;
+
+ pixel = pixels[x];
+ for (int i = 0; i <= radius; i++) {
+ stack[i] = qAlpha(pixel);
+
+ sum += stack[i] * (i + 1);
+ sumOut += stack[i];
+ }
+
+ for (int i = 1; i <= radius; i++) {
+ pixel = pixels[qMin(i, hm) * w + x];
+
+ unsigned int* stackpix = &stack[i + radius];
+ *stackpix = qAlpha(pixel);
+
+ sum += *stackpix * (radius + 1 - i);
+ sumIn += *stackpix;
+ }
+
+ stackindex = radius;
+ for (int y = 0, i = x; y < h; y++, i += w) {
+ pixels[i] = (((sum * mul_sum) >> shr_sum) << 24) & 0xff000000;
+
+ sum -= sumOut;
+
+ stackstart = stackindex + div - radius;
+ if (stackstart >= div)
+ stackstart -= div;
+
+ unsigned int* stackpix = &stack[stackstart];
+
+ sumOut -= *stackpix;
+
+ pixel = pixels[qMin(y + radius + 1, hm) * w + x];
+
+ *stackpix = qAlpha(pixel);
+
+ sumIn += *stackpix;
+ sum += sumIn;
+
+ if (++stackindex >= div) {
+ stackindex = 0;
+ }
+
+ stackpix = &stack[stackindex];
+
+ sumOut += *stackpix;
+ sumIn -= *stackpix;
+ }
+ }
+}
+
+static void stackBlur(QImage& image, float radius)
+{
+ radius = qRound(radius);
+
+ int div = int(radius * 2) + 1;
+ unsigned int* stack = new unsigned int[div];
+
+ blurHorizontal(image, stack, div, radius);
+ blurVertical(image, stack, div, radius);
+
+ delete [] stack;
+}
+
+static void shadowBlur(QImage& image, float radius, const QColor& color)
+{
+ if (radius < 0) {
+ return;
+ }
+
+ if (radius > 0) {
+ stackBlur(image, radius);
+ }
+
+ // Correct the color and opacity of the shadow
+ QPainter p(&image);
+ p.setCompositionMode(QPainter::CompositionMode_SourceIn);
+ p.fillRect(image.rect(), color);
+}
+
+namespace {
+ /** Helper class for drawing frames for KPixmapModifier::applyFrame(). */
+ class TileSet
+ {
+ public:
+ enum { LeftMargin = 3, TopMargin = 2, RightMargin = 3, BottomMargin = 4 };
+
+ enum Tile { TopLeftCorner = 0, TopSide, TopRightCorner, LeftSide,
+ RightSide, BottomLeftCorner, BottomSide, BottomRightCorner,
+ NumTiles };
+
+ TileSet()
+ {
+ QImage image(8 * 3, 8 * 3, QImage::Format_ARGB32_Premultiplied);
+
+ QPainter p(&image);
+ p.setCompositionMode(QPainter::CompositionMode_Source);
+ p.fillRect(image.rect(), Qt::transparent);
+ p.fillRect(image.rect().adjusted(3, 3, -3, -3), Qt::black);
+ p.end();
+
+ shadowBlur(image, 3, Qt::black);
+
+ QPixmap pixmap = QPixmap::fromImage(image);
+ m_tiles[TopLeftCorner] = pixmap.copy(0, 0, 8, 8);
+ m_tiles[TopSide] = pixmap.copy(8, 0, 8, 8);
+ m_tiles[TopRightCorner] = pixmap.copy(16, 0, 8, 8);
+ m_tiles[LeftSide] = pixmap.copy(0, 8, 8, 8);
+ m_tiles[RightSide] = pixmap.copy(16, 8, 8, 8);
+ m_tiles[BottomLeftCorner] = pixmap.copy(0, 16, 8, 8);
+ m_tiles[BottomSide] = pixmap.copy(8, 16, 8, 8);
+ m_tiles[BottomRightCorner] = pixmap.copy(16, 16, 8, 8);
+ }
+
+ void paint(QPainter* p, const QRect& r)
+ {
+ p->drawPixmap(r.topLeft(), m_tiles[TopLeftCorner]);
+ if (r.width() - 16 > 0) {
+ p->drawTiledPixmap(r.x() + 8, r.y(), r.width() - 16, 8, m_tiles[TopSide]);
+ }
+ p->drawPixmap(r.right() - 8 + 1, r.y(), m_tiles[TopRightCorner]);
+ if (r.height() - 16 > 0) {
+ p->drawTiledPixmap(r.x(), r.y() + 8, 8, r.height() - 16, m_tiles[LeftSide]);
+ p->drawTiledPixmap(r.right() - 8 + 1, r.y() + 8, 8, r.height() - 16, m_tiles[RightSide]);
+ }
+ p->drawPixmap(r.x(), r.bottom() - 8 + 1, m_tiles[BottomLeftCorner]);
+ if (r.width() - 16 > 0) {
+ p->drawTiledPixmap(r.x() + 8, r.bottom() - 8 + 1, r.width() - 16, 8, m_tiles[BottomSide]);
+ }
+ p->drawPixmap(r.right() - 8 + 1, r.bottom() - 8 + 1, m_tiles[BottomRightCorner]);
+
+ const QRect contentRect = r.adjusted(LeftMargin + 1, TopMargin + 1,
+ -(RightMargin + 1), -(BottomMargin + 1));
+ p->fillRect(contentRect, Qt::transparent);
+ }
+
+ QPixmap m_tiles[NumTiles];
+ };
+}
+
+void KPixmapModifier::scale(QPixmap& pixmap, const QSize& scaledSize)
+{
+ if (scaledSize.isEmpty()) {
+ pixmap = QPixmap();
+ return;
+ }
+
+#if defined(Q_WS_X11) && defined(HAVE_XRENDER)
+ // Assume that the texture size limit is 2048x2048
+ if ((pixmap.width() <= 2048) && (pixmap.height() <= 2048) && pixmap.x11PictureHandle()) {
+ const QPixmap unscaledPixmap = pixmap.copy(); // Make a deep copy for XRender
+ QSize scaledPixmapSize = pixmap.size();
+ scaledPixmapSize.scale(scaledSize, Qt::KeepAspectRatio);
+
+ const qreal factor = scaledPixmapSize.width() / qreal(unscaledPixmap.width());
+
+ XTransform xform = {{
+ { XDoubleToFixed(1 / factor), 0, 0 },
+ { 0, XDoubleToFixed(1 / factor), 0 },
+ { 0, 0, XDoubleToFixed(1) }
+ }};
+
+ QPixmap scaledPixmap(scaledPixmapSize);
+ scaledPixmap.fill(Qt::transparent);
+
+ Display* dpy = QX11Info::display();
+
+ XRenderPictureAttributes attr;
+ attr.repeat = RepeatPad;
+ XRenderChangePicture(dpy, unscaledPixmap.x11PictureHandle(), CPRepeat, &attr);
+
+ XRenderSetPictureFilter(dpy, unscaledPixmap.x11PictureHandle(), FilterBilinear, 0, 0);
+ XRenderSetPictureTransform(dpy, unscaledPixmap.x11PictureHandle(), &xform);
+ XRenderComposite(dpy, PictOpOver, unscaledPixmap.x11PictureHandle(), None, scaledPixmap.x11PictureHandle(),
+ 0, 0, 0, 0, 0, 0, scaledPixmap.width(), scaledPixmap.height());
+ pixmap = scaledPixmap;
+ } else {
+ pixmap = pixmap.scaled(scaledSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
+ }
+#else
+ pixmap = pixmap.scaled(scaledSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
+#endif
+}
+
+void KPixmapModifier::applyFrame(QPixmap& icon, const QSize& scaledSize)
+{
+ static TileSet tileSet;
+
+ // Resize the icon to the maximum size minus the space required for the frame
+ const QSize size(scaledSize.width() - TileSet::LeftMargin - TileSet::RightMargin,
+ scaledSize.height() - TileSet::TopMargin - TileSet::BottomMargin);
+ scale(icon, size);
+
+ QPixmap framedIcon(icon.size().width() + TileSet::LeftMargin + TileSet::RightMargin,
+ icon.size().height() + TileSet::TopMargin + TileSet::BottomMargin);
+ framedIcon.fill(Qt::transparent);
+
+ QPainter painter;
+ painter.begin(&framedIcon);
+ painter.setCompositionMode(QPainter::CompositionMode_Source);
+ tileSet.paint(&painter, framedIcon.rect());
+ painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
+ painter.drawPixmap(TileSet::LeftMargin, TileSet::TopMargin, icon);
+
+ icon = framedIcon;
+}
+
+QSize KPixmapModifier::sizeInsideFrame(const QSize& frameSize)
+{
+ return QSize(frameSize.width() - TileSet::LeftMargin - TileSet::RightMargin,
+ frameSize.height() - TileSet::TopMargin - TileSet::BottomMargin);
+}
+
diff --git a/src/kitemviews/private/kpixmapmodifier.h b/src/kitemviews/private/kpixmapmodifier.h
new file mode 100644
index 000000000..4f863c349
--- /dev/null
+++ b/src/kitemviews/private/kpixmapmodifier.h
@@ -0,0 +1,38 @@
+/***************************************************************************
+ * Copyright (C) 2011 by Peter Penz <[email protected]> *
+ * *
+ * 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, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#ifndef KPIXMAPMODIFIER_H
+#define KPIXMAPMODIFIER_H
+
+#include <libdolphin_export.h>
+
+class QPixmap;
+class QSize;
+
+class LIBDOLPHINPRIVATE_EXPORT KPixmapModifier
+{
+public:
+ static void scale(QPixmap& pixmap, const QSize& scaledSize);
+ static void applyFrame(QPixmap& icon, const QSize& scaledSize);
+ static QSize sizeInsideFrame(const QSize& frameSize);
+};
+
+#endif
+
+