1
0
Fork 0
mirror of https://github.com/transmission/transmission synced 2025-03-19 18:25:38 +00:00
transmission/qt/filterbar.cc
Mike Gelfand efc04fdd77 Improve overall look of torrents in main window
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.
2015-01-17 16:59:42 +00:00

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));
}