Commit edfee468 authored by Rafael Fernández López's avatar Rafael Fernández López
Browse files

New and powerful KListView. Still pending class renaming. There are two

methods that I need to think about it, and boost. Small issues like 
reloading all data when sorting role suddenly changes. In general terms 
it will work nice when you sort by name or size. We have to work further 
when we sort by other roles. Nice times.

svn path=/trunk/KDE/kdebase/apps/; revision=676732
parent ba89bd5a
......@@ -15,6 +15,7 @@ set(dolphinprivate_LIB_SRCS
dolphiniconsview.cpp
dolphinitemcategorizer.cpp
klistview.cpp
ksortfilterproxymodel.cpp
dolphinsettings.cpp
viewproperties.cpp
dolphinsortfilterproxymodel.cpp
......
......@@ -38,7 +38,7 @@ DolphinIconsView::DolphinIconsView(QWidget* parent, DolphinController* controlle
Q_ASSERT(controller != 0);
setViewMode(QListView::IconMode);
setResizeMode(QListView::Adjust);
setSpacing(10);
setMouseTracking(true);
viewport()->setAttribute(Qt::WA_Hover);
......
......@@ -41,38 +41,79 @@ QString DolphinItemCategorizer::categoryForItem(const QModelIndex& index,
{
QString retString;
if (!index.isValid()) {
if (!index.isValid())
{
return retString;
}
int indexColumn;
switch (sortRole)
{
case DolphinView::SortByName:
indexColumn = KDirModel::Name;
break;
case DolphinView::SortBySize:
indexColumn = KDirModel::Size;
break;
default:
return retString;
}
// KDirModel checks columns to know to which role are
// we talking about
QModelIndex theIndex = index.model()->index(index.row(),
sortRole,
indexColumn,
index.parent());
const QSortFilterProxyModel* proxyModel = static_cast<const QSortFilterProxyModel*>(index.model());
const KDirModel* dirModel = static_cast<const KDirModel*>(proxyModel->sourceModel());
if (!theIndex.isValid()) {
return retString;
}
QVariant data = theIndex.model()->data(theIndex, Qt::DisplayRole);
QModelIndex mappedIndex = proxyModel->mapToSource(theIndex);
KFileItem* item = dirModel->itemForIndex(mappedIndex);
const KDirModel *dirModel = qobject_cast<const KDirModel*>(index.model());
KFileItem* item = dirModel->itemForIndex(index);
switch (sortRole) {
switch (sortRole)
{
case DolphinView::SortByName:
retString = data.toString().toUpper().at(0);
if (data.toString().size())
{
if (!item->isHidden() && data.toString().at(0).isLetter())
retString = data.toString().toUpper().at(0);
else if (item->isHidden() && data.toString().at(0) == '.' &&
data.toString().at(1).isLetter())
retString = i18n(".%1 (Hidden)", data.toString().toUpper().at(1));
else if (item->isHidden() && data.toString().at(0) == '.' &&
!data.toString().at(1).isLetter())
retString = i18n("Others (Hidden)");
else if (item->isHidden() && data.toString().at(0) != '.')
retString = i18n("%1 (Hidden)", data.toString().toUpper().at(0));
else if (item->isHidden())
retString = data.toString().toUpper().at(0);
else
retString = i18n("Others");
}
break;
case DolphinView::SortBySize:
int fileSize = (item) ? item->size() : -1;
if (item && item->isDir()) {
retString = i18n("Unknown");
} else if (fileSize < 5242880) {
if (item && item->isDir() && !item->isHidden()) {
retString = i18n("Folders");
} else if (fileSize < 5242880 && !item->isHidden()) {
retString = i18n("Small");
} else if (fileSize < 10485760) {
} else if (fileSize < 10485760 && !item->isHidden()) {
retString = i18n("Medium");
} else {
} else if (!item->isHidden()){
retString = i18n("Big");
} else if (item && item->isDir() && item->isHidden()) {
retString = i18n("Folders (Hidden)");
} else if (fileSize < 5242880 && item->isHidden()) {
retString = i18n("Small (Hidden)");
} else if (fileSize < 10485760 && item->isHidden()) {
retString = i18n("Medium (Hidden)");
} else if (item->isHidden()){
retString = i18n("Big (Hidden)");
}
break;
}
......
......@@ -2,6 +2,7 @@
* Copyright (C) 2006 by Peter Penz <peter.penz@gmx.at> *
* Copyright (C) 2006 by Dominic Battre <dominic@battre.de> *
* Copyright (C) 2006 by Martin Pool <mbp@canonical.com> *
* Copyright (C) 2007 by Rafael Fernández López <ereslibre@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 *
......@@ -49,7 +50,7 @@ static DolphinView::Sorting dirModelColumnToDolphinView[] =
DolphinSortFilterProxyModel::DolphinSortFilterProxyModel(QObject* parent) :
QSortFilterProxyModel(parent),
KSortFilterProxyModel(parent),
m_sortColumn(0),
m_sorting(DolphinView::SortByName),
m_sortOrder(Qt::AscendingOrder)
......@@ -57,7 +58,7 @@ DolphinSortFilterProxyModel::DolphinSortFilterProxyModel(QObject* parent) :
setDynamicSortFilter(true);
// sort by the user visible string for now
setSortRole(Qt::DisplayRole);
setSortRole(DolphinView::SortByName);
setSortCaseSensitivity(Qt::CaseInsensitive);
sort(KDirModel::Name, Qt::AscendingOrder);
}
......@@ -87,6 +88,7 @@ void DolphinSortFilterProxyModel::sort(int column, Qt::SortOrder sortOrder)
m_sorting = (column >= 0) && (column < dolphinMapSize) ?
dirModelColumnToDolphinView[column] :
DolphinView::SortByName;
setSortRole(m_sorting);
QSortFilterProxyModel::sort(column, sortOrder);
}
......@@ -110,38 +112,150 @@ DolphinView::Sorting DolphinSortFilterProxyModel::sortingForColumn(int column)
return DolphinView::SortByName;
}
bool DolphinSortFilterProxyModel::lessThanGeneralPurpose(const QModelIndex &left,
const QModelIndex &right) const
{
KDirModel* dirModel = static_cast<KDirModel*>(sourceModel());
const KFileItem *leftFileItem = dirModel->itemForIndex(left);
const KFileItem *rightFileItem = dirModel->itemForIndex(right);
if (sortRole() == DolphinView::SortByName) // If we are sorting by name
{
const QVariant leftData = dirModel->data(left, sortRole());
const QVariant rightData = dirModel->data(right, sortRole());
// Give preference to hidden items. They will be shown above regular
// items
if (leftFileItem->isHidden() && !rightFileItem->isHidden())
return true;
else if (!leftFileItem->isHidden() && rightFileItem->isHidden())
return false;
// If we are handling two items of the same preference, just take in
// count their names. There is no need to check for case sensitivity
// here, since this is the method that explores for new categories
return leftData.toString().toLower() < rightData.toString().toLower();
}
else if (sortRole() == DolphinView::SortBySize) // If we are sorting by size
{
// Give preference to hidden items. They will be shown above regular
// items
if (leftFileItem->isHidden() && !rightFileItem->isHidden())
return true;
else if (!leftFileItem->isHidden() && rightFileItem->isHidden())
return false;
// If we are sorting by size, show folders first. We will sort them
// correctly later
if (leftFileItem->isDir() && !rightFileItem->isDir())
return true;
return false;
}
}
bool DolphinSortFilterProxyModel::lessThan(const QModelIndex& left,
const QModelIndex& right) const
const QModelIndex& right) const
{
KDirModel* dirModel = static_cast<KDirModel*>(sourceModel());
QVariant leftData = dirModel->data(left, sortRole());
QVariant rightData = dirModel->data(right, sortRole());
if ((leftData.type() == QVariant::String) && (rightData.type() == QVariant::String)) {
// assure that directories are always sorted before files
// if the sorting is done by the 'Name' column
if (m_sortColumn == KDirModel::Name) {
const bool leftIsDir = dirModel->itemForIndex(left)->isDir();
const bool rightIsDir = dirModel->itemForIndex(right)->isDir();
if (leftIsDir && !rightIsDir) {
const KFileItem *leftFileItem = dirModel->itemForIndex(left);
const KFileItem *rightFileItem = dirModel->itemForIndex(right);
if (sortRole() == DolphinView::SortByName) { // If we are sorting by name
if ((leftData.type() == QVariant::String) && (rightData.type() ==
QVariant::String)) {
// Priority: hidden > folders > regular files. If an item is
// hidden (doesn't matter if file or folder) will have higher
// preference than a non-hidden item
if (leftFileItem->isHidden() && !rightFileItem->isHidden()) {
return true;
}
else if (!leftFileItem->isHidden() && rightFileItem->isHidden()) {
return false;
}
if (!leftIsDir && rightIsDir) {
// On our priority, folders go above regular files
if (leftFileItem->isDir() && !rightFileItem->isDir()) {
return true;
}
else if (!leftFileItem->isDir() && rightFileItem->isDir()) {
return false;
}
// So we are in the same priority, what counts now is their names
const QString leftStr = leftData.toString();
const QString rightStr = rightData.toString();
return sortCaseSensitivity() ? (naturalCompare(leftStr, rightStr) < 0) :
(naturalCompare(leftStr.toLower(), rightStr.toLower()) < 0);
}
}
else if (sortRole() == DolphinView::SortBySize) { // If we are sorting by size
// Priority: hidden > folders > regular files. If an item is
// hidden (doesn't matter if file or folder) will have higher
// preference than a non-hidden item
if (leftFileItem->isHidden() && !rightFileItem->isHidden()) {
return true;
}
else if (!leftFileItem->isHidden() && rightFileItem->isHidden()) {
return false;
}
// On our priority, folders go above regular files
if (leftFileItem->isDir() && !rightFileItem->isDir()) {
return true;
}
else if (!leftFileItem->isDir() && rightFileItem->isDir()) {
return false;
}
// If we have two folders, what we have to measure is the number of
// items that contains each other
if (leftFileItem->isDir() && rightFileItem->isDir()) {
const QVariant leftValue = dirModel->data(left, KDirModel::ChildCountRole);
const int leftCount = leftValue.type() == QVariant::Int ? leftValue.toInt() : KDirModel::ChildCountUnknown;
const QVariant rightValue = dirModel->data(right, KDirModel::ChildCountRole);
const int rightCount = rightValue.type() == QVariant::Int ? rightValue.toInt() : KDirModel::ChildCountUnknown;
// In the case they two have the same child items, we sort them by
// their names. So we have always everything ordered. We also check
// if we are taking in count their cases
if (leftCount == rightCount) {
const QString leftStr = leftData.toString();
const QString rightStr = rightData.toString();
return sortCaseSensitivity() ? (naturalCompare(leftStr, rightStr) < 0) :
(naturalCompare(leftStr.toLower(), rightStr.toLower()) < 0);
}
// If they had different number of items, we sort them depending
// on how many items had each other
return leftCount < rightCount;
}
const QString leftStr = leftData.toString();
const QString rightStr = rightData.toString();
// If what we are measuring is two files and they have the same size,
// sort them by their file names
if (leftFileItem->size() == rightFileItem->size()) {
const QString leftStr = leftData.toString();
const QString rightStr = rightData.toString();
return sortCaseSensitivity() ? (naturalCompare(leftStr, rightStr) < 0) :
(naturalCompare(leftStr.toLower(), rightStr.toLower()) < 0);
return sortCaseSensitivity() ? (naturalCompare(leftStr, rightStr) < 0) :
(naturalCompare(leftStr.toLower(), rightStr.toLower()) < 0);
}
// If their sizes are different, sort them by their sizes, as expected
return leftFileItem->size() < rightFileItem->size();
}
// We have set a SortRole and trust the ProxyModel to do
// the right thing for now.
return QSortFilterProxyModel::lessThan(left, right);
}
......
......@@ -20,7 +20,7 @@
#ifndef DOLPHINSORTFILTERPROXYMODEL_H
#define DOLPHINSORTFILTERPROXYMODEL_H
#include <QtGui/QSortFilterProxyModel>
#include <ksortfilterproxymodel.h>
#include <dolphinview.h>
#include <libdolphin_export.h>
......@@ -39,7 +39,7 @@
*
* It is assured that directories are always sorted before files.
*/
class LIBDOLPHINPRIVATE_EXPORT DolphinSortFilterProxyModel : public QSortFilterProxyModel
class LIBDOLPHINPRIVATE_EXPORT DolphinSortFilterProxyModel : public KSortFilterProxyModel
{
Q_OBJECT
......@@ -81,6 +81,9 @@ public:
*/
static DolphinView::Sorting sortingForColumn(int column);
virtual bool lessThanGeneralPurpose(const QModelIndex &left,
const QModelIndex &right) const;
protected:
virtual bool lessThan(const QModelIndex& left,
const QModelIndex& right) const;
......
......@@ -18,8 +18,8 @@
* Boston, MA 02110-1301, USA.
*/
#ifndef __KITEMCATEGORIZER_H__
#define __KITEMCATEGORIZER_H__
#ifndef KITEMCATEGORIZER_H
#define KITEMCATEGORIZER_H
#include <libdolphin_export.h>
......
This diff is collapsed.
......@@ -18,8 +18,8 @@
* Boston, MA 02110-1301, USA.
*/
#ifndef __KLISTVIEW_H__
#define __KLISTVIEW_H__
#ifndef KLISTVIEW_H
#define KLISTVIEW_H
#include <QtGui/QListView>
......@@ -37,9 +37,9 @@ public:
~KListView();
void setModel(QAbstractItemModel *model);
virtual void setModel(QAbstractItemModel *model);
QRect visualRect(const QModelIndex &index) const;
virtual QRect visualRect(const QModelIndex &index) const;
KItemCategorizer *itemCategorizer() const;
......@@ -47,42 +47,41 @@ public:
virtual QModelIndex indexAt(const QPoint &point) const;
virtual int sizeHintForRow(int row) const;
public Q_SLOTS:
virtual void reset();
protected:
virtual void drawNewCategory(const QString &category,
const QStyleOptionViewItem &option,
QPainter *painter);
virtual int categoryHeight(const QStyleOptionViewItem &option) const;
virtual void paintEvent(QPaintEvent *event);
virtual void resizeEvent(QResizeEvent *event);
virtual void setSelection(const QRect &rect,
QItemSelectionModel::SelectionFlags flags);
virtual void timerEvent(QTimerEvent *event);
virtual void mouseMoveEvent(QMouseEvent *event);
virtual void mousePressEvent(QMouseEvent *event);
virtual void mouseReleaseEvent(QMouseEvent *event);
virtual void leaveEvent(QEvent *event);
protected Q_SLOTS:
virtual void rowsInserted(const QModelIndex &parent,
int start,
int end);
virtual void rowsAboutToBeRemoved(const QModelIndex &parent,
int start,
int end);
virtual void rowsInsertedArtifficial(const QModelIndex &parent,
int start,
int end);
virtual void rowsAboutToBeRemovedArtifficial(const QModelIndex &parent,
int start,
int end);
virtual void rowsRemoved(const QModelIndex &parent,
int start,
int end);
virtual void updateGeometries();
virtual void itemsLayoutChanged();
virtual void slotSortingRoleChanged();
private:
......
......@@ -18,27 +18,121 @@
* Boston, MA 02110-1301, USA.
*/
#ifndef __KLISTVIEW_P_H__
#define __KLISTVIEW_P_H__
#ifndef KLISTVIEW_P_H
#define KLISTVIEW_P_H
class QSortFilterProxyModel;
class KSortFilterProxyModel;
/**
* @internal
*/
class KListView::Private
{
public:
Private(KListView *listView);
~Private();
QModelIndexList intersectionSet(const QRect &rect) const;
// Methods
/**
* Returns the list of items that intersects with @p rect
*/
const QModelIndexList &intersectionSet(const QRect &rect);
/**
* Gets the item rect in the viewport for @p index
*/
QRect visualRectInViewport(const QModelIndex &index) const;
/**
* Returns the category rect in the viewport for @p category
*/
QRect visualCategoryRectInViewport(const QString &category) const;
/**
* Caches and returns the rect that corresponds to @p index
*/
const QRect &cacheIndex(const QModelIndex &index);
/**
* Caches and returns the rect that corresponds to @p category
*/
const QRect &cacheCategory(const QString &category);
/**
* Returns the rect that corresponds to @p index
* @note If the rect is not cached, it becomes cached
*/
const QRect &cachedRectIndex(const QModelIndex &index);
/**
* Returns the rect that corresponds to @p category
* @note If the rect is not cached, it becomes cached
*/
const QRect &cachedRectCategory(const QString &category);
/**
* Returns the visual rect (taking in count x and y offsets) for @p index
* @note If the rect is not cached, it becomes cached
*/
QRect visualRect(const QModelIndex &index);
/**
* Returns the visual rect (taking in count x and y offsets) for @p category
* @note If the rect is not cached, it becomes cached
*/
QRect categoryVisualRect(const QString &category);
/**
* This method will draw a new category with name @p category on the rect
* specified by @p option.rect, with painter @p painter
*/
void drawNewCategory(const QString &category,
const QStyleOption &option,
QPainter *painter);
/**
* This method will update scrollbars ranges. Called when our model changes
* or when the view is resized
*/
void updateScrollbars();
// Attributes
struct ElementInfo
{
QString category;
int relativeOffsetToCategory;
};
// Basic data
KListView *listView;
QModelIndex hovered;
bool modelSortCapable;
int numCategories;
QList<QString> categories;
QHash<QString, int> elementsPerCategory;
KItemCategorizer *itemCategorizer;
QSortFilterProxyModel *proxyModel;
// Behavior data
bool mouseButtonPressed;
QModelIndex hovered;
QPoint initialPressPosition;
QPoint mousePosition;
// Cache data
// We cannot merge some of them into structs because it would affect
// performance
QHash<QModelIndex, struct ElementInfo> elementsInfo; // in source model
QHash<QModelIndex, QRect> elementsPosition; // in source model
QHash<QModelIndex, QModelIndex> elementDictionary; // mapped indexes
QHash<QString, QModelIndexList> categoriesIndexes;
QHash<QString, QRect> categoriesPosition;
QStringList categories;
QModelIndexList intersectedIndexes;
QModelIndexList tempSelected;
// Attributes for speed reasons
KSortFilterProxyModel *proxyModel;
QModelIndexList sourceModelIndexList; // in source model
QModelIndex lastIndex;
};
#endif // __KLISTVIEW_P_H__
/**
* This file is part of the KDE project
* Copyright (C) 2007 Rafael Fernández López <ereslibre@gmail.com>
*
* 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.
*/
#include "ksortfilterproxymodel.h"
KSortFilterProxyModel::KSortFilterProxyModel(QObject *parent)
: QSortFilterProxyModel(parent)
{
}
KSortFilterProxyModel::~KSortFilterProxyModel()
{
}
bool KSortFilterProxyModel::lessThanCategoryPurpose(const QModelIndex &left,
const QModelIndex &right) const