source: trunk/qt/FilterBar.cc @ 14539

Last change on this file since 14539 was 14539, checked in by mikedld, 7 years ago

Unify/prettify Qt client headers style

  • Property svn:keywords set to Date Rev Author Id
File size: 12.4 KB
Line 
1/*
2 * This file Copyright (C) 2012-2015 Mnemosyne LLC
3 *
4 * It may be used under the GNU GPL versions 2 or 3
5 * or any future license endorsed by Mnemosyne LLC.
6 *
7 * $Id: FilterBar.cc 14539 2015-06-12 22:12:12Z mikedld $
8 */
9
10#include <QHBoxLayout>
11#include <QLabel>
12#include <QStandardItemModel>
13
14#include "Application.h"
15#include "FaviconCache.h"
16#include "Filters.h"
17#include "FilterBar.h"
18#include "FilterBarComboBox.h"
19#include "FilterBarComboBoxDelegate.h"
20#include "FilterBarLineEdit.h"
21#include "Prefs.h"
22#include "Torrent.h"
23#include "TorrentFilter.h"
24#include "TorrentModel.h"
25
26enum
27{
28  ActivityRole = FilterBarComboBox::UserRole,
29  TrackerRole
30};
31
32namespace
33{
34  QString
35  readableHostName (const QString& host)
36  {
37    // get the readable name...
38    QString name = host;
39    const int pos = name.lastIndexOf (QLatin1Char ('.'));
40    if (pos >= 0)
41      name.truncate (pos);
42    if (!name.isEmpty ())
43      name[0] = name[0].toUpper ();
44    return name;
45  }
46}
47
48/***
49****
50***/
51
52FilterBarComboBox *
53FilterBar::createActivityCombo ()
54{
55  FilterBarComboBox * c = new FilterBarComboBox (this);
56  FilterBarComboBoxDelegate * delegate = new FilterBarComboBoxDelegate (this, c);
57  c->setItemDelegate (delegate);
58
59  QStandardItemModel * model = new QStandardItemModel (this);
60
61  QStandardItem * row = new QStandardItem (tr ("All"));
62  row->setData (FilterMode::SHOW_ALL, ActivityRole);
63  model->appendRow (row);
64
65  model->appendRow (new QStandardItem); // separator
66  delegate->setSeparator (model, model->index (1, 0));
67
68  row = new QStandardItem (QIcon::fromTheme (QLatin1String ("system-run")), tr ("Active"));
69  row->setData (FilterMode::SHOW_ACTIVE, ActivityRole);
70  model->appendRow (row);
71
72  row = new QStandardItem (QIcon::fromTheme (QLatin1String ("go-down")), tr ("Downloading"));
73  row->setData (FilterMode::SHOW_DOWNLOADING, ActivityRole);
74  model->appendRow (row);
75
76  row = new QStandardItem (QIcon::fromTheme (QLatin1String ("go-up")), tr ("Seeding"));
77  row->setData (FilterMode::SHOW_SEEDING, ActivityRole);
78  model->appendRow (row);
79
80  row = new QStandardItem (QIcon::fromTheme (QLatin1String ("media-playback-pause")), tr ("Paused"));
81  row->setData (FilterMode::SHOW_PAUSED, ActivityRole);
82  model->appendRow (row);
83
84  row = new QStandardItem (QIcon::fromTheme (QLatin1String ("dialog-ok")), tr ("Finished"));
85  row->setData (FilterMode::SHOW_FINISHED, ActivityRole);
86  model->appendRow (row);
87
88  row = new QStandardItem (QIcon::fromTheme (QLatin1String ("view-refresh")), tr ("Verifying"));
89  row->setData (FilterMode::SHOW_VERIFYING, ActivityRole);
90  model->appendRow (row);
91
92  row = new QStandardItem (QIcon::fromTheme (QLatin1String ("process-stop")), tr ("Error"));
93  row->setData (FilterMode::SHOW_ERROR, ActivityRole);
94  model->appendRow (row);
95
96  c->setModel (model);
97  return c;
98}
99
100/***
101****
102***/
103
104void
105FilterBar::refreshTrackers ()
106{
107  FaviconCache& favicons = qApp->faviconCache ();
108  const int firstTrackerRow = 2; // skip over the "All" and separator...
109
110  // pull info from the tracker model...
111  QSet<QString> oldHosts;
112  for (int row=firstTrackerRow; ; ++row)
113    {
114      QModelIndex index = myTrackerModel->index (row, 0);
115      if (!index.isValid ())
116        break;
117      oldHosts << index.data (TrackerRole).toString ();
118    }
119
120  // pull the new stats from the torrent model...
121  QSet<QString> newHosts;
122  QMap<QString,int> torrentsPerHost;
123  for (int row=0; ; ++row)
124    {
125      QModelIndex index = myTorrents.index (row, 0);
126      if (!index.isValid ())
127        break;
128      const Torrent * tor = index.data (TorrentModel::TorrentRole).value<const Torrent*> ();
129      QSet<QString> torrentNames;
130      for (const QString& host: tor->hosts ())
131        {
132          newHosts.insert (host);
133          torrentNames.insert (readableHostName (host));
134        }
135      for (const QString& name: torrentNames)
136        ++torrentsPerHost[name];
137    }
138
139  // update the "All" row
140  myTrackerModel->setData (myTrackerModel->index (0,0), myTorrents.rowCount (), FilterBarComboBox::CountRole);
141  myTrackerModel->setData (myTrackerModel->index (0,0), getCountString (myTorrents.rowCount ()), FilterBarComboBox::CountStringRole);
142
143  // rows to update
144  for (const QString& host: oldHosts & newHosts)
145    {
146      const QString name = readableHostName (host);
147      QStandardItem * row = myTrackerModel->findItems (name).front ();
148      const int count = torrentsPerHost[name];
149      row->setData (count, FilterBarComboBox::CountRole);
150      row->setData (getCountString (count), FilterBarComboBox::CountStringRole);
151      row->setData (favicons.findFromHost (host), Qt::DecorationRole);
152    }
153
154  // rows to remove
155  for (const QString& host: oldHosts - newHosts)
156    {
157      const QString name = readableHostName (host);
158      QStandardItem * item = myTrackerModel->findItems (name).front ();
159      if (!item->data (TrackerRole).toString ().isEmpty ()) // don't remove "All"
160        myTrackerModel->removeRows (item->row (), 1);
161    }
162
163  // rows to add
164  bool anyAdded = false;
165  for (const QString& host: newHosts - oldHosts)
166    {
167      const QString name = readableHostName (host);
168
169      if (!myTrackerModel->findItems (name).isEmpty ())
170        continue;
171
172      // find the sorted position to add this row
173      int i = firstTrackerRow;
174      for (int n=myTrackerModel->rowCount (); i<n; ++i)
175        {
176          const QString rowName = myTrackerModel->index (i,0).data (Qt::DisplayRole).toString ();
177          if (rowName >= name)
178            break;
179        }
180
181      // add the row
182      QStandardItem * row = new QStandardItem (favicons.findFromHost (host), name);
183      const int count = torrentsPerHost[host];
184      row->setData (count, FilterBarComboBox::CountRole);
185      row->setData (getCountString (count), FilterBarComboBox::CountStringRole);
186      row->setData (favicons.findFromHost (host), Qt::DecorationRole);
187      row->setData (host, TrackerRole);
188      myTrackerModel->insertRow (i, row);
189      anyAdded = true;
190    }
191
192  if (anyAdded) // the one added might match our filter...
193    refreshPref (Prefs::FILTER_TRACKERS);
194}
195
196
197FilterBarComboBox *
198FilterBar::createTrackerCombo (QStandardItemModel * model)
199{
200  FilterBarComboBox * c = new FilterBarComboBox (this);
201  FilterBarComboBoxDelegate * delegate = new FilterBarComboBoxDelegate (this, c);
202  c->setItemDelegate (delegate);
203
204  QStandardItem * row = new QStandardItem (tr ("All"));
205  row->setData (QString (), TrackerRole);
206  const int count = myTorrents.rowCount ();
207  row->setData (count, FilterBarComboBox::CountRole);
208  row->setData (getCountString (count), FilterBarComboBox::CountStringRole);
209  model->appendRow (row);
210
211  model->appendRow (new QStandardItem); // separator
212  delegate->setSeparator (model, model->index (1, 0));
213
214  c->setModel (model);
215  return c;
216}
217
218/***
219****
220***/
221
222FilterBar::FilterBar (Prefs& prefs, const TorrentModel& torrents, const TorrentFilter& filter, QWidget * parent):
223  QWidget (parent),
224  myPrefs (prefs),
225  myTorrents (torrents),
226  myFilter (filter),
227  myRecountTimer (new QTimer (this)),
228  myIsBootstrapping (true)
229{
230  QHBoxLayout * h = new QHBoxLayout (this);
231  h->setContentsMargins (3, 3, 3, 3);
232
233  myCountLabel = new QLabel (this);
234  h->addWidget (myCountLabel);
235
236  myActivityCombo = createActivityCombo ();
237  myActivityCombo->setSizePolicy (QSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed));
238  h->addWidget (myActivityCombo);
239
240  myTrackerModel = new QStandardItemModel (this);
241  myTrackerCombo = createTrackerCombo (myTrackerModel);
242  h->addWidget (myTrackerCombo);
243
244  myLineEdit = new FilterBarLineEdit (this);
245  h->addWidget (myLineEdit);
246  connect (myLineEdit, SIGNAL (textChanged (QString)), this, SLOT (onTextChanged (QString)));
247
248  // listen for changes from the other players
249  connect (&myPrefs, SIGNAL (changed (int)), this, SLOT (refreshPref (int)));
250  connect (myActivityCombo, SIGNAL (currentIndexChanged (int)), this, SLOT (onActivityIndexChanged (int)));
251  connect (myTrackerCombo, SIGNAL (currentIndexChanged (int)), this, SLOT (onTrackerIndexChanged (int)));
252  connect (&myFilter, SIGNAL (rowsInserted (QModelIndex, int, int)), this, SLOT (refreshCountLabel ()));
253  connect (&myFilter, SIGNAL (rowsRemoved (QModelIndex, int, int)), this, SLOT (refreshCountLabel ()));
254  connect (&myTorrents, SIGNAL (modelReset ()), this, SLOT (onTorrentModelReset ()));
255  connect (&myTorrents, SIGNAL (rowsInserted (QModelIndex, int, int)), this, SLOT (onTorrentModelRowsInserted (QModelIndex, int, int)));
256  connect (&myTorrents, SIGNAL (rowsRemoved (QModelIndex, int, int)), this, SLOT (onTorrentModelRowsRemoved (QModelIndex, int, int)));
257  connect (&myTorrents, SIGNAL (dataChanged (QModelIndex, QModelIndex)), this, SLOT (onTorrentModelDataChanged (QModelIndex, QModelIndex)));
258  connect (myRecountTimer, SIGNAL (timeout ()), this, SLOT (recount ()));
259
260  recountSoon ();
261  refreshTrackers ();
262  refreshCountLabel ();
263  myIsBootstrapping = false;
264
265  // initialize our state
266  QList<int> initKeys;
267  initKeys << Prefs::FILTER_MODE
268           << Prefs::FILTER_TRACKERS;
269  for (const int key: initKeys)
270      refreshPref (key);
271}
272
273FilterBar::~FilterBar ()
274{
275  delete myRecountTimer;
276}
277
278/***
279****
280***/
281
282void
283FilterBar::refreshPref (int key)
284{
285  switch (key)
286    {
287      case Prefs::FILTER_MODE:
288        {
289          const FilterMode m = myPrefs.get<FilterMode> (key);
290          QAbstractItemModel * model = myActivityCombo->model ();
291          QModelIndexList indices = model->match (model->index (0,0), ActivityRole, m.mode ());
292          myActivityCombo->setCurrentIndex (indices.isEmpty () ? 0 : indices.first ().row ());
293          break;
294        }
295
296      case Prefs::FILTER_TRACKERS:
297        {
298          const QString tracker = myPrefs.getString (key);
299          const QString name = readableHostName (tracker);
300          QList<QStandardItem*> rows = myTrackerModel->findItems (name);
301          if (!rows.isEmpty ())
302            {
303              myTrackerCombo->setCurrentIndex (rows.front ()->row ());
304            }
305          else // hm, we don't seem to have this tracker anymore...
306            {
307              const bool isBootstrapping = myTrackerModel->rowCount () <= 2;
308              if (!isBootstrapping)
309                myPrefs.set (key, QString ());
310            }
311          break;
312        }
313    }
314}
315
316void
317FilterBar::onTextChanged (const QString& str)
318{
319  if (!myIsBootstrapping)
320    myPrefs.set (Prefs::FILTER_TEXT, str.trimmed ());
321}
322
323void
324FilterBar::onTrackerIndexChanged (int i)
325{
326  if (!myIsBootstrapping)
327    {
328      QString str;
329      const bool isTracker = !myTrackerCombo->itemData (i,TrackerRole).toString ().isEmpty ();
330      if (!isTracker)
331        {
332          // show all
333        }
334      else
335        {
336          str = myTrackerCombo->itemData (i,TrackerRole).toString ();
337          const int pos = str.lastIndexOf (QLatin1Char ('.'));
338          if (pos >= 0)
339            str.truncate (pos+1);
340        }
341      myPrefs.set (Prefs::FILTER_TRACKERS, str);
342    }
343}
344
345void
346FilterBar::onActivityIndexChanged (int i)
347{
348  if (!myIsBootstrapping)
349    {
350      const FilterMode mode = myActivityCombo->itemData (i, ActivityRole).toInt ();
351      myPrefs.set (Prefs::FILTER_MODE, mode);
352    }
353}
354
355/***
356****
357***/
358
359void FilterBar::onTorrentModelReset () { recountSoon (); }
360void FilterBar::onTorrentModelRowsInserted (const QModelIndex&, int, int) { recountSoon (); }
361void FilterBar::onTorrentModelRowsRemoved (const QModelIndex&, int, int) { recountSoon (); }
362void FilterBar::onTorrentModelDataChanged (const QModelIndex&, const QModelIndex&) { recountSoon (); }
363
364void
365FilterBar::recountSoon ()
366{
367  if (!myRecountTimer->isActive ())
368    {
369      myRecountTimer->setSingleShot (true);
370      myRecountTimer->start (800);
371    }
372}
373void
374FilterBar::recount ()
375{
376  QAbstractItemModel * model = myActivityCombo->model ();
377
378  int torrentsPerMode[FilterMode::NUM_MODES] = {};
379  myFilter.countTorrentsPerMode (torrentsPerMode);
380
381  for (int row=0, n=model->rowCount (); row<n; ++row)
382    {
383      QModelIndex index = model->index (row, 0);
384      const int mode = index.data (ActivityRole).toInt ();
385      const int count = torrentsPerMode [mode];
386      model->setData (index, count, FilterBarComboBox::CountRole);
387      model->setData (index, getCountString (count), FilterBarComboBox::CountStringRole);
388    }
389
390  refreshTrackers ();
391  refreshCountLabel ();
392}
393
394QString
395FilterBar::getCountString (int n) const
396{
397  return QString::fromLatin1 ("%L1").arg (n);
398}
399
400void
401FilterBar::refreshCountLabel ()
402{
403  const int visibleCount = myFilter.rowCount ();
404  const int trackerCount = myTrackerCombo->currentCount ();
405  const int activityCount = myActivityCombo->currentCount ();
406
407  if ((visibleCount == activityCount) || (visibleCount == trackerCount))
408    myCountLabel->setText (tr("Show:"));
409  else
410    myCountLabel->setText (tr("Show %Ln of:", 0, visibleCount));
411}
Note: See TracBrowser for help on using the repository browser.