mirror of
https://github.com/transmission/transmission.git
synced 2025-12-20 02:18:42 +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:
@@ -119,7 +119,7 @@ FileTreeItem::data (int column, int role) const
|
|||||||
if (role == Qt::DisplayRole)
|
if (role == Qt::DisplayRole)
|
||||||
value.setValue (sizeString());
|
value.setValue (sizeString());
|
||||||
else
|
else
|
||||||
value.setValue (size ());
|
value.setValue<quint64> (size ());
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case FileTreeModel::COL_PROGRESS:
|
case FileTreeModel::COL_PROGRESS:
|
||||||
|
|||||||
@@ -9,11 +9,81 @@
|
|||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
|
||||||
#include <QStringList>
|
|
||||||
|
|
||||||
#include "FileTreeItem.h"
|
#include "FileTreeItem.h"
|
||||||
#include "FileTreeModel.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):
|
FileTreeModel::FileTreeModel (QObject * parent, bool isEditable):
|
||||||
QAbstractItemModel(parent),
|
QAbstractItemModel(parent),
|
||||||
myIsEditable (isEditable),
|
myIsEditable (isEditable),
|
||||||
@@ -221,26 +291,25 @@ FileTreeModel::findItemForFileIndex (int fileIndex) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
FileTreeModel::addFile (int fileIndex,
|
FileTreeModel::addFile (int fileIndex,
|
||||||
const QString & filename,
|
const QString& filename,
|
||||||
bool wanted,
|
bool wanted,
|
||||||
int priority,
|
int priority,
|
||||||
uint64_t totalSize,
|
uint64_t totalSize,
|
||||||
uint64_t have,
|
uint64_t have,
|
||||||
QList<QModelIndex> & rowsAdded,
|
bool updateFields)
|
||||||
bool updateFields)
|
|
||||||
{
|
{
|
||||||
FileTreeItem * item;
|
FileTreeItem * item;
|
||||||
QStringList tokens = filename.split (QChar::fromLatin1('/'));
|
|
||||||
|
|
||||||
item = findItemForFileIndex (fileIndex);
|
item = findItemForFileIndex (fileIndex);
|
||||||
|
|
||||||
if (item) // this file is already in the tree, we've added this
|
if (item) // this file is already in the tree, we've added this
|
||||||
{
|
{
|
||||||
QModelIndex indexWithChangedParents;
|
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);
|
const std::pair<int,int> changed = item->update (token, wanted, priority, have, updateFields);
|
||||||
if (changed.first >= 0)
|
if (changed.first >= 0)
|
||||||
{
|
{
|
||||||
@@ -260,9 +329,10 @@ FileTreeModel::addFile (int fileIndex,
|
|||||||
bool added = false;
|
bool added = false;
|
||||||
|
|
||||||
item = myRootItem;
|
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));
|
FileTreeItem * child(item->child(token));
|
||||||
if (!child)
|
if (!child)
|
||||||
{
|
{
|
||||||
@@ -271,14 +341,12 @@ FileTreeModel::addFile (int fileIndex,
|
|||||||
const int n (item->childCount());
|
const int n (item->childCount());
|
||||||
|
|
||||||
beginInsertRows (parentIndex, n, n);
|
beginInsertRows (parentIndex, n, n);
|
||||||
if (tokens.isEmpty())
|
if (!filenameIt.hasNext ())
|
||||||
child = new FileTreeItem (token, fileIndex, totalSize);
|
child = new FileTreeItem (token, fileIndex, totalSize);
|
||||||
else
|
else
|
||||||
child = new FileTreeItem (token);
|
child = new FileTreeItem (token);
|
||||||
item->appendChild (child);
|
item->appendChild (child);
|
||||||
endInsertRows ();
|
endInsertRows ();
|
||||||
|
|
||||||
rowsAdded.append (indexOf(child, 0));
|
|
||||||
}
|
}
|
||||||
item = child;
|
item = child;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,7 +53,6 @@ class FileTreeModel: public QAbstractItemModel
|
|||||||
void addFile (int index, const QString& filename,
|
void addFile (int index, const QString& filename,
|
||||||
bool wanted, int priority,
|
bool wanted, int priority,
|
||||||
uint64_t size, uint64_t have,
|
uint64_t size, uint64_t have,
|
||||||
QList<QModelIndex>& rowsAdded,
|
|
||||||
bool torrentChanged);
|
bool torrentChanged);
|
||||||
|
|
||||||
QModelIndex parent (const QModelIndex& child, int column) const;
|
QModelIndex parent (const QModelIndex& child, int column) const;
|
||||||
|
|||||||
@@ -169,13 +169,37 @@ FileTreeView::keyPressEvent (QKeyEvent * event)
|
|||||||
void
|
void
|
||||||
FileTreeView::update (const FileList& files, bool updateFields)
|
FileTreeView::update (const FileList& files, bool updateFields)
|
||||||
{
|
{
|
||||||
|
const bool modelWasEmpty = myProxy->rowCount () == 0;
|
||||||
|
|
||||||
for (const TorrentFile& file: files)
|
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;
|
// expand up until the item with more than one expandable child
|
||||||
myModel->addFile (file.index, file.filename, file.wanted, file.priority, file.size, file.have, added, updateFields);
|
for (QModelIndex index = myProxy->index (0, 0); index.isValid ();)
|
||||||
for (const QModelIndex& i: added)
|
{
|
||||||
expand (myProxy->mapFromSource(i));
|
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
|
void
|
||||||
|
|||||||
Reference in New Issue
Block a user