};
int
- measureViewItem (QAbstractItemView * view, const QString& text)
+ measureViewItem (QTreeWidget * view, int column, const QString& text)
{
- QStyleOptionViewItemV4 option;
- option.features = QStyleOptionViewItemV2::HasDisplay;
- option.text = text;
- return view->style ()->sizeFromContents (QStyle::CT_ItemViewItem, &option,
- QSize (QWIDGETSIZE_MAX, QWIDGETSIZE_MAX), view).width ();
+ const QTreeWidgetItem * headerItem = view->headerItem ();
+
+ const int itemWidth = Utils::measureViewItem (view, text);
+ const int headerWidth = Utils::measureHeaderItem (view->header (), headerItem->text (column));
+
+ return std::max (itemWidth, headerWidth);
}
}
ui.peersView->sortByColumn (COL_ADDRESS, Qt::AscendingOrder);
ui.peersView->setColumnWidth (COL_LOCK, 20);
- ui.peersView->setColumnWidth (COL_UP, measureViewItem (ui.peersView, QLatin1String ("1024 MiB/s")));
- ui.peersView->setColumnWidth (COL_DOWN, measureViewItem (ui.peersView, QLatin1String ("1024 MiB/s")));
- ui.peersView->setColumnWidth (COL_PERCENT, measureViewItem (ui.peersView, QLatin1String ("100%")));
- ui.peersView->setColumnWidth (COL_STATUS, measureViewItem (ui.peersView, QLatin1String ("ODUK?EXI")));
- ui.peersView->setColumnWidth (COL_ADDRESS, measureViewItem (ui.peersView, QLatin1String ("888.888.888.888")));
+ ui.peersView->setColumnWidth (COL_UP, measureViewItem (ui.peersView, COL_UP, QLatin1String ("1024 MiB/s")));
+ ui.peersView->setColumnWidth (COL_DOWN, measureViewItem (ui.peersView, COL_DOWN, QLatin1String ("1024 MiB/s")));
+ ui.peersView->setColumnWidth (COL_PERCENT, measureViewItem (ui.peersView, COL_PERCENT, QLatin1String ("100%")));
+ ui.peersView->setColumnWidth (COL_STATUS, measureViewItem (ui.peersView, COL_STATUS, QLatin1String ("ODUK?EXI")));
+ ui.peersView->setColumnWidth (COL_ADDRESS, measureViewItem (ui.peersView, COL_ADDRESS, QLatin1String ("888.888.888.888")));
}
/***
<layout class="QVBoxLayout" name="filesTabLayout">
<item>
<widget class="FileTreeView" name="filesView">
+ <property name="horizontalScrollBarPolicy">
+ <enum>Qt::ScrollBarAlwaysOff</enum>
+ </property>
+ <property name="alternatingRowColors">
+ <bool>true</bool>
+ </property>
+ <property name="selectionMode">
+ <enum>QAbstractItemView::ExtendedSelection</enum>
+ </property>
<property name="verticalScrollMode">
<enum>QAbstractItemView::ScrollPerPixel</enum>
</property>
+ <property name="sortingEnabled">
+ <bool>true</bool>
+ </property>
</widget>
</item>
</layout>
p.state = option.state | QStyle::State_Small;
p.direction = qApp->layoutDirection();
p.rect = option.rect;
- p.rect.setSize (QSize(option.rect.width()-2, option.rect.height()-8));
+ p.rect.setSize (QSize(option.rect.width() - 4, option.rect.height() - 8));
p.rect.moveCenter (option.rect.center());
p.fontMetrics = qApp->fontMetrics();
p.minimum = 0;
}
else if(column == FileTreeModel::COL_WANTED)
{
- QStyleOptionButton o;
- o.state = option.state;
- o.direction = qApp->layoutDirection();
- o.rect.setSize (QSize(20, option.rect.height()));
- o.rect.moveCenter (option.rect.center());
- o.fontMetrics = qApp->fontMetrics();
- switch (index.data().toInt())
- {
- case Qt::Unchecked: o.state |= QStyle::State_Off; break;
- case Qt::Checked: o.state |= QStyle::State_On; break;
- default: o.state |= QStyle::State_NoChange;break;
- }
- style->drawControl (QStyle::CE_CheckBox, &o, painter);
+ QStyleOptionViewItemV4 vi (option);
+ vi.features |= QStyleOptionViewItemV4::HasCheckIndicator;
+ QRect checkRect = style->subElementRect (QStyle::SE_ItemViewItemCheckIndicator, &vi, nullptr);
+ checkRect.moveCenter (option.rect.center ());
+ drawCheck (painter, vi, checkRect, static_cast<Qt::CheckState> (index.data ().toInt ()));
}
QItemDelegate::drawFocus (painter, option, option.rect);
{
value = Qt::AlignRight + Qt::AlignVCenter;
}
- else if (role == Qt::DisplayRole)
+ else if (role == Qt::DisplayRole || role == FileTreeModel::SortRole)
{
switch(column)
- {
- case FileTreeModel::COL_NAME:
- value.setValue (name());
- break;
-
- case FileTreeModel::COL_SIZE:
- value.setValue (sizeString() + QLatin1String (" "));
- break;
-
- case FileTreeModel::COL_PROGRESS:
- value.setValue (progress());
- break;
-
- case FileTreeModel::COL_WANTED:
- value.setValue (isSubtreeWanted());
- break;
-
- case FileTreeModel::COL_PRIORITY:
- value.setValue (priorityString());
- break;
+ {
+ case FileTreeModel::COL_NAME:
+ value.setValue (name());
+ break;
+
+ case FileTreeModel::COL_SIZE:
+ if (role == Qt::DisplayRole)
+ value.setValue (sizeString());
+ else
+ value.setValue (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;
}
}
else if (role == Qt::DecorationRole && column == FileTreeModel::COL_NAME)
QString
FileTreeItem::sizeString () const
{
- QString str;
+ return Formatter::sizeToString (size ());
+}
+uint64_t
+FileTreeItem::size () const
+{
if (myChildren.isEmpty())
- {
- str = Formatter::sizeToString (myTotalSize);
- }
- else
- {
- uint64_t have = 0;
- uint64_t total = 0;
- getSubtreeWantedSize (have, total);
- str = Formatter::sizeToString (total);
- }
+ return myTotalSize;
- return str;
+ uint64_t have = 0;
+ uint64_t total = 0;
+ getSubtreeWantedSize (have, total);
+ return total;
}
std::pair<int,int>
class FileTreeItem
{
Q_DECLARE_TR_FUNCTIONS (FileTreeItem)
+ Q_DISABLE_COPY (FileTreeItem)
public:
FileTreeItem (const QString& name = QString (), int fileIndex = -1, uint64_t size = 0):
void getSubtreeWantedSize (uint64_t& have, uint64_t& total) const;
double progress () const;
int priority () const;
+ uint64_t size () const;
int isSubtreeWanted () const;
const QHash<QString,int>& getMyChildRows();
NUM_COLUMNS
};
+ enum Role
+ {
+ SortRole = Qt::UserRole
+ };
+
public:
FileTreeModel (QObject * parent = nullptr, bool isEditable = true);
virtual ~FileTreeModel ();
#include <QSortFilterProxyModel>
#include "FileTreeDelegate.h"
+#include "FileTreeItem.h"
#include "FileTreeModel.h"
#include "FileTreeView.h"
+#include "Formatter.h"
+#include "Utils.h"
FileTreeView::FileTreeView (QWidget * parent, bool isEditable):
QTreeView (parent),
myProxy (new QSortFilterProxyModel (this)),
myDelegate (new FileTreeDelegate (this))
{
- setSortingEnabled (true);
- setAlternatingRowColors (true);
- setSelectionBehavior (QAbstractItemView::SelectRows);
- setSelectionMode (QAbstractItemView::ExtendedSelection);
myProxy->setSourceModel (myModel);
+ myProxy->setSortRole (FileTreeModel::SortRole);
+ myProxy->setSortCaseSensitivity (Qt::CaseInsensitive);
+
setModel (myProxy);
setItemDelegate (myDelegate);
- setHorizontalScrollBarPolicy (Qt::ScrollBarAlwaysOff);
sortByColumn (FileTreeModel::COL_NAME, Qt::AscendingOrder);
- installEventFilter (this);
for (int i=0; i<FileTreeModel::NUM_COLUMNS; ++i)
- {
- setColumnHidden (i, (i<FileTreeModel::FIRST_VISIBLE_COLUMN) || (FileTreeModel::LAST_VISIBLE_COLUMN<i));
-
-#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
- header()->setResizeMode(i, QHeaderView::Interactive);
-#else
- header()->setSectionResizeMode(i, QHeaderView::Interactive);
-#endif
- }
+ setColumnHidden (i, (i<FileTreeModel::FIRST_VISIBLE_COLUMN) || (FileTreeModel::LAST_VISIBLE_COLUMN<i));
connect (this, SIGNAL(clicked(QModelIndex)),
this, SLOT(onClicked(QModelIndex)));
emit openRequested (path);
}
-bool
-FileTreeView::eventFilter (QObject * o, QEvent * event)
+void
+FileTreeView::resizeEvent (QResizeEvent * event)
{
- // this is kind of a hack to get the last three columns be the
+ QTreeView::resizeEvent (event);
+
+ // this is kind of a hack to get the last four columns be the
// right size, and to have the filename column use whatever
// space is left over...
- if ((o == this) && (event->type() == QEvent::Resize))
+
+ int left = event->size ().width () - 1;
+ for (int column = FileTreeModel::FIRST_VISIBLE_COLUMN; column <= FileTreeModel::LAST_VISIBLE_COLUMN; ++column)
{
- QResizeEvent * r = static_cast<QResizeEvent*> (event);
- int left = r->size().width();
- const QFontMetrics fontMetrics(font());
- for (int column=FileTreeModel::FIRST_VISIBLE_COLUMN; column<=FileTreeModel::LAST_VISIBLE_COLUMN; ++column)
+ if (column == FileTreeModel::COL_NAME)
+ continue;
+ if (isColumnHidden (column))
+ continue;
+
+ int minWidth = 0;
+
+ QStringList itemTexts;
+ switch (column)
{
- if (column == FileTreeModel::COL_NAME)
- continue;
- if (isColumnHidden (column))
- continue;
-
- QString header;
- if (column == FileTreeModel::COL_SIZE)
- header = QLatin1String ("999.9 KiB");
- else
- header = myModel->headerData (column, Qt::Horizontal).toString();
- header += QLatin1String (" ");
- const int width = fontMetrics.size (0, header).width();
- setColumnWidth (column, width);
- left -= width;
+ case FileTreeModel::COL_SIZE:
+ for (int s = Formatter::B; s <= Formatter::TB; ++s)
+ itemTexts << QLatin1String ("999.9 ") +
+ Formatter::unitStr (Formatter::MEM, static_cast<Formatter::Size> (s));
+ break;
+
+ case FileTreeModel::COL_PROGRESS:
+ itemTexts << QLatin1String (" 100% ");
+ break;
+
+ case FileTreeModel::COL_WANTED:
+ minWidth = 20;
+ break;
+
+ case FileTreeModel::COL_PRIORITY:
+ itemTexts << FileTreeItem::tr ("Low") << FileTreeItem::tr ("Normal") <<
+ FileTreeItem::tr ("High") << FileTreeItem::tr ("Mixed");
+ break;
}
- left -= 20; // not sure why this is necessary. it works in different themes + font sizes though...
- setColumnWidth(FileTreeModel::COL_NAME, std::max(left,0));
+
+ int itemWidth = 0;
+ for (const QString& itemText: itemTexts)
+ itemWidth = std::max (itemWidth, Utils::measureViewItem (this, itemText));
+
+ const QString headerText = myModel->headerData (column, Qt::Horizontal).toString ();
+ int headerWidth = Utils::measureHeaderItem (this->header (), headerText);
+
+ const int width = std::max (minWidth, std::max (itemWidth, headerWidth));
+ setColumnWidth (column, width);
+
+ left -= width;
}
+ setColumnWidth (FileTreeModel::COL_NAME, std::max (left, 0));
+}
+
+void
+FileTreeView::keyPressEvent (QKeyEvent * event)
+{
+ QTreeView::keyPressEvent (event);
+
// handle using the keyboard to toggle the
// wanted/unwanted state or the file priority
- else if (event->type () == QEvent::KeyPress && state () != EditingState)
+
+ if (state () == EditingState)
+ return;
+
+ if (event->key () == Qt::Key_Space)
{
- switch (static_cast<QKeyEvent*> (event)->key ())
- {
- case Qt::Key_Space:
- for (const QModelIndex& i: selectionModel ()->selectedRows (FileTreeModel::COL_WANTED))
- clicked (i);
- break;
-
- case Qt::Key_Enter:
- case Qt::Key_Return:
- for (const QModelIndex& i: selectionModel ()->selectedRows (FileTreeModel::COL_PRIORITY))
- clicked (i);
- break;
- }
+ int column;
+
+ const Qt::KeyboardModifiers modifiers = event->modifiers ();
+ if (modifiers == Qt::NoModifier)
+ column = FileTreeModel::COL_WANTED;
+ else if (modifiers == Qt::ShiftModifier)
+ column = FileTreeModel::COL_PRIORITY;
+ else
+ return;
+
+ for (const QModelIndex& i: selectionModel ()->selectedRows (column))
+ clicked (i);
}
-
- return false;
}
void
void openRequested (const QString& path);
protected:
- // QObject
- bool eventFilter (QObject *, QEvent *);
+ // QWidget
+ virtual void resizeEvent (QResizeEvent * event);
+ virtual void keyPressEvent (QKeyEvent * event);
private:
FileTreeModel * myModel;
const TrackerInfo & inf) const
{
const bool isItemSelected ((option.state & QStyle::State_Selected) != 0);
+ const bool isItemEnabled ((option.state & QStyle::State_Enabled) != 0);
+ const bool isItemActive ((option.state & QStyle::State_Active) != 0);
QIcon trackerIcon (inf.st.getFavicon());
painter->save();
+ if (isItemSelected)
+ {
+ QPalette::ColorGroup cg = isItemEnabled ? QPalette::Normal : QPalette::Disabled;
+ if (cg == QPalette::Normal && !isItemActive)
+ cg = QPalette::Inactive;
+
+ painter->fillRect (option.rect, option.palette.brush (cg, QPalette::Highlight));
+ }
+
trackerIcon.paint (painter, layout.iconRect, Qt::AlignCenter, isItemSelected ? QIcon::Selected : QIcon::Normal, QIcon::On);
QAbstractTextDocumentLayout::PaintContext paintContext;
#include <shellapi.h>
#endif
+#include <QAbstractItemView>
#include <QApplication>
#include <QColor>
#include <QDataStream>
#include <QFile>
#include <QFileDialog>
#include <QFileInfo>
+#include <QHeaderView>
#include <QIcon>
#include <QInputDialog>
#include <QObject>
return pathInfo.fileName ().isEmpty () ? pathInfo.absolutePath () : pathInfo.absoluteFilePath ();
}
+int
+Utils::measureViewItem (QAbstractItemView * view, const QString& text)
+{
+ QStyleOptionViewItemV4 option;
+ option.initFrom (view);
+ option.features = QStyleOptionViewItemV2::HasDisplay;
+ option.text = text;
+ option.textElideMode = Qt::ElideNone;
+ option.font = view->font ();
+
+ return view->style ()->sizeFromContents (QStyle::CT_ItemViewItem, &option,
+ QSize (QWIDGETSIZE_MAX, QWIDGETSIZE_MAX), view).width ();
+}
+
+int
+Utils::measureHeaderItem (QHeaderView * view, const QString& text)
+{
+ QStyleOptionHeader option;
+ option.initFrom (view);
+ option.text = text;
+ option.sortIndicator = view->isSortIndicatorShown () ? QStyleOptionHeader::SortDown :
+ QStyleOptionHeader::None;
+
+ return view->style ()->sizeFromContents (QStyle::CT_HeaderSection, &option,
+ QSize (QWIDGETSIZE_MAX, QWIDGETSIZE_MAX), view).width ();
+}
+
QColor
Utils::getFadedColor (const QColor& color)
{
#include <QRect>
#include <QString>
+class QAbstractItemView;
class QColor;
+class QHeaderView;
class QIcon;
class Utils
rect.adjust (dx1, 0, -dx2, 0);
}
+ static int measureViewItem (QAbstractItemView * view, const QString& text);
+ static int measureHeaderItem (QHeaderView * view, const QString& text);
+
static QColor getFadedColor (const QColor& color);
///