mirror of
https://github.com/transmission/transmission
synced 2025-03-19 18:25:38 +00:00
Refactor and use the same code to calculate element positions in sizeHint () and paint () to prevent discrepancies. While we are at it, properly support RTL layout.
728 lines
23 KiB
C++
728 lines
23 KiB
C++
/*
|
|
* This file Copyright (C) 2012-2014 Mnemosyne LLC
|
|
*
|
|
* It may be used under the GNU GPL versions 2 or 3
|
|
* or any future license endorsed by Mnemosyne LLC.
|
|
*
|
|
* $Id$
|
|
*/
|
|
|
|
#include <QAbstractItemView>
|
|
#include <QLabel>
|
|
#include <QHBoxLayout>
|
|
#include <QStylePainter>
|
|
#include <QString>
|
|
#include <QToolButton>
|
|
#include <QtGui>
|
|
|
|
#include "app.h"
|
|
#include "favicon.h"
|
|
#include "filters.h"
|
|
#include "filterbar.h"
|
|
#include "hig.h"
|
|
#include "prefs.h"
|
|
#include "torrent-filter.h"
|
|
#include "torrent-model.h"
|
|
#include "utils.h"
|
|
|
|
/****
|
|
*****
|
|
***** DELEGATE
|
|
*****
|
|
****/
|
|
|
|
enum
|
|
{
|
|
TorrentCountRole = Qt::UserRole + 1,
|
|
TorrentCountStringRole,
|
|
ActivityRole,
|
|
TrackerRole
|
|
};
|
|
|
|
namespace
|
|
{
|
|
int getHSpacing (const QWidget * w)
|
|
{
|
|
return qMax (int (HIG::PAD_SMALL), w->style ()->pixelMetric (QStyle::PM_LayoutHorizontalSpacing, 0, w));
|
|
}
|
|
|
|
QColor
|
|
getFadedColor (const QColor& color)
|
|
{
|
|
QColor fadedColor (color);
|
|
fadedColor.setAlpha (128);
|
|
return fadedColor;
|
|
}
|
|
}
|
|
|
|
FilterBarComboBoxDelegate::FilterBarComboBoxDelegate (QObject * parent, QComboBox * combo):
|
|
QItemDelegate (parent),
|
|
myCombo (combo)
|
|
{
|
|
}
|
|
|
|
bool
|
|
FilterBarComboBoxDelegate::isSeparator (const QModelIndex& index)
|
|
{
|
|
return index.data (Qt::AccessibleDescriptionRole).toString () == QLatin1String ("separator");
|
|
}
|
|
void
|
|
FilterBarComboBoxDelegate::setSeparator (QAbstractItemModel * model, const QModelIndex& index)
|
|
{
|
|
model->setData (index, QString::fromLatin1 ("separator"), Qt::AccessibleDescriptionRole);
|
|
|
|
if (QStandardItemModel *m = qobject_cast<QStandardItemModel*> (model))
|
|
if (QStandardItem *item = m->itemFromIndex (index))
|
|
item->setFlags (item->flags () & ~ (Qt::ItemIsSelectable|Qt::ItemIsEnabled));
|
|
}
|
|
|
|
void
|
|
FilterBarComboBoxDelegate::paint (QPainter * painter,
|
|
const QStyleOptionViewItem & option,
|
|
const QModelIndex & index) const
|
|
{
|
|
if (isSeparator (index))
|
|
{
|
|
QRect rect = option.rect;
|
|
if (const QStyleOptionViewItemV3 *v3 = qstyleoption_cast<const QStyleOptionViewItemV3*> (&option))
|
|
if (const QAbstractItemView *view = qobject_cast<const QAbstractItemView*> (v3->widget))
|
|
rect.setWidth (view->viewport ()->width ());
|
|
QStyleOption opt;
|
|
opt.rect = rect;
|
|
myCombo->style ()->drawPrimitive (QStyle::PE_IndicatorToolBarSeparator, &opt, painter, myCombo);
|
|
}
|
|
else
|
|
{
|
|
QStyleOptionViewItem disabledOption = option;
|
|
const QPalette::ColorRole disabledColorRole = (disabledOption.state & QStyle::State_Selected) ?
|
|
QPalette::HighlightedText : QPalette::Text;
|
|
disabledOption.palette.setColor (disabledColorRole, getFadedColor (disabledOption.palette.color (disabledColorRole)));
|
|
|
|
QRect boundingBox = option.rect;
|
|
|
|
const int hmargin = getHSpacing (myCombo);
|
|
boundingBox.adjust (hmargin, 0, -hmargin, 0);
|
|
|
|
QRect decorationRect = rect (option, index, Qt::DecorationRole);
|
|
decorationRect.setSize (myCombo->iconSize ());
|
|
decorationRect = QStyle::alignedRect (option.direction,
|
|
Qt::AlignLeft|Qt::AlignVCenter,
|
|
decorationRect.size (), boundingBox);
|
|
Utils::narrowRect (boundingBox, decorationRect.width () + hmargin, 0, option.direction);
|
|
|
|
QRect countRect = rect (option, index, TorrentCountStringRole);
|
|
countRect = QStyle::alignedRect (option.direction,
|
|
Qt::AlignRight|Qt::AlignVCenter,
|
|
countRect.size (), boundingBox);
|
|
Utils::narrowRect (boundingBox, 0, countRect.width () + hmargin, option.direction);
|
|
const QRect displayRect = boundingBox;
|
|
|
|
drawBackground (painter, option, index);
|
|
QStyleOptionViewItem option2 = option;
|
|
option2.decorationSize = myCombo->iconSize ();
|
|
drawDecoration (painter, option, decorationRect, decoration (option2,index.data (Qt::DecorationRole)));
|
|
drawDisplay (painter, option, displayRect, index.data (Qt::DisplayRole).toString ());
|
|
drawDisplay (painter, disabledOption, countRect, index.data (TorrentCountStringRole).toString ());
|
|
drawFocus (painter, option, displayRect|countRect);
|
|
}
|
|
}
|
|
|
|
QSize
|
|
FilterBarComboBoxDelegate::sizeHint (const QStyleOptionViewItem & option,
|
|
const QModelIndex & index) const
|
|
{
|
|
if (isSeparator (index))
|
|
{
|
|
const int pm = myCombo->style ()->pixelMetric (QStyle::PM_DefaultFrameWidth, 0, myCombo);
|
|
return QSize (pm, pm + 10);
|
|
}
|
|
else
|
|
{
|
|
QStyle * s = myCombo->style ();
|
|
const int hmargin = getHSpacing (myCombo);
|
|
|
|
QSize size = QItemDelegate::sizeHint (option, index);
|
|
size.setHeight (qMax (size.height (), myCombo->iconSize ().height () + 6));
|
|
size.rwidth () += s->pixelMetric (QStyle::PM_FocusFrameHMargin, 0, myCombo);
|
|
size.rwidth () += rect (option,index,TorrentCountStringRole).width ();
|
|
size.rwidth () += hmargin * 4;
|
|
return size;
|
|
}
|
|
}
|
|
|
|
/**
|
|
***
|
|
**/
|
|
|
|
FilterBarComboBox::FilterBarComboBox (QWidget * parent):
|
|
QComboBox (parent)
|
|
{
|
|
setSizeAdjustPolicy (QComboBox::AdjustToContents);
|
|
}
|
|
|
|
int
|
|
FilterBarComboBox::currentCount () const
|
|
{
|
|
int count = 0;
|
|
|
|
const QModelIndex modelIndex = model ()->index (currentIndex (), 0, rootModelIndex ());
|
|
if (modelIndex.isValid ())
|
|
count = modelIndex.data (TorrentCountRole).toInt ();
|
|
|
|
return count;
|
|
}
|
|
|
|
QSize
|
|
FilterBarComboBox::minimumSizeHint () const
|
|
{
|
|
QFontMetrics fm (fontMetrics ());
|
|
const QSize textSize = fm.boundingRect (itemText (0)).size ();
|
|
const QSize countSize = fm.boundingRect (itemData (0, TorrentCountStringRole).toString ()).size ();
|
|
return calculateSize (textSize, countSize);
|
|
}
|
|
|
|
QSize
|
|
FilterBarComboBox::sizeHint () const
|
|
{
|
|
QFontMetrics fm (fontMetrics ());
|
|
QSize maxTextSize (0, 0);
|
|
QSize maxCountSize (0, 0);
|
|
for (int i = 0, n = count (); i < n; ++i)
|
|
{
|
|
const QSize textSize = fm.boundingRect (itemText (i)).size ();
|
|
maxTextSize.setHeight (qMax (maxTextSize.height (), textSize.height ()));
|
|
maxTextSize.setWidth (qMax (maxTextSize.width (), textSize.width ()));
|
|
|
|
const QSize countSize = fm.boundingRect (itemData (i, TorrentCountStringRole).toString ()).size ();
|
|
maxCountSize.setHeight (qMax (maxCountSize.height (), countSize.height ()));
|
|
maxCountSize.setWidth (qMax (maxCountSize.width (), countSize.width ()));
|
|
}
|
|
|
|
return calculateSize (maxTextSize, maxCountSize);
|
|
}
|
|
|
|
QSize
|
|
FilterBarComboBox::calculateSize (const QSize& textSize, const QSize& countSize) const
|
|
{
|
|
const int hmargin = getHSpacing (this);
|
|
|
|
QStyleOptionComboBox option;
|
|
initStyleOption (&option);
|
|
|
|
QSize contentSize = iconSize () + QSize (4, 2);
|
|
contentSize.setHeight (qMax (contentSize.height (), textSize.height ()));
|
|
contentSize.rwidth () += hmargin + textSize.width ();
|
|
contentSize.rwidth () += hmargin + countSize.width ();
|
|
|
|
return style ()->sizeFromContents (QStyle::CT_ComboBox, &option, contentSize, this).expandedTo (qApp->globalStrut ());
|
|
}
|
|
|
|
void
|
|
FilterBarComboBox::paintEvent (QPaintEvent * e)
|
|
{
|
|
Q_UNUSED (e);
|
|
|
|
QStylePainter painter (this);
|
|
painter.setPen (palette ().color (QPalette::Text));
|
|
|
|
// draw the combobox frame, focusrect and selected etc.
|
|
QStyleOptionComboBox opt;
|
|
initStyleOption (&opt);
|
|
painter.drawComplexControl (QStyle::CC_ComboBox, opt);
|
|
|
|
// draw the icon and text
|
|
const QModelIndex modelIndex = model ()->index (currentIndex (), 0, rootModelIndex ());
|
|
if (modelIndex.isValid ())
|
|
{
|
|
QStyle * s = style ();
|
|
const int hmargin = getHSpacing (this);
|
|
|
|
QRect rect = s->subControlRect (QStyle::CC_ComboBox, &opt, QStyle::SC_ComboBoxEditField, this);
|
|
rect.adjust (2, 1, -2, -1);
|
|
|
|
// draw the icon
|
|
QPixmap pixmap;
|
|
QVariant variant = modelIndex.data (Qt::DecorationRole);
|
|
switch (variant.type ())
|
|
{
|
|
case QVariant::Pixmap: pixmap = qvariant_cast<QPixmap> (variant); break;
|
|
case QVariant::Icon: pixmap = qvariant_cast<QIcon> (variant).pixmap (iconSize ()); break;
|
|
default: break;
|
|
}
|
|
if (!pixmap.isNull ())
|
|
{
|
|
const QRect iconRect = QStyle::alignedRect(opt.direction, Qt::AlignLeft | Qt::AlignVCenter,
|
|
opt.iconSize, rect);
|
|
painter.drawPixmap (iconRect.topLeft (), pixmap);
|
|
Utils::narrowRect (rect, iconRect.width () + hmargin, 0, opt.direction);
|
|
}
|
|
|
|
// draw the count
|
|
QString text = modelIndex.data (TorrentCountStringRole).toString ();
|
|
if (!text.isEmpty ())
|
|
{
|
|
const QPen pen = painter.pen ();
|
|
painter.setPen (getFadedColor (pen.color ()));
|
|
const QRect textRect = QStyle::alignedRect(opt.direction, Qt::AlignRight | Qt::AlignVCenter,
|
|
QSize (opt.fontMetrics.width (text), rect.height ()), rect);
|
|
painter.drawText (textRect, Qt::AlignRight | Qt::AlignVCenter, text);
|
|
Utils::narrowRect (rect, 0, textRect.width () + hmargin, opt.direction);
|
|
painter.setPen (pen);
|
|
}
|
|
|
|
// draw the text
|
|
text = modelIndex.data (Qt::DisplayRole).toString ();
|
|
text = painter.fontMetrics ().elidedText (text, Qt::ElideRight, rect.width ());
|
|
painter.drawText (rect, Qt::AlignLeft | Qt::AlignVCenter, text);
|
|
}
|
|
}
|
|
|
|
/****
|
|
*****
|
|
****/
|
|
|
|
FilterBarLineEdit::FilterBarLineEdit (QWidget * parent):
|
|
QLineEdit (parent),
|
|
myClearButton (nullptr)
|
|
{
|
|
#if QT_VERSION < QT_VERSION_CHECK(5, 2, 0)
|
|
const QIcon icon = QIcon::fromTheme ("edit-clear", style ()->standardIcon (QStyle::SP_DialogCloseButton));
|
|
const int iconSize = style ()->pixelMetric (QStyle::PM_SmallIconSize);
|
|
|
|
myClearButton = new QToolButton (this);
|
|
myClearButton->setStyleSheet (QLatin1String ("QToolButton{border:0;padding:0;margin:0}"));
|
|
myClearButton->setToolButtonStyle (Qt::ToolButtonIconOnly);
|
|
myClearButton->setFocusPolicy (Qt::NoFocus);
|
|
myClearButton->setCursor (Qt::ArrowCursor);
|
|
myClearButton->setIconSize (QSize (iconSize, iconSize));
|
|
myClearButton->setIcon (icon);
|
|
myClearButton->setFixedSize (myClearButton->iconSize () + QSize (2, 2));
|
|
myClearButton->hide ();
|
|
|
|
const int frameWidth = style ()->pixelMetric (QStyle::PM_DefaultFrameWidth);
|
|
const QSize minSizeHint = minimumSizeHint ();
|
|
const QSize buttonSize = myClearButton->size ();
|
|
|
|
setStyleSheet (QString::fromLatin1 ("QLineEdit{padding-right:%1px}").arg (buttonSize.width () + frameWidth + 1));
|
|
setMinimumSize (qMax (minSizeHint.width (), buttonSize.width () + frameWidth * 2 + 2),
|
|
qMax (minSizeHint.height (), buttonSize.height () + frameWidth * 2 + 2));
|
|
|
|
connect (this, SIGNAL (textChanged (QString)), this, SLOT (updateClearButtonVisibility ()));
|
|
connect (myClearButton, SIGNAL (clicked ()), this, SLOT (clear ()));
|
|
#else
|
|
setClearButtonEnabled (true);
|
|
#endif
|
|
|
|
#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)
|
|
setPlaceholderText (tr ("Search..."));
|
|
#endif
|
|
}
|
|
|
|
void
|
|
FilterBarLineEdit::resizeEvent (QResizeEvent * event)
|
|
{
|
|
QLineEdit::resizeEvent (event);
|
|
|
|
#if QT_VERSION < QT_VERSION_CHECK(5, 2, 0)
|
|
const int frameWidth = style ()->pixelMetric (QStyle::PM_DefaultFrameWidth);
|
|
const QRect editRect = rect();
|
|
const QSize buttonSize = myClearButton->size ();
|
|
|
|
myClearButton->move (editRect.right () - frameWidth - buttonSize.width (),
|
|
editRect.top () + (editRect.height () - buttonSize.height ()) / 2);
|
|
#endif
|
|
}
|
|
|
|
void
|
|
FilterBarLineEdit::updateClearButtonVisibility ()
|
|
{
|
|
#if QT_VERSION < QT_VERSION_CHECK(5, 2, 0)
|
|
myClearButton->setVisible (!text ().isEmpty ());
|
|
#endif
|
|
}
|
|
|
|
/****
|
|
*****
|
|
***** ACTIVITY
|
|
*****
|
|
****/
|
|
|
|
FilterBarComboBox *
|
|
FilterBar::createActivityCombo ()
|
|
{
|
|
FilterBarComboBox * c = new FilterBarComboBox (this);
|
|
FilterBarComboBoxDelegate * delegate = new FilterBarComboBoxDelegate (this, c);
|
|
c->setItemDelegate (delegate);
|
|
|
|
QStandardItemModel * model = new QStandardItemModel (this);
|
|
|
|
QStandardItem * row = new QStandardItem (tr ("All"));
|
|
row->setData (FilterMode::SHOW_ALL, ActivityRole);
|
|
model->appendRow (row);
|
|
|
|
model->appendRow (new QStandardItem); // separator
|
|
delegate->setSeparator (model, model->index (1, 0));
|
|
|
|
row = new QStandardItem (QIcon::fromTheme ("system-run"), tr ("Active"));
|
|
row->setData (FilterMode::SHOW_ACTIVE, ActivityRole);
|
|
model->appendRow (row);
|
|
|
|
row = new QStandardItem (QIcon::fromTheme ("go-down"), tr ("Downloading"));
|
|
row->setData (FilterMode::SHOW_DOWNLOADING, ActivityRole);
|
|
model->appendRow (row);
|
|
|
|
row = new QStandardItem (QIcon::fromTheme ("go-up"), tr ("Seeding"));
|
|
row->setData (FilterMode::SHOW_SEEDING, ActivityRole);
|
|
model->appendRow (row);
|
|
|
|
row = new QStandardItem (QIcon::fromTheme ("media-playback-pause"), tr ("Paused"));
|
|
row->setData (FilterMode::SHOW_PAUSED, ActivityRole);
|
|
model->appendRow (row);
|
|
|
|
row = new QStandardItem (QIcon::fromTheme ("dialog-ok"), tr ("Finished"));
|
|
row->setData (FilterMode::SHOW_FINISHED, ActivityRole);
|
|
model->appendRow (row);
|
|
|
|
row = new QStandardItem (QIcon::fromTheme ("view-refresh"), tr ("Verifying"));
|
|
row->setData (FilterMode::SHOW_VERIFYING, ActivityRole);
|
|
model->appendRow (row);
|
|
|
|
row = new QStandardItem (QIcon::fromTheme ("process-stop"), tr ("Error"));
|
|
row->setData (FilterMode::SHOW_ERROR, ActivityRole);
|
|
model->appendRow (row);
|
|
|
|
c->setModel (model);
|
|
return c;
|
|
}
|
|
|
|
/****
|
|
*****
|
|
*****
|
|
*****
|
|
****/
|
|
|
|
namespace
|
|
{
|
|
QString readableHostName (const QString& host)
|
|
{
|
|
// get the readable name...
|
|
QString name = host;
|
|
const int pos = name.lastIndexOf ('.');
|
|
if (pos >= 0)
|
|
name.truncate (pos);
|
|
if (!name.isEmpty ())
|
|
name[0] = name[0].toUpper ();
|
|
return name;
|
|
}
|
|
}
|
|
|
|
void
|
|
FilterBar::refreshTrackers ()
|
|
{
|
|
Favicons& favicons = qApp->favicons;
|
|
const int firstTrackerRow = 2; // skip over the "All" and separator...
|
|
|
|
// pull info from the tracker model...
|
|
QSet<QString> oldHosts;
|
|
for (int row=firstTrackerRow; ; ++row)
|
|
{
|
|
QModelIndex index = myTrackerModel->index (row, 0);
|
|
if (!index.isValid ())
|
|
break;
|
|
oldHosts << index.data (TrackerRole).toString ();
|
|
}
|
|
|
|
// pull the new stats from the torrent model...
|
|
QSet<QString> newHosts;
|
|
QMap<QString,int> torrentsPerHost;
|
|
for (int row=0; ; ++row)
|
|
{
|
|
QModelIndex index = myTorrents.index (row, 0);
|
|
if (!index.isValid ())
|
|
break;
|
|
const Torrent * tor = index.data (TorrentModel::TorrentRole).value<const Torrent*> ();
|
|
QSet<QString> torrentNames;
|
|
foreach (QString host, tor->hosts ())
|
|
{
|
|
newHosts.insert (host);
|
|
torrentNames.insert (readableHostName (host));
|
|
}
|
|
foreach (QString name, torrentNames)
|
|
++torrentsPerHost[name];
|
|
}
|
|
|
|
// update the "All" row
|
|
myTrackerModel->setData (myTrackerModel->index (0,0), myTorrents.rowCount (), TorrentCountRole);
|
|
myTrackerModel->setData (myTrackerModel->index (0,0), getCountString (myTorrents.rowCount ()), TorrentCountStringRole);
|
|
|
|
// rows to update
|
|
foreach (QString host, oldHosts & newHosts)
|
|
{
|
|
const QString name = readableHostName (host);
|
|
QStandardItem * row = myTrackerModel->findItems (name).front ();
|
|
const int count = torrentsPerHost[name];
|
|
row->setData (count, TorrentCountRole);
|
|
row->setData (getCountString (count), TorrentCountStringRole);
|
|
row->setData (favicons.findFromHost (host), Qt::DecorationRole);
|
|
}
|
|
|
|
// rows to remove
|
|
foreach (QString host, oldHosts - newHosts)
|
|
{
|
|
const QString name = readableHostName (host);
|
|
QStandardItem * item = myTrackerModel->findItems (name).front ();
|
|
if (!item->data (TrackerRole).toString ().isEmpty ()) // don't remove "All"
|
|
myTrackerModel->removeRows (item->row (), 1);
|
|
}
|
|
|
|
// rows to add
|
|
bool anyAdded = false;
|
|
foreach (QString host, newHosts - oldHosts)
|
|
{
|
|
const QString name = readableHostName (host);
|
|
|
|
if (!myTrackerModel->findItems (name).isEmpty ())
|
|
continue;
|
|
|
|
// find the sorted position to add this row
|
|
int i = firstTrackerRow;
|
|
for (int n=myTrackerModel->rowCount (); i<n; ++i)
|
|
{
|
|
const QString rowName = myTrackerModel->index (i,0).data (Qt::DisplayRole).toString ();
|
|
if (rowName >= name)
|
|
break;
|
|
}
|
|
|
|
// add the row
|
|
QStandardItem * row = new QStandardItem (favicons.findFromHost (host), name);
|
|
const int count = torrentsPerHost[host];
|
|
row->setData (count, TorrentCountRole);
|
|
row->setData (getCountString (count), TorrentCountStringRole);
|
|
row->setData (favicons.findFromHost (host), Qt::DecorationRole);
|
|
row->setData (host, TrackerRole);
|
|
myTrackerModel->insertRow (i, row);
|
|
anyAdded = true;
|
|
}
|
|
|
|
if (anyAdded) // the one added might match our filter...
|
|
refreshPref (Prefs::FILTER_TRACKERS);
|
|
}
|
|
|
|
|
|
FilterBarComboBox *
|
|
FilterBar::createTrackerCombo (QStandardItemModel * model)
|
|
{
|
|
FilterBarComboBox * c = new FilterBarComboBox (this);
|
|
FilterBarComboBoxDelegate * delegate = new FilterBarComboBoxDelegate (this, c);
|
|
c->setItemDelegate (delegate);
|
|
|
|
QStandardItem * row = new QStandardItem (tr ("All"));
|
|
row->setData ("", TrackerRole);
|
|
const int count = myTorrents.rowCount ();
|
|
row->setData (count, TorrentCountRole);
|
|
row->setData (getCountString (count), TorrentCountStringRole);
|
|
model->appendRow (row);
|
|
|
|
model->appendRow (new QStandardItem); // separator
|
|
delegate->setSeparator (model, model->index (1, 0));
|
|
|
|
c->setModel (model);
|
|
return c;
|
|
}
|
|
|
|
/****
|
|
*****
|
|
*****
|
|
*****
|
|
****/
|
|
|
|
FilterBar::FilterBar (Prefs& prefs, TorrentModel& torrents, TorrentFilter& filter, QWidget * parent):
|
|
QWidget (parent),
|
|
myPrefs (prefs),
|
|
myTorrents (torrents),
|
|
myFilter (filter),
|
|
myRecountTimer (new QTimer (this)),
|
|
myIsBootstrapping (true)
|
|
{
|
|
QHBoxLayout * h = new QHBoxLayout (this);
|
|
h->setContentsMargins (3, 3, 3, 3);
|
|
|
|
myCountLabel = new QLabel (this);
|
|
h->addWidget (myCountLabel);
|
|
|
|
myActivityCombo = createActivityCombo ();
|
|
myActivityCombo->setSizePolicy (QSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed));
|
|
h->addWidget (myActivityCombo);
|
|
|
|
myTrackerModel = new QStandardItemModel (this);
|
|
myTrackerCombo = createTrackerCombo (myTrackerModel);
|
|
h->addWidget (myTrackerCombo);
|
|
|
|
myLineEdit = new FilterBarLineEdit (this);
|
|
h->addWidget (myLineEdit);
|
|
connect (myLineEdit, SIGNAL (textChanged (QString)), this, SLOT (onTextChanged (QString)));
|
|
|
|
// listen for changes from the other players
|
|
connect (&myPrefs, SIGNAL (changed (int)), this, SLOT (refreshPref (int)));
|
|
connect (myActivityCombo, SIGNAL (currentIndexChanged (int)), this, SLOT (onActivityIndexChanged (int)));
|
|
connect (myTrackerCombo, SIGNAL (currentIndexChanged (int)), this, SLOT (onTrackerIndexChanged (int)));
|
|
connect (&myFilter, SIGNAL (rowsInserted (QModelIndex, int, int)), this, SLOT (refreshCountLabel ()));
|
|
connect (&myFilter, SIGNAL (rowsRemoved (QModelIndex, int, int)), this, SLOT (refreshCountLabel ()));
|
|
connect (&myTorrents, SIGNAL (modelReset ()), this, SLOT (onTorrentModelReset ()));
|
|
connect (&myTorrents, SIGNAL (rowsInserted (QModelIndex, int, int)), this, SLOT (onTorrentModelRowsInserted (QModelIndex, int, int)));
|
|
connect (&myTorrents, SIGNAL (rowsRemoved (QModelIndex, int, int)), this, SLOT (onTorrentModelRowsRemoved (QModelIndex, int, int)));
|
|
connect (&myTorrents, SIGNAL (dataChanged (QModelIndex, QModelIndex)), this, SLOT (onTorrentModelDataChanged (QModelIndex, QModelIndex)));
|
|
connect (myRecountTimer, SIGNAL (timeout ()), this, SLOT (recount ()));
|
|
|
|
recountSoon ();
|
|
refreshTrackers ();
|
|
refreshCountLabel ();
|
|
myIsBootstrapping = false;
|
|
|
|
// initialize our state
|
|
QList<int> initKeys;
|
|
initKeys << Prefs::FILTER_MODE
|
|
<< Prefs::FILTER_TRACKERS;
|
|
foreach (int key, initKeys)
|
|
refreshPref (key);
|
|
}
|
|
|
|
FilterBar::~FilterBar ()
|
|
{
|
|
delete myRecountTimer;
|
|
}
|
|
|
|
/***
|
|
****
|
|
***/
|
|
|
|
void
|
|
FilterBar::refreshPref (int key)
|
|
{
|
|
switch (key)
|
|
{
|
|
case Prefs::FILTER_MODE:
|
|
{
|
|
const FilterMode m = myPrefs.get<FilterMode> (key);
|
|
QAbstractItemModel * model = myActivityCombo->model ();
|
|
QModelIndexList indices = model->match (model->index (0,0), ActivityRole, m.mode ());
|
|
myActivityCombo->setCurrentIndex (indices.isEmpty () ? 0 : indices.first ().row ());
|
|
break;
|
|
}
|
|
|
|
case Prefs::FILTER_TRACKERS:
|
|
{
|
|
const QString tracker = myPrefs.getString (key);
|
|
const QString name = readableHostName (tracker);
|
|
QList<QStandardItem*> rows = myTrackerModel->findItems (name);
|
|
if (!rows.isEmpty ())
|
|
{
|
|
myTrackerCombo->setCurrentIndex (rows.front ()->row ());
|
|
}
|
|
else // hm, we don't seem to have this tracker anymore...
|
|
{
|
|
const bool isBootstrapping = myTrackerModel->rowCount () <= 2;
|
|
if (!isBootstrapping)
|
|
myPrefs.set (key, "");
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
FilterBar::onTextChanged (const QString& str)
|
|
{
|
|
if (!myIsBootstrapping)
|
|
myPrefs.set (Prefs::FILTER_TEXT, str.trimmed ());
|
|
}
|
|
|
|
void
|
|
FilterBar::onTrackerIndexChanged (int i)
|
|
{
|
|
if (!myIsBootstrapping)
|
|
{
|
|
QString str;
|
|
const bool isTracker = !myTrackerCombo->itemData (i,TrackerRole).toString ().isEmpty ();
|
|
if (!isTracker) // show all
|
|
{
|
|
str = "";
|
|
}
|
|
else
|
|
{
|
|
str = myTrackerCombo->itemData (i,TrackerRole).toString ();
|
|
const int pos = str.lastIndexOf ('.');
|
|
if (pos >= 0)
|
|
str.truncate (pos+1);
|
|
}
|
|
myPrefs.set (Prefs::FILTER_TRACKERS, str);
|
|
}
|
|
}
|
|
|
|
void
|
|
FilterBar::onActivityIndexChanged (int i)
|
|
{
|
|
if (!myIsBootstrapping)
|
|
{
|
|
const FilterMode mode = myActivityCombo->itemData (i, ActivityRole).toInt ();
|
|
myPrefs.set (Prefs::FILTER_MODE, mode);
|
|
}
|
|
}
|
|
|
|
/***
|
|
****
|
|
***/
|
|
|
|
void FilterBar::onTorrentModelReset () { recountSoon (); }
|
|
void FilterBar::onTorrentModelRowsInserted (const QModelIndex&, int, int) { recountSoon (); }
|
|
void FilterBar::onTorrentModelRowsRemoved (const QModelIndex&, int, int) { recountSoon (); }
|
|
void FilterBar::onTorrentModelDataChanged (const QModelIndex&, const QModelIndex&) { recountSoon (); }
|
|
|
|
void
|
|
FilterBar::recountSoon ()
|
|
{
|
|
if (!myRecountTimer->isActive ())
|
|
{
|
|
myRecountTimer->setSingleShot (true);
|
|
myRecountTimer->start (800);
|
|
}
|
|
}
|
|
void
|
|
FilterBar::recount ()
|
|
{
|
|
QAbstractItemModel * model = myActivityCombo->model ();
|
|
|
|
int torrentsPerMode[FilterMode::NUM_MODES] = {};
|
|
myFilter.countTorrentsPerMode (torrentsPerMode);
|
|
|
|
for (int row=0, n=model->rowCount (); row<n; ++row)
|
|
{
|
|
QModelIndex index = model->index (row, 0);
|
|
const int mode = index.data (ActivityRole).toInt ();
|
|
const int count = torrentsPerMode [mode];
|
|
model->setData (index, count, TorrentCountRole);
|
|
model->setData (index, getCountString (count), TorrentCountStringRole);
|
|
}
|
|
|
|
refreshTrackers ();
|
|
refreshCountLabel ();
|
|
}
|
|
|
|
QString
|
|
FilterBar::getCountString (int n) const
|
|
{
|
|
return QString::fromLatin1 ("%L1").arg (n);
|
|
}
|
|
|
|
void
|
|
FilterBar::refreshCountLabel ()
|
|
{
|
|
const int visibleCount = myFilter.rowCount ();
|
|
const int trackerCount = myTrackerCombo->currentCount ();
|
|
const int activityCount = myActivityCombo->currentCount ();
|
|
|
|
if ((visibleCount == activityCount) || (visibleCount == trackerCount))
|
|
myCountLabel->setText (tr("Show:"));
|
|
else
|
|
myCountLabel->setText (tr("Show %Ln of:", 0, visibleCount));
|
|
}
|