mirror of
https://github.com/transmission/transmission
synced 2024-12-27 18:18:10 +00:00
ecefb992a1
File names are often too long to fit. Hovering is much easier than resizing the column and possibly the window.
426 lines
8.2 KiB
C++
426 lines
8.2 KiB
C++
/*
|
|
* This file Copyright (C) 2009-2015 Mnemosyne LLC
|
|
*
|
|
* It may be used under the GNU GPL versions 2 or 3
|
|
* or any future license endorsed by Mnemosyne LLC.
|
|
*
|
|
*/
|
|
|
|
#include <algorithm>
|
|
#include <cassert>
|
|
|
|
#include <QApplication>
|
|
#include <QStyle>
|
|
|
|
#include <libtransmission/transmission.h> // priorities
|
|
|
|
#include "FileTreeItem.h"
|
|
#include "FileTreeModel.h"
|
|
#include "Formatter.h"
|
|
#include "Utils.h" // mime icons
|
|
|
|
QHash<QString, int> const& FileTreeItem::getMyChildRows()
|
|
{
|
|
size_t const n = childCount();
|
|
|
|
// ensure that all the rows are hashed
|
|
while (myFirstUnhashedRow < n)
|
|
{
|
|
myChildRows.insert(myChildren[myFirstUnhashedRow]->name(), myFirstUnhashedRow);
|
|
++myFirstUnhashedRow;
|
|
}
|
|
|
|
return myChildRows;
|
|
}
|
|
|
|
FileTreeItem::~FileTreeItem()
|
|
{
|
|
assert(myChildren.isEmpty());
|
|
|
|
if (myParent != nullptr)
|
|
{
|
|
int const pos = row();
|
|
assert(pos >= 0 && "couldn't find child in parent's lookup");
|
|
myParent->myChildren.removeAt(pos);
|
|
myParent->myChildRows.remove(name());
|
|
myParent->myFirstUnhashedRow = pos;
|
|
}
|
|
}
|
|
|
|
void FileTreeItem::appendChild(FileTreeItem* child)
|
|
{
|
|
size_t const n = childCount();
|
|
child->myParent = this;
|
|
myChildren.append(child);
|
|
myFirstUnhashedRow = n;
|
|
}
|
|
|
|
FileTreeItem* FileTreeItem::child(QString const& filename)
|
|
{
|
|
FileTreeItem* item(nullptr);
|
|
|
|
int const row = getMyChildRows().value(filename, -1);
|
|
|
|
if (row != -1)
|
|
{
|
|
item = child(row);
|
|
assert(filename == item->name());
|
|
}
|
|
|
|
return item;
|
|
}
|
|
|
|
int FileTreeItem::row() const
|
|
{
|
|
int i(-1);
|
|
|
|
if (myParent != nullptr)
|
|
{
|
|
i = myParent->getMyChildRows().value(name(), -1);
|
|
assert(this == myParent->myChildren[i]);
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
QVariant FileTreeItem::data(int column, int role) const
|
|
{
|
|
QVariant value;
|
|
|
|
switch (role)
|
|
{
|
|
case FileTreeModel::FileIndexRole:
|
|
value.setValue(myFileIndex);
|
|
break;
|
|
|
|
case FileTreeModel::WantedRole:
|
|
value.setValue(isSubtreeWanted());
|
|
break;
|
|
|
|
case FileTreeModel::CompleteRole:
|
|
value.setValue(isComplete());
|
|
break;
|
|
|
|
case Qt::ToolTipRole:
|
|
case Qt::EditRole:
|
|
if (column == FileTreeModel::COL_NAME)
|
|
{
|
|
value.setValue(name());
|
|
}
|
|
|
|
break;
|
|
|
|
case Qt::TextAlignmentRole:
|
|
if (column == FileTreeModel::COL_SIZE)
|
|
{
|
|
value = Qt::AlignRight + Qt::AlignVCenter;
|
|
}
|
|
|
|
break;
|
|
|
|
case Qt::DisplayRole:
|
|
case FileTreeModel::SortRole:
|
|
switch (column)
|
|
{
|
|
case FileTreeModel::COL_NAME:
|
|
value.setValue(name());
|
|
break;
|
|
|
|
case FileTreeModel::COL_SIZE:
|
|
if (role == Qt::DisplayRole)
|
|
{
|
|
value.setValue(sizeString());
|
|
}
|
|
else
|
|
{
|
|
value.setValue<quint64>(size());
|
|
}
|
|
|
|
break;
|
|
|
|
case FileTreeModel::COL_PROGRESS:
|
|
value.setValue(progress());
|
|
break;
|
|
|
|
case FileTreeModel::COL_WANTED:
|
|
value.setValue(isSubtreeWanted());
|
|
break;
|
|
|
|
case FileTreeModel::COL_PRIORITY:
|
|
if (role == Qt::DisplayRole)
|
|
{
|
|
value.setValue(priorityString());
|
|
}
|
|
else
|
|
{
|
|
value.setValue(priority());
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
break;
|
|
|
|
case Qt::DecorationRole:
|
|
if (column == FileTreeModel::COL_NAME)
|
|
{
|
|
if (myFileIndex < 0)
|
|
{
|
|
value = qApp->style()->standardIcon(QStyle::SP_DirOpenIcon);
|
|
}
|
|
else
|
|
{
|
|
value = Utils::guessMimeIcon(name());
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
void FileTreeItem::getSubtreeWantedSize(uint64_t& have, uint64_t& total) const
|
|
{
|
|
if (myIsWanted)
|
|
{
|
|
have += myHaveSize;
|
|
total += myTotalSize;
|
|
}
|
|
|
|
for (FileTreeItem const* const i : myChildren)
|
|
{
|
|
i->getSubtreeWantedSize(have, total);
|
|
}
|
|
}
|
|
|
|
double FileTreeItem::progress() const
|
|
{
|
|
double d(0);
|
|
uint64_t have(0);
|
|
uint64_t total(0);
|
|
|
|
getSubtreeWantedSize(have, total);
|
|
|
|
if (total != 0)
|
|
{
|
|
d = have / double(total);
|
|
}
|
|
|
|
return d;
|
|
}
|
|
|
|
QString FileTreeItem::sizeString() const
|
|
{
|
|
return Formatter::sizeToString(size());
|
|
}
|
|
|
|
uint64_t FileTreeItem::size() const
|
|
{
|
|
if (myChildren.isEmpty())
|
|
{
|
|
return myTotalSize;
|
|
}
|
|
|
|
uint64_t have = 0;
|
|
uint64_t total = 0;
|
|
getSubtreeWantedSize(have, total);
|
|
return total;
|
|
}
|
|
|
|
std::pair<int, int> FileTreeItem::update(QString const& name, bool wanted, int priority, uint64_t haveSize, bool updateFields)
|
|
{
|
|
int changed_count = 0;
|
|
int changed_columns[4];
|
|
|
|
if (myName != name)
|
|
{
|
|
if (myParent != nullptr)
|
|
{
|
|
myParent->myFirstUnhashedRow = row();
|
|
}
|
|
|
|
myName = name;
|
|
changed_columns[changed_count++] = FileTreeModel::COL_NAME;
|
|
}
|
|
|
|
if (fileIndex() != -1)
|
|
{
|
|
if (myHaveSize != haveSize)
|
|
{
|
|
myHaveSize = haveSize;
|
|
changed_columns[changed_count++] = FileTreeModel::COL_PROGRESS;
|
|
}
|
|
|
|
if (updateFields)
|
|
{
|
|
if (myIsWanted != wanted)
|
|
{
|
|
myIsWanted = wanted;
|
|
changed_columns[changed_count++] = FileTreeModel::COL_WANTED;
|
|
}
|
|
|
|
if (myPriority != priority)
|
|
{
|
|
myPriority = priority;
|
|
changed_columns[changed_count++] = FileTreeModel::COL_PRIORITY;
|
|
}
|
|
}
|
|
}
|
|
|
|
std::pair<int, int> changed(-1, -1);
|
|
|
|
if (changed_count > 0)
|
|
{
|
|
std::sort(changed_columns, changed_columns + changed_count);
|
|
changed.first = changed_columns[0];
|
|
changed.second = changed_columns[changed_count - 1];
|
|
}
|
|
|
|
return changed;
|
|
}
|
|
|
|
QString FileTreeItem::priorityString() const
|
|
{
|
|
int const i = priority();
|
|
|
|
switch (i)
|
|
{
|
|
case LOW:
|
|
return tr("Low");
|
|
|
|
case HIGH:
|
|
return tr("High");
|
|
|
|
case NORMAL:
|
|
return tr("Normal");
|
|
|
|
default:
|
|
return tr("Mixed");
|
|
}
|
|
}
|
|
|
|
int FileTreeItem::priority() const
|
|
{
|
|
int i(0);
|
|
|
|
if (myChildren.isEmpty())
|
|
{
|
|
switch (myPriority)
|
|
{
|
|
case TR_PRI_LOW:
|
|
i |= LOW;
|
|
break;
|
|
|
|
case TR_PRI_HIGH:
|
|
i |= HIGH;
|
|
break;
|
|
|
|
default:
|
|
i |= NORMAL;
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (FileTreeItem const* const child : myChildren)
|
|
{
|
|
i |= child->priority();
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
void FileTreeItem::setSubtreePriority(int i, QSet<int>& ids)
|
|
{
|
|
if (myPriority != i)
|
|
{
|
|
myPriority = i;
|
|
|
|
if (myFileIndex >= 0)
|
|
{
|
|
ids.insert(myFileIndex);
|
|
}
|
|
}
|
|
|
|
for (FileTreeItem* const child : myChildren)
|
|
{
|
|
child->setSubtreePriority(i, ids);
|
|
}
|
|
}
|
|
|
|
int FileTreeItem::isSubtreeWanted() const
|
|
{
|
|
if (myChildren.isEmpty())
|
|
{
|
|
return myIsWanted ? Qt::Checked : Qt::Unchecked;
|
|
}
|
|
|
|
int wanted(-1);
|
|
|
|
for (FileTreeItem const* const child : myChildren)
|
|
{
|
|
int const childWanted = child->isSubtreeWanted();
|
|
|
|
if (wanted == -1)
|
|
{
|
|
wanted = childWanted;
|
|
}
|
|
|
|
if (wanted != childWanted)
|
|
{
|
|
wanted = Qt::PartiallyChecked;
|
|
}
|
|
|
|
if (wanted == Qt::PartiallyChecked)
|
|
{
|
|
return wanted;
|
|
}
|
|
}
|
|
|
|
return wanted;
|
|
}
|
|
|
|
void FileTreeItem::setSubtreeWanted(bool b, QSet<int>& ids)
|
|
{
|
|
if (myIsWanted != b)
|
|
{
|
|
myIsWanted = b;
|
|
|
|
if (myFileIndex >= 0)
|
|
{
|
|
ids.insert(myFileIndex);
|
|
}
|
|
}
|
|
|
|
for (FileTreeItem* const child : myChildren)
|
|
{
|
|
child->setSubtreeWanted(b, ids);
|
|
}
|
|
}
|
|
|
|
QString FileTreeItem::path() const
|
|
{
|
|
QString itemPath;
|
|
FileTreeItem const* item = this;
|
|
|
|
while (item != nullptr && !item->name().isEmpty())
|
|
{
|
|
if (itemPath.isEmpty())
|
|
{
|
|
itemPath = item->name();
|
|
}
|
|
else
|
|
{
|
|
itemPath = item->name() + QLatin1Char('/') + itemPath;
|
|
}
|
|
|
|
item = item->parent();
|
|
}
|
|
|
|
return itemPath;
|
|
}
|
|
|
|
bool FileTreeItem::isComplete() const
|
|
{
|
|
return myHaveSize == totalSize();
|
|
}
|