mirror of
https://github.com/transmission/transmission
synced 2024-12-23 00:04:06 +00:00
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:
parent
b9adf279ca
commit
5296570476
4 changed files with 115 additions and 24 deletions
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue