Improve file tree population/update performance

Use simple tokenization instead of splitting the file path into
QStringList for each item. Don't expand each added item separetely
during population/update. Don't expand all the items but just up to the
point where parent has more than one expandable child.

Also, fix items order by sorting items after population/update. Fix
sorting by size on systems where uint64_t != quint64.
This commit is contained in:
Mike Gelfand 2015-07-12 20:48:54 +00:00
parent b9adf279ca
commit 5296570476
4 changed files with 115 additions and 24 deletions

View File

@ -119,7 +119,7 @@ FileTreeItem::data (int column, int role) const
if (role == Qt::DisplayRole)
value.setValue (sizeString());
else
value.setValue (size ());
value.setValue<quint64> (size ());
break;
case FileTreeModel::COL_PROGRESS:

View File

@ -9,11 +9,81 @@
#include <cassert>
#include <QStringList>
#include "FileTreeItem.h"
#include "FileTreeModel.h"
namespace
{
class PathIteratorBase
{
protected:
PathIteratorBase(const QString& path, int slashIndex):
myPath (path),
mySlashIndex (slashIndex),
myToken ()
{
myToken.reserve (path.size () / 2);
}
protected:
const QString& myPath;
int mySlashIndex;
QString myToken;
static const QChar SlashChar;
};
const QChar PathIteratorBase::SlashChar = QLatin1Char ('/');
class ForwardPathIterator: public PathIteratorBase
{
public:
ForwardPathIterator (const QString& path):
PathIteratorBase (path, path.size () - 1)
{
}
bool hasNext () const
{
return mySlashIndex > 0;
}
const QString& next ()
{
int newSlashIndex = myPath.lastIndexOf (SlashChar, mySlashIndex);
myToken.truncate (0);
myToken += myPath.midRef (newSlashIndex + 1, mySlashIndex - newSlashIndex);
mySlashIndex = newSlashIndex - 1;
return myToken;
}
};
class BackwardPathIterator: public PathIteratorBase
{
public:
BackwardPathIterator (const QString& path):
PathIteratorBase (path, 0)
{
}
bool hasNext () const
{
return mySlashIndex < myPath.size ();
}
const QString& next ()
{
int newSlashIndex = myPath.indexOf (SlashChar, mySlashIndex);
if (newSlashIndex == -1)
newSlashIndex = myPath.size ();
myToken.truncate (0);
myToken += myPath.midRef (mySlashIndex, newSlashIndex - mySlashIndex);
mySlashIndex = newSlashIndex + 1;
return myToken;
}
};
}
FileTreeModel::FileTreeModel (QObject * parent, bool isEditable):
QAbstractItemModel(parent),
myIsEditable (isEditable),
@ -221,26 +291,25 @@ FileTreeModel::findItemForFileIndex (int fileIndex) const
}
void
FileTreeModel::addFile (int fileIndex,
const QString & filename,
bool wanted,
int priority,
uint64_t totalSize,
uint64_t have,
QList<QModelIndex> & rowsAdded,
bool updateFields)
FileTreeModel::addFile (int fileIndex,
const QString& filename,
bool wanted,
int priority,
uint64_t totalSize,
uint64_t have,
bool updateFields)
{
FileTreeItem * item;
QStringList tokens = filename.split (QChar::fromLatin1('/'));
item = findItemForFileIndex (fileIndex);
if (item) // this file is already in the tree, we've added this
{
QModelIndex indexWithChangedParents;
while (!tokens.isEmpty())
ForwardPathIterator filenameIt (filename);
while (filenameIt.hasNext ())
{
const QString token = tokens.takeLast();
const QString& token = filenameIt.next ();
const std::pair<int,int> changed = item->update (token, wanted, priority, have, updateFields);
if (changed.first >= 0)
{
@ -260,9 +329,10 @@ FileTreeModel::addFile (int fileIndex,
bool added = false;
item = myRootItem;
while (!tokens.isEmpty())
BackwardPathIterator filenameIt (filename);
while (filenameIt.hasNext ())
{
const QString token = tokens.takeFirst();
const QString& token = filenameIt.next ();
FileTreeItem * child(item->child(token));
if (!child)
{
@ -271,14 +341,12 @@ FileTreeModel::addFile (int fileIndex,
const int n (item->childCount());
beginInsertRows (parentIndex, n, n);
if (tokens.isEmpty())
if (!filenameIt.hasNext ())
child = new FileTreeItem (token, fileIndex, totalSize);
else
child = new FileTreeItem (token);
item->appendChild (child);
endInsertRows ();
rowsAdded.append (indexOf(child, 0));
}
item = child;
}

View File

@ -53,7 +53,6 @@ class FileTreeModel: public QAbstractItemModel
void addFile (int index, const QString& filename,
bool wanted, int priority,
uint64_t size, uint64_t have,
QList<QModelIndex>& rowsAdded,
bool torrentChanged);
QModelIndex parent (const QModelIndex& child, int column) const;

View File

@ -169,13 +169,37 @@ FileTreeView::keyPressEvent (QKeyEvent * event)
void
FileTreeView::update (const FileList& files, bool updateFields)
{
const bool modelWasEmpty = myProxy->rowCount () == 0;
for (const TorrentFile& file: files)
myModel->addFile (file.index, file.filename, file.wanted, file.priority, file.size, file.have, updateFields);
if (modelWasEmpty)
{
QList<QModelIndex> added;
myModel->addFile (file.index, file.filename, file.wanted, file.priority, file.size, file.have, added, updateFields);
for (const QModelIndex& i: added)
expand (myProxy->mapFromSource(i));
// expand up until the item with more than one expandable child
for (QModelIndex index = myProxy->index (0, 0); index.isValid ();)
{
const QModelIndex oldIndex = index;
expand (oldIndex);
index = QModelIndex ();
for (int i = 0, count = myProxy->rowCount (oldIndex); i < count; ++i)
{
const QModelIndex newIndex = myProxy->index (i, 0, oldIndex);
if (myProxy->rowCount (newIndex) == 0)
continue;
if (index.isValid ())
{
index = QModelIndex ();
break;
}
index = newIndex;
}
}
}
myProxy->sort (header ()->sortIndicatorSection (), header ()->sortIndicatorOrder ());
}
void