Commit 9b8f05ff authored by Nick Shaforostoff's avatar Nick Shaforostoff
Browse files

ok, reverting as requested by Peter Penz.

svn path=/trunk/KDE/kdebase/apps/; revision=690417
parent 723d2afc
......@@ -16,6 +16,7 @@ set(dolphinprivate_LIB_SRCS
dolphincolumnview.cpp
dolphinitemcategorizer.cpp
kcategorizedview.cpp
ksortfilterproxymodel.cpp
kitemcategorizer.cpp
dolphinsettings.cpp
viewproperties.cpp
......
......@@ -50,10 +50,16 @@ static DolphinView::Sorting sortingTypeTable[] =
};
DolphinSortFilterProxyModel::DolphinSortFilterProxyModel(QObject* parent) :
KDirSortFilterProxyModel(parent),
KSortFilterProxyModel(parent),
m_sorting(DolphinView::SortByName),
m_sortOrder(Qt::AscendingOrder)
{
setDynamicSortFilter(true);
// sort by the user visible string for now
setSortRole(DolphinView::SortByName);
setSortCaseSensitivity(Qt::CaseInsensitive);
sort(KDirModel::Name, Qt::AscendingOrder);
}
DolphinSortFilterProxyModel::~DolphinSortFilterProxyModel()
......@@ -77,7 +83,19 @@ void DolphinSortFilterProxyModel::sort(int column, Qt::SortOrder sortOrder)
m_sorting = sortingForColumn(column);
m_sortOrder = sortOrder;
setSortRole(m_sorting);
KDirSortFilterProxyModel::sort(column, sortOrder);
KSortFilterProxyModel::sort(column, sortOrder);
}
bool DolphinSortFilterProxyModel::hasChildren(const QModelIndex& parent) const
{
const QModelIndex sourceParent = mapToSource(parent);
return sourceModel()->hasChildren(sourceParent);
}
bool DolphinSortFilterProxyModel::canFetchMore(const QModelIndex& parent) const
{
const QModelIndex sourceParent = mapToSource(parent);
return sourceModel()->canFetchMore(sourceParent);
}
DolphinView::Sorting DolphinSortFilterProxyModel::sortingForColumn(int column)
......@@ -178,12 +196,27 @@ bool DolphinSortFilterProxyModel::lessThanGeneralPurpose(const QModelIndex &left
bool DolphinSortFilterProxyModel::lessThan(const QModelIndex& left,
const QModelIndex& right) const
{
#ifdef HAVE_NEPOMUK
KDirModel* dirModel = static_cast<KDirModel*>(sourceModel());
const KFileItem* leftFileItem = dirModel->itemForIndex(left);
const KFileItem* rightFileItem = dirModel->itemForIndex(right);
// If we are sorting by rating, folders and files are citizens of the same
// class. Same if we are sorting by tags.
#ifdef HAVE_NEPOMUK
if ((sortRole() != DolphinView::SortByRating) &&
(sortRole() != DolphinView::SortByTags))
{
#endif
// On our priority, folders go above regular files.
if (leftFileItem->isDir() && !rightFileItem->isDir()) {
return true;
} else if (!leftFileItem->isDir() && rightFileItem->isDir()) {
return false;
}
#ifdef HAVE_NEPOMUK
}
#endif
// Hidden elements go before visible ones, if they both are
// folders or files.
......@@ -194,60 +227,164 @@ bool DolphinSortFilterProxyModel::lessThan(const QModelIndex& left,
}
switch (sortRole()) {
case DolphinView::SortByRating: {
const quint32 leftRating = ratingForIndex(left);
const quint32 rightRating = ratingForIndex(right);
if (leftRating == rightRating) {
// On our priority, folders go above regular files.
// This checks are needed (don't think it's the same doing it here
// than above). Here we make dirs citizens of first class because
// we know we are on the same category. On the check we do on the
// top of the method we don't know, so we remove that check when we
// are sorting by rating. (ereslibre)
if (leftFileItem->isDir() && !rightFileItem->isDir()) {
return true;
} else if (!leftFileItem->isDir() && rightFileItem->isDir()) {
return false;
}
case DolphinView::SortByName: {
// So we are in the same priority, what counts now is their names.
const QVariant leftData = dirModel->data(left, KDirModel::Name);
const QVariant rightData = dirModel->data(right, KDirModel::Name);
const QString leftValueString(leftData.toString());
const QString rightValueString(rightData.toString());
return sortCaseSensitivity() ?
(naturalCompare(leftValueString, rightValueString) < 0) :
(naturalCompare(leftValueString.toLower(), rightValueString.toLower()) < 0);
}
return sortCaseSensitivity() ?
(naturalCompare(leftFileItem->name(), rightFileItem->name()) < 0) :
(naturalCompare(leftFileItem->name().toLower(), rightFileItem->name().toLower()) < 0);
case DolphinView::SortBySize: {
// If we have two folders, what we have to measure is the number of
// items that contains each other
if (leftFileItem->isDir() && rightFileItem->isDir()) {
QVariant leftValue = dirModel->data(left, KDirModel::ChildCountRole);
int leftCount = leftValue.type() == QVariant::Int ? leftValue.toInt() : KDirModel::ChildCountUnknown;
QVariant rightValue = dirModel->data(right, KDirModel::ChildCountRole);
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) {
return sortCaseSensitivity() ? (naturalCompare(leftFileItem->name(), rightFileItem->name()) < 0) :
(naturalCompare(leftFileItem->name().toLower(), rightFileItem->name().toLower()) < 0);
}
return leftRating > rightRating;
// If they had different number of items, we sort them depending
// on how many items had each other.
return leftCount < rightCount;
}
case DolphinView::SortByTags: {
const QString leftTags = tagsForIndex(left);
const QString rightTags = tagsForIndex(right);
if (leftTags == rightTags) {
// On our priority, folders go above regular files.
// This checks are needed (don't think it's the same doing it here
// than above). Here we make dirs citizens of first class because
// we know we are on the same category. On the check we do on the
// top of the method we don't know, so we remove that check when we
// are sorting by tags. (ereslibre)
if (leftFileItem->isDir() && !rightFileItem->isDir()) {
return true;
} else if (!leftFileItem->isDir() && rightFileItem->isDir()) {
return false;
}
// 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()) {
return sortCaseSensitivity() ? (naturalCompare(leftFileItem->name(), rightFileItem->name()) < 0) :
(naturalCompare(leftFileItem->name().toLower(), rightFileItem->name().toLower()) < 0);
}
return sortCaseSensitivity() ?
(naturalCompare(leftFileItem->name(), rightFileItem->name()) < 0) :
// If their sizes are different, sort them by their sizes, as expected.
return leftFileItem->size() < rightFileItem->size();
}
case DolphinView::SortByDate: {
KDateTime leftTime, rightTime;
leftTime.setTime_t(leftFileItem->time(KIO::UDS_MODIFICATION_TIME));
rightTime.setTime_t(rightFileItem->time(KIO::UDS_MODIFICATION_TIME));
if (leftTime == rightTime) {
return sortCaseSensitivity() ?
(naturalCompare(leftFileItem->name(), rightFileItem->name()) < 0) :
(naturalCompare(leftFileItem->name().toLower(), rightFileItem->name().toLower()) < 0);
}
return leftTime > rightTime;
}
case DolphinView::SortByPermissions: {
if (leftFileItem->permissionsString() == rightFileItem->permissionsString()) {
return sortCaseSensitivity() ?
(naturalCompare(leftFileItem->name(), rightFileItem->name()) < 0) :
(naturalCompare(leftFileItem->name().toLower(), rightFileItem->name().toLower()) < 0);
}
return naturalCompare(leftFileItem->permissionsString(),
rightFileItem->permissionsString()) < 0;
}
case DolphinView::SortByOwner: {
if (leftFileItem->user() == rightFileItem->user()) {
return sortCaseSensitivity() ?
(naturalCompare(leftFileItem->name(), rightFileItem->name()) < 0) :
(naturalCompare(leftFileItem->name().toLower(), rightFileItem->name().toLower()) < 0);
}
return naturalCompare(leftFileItem->user(), rightFileItem->user()) < 0;
}
case DolphinView::SortByGroup: {
if (leftFileItem->group() == rightFileItem->group()) {
return sortCaseSensitivity() ? (naturalCompare(leftFileItem->name(), rightFileItem->name()) < 0) :
(naturalCompare(leftFileItem->name().toLower(), rightFileItem->name().toLower()) < 0);
}
return naturalCompare(leftFileItem->group(),
rightFileItem->group()) < 0;
}
case DolphinView::SortByType: {
if (leftFileItem->mimetype() == rightFileItem->mimetype()) {
return sortCaseSensitivity() ?
(naturalCompare(leftFileItem->name(), rightFileItem->name()) < 0) :
(naturalCompare(leftFileItem->name().toLower(), rightFileItem->name().toLower()) < 0);
}
return naturalCompare(leftFileItem->mimeComment(),
rightFileItem->mimeComment()) < 0;
}
#ifdef HAVE_NEPOMUK
case DolphinView::SortByRating: {
const quint32 leftRating = ratingForIndex(left);
const quint32 rightRating = ratingForIndex(right);
if (leftRating == rightRating) {
// On our priority, folders go above regular files.
// This checks are needed (don't think it's the same doing it here
// than above). Here we make dirs citizens of first class because
// we know we are on the same category. On the check we do on the
// top of the method we don't know, so we remove that check when we
// are sorting by rating. (ereslibre)
if (leftFileItem->isDir() && !rightFileItem->isDir()) {
return true;
} else if (!leftFileItem->isDir() && rightFileItem->isDir()) {
return false;
}
return naturalCompare(leftTags, rightTags) < 0;
return sortCaseSensitivity() ?
(naturalCompare(leftFileItem->name(), rightFileItem->name()) < 0) :
(naturalCompare(leftFileItem->name().toLower(), rightFileItem->name().toLower()) < 0);
}
return leftRating > rightRating;
}
case DolphinView::SortByTags: {
const QString leftTags = tagsForIndex(left);
const QString rightTags = tagsForIndex(right);
if (leftTags == rightTags) {
// On our priority, folders go above regular files.
// This checks are needed (don't think it's the same doing it here
// than above). Here we make dirs citizens of first class because
// we know we are on the same category. On the check we do on the
// top of the method we don't know, so we remove that check when we
// are sorting by tags. (ereslibre)
if (leftFileItem->isDir() && !rightFileItem->isDir()) {
return true;
} else if (!leftFileItem->isDir() && rightFileItem->isDir()) {
return false;
}
return sortCaseSensitivity() ?
(naturalCompare(leftFileItem->name(), rightFileItem->name()) < 0) :
(naturalCompare(leftFileItem->name().toLower(), rightFileItem->name().toLower()) < 0);
}
return naturalCompare(leftTags, rightTags) < 0;
}
#endif
}
// We have set a SortRole and trust the ProxyModel to do
// the right thing for now.
return KDirSortFilterProxyModel::lessThan(left, right);
return QSortFilterProxyModel::lessThan(left, right);
}
quint32 DolphinSortFilterProxyModel::ratingForIndex(const QModelIndex& index)
......@@ -295,10 +432,111 @@ QString DolphinSortFilterProxyModel::tagsForIndex(const QModelIndex& index)
return tagsString;
#else
Q_UNUSED(index);
return QString();
#endif
}
int DolphinSortFilterProxyModel::naturalCompare(const QString& a,
const QString& b)
{
// This method chops the input a and b into pieces of
// digits and non-digits (a1.05 becomes a | 1 | . | 05)
// and compares these pieces of a and b to each other
// (first with first, second with second, ...).
//
// This is based on the natural sort order code code by Martin Pool
// http://sourcefrog.net/projects/natsort/
// Martin Pool agreed to license this under LGPL or GPL.
const QChar* currA = a.unicode(); // iterator over a
const QChar* currB = b.unicode(); // iterator over b
if (currA == currB) {
return 0;
}
const QChar* begSeqA = currA; // beginning of a new character sequence of a
const QChar* begSeqB = currB;
while (!currA->isNull() && !currB->isNull()) {
// find sequence of characters ending at the first non-character
while (!currA->isNull() && !currA->isDigit()) {
++currA;
}
while (!currB->isNull() && !currB->isDigit()) {
++currB;
}
// compare these sequences
const QString subA(begSeqA, currA - begSeqA);
const QString subB(begSeqB, currB - begSeqB);
const int cmp = QString::localeAwareCompare(subA, subB);
if (cmp != 0) {
return cmp;
}
if (currA->isNull() || currB->isNull()) {
break;
}
// now some digits follow...
if ((*currA == '0') || (*currB == '0')) {
// one digit-sequence starts with 0 -> assume we are in a fraction part
// do left aligned comparison (numbers are considered left aligned)
while (1) {
if (!currA->isDigit() && !currB->isDigit()) {
break;
} else if (!currA->isDigit()) {
return -1;
} else if (!currB->isDigit()) {
return + 1;
} else if (*currA < *currB) {
return -1;
} else if (*currA > *currB) {
return + 1;
}
++currA;
++currB;
}
} else {
// No digit-sequence starts with 0 -> assume we are looking at some integer
// do right aligned comparison.
//
// The longest run of digits wins. That aside, the greatest
// value wins, but we can't know that it will until we've scanned
// both numbers to know that they have the same magnitude.
int weight = 0;
while (1) {
if (!currA->isDigit() && !currB->isDigit()) {
if (weight != 0) {
return weight;
}
break;
} else if (!currA->isDigit()) {
return -1;
} else if (!currB->isDigit()) {
return + 1;
} else if ((*currA < *currB) && (weight == 0)) {
weight = -1;
} else if ((*currA > *currB) && (weight == 0)) {
weight = + 1;
}
++currA;
++currB;
}
}
begSeqA = currA;
begSeqB = currB;
}
if (currA->isNull() && currB->isNull()) {
return 0;
}
return currA->isNull() ? -1 : + 1;
}
#include "dolphinsortfilterproxymodel.moc"
......@@ -20,7 +20,7 @@
#ifndef DOLPHINSORTFILTERPROXYMODEL_H
#define DOLPHINSORTFILTERPROXYMODEL_H
#include <kdirsortfilterproxymodel.h>
#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 KDirSortFilterProxyModel
class LIBDOLPHINPRIVATE_EXPORT DolphinSortFilterProxyModel : public KSortFilterProxyModel
{
Q_OBJECT
......@@ -62,6 +62,12 @@ public:
virtual void sort(int column,
Qt::SortOrder order = Qt::AscendingOrder);
/** Reimplemented from QAbstractItemModel. Returns true for directories. */
virtual bool hasChildren(const QModelIndex& parent = QModelIndex()) const;
/** Reimplemented from QAbstractItemModel. Returns true for empty directories. */
virtual bool canFetchMore(const QModelIndex& parent) const;
/**
* Helper method to get the DolphinView::Sorting type for a given
* column \a column. If the column is smaller 0 or greater than the
......@@ -69,39 +75,9 @@ public:
*/
static DolphinView::Sorting sortingForColumn(int column);
/**
* This method is essential on the categorized view.
* It will does a "basic" sorting, just for finding out categories,
* and their order. Then over those elements DISORDERED on categories,
* the lessThan method will be applied for each category.
*
* The easy explanation is that not always folders go first. That will depend.
* Imagine we sort by Rating. Categories will be created by 10 stars,
* 9 stars, 8 stars... but a category with only a file with rating 10
* will go before a category with a folder with rating 8.
* That's the main reason, and that's lessThanGeneralPurpose() method.
* That will go category by category creating sets of elements...
*/
virtual bool lessThanGeneralPurpose(const QModelIndex &left,
const QModelIndex &right) const;
/**
* Then for each set of elements lessThanCategoryPurpose() will be applied,
* because for each category we wan't first folders and bla bla bla...
* That's the main reason of that method existence.
*
* For that reason, is not that clear that we want ALWAYS folders first.
* On each category, yes, that's true. But that's not true always,
* as I have pointed out on the example before.
*/
bool lessThanCategoryPurpose(const QModelIndex &left,
const QModelIndex &right) const
{
//when we sort inside 1 category its the usual lessThan()
//from KDirSortFilterProxyModel(+nepomuk)
return lessThan(left,right);
}
protected:
virtual bool lessThan(const QModelIndex& left,
const QModelIndex& right) const;
......@@ -119,6 +95,8 @@ private:
*/
static QString tagsForIndex(const QModelIndex& index);
static int naturalCompare(const QString& a, const QString& b);
private:
DolphinView::Sorting m_sorting;
Qt::SortOrder m_sortOrder;
......
......@@ -33,7 +33,7 @@
#include <kstyle.h>
#include "kitemcategorizer.h"
#include "dolphinsortfilterproxymodel.h"
#include "ksortfilterproxymodel.h"
class LessThan
{
......@@ -44,7 +44,7 @@ public:
CategoryPurpose
};
inline LessThan(const DolphinSortFilterProxyModel *proxyModel,
inline LessThan(const KSortFilterProxyModel *proxyModel,
Purpose purpose)
: proxyModel(proxyModel)
, purpose(purpose)
......@@ -67,7 +67,7 @@ public:
}
private:
const DolphinSortFilterProxyModel *proxyModel;
const KSortFilterProxyModel *proxyModel;
const Purpose purpose;
};
......@@ -483,7 +483,7 @@ void KCategorizedView::setModel(QAbstractItemModel *model)
QListView::setModel(model);
d->proxyModel = dynamic_cast<DolphinSortFilterProxyModel*>(model);
d->proxyModel = dynamic_cast<KSortFilterProxyModel*>(model);
if (d->proxyModel)
{
......
......@@ -21,7 +21,7 @@
#ifndef KCATEGORIZEDVIEW_P_H
#define KCATEGORIZEDVIEW_P_H
class DolphinSortFilterProxyModel;
class KSortFilterProxyModel;
/**
* @internal
......@@ -152,7 +152,7 @@ public:
QRect lastSelectionRect;
// Attributes for speed reasons
DolphinSortFilterProxyModel *proxyModel;
KSortFilterProxyModel *proxyModel;
QModelIndexList sourceModelIndexList; // in source model
QModelIndex lastIndex;
};
......
/**
* 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()
{
}
void KSortFilterProxyModel::sort(int column, Qt::SortOrder order)
{
QSortFilterProxyModel::sort(column, order);
m_sortOrder = order;
emit sortingRoleChanged();
}
Qt::SortOrder KSortFilterProxyModel::sortOrder() const
{
return m_sortOrder;
}
bool KSortFilterProxyModel::lessThanCategoryPurpose(const QModelIndex &left,
const QModelIndex &right) const
{
return lessThan(left, right);
}
#include "ksortfilterproxymodel.moc"
/**
* 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.