########### next target ###############
set(dolphinprivate_LIB_SRCS
+ kitemviews/kfileitemlistgroupheader.cpp
kitemviews/kfileitemlistview.cpp
kitemviews/kfileitemlistwidget.cpp
kitemviews/kfileitemmodel.cpp
--- /dev/null
+/***************************************************************************
+ * Copyright (C) 2011 by Peter Penz <peter.penz19@gmail.com> *
+ * *
+ * 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 "kfileitemlistgroupheader.h"
+
+#include <QPainter>
+
+KFileItemListGroupHeader::KFileItemListGroupHeader(QGraphicsWidget* parent) :
+ KItemListGroupHeader(parent)
+{
+}
+
+KFileItemListGroupHeader::~KFileItemListGroupHeader()
+{
+}
+
+void KFileItemListGroupHeader::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
+{
+ KItemListGroupHeader::paint(painter, option, widget);
+ // TODO: Use dataChanged() hook to prepare a cached property
+ painter->drawText(QRectF(0, 0, size().width(), size().height()), data().toString());
+}
+
+#include "kfileitemlistgroupheader.moc"
--- /dev/null
+/***************************************************************************
+ * Copyright (C) 2011 by Peter Penz <peter.penz19@gmail.com> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 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 KFILEITEMLISTGROUPHEADER_H
+#define KFILEITEMLISTGROUPHEADER_H
+
+#include <libdolphin_export.h>
+
+#include <kitemviews/kitemlistgroupheader.h>
+
+class LIBDOLPHINPRIVATE_EXPORT KFileItemListGroupHeader : public KItemListGroupHeader
+{
+ Q_OBJECT
+
+public:
+ KFileItemListGroupHeader(QGraphicsWidget* parent = 0);
+ virtual ~KFileItemListGroupHeader();
+
+ /** @reimp */
+ virtual void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget = 0);
+};
+#endif
+
+
#include "kfileitemlistview.h"
-#include "kitemlistgroupheader.h"
+#include "kfileitemlistgroupheader.h"
#include "kfileitemmodelrolesupdater.h"
#include "kfileitemlistwidget.h"
#include "kfileitemmodel.h"
setScrollOrientation(Qt::Vertical);
setWidgetCreator(new KItemListWidgetCreator<KFileItemListWidget>());
- setGroupHeaderCreator(new KItemListGroupHeaderCreator<KItemListGroupHeader>());
+ setGroupHeaderCreator(new KItemListGroupHeaderCreator<KFileItemListGroupHeader>());
m_updateVisibleIndexRangeTimer = new QTimer(this);
m_updateVisibleIndexRangeTimer->setSingleShot(true);
m_maximumUpdateIntervalTimer(0),
m_pendingItemsToInsert(),
m_pendingEmitLoadingCompleted(false),
+ m_groups(),
m_rootExpansionLevel(-1),
m_expandedUrls(),
m_restoredExpandedUrls()
QList<QPair<int, QVariant> > KFileItemModel::groups() const
{
- // TODO: dirty hack for initial testing of grouping functionality
- QPair<int, QVariant> group1(0, "Group 1");
- QPair<int, QVariant> group2(2, "Group 2");
- QPair<int, QVariant> group3(10, "Group 3");
- QPair<int, QVariant> group4(11, "Group 4");
- QPair<int, QVariant> group5(40, "Group 5");
+ if (!m_data.isEmpty() && m_groups.isEmpty()) {
+#ifdef KFILEITEMMODEL_DEBUG
+ QElapsedTimer timer;
+ timer.start();
+#endif
+ switch (roleIndex(sortRole())) {
+ case NameRole: m_groups = nameRoleGroups(); break;
+ case SizeRole: m_groups = sizeRoleGroups(); break;
+ case DateRole: m_groups = dateRoleGroups(); break;
+ case PermissionsRole: m_groups = permissionRoleGroups(); break;
+ case OwnerRole: m_groups = ownerRoleGroups(); break;
+ case GroupRole: m_groups = groupRoleGroups(); break;
+ case TypeRole: m_groups = typeRoleGroups(); break;
+ case DestinationRole: m_groups = destinationRoleGroups(); break;
+ case PathRole: m_groups = pathRoleGroups(); break;
+ case NoRole: break;
+ case IsDirRole: break;
+ case IsExpandedRole: break;
+ case ExpansionLevelRole: break;
+ default: Q_ASSERT(false); break;
+ }
- QList<QPair<int, QVariant> > groups;
- groups.append(group1);
- groups.append(group2);
- groups.append(group3);
- groups.append(group4);
- groups.append(group5);
- return groups;
+#ifdef KFILEITEMMODEL_DEBUG
+ kDebug() << "[TIME] Calculating groups for" << count() << "items:" << timer.elapsed();
+#endif
+ }
+
+ return m_groups;
}
KFileItem KFileItemModel::fileItem(int index) const
void KFileItemModel::onGroupedSortingChanged(bool current)
{
Q_UNUSED(current);
+ m_groups.clear();
}
void KFileItemModel::onSortRoleChanged(const QByteArray& current, const QByteArray& previous)
kDebug() << "Refreshing" << items.count() << "items";
#endif
+ m_groups.clear();
+
// Get the indexes of all items that have been refreshed
QList<int> indexes;
indexes.reserve(items.count());
kDebug() << "Clearing all items";
#endif
+ m_groups.clear();
+
m_minimumUpdateIntervalTimer->stop();
m_maximumUpdateIntervalTimer->stop();
m_pendingItemsToInsert.clear();
kDebug() << "Inserting" << items.count() << "items";
#endif
+ m_groups.clear();
+
KFileItemList sortedItems = items;
sort(sortedItems.begin(), sortedItems.end());
kDebug() << "Removing " << items.count() << "items";
#endif
+ m_groups.clear();
+
KFileItemList sortedItems = items;
sort(sortedItems.begin(), sortedItems.end());
return;
}
+ m_groups.clear();
+
const KFileItemList oldSortedItems = m_sortedItems;
const QHash<KUrl, int> oldItems = m_items;
const QList<QHash<QByteArray, QVariant> > oldData = m_data;
return dirLister && !dirLister->url().isLocalFile();
}
+QList<QPair<int, QVariant> > KFileItemModel::nameRoleGroups() const
+{
+ Q_ASSERT(!m_data.isEmpty());
+
+ const int maxIndex = count() - 1;
+ QList<QPair<int, QVariant> > groups;
+
+ QString groupValue;
+ QChar firstChar;
+ bool isLetter = false;
+ for (int i = 0; i <= maxIndex; ++i) {
+ const QString name = m_data.at(i).value("name").toString();
+
+ // Use the first character of the name as group indication
+ QChar newFirstChar = name.at(0).toUpper();
+ if (newFirstChar == QLatin1Char('~') && name.length() > 1) {
+ newFirstChar = name.at(1);
+ }
+
+ if (firstChar != newFirstChar) {
+ QString newGroupValue;
+ if (newFirstChar >= QLatin1Char('A') && newFirstChar <= QLatin1Char('Z')) {
+ // Apply group 'A' - 'Z'
+ newGroupValue = newFirstChar;
+ isLetter = true;
+ } else if (newFirstChar >= QLatin1Char('0') && newFirstChar <= QLatin1Char('9')) {
+ // Apply group 'Numerics' for any name that starts with a digit
+ newGroupValue = i18nc("@title:group", "Numerics");
+ isLetter = false;
+ } else {
+ if (isLetter) {
+ // If the current group is 'A' - 'Z' check whether a locale character
+ // fits into the existing group.
+ const QChar prevChar(firstChar.unicode() - ushort(1));
+ const QChar nextChar(firstChar.unicode() + ushort(1));
+ const QString currChar(newFirstChar);
+ const bool partOfCurrentGroup = currChar.localeAwareCompare(prevChar) > 0 &&
+ currChar.localeAwareCompare(nextChar) < 0;
+ if (partOfCurrentGroup) {
+ continue;
+ }
+ }
+ newGroupValue = i18nc("@title:group", "Others");
+ isLetter = false;
+ }
+
+ if (newGroupValue != groupValue) {
+ groupValue = newGroupValue;
+ groups.append(QPair<int, QVariant>(i, newGroupValue));
+ }
+
+ firstChar = newFirstChar;
+ }
+ }
+ return groups;
+}
+
+QList<QPair<int, QVariant> > KFileItemModel::sizeRoleGroups() const
+{
+ Q_ASSERT(!m_data.isEmpty());
+
+ return QList<QPair<int, QVariant> >();
+}
+
+QList<QPair<int, QVariant> > KFileItemModel::dateRoleGroups() const
+{
+ Q_ASSERT(!m_data.isEmpty());
+ return QList<QPair<int, QVariant> >();
+}
+
+QList<QPair<int, QVariant> > KFileItemModel::permissionRoleGroups() const
+{
+ Q_ASSERT(!m_data.isEmpty());
+ return QList<QPair<int, QVariant> >();
+}
+
+QList<QPair<int, QVariant> > KFileItemModel::ownerRoleGroups() const
+{
+ Q_ASSERT(!m_data.isEmpty());
+ return QList<QPair<int, QVariant> >();
+}
+
+QList<QPair<int, QVariant> > KFileItemModel::groupRoleGroups() const
+{
+ Q_ASSERT(!m_data.isEmpty());
+ return QList<QPair<int, QVariant> >();
+}
+
+QList<QPair<int, QVariant> > KFileItemModel::typeRoleGroups() const
+{
+ Q_ASSERT(!m_data.isEmpty());
+ return QList<QPair<int, QVariant> >();
+}
+
+QList<QPair<int, QVariant> > KFileItemModel::destinationRoleGroups() const
+{
+ Q_ASSERT(!m_data.isEmpty());
+ return QList<QPair<int, QVariant> >();
+}
+
+QList<QPair<int, QVariant> > KFileItemModel::pathRoleGroups() const
+{
+ Q_ASSERT(!m_data.isEmpty());
+ return QList<QPair<int, QVariant> >();
+}
+
#include "kfileitemmodel.moc"
bool useMaximumUpdateInterval() const;
+ QList<QPair<int, QVariant> > nameRoleGroups() const;
+ QList<QPair<int, QVariant> > sizeRoleGroups() const;
+ QList<QPair<int, QVariant> > dateRoleGroups() const;
+ QList<QPair<int, QVariant> > permissionRoleGroups() const;
+ QList<QPair<int, QVariant> > ownerRoleGroups() const;
+ QList<QPair<int, QVariant> > groupRoleGroups() const;
+ QList<QPair<int, QVariant> > typeRoleGroups() const;
+ QList<QPair<int, QVariant> > destinationRoleGroups() const;
+ QList<QPair<int, QVariant> > pathRoleGroups() const;
+
private:
QWeakPointer<KDirLister> m_dirLister;
KFileItemList m_pendingItemsToInsert;
bool m_pendingEmitLoadingCompleted;
+ // Cache for KFileItemModel::groups()
+ mutable QList<QPair<int, QVariant> > m_groups;
+
// Stores the smallest expansion level of the root-URL. Is required to calculate
// the "expansionLevel" role in an efficient way. A value < 0 indicates that
// it has not been initialized yet.
#include <KDebug>
KItemListGroupHeader::KItemListGroupHeader(QGraphicsWidget* parent) :
- QGraphicsWidget(parent, 0)
+ QGraphicsWidget(parent, 0),
+ m_role(),
+ m_data()
{
}
{
}
+void KItemListGroupHeader::setRole(const QByteArray& role)
+{
+ if (m_role != role) {
+ const QByteArray previous = m_role;
+ m_role = role;
+ update();
+ roleChanged(role, previous);
+ }
+}
+
+QByteArray KItemListGroupHeader::role() const
+{
+ return m_role;
+}
+
+void KItemListGroupHeader::setData(const QVariant& data)
+{
+ if (m_data != data) {
+ const QVariant previous = m_data;
+ m_data = data;
+ update();
+ dataChanged(data, previous);
+ }
+}
+
+QVariant KItemListGroupHeader::data() const
+{
+ return m_data;
+}
+
QSizeF KItemListGroupHeader::sizeHint(Qt::SizeHint which, const QSizeF& constraint) const
{
Q_UNUSED(which);
//painter->drawText(rect(), QString::number(m_index));
}
+void KItemListGroupHeader::roleChanged(const QByteArray& current, const QByteArray& previous)
+{
+ Q_UNUSED(current);
+ Q_UNUSED(previous);
+}
+
+void KItemListGroupHeader::dataChanged(const QVariant& current, const QVariant& previous)
+{
+ Q_UNUSED(current);
+ Q_UNUSED(previous);
+}
+
#include "kitemlistgroupheader.moc"
#include <libdolphin_export.h>
+#include <QByteArray>
#include <QGraphicsWidget>
+#include <QVariant>
class KItemListView;
KItemListGroupHeader(QGraphicsWidget* parent = 0);
virtual ~KItemListGroupHeader();
- void setIndex(int index);
- int index() const;
+ void setRole(const QByteArray& role);
+ QByteArray role() const;
+
+ void setData(const QVariant& data);
+ QVariant data() const;
virtual QSizeF sizeHint(Qt::SizeHint which = Qt::PreferredSize, const QSizeF& constraint = QSizeF()) const;
virtual void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget = 0);
+
+protected:
+ virtual void roleChanged(const QByteArray& current, const QByteArray& previous);
+ virtual void dataChanged(const QVariant& current, const QVariant& previous);
+
+private:
+ QByteArray m_role;
+ QVariant m_data;
+
};
#endif
void KItemListView::slotGroupedSortingChanged(bool current)
{
m_grouped = current;
+ if (m_grouped) {
+ // Assure that headers from already visible items get created
+ QHashIterator<int, KItemListWidget*> it(m_visibleItems);
+ while (it.hasNext()) {
+ it.next();
+ KItemListWidget* widget = it.value();
+ updateGroupHeaderForWidget(widget);
+ }
+ } else {
+ // Clear all visible headers
+ QHashIterator<KItemListWidget*, KItemListGroupHeader*> it (m_visibleGroups);
+ while (it.hasNext()) {
+ it.next();
+ KItemListGroupHeader* header = it.value();
+ m_groupHeaderCreator->recycle(header);
+ }
+ m_visibleGroups.clear();
+ }
+
+ m_layouter->markAsDirty();
+ updateLayout();
}
void KItemListView::slotCurrentChanged(int current, int previous)
return;
}
- //markVisibleRolesSizesAsDirty();
-
const int firstVisibleIndex = m_layouter->firstVisibleIndex();
const int lastVisibleIndex = m_layouter->lastVisibleIndex();
if (firstVisibleIndex < 0) {
m_visibleItems.insert(index, widget);
if (m_grouped) {
- if (m_layouter->isFirstGroupItem(index)) {
- KItemListGroupHeader* header = m_groupHeaderCreator->create(widget);
- header->show();
- // TODO:
- header->setPos(0, -50);
- header->resize(50, 50);
- m_visibleGroups.insert(widget, header);
- }
+ updateGroupHeaderForWidget(widget);
}
initializeItemListWidget(widget);
void KItemListView::setWidgetIndex(KItemListWidget* widget, int index)
{
- if (m_grouped) {
- bool createHeader = m_layouter->isFirstGroupItem(index);
- KItemListGroupHeader* header = m_visibleGroups.value(widget);
- if (header) {
- if (createHeader) {
- createHeader = false;
- } else {
- m_groupHeaderCreator->recycle(header);
- m_visibleGroups.remove(widget);
- }
- }
-
- if (createHeader) {
- KItemListGroupHeader* header = m_groupHeaderCreator->create(widget);
- header->show();
- // TODO:
- header->setPos(0, -50);
- header->resize(50, 50);
- m_visibleGroups.insert(widget, header);
- }
- }
-
const int oldIndex = widget->index();
m_visibleItems.remove(oldIndex);
updateWidgetProperties(widget, index);
m_visibleItems.insert(index, widget);
+ if (m_grouped) {
+ updateGroupHeaderForWidget(widget);
+ }
+
initializeItemListWidget(widget);
}
widget->setData(m_model->data(index));
}
+void KItemListView::updateGroupHeaderForWidget(KItemListWidget* widget)
+{
+ const int index = widget->index();
+ KItemListGroupHeader* header = m_visibleGroups.value(widget);
+ if (!m_layouter->isFirstGroupItem(index)) {
+ // The widget does not represent the first item of a group
+ // and hence requires no header
+ if (header) {
+ m_groupHeaderCreator->recycle(header);
+ m_visibleGroups.remove(widget);
+ }
+ return;
+ }
+
+ if (!header) {
+ header = m_groupHeaderCreator->create(widget);
+ m_visibleGroups.insert(widget, header);
+ }
+
+ // TODO:
+ header->show();
+ header->setPos(0, -50);
+ header->resize(200, 50);
+ header->setRole(model()->sortRole());
+
+ // Determine the shown data for the header by doing a binary
+ // search in the groups-list
+ const QList<QPair<int, QVariant> > groups = model()->groups();
+ int min = 0;
+ int max = groups.count() - 1;
+ int mid = 0;
+ do {
+ mid = (min + max) / 2;
+ if (index > groups.at(mid).first) {
+ min = mid + 1;
+ } else {
+ max = mid - 1;
+ }
+ } while (groups.at(mid).first != index && min <= max);
+ header->setData(groups.at(mid).second);
+}
+
QHash<QByteArray, qreal> KItemListView::headerRolesWidths() const
{
QHash<QByteArray, qreal> rolesWidths;
*/
void updateWidgetProperties(KItemListWidget* widget, int index);
+ /**
+ * Helper method for createWidget() and setWidgetIndex() to create or update
+ * the itemlist groupheader.
+ */
+ void updateGroupHeaderForWidget(KItemListWidget* widget);
+
/**
* @return The widths of each visible role that is shown in the KItemListHeader.
*/