1 | /* |
---|
2 | * This file Copyright (C) 2012-2014 Mnemosyne LLC |
---|
3 | * |
---|
4 | * It may be used under the GNU Public License v2 or v3 licenses, |
---|
5 | * or any future license endorsed by Mnemosyne LLC. |
---|
6 | * |
---|
7 | * $Id: filterbar.cc 14225 2014-01-19 01:09:44Z jordan $ |
---|
8 | */ |
---|
9 | |
---|
10 | #include <QAbstractItemView> |
---|
11 | #include <QPushButton> |
---|
12 | #include <QLabel> |
---|
13 | #include <QHBoxLayout> |
---|
14 | #include <QLineEdit> |
---|
15 | #include <QStylePainter> |
---|
16 | #include <QString> |
---|
17 | #include <QtGui> |
---|
18 | |
---|
19 | #include "app.h" |
---|
20 | #include "favicon.h" |
---|
21 | #include "filters.h" |
---|
22 | #include "filterbar.h" |
---|
23 | #include "hig.h" |
---|
24 | #include "prefs.h" |
---|
25 | #include "torrent-filter.h" |
---|
26 | #include "torrent-model.h" |
---|
27 | #include "utils.h" |
---|
28 | |
---|
29 | /**** |
---|
30 | ***** |
---|
31 | ***** DELEGATE |
---|
32 | ***** |
---|
33 | ****/ |
---|
34 | |
---|
35 | enum |
---|
36 | { |
---|
37 | TorrentCountRole = Qt::UserRole + 1, |
---|
38 | TorrentCountStringRole, |
---|
39 | ActivityRole, |
---|
40 | TrackerRole |
---|
41 | }; |
---|
42 | |
---|
43 | namespace |
---|
44 | { |
---|
45 | int getHSpacing (QWidget * w) |
---|
46 | { |
---|
47 | return qMax (int (HIG::PAD_SMALL), w->style ()->pixelMetric (QStyle::PM_LayoutHorizontalSpacing, 0, w)); |
---|
48 | } |
---|
49 | } |
---|
50 | |
---|
51 | FilterBarComboBoxDelegate :: FilterBarComboBoxDelegate (QObject * parent, QComboBox * combo): |
---|
52 | QItemDelegate (parent), |
---|
53 | myCombo (combo) |
---|
54 | { |
---|
55 | } |
---|
56 | |
---|
57 | bool |
---|
58 | FilterBarComboBoxDelegate :: isSeparator (const QModelIndex &index) |
---|
59 | { |
---|
60 | return index.data (Qt::AccessibleDescriptionRole).toString () == QLatin1String ("separator"); |
---|
61 | } |
---|
62 | void |
---|
63 | FilterBarComboBoxDelegate :: setSeparator (QAbstractItemModel * model, const QModelIndex& index) |
---|
64 | { |
---|
65 | model->setData (index, QString::fromLatin1 ("separator"), Qt::AccessibleDescriptionRole); |
---|
66 | |
---|
67 | if (QStandardItemModel *m = qobject_cast<QStandardItemModel*> (model)) |
---|
68 | if (QStandardItem *item = m->itemFromIndex (index)) |
---|
69 | item->setFlags (item->flags () & ~ (Qt::ItemIsSelectable|Qt::ItemIsEnabled)); |
---|
70 | } |
---|
71 | |
---|
72 | void |
---|
73 | FilterBarComboBoxDelegate :: paint (QPainter * painter, |
---|
74 | const QStyleOptionViewItem & option, |
---|
75 | const QModelIndex & index) const |
---|
76 | { |
---|
77 | if (isSeparator (index)) |
---|
78 | { |
---|
79 | QRect rect = option.rect; |
---|
80 | if (const QStyleOptionViewItemV3 *v3 = qstyleoption_cast<const QStyleOptionViewItemV3*> (&option)) |
---|
81 | if (const QAbstractItemView *view = qobject_cast<const QAbstractItemView*> (v3->widget)) |
---|
82 | rect.setWidth (view->viewport ()->width ()); |
---|
83 | QStyleOption opt; |
---|
84 | opt.rect = rect; |
---|
85 | myCombo->style ()->drawPrimitive (QStyle::PE_IndicatorToolBarSeparator, &opt, painter, myCombo); |
---|
86 | } |
---|
87 | else |
---|
88 | { |
---|
89 | QStyleOptionViewItem disabledOption = option; |
---|
90 | disabledOption.state &= ~ (QStyle::State_Enabled | QStyle::State_Selected); |
---|
91 | QRect boundingBox = option.rect; |
---|
92 | |
---|
93 | const int hmargin = getHSpacing (myCombo); |
---|
94 | boundingBox.setLeft (boundingBox.left () + hmargin); |
---|
95 | boundingBox.setRight (boundingBox.right () - hmargin); |
---|
96 | |
---|
97 | QRect decorationRect = rect (option, index, Qt::DecorationRole); |
---|
98 | decorationRect.moveLeft (decorationRect.left ()); |
---|
99 | decorationRect.setSize (myCombo->iconSize ()); |
---|
100 | decorationRect = QStyle::alignedRect (Qt::LeftToRight, |
---|
101 | Qt::AlignLeft|Qt::AlignVCenter, |
---|
102 | decorationRect.size (), boundingBox); |
---|
103 | boundingBox.setLeft (decorationRect.right () + hmargin); |
---|
104 | |
---|
105 | QRect countRect = rect (option, index, TorrentCountStringRole); |
---|
106 | countRect = QStyle::alignedRect (Qt::LeftToRight, |
---|
107 | Qt::AlignRight|Qt::AlignVCenter, |
---|
108 | countRect.size (), boundingBox); |
---|
109 | boundingBox.setRight (countRect.left () - hmargin); |
---|
110 | const QRect displayRect = boundingBox; |
---|
111 | |
---|
112 | drawBackground (painter, option, index); |
---|
113 | QStyleOptionViewItem option2 = option; |
---|
114 | option2.decorationSize = myCombo->iconSize (); |
---|
115 | drawDecoration (painter, option, decorationRect, decoration (option2,index.data (Qt::DecorationRole))); |
---|
116 | drawDisplay (painter, option, displayRect, index.data (Qt::DisplayRole).toString ()); |
---|
117 | drawDisplay (painter, disabledOption, countRect, index.data (TorrentCountStringRole).toString ()); |
---|
118 | drawFocus (painter, option, displayRect|countRect); |
---|
119 | } |
---|
120 | } |
---|
121 | |
---|
122 | QSize |
---|
123 | FilterBarComboBoxDelegate :: sizeHint (const QStyleOptionViewItem & option, |
---|
124 | const QModelIndex & index) const |
---|
125 | { |
---|
126 | if (isSeparator (index)) |
---|
127 | { |
---|
128 | const int pm = myCombo->style ()->pixelMetric (QStyle::PM_DefaultFrameWidth, 0, myCombo); |
---|
129 | return QSize (pm, pm + 10); |
---|
130 | } |
---|
131 | else |
---|
132 | { |
---|
133 | QStyle * s = myCombo->style (); |
---|
134 | const int hmargin = getHSpacing (myCombo); |
---|
135 | |
---|
136 | QSize size = QItemDelegate::sizeHint (option, index); |
---|
137 | size.setHeight (qMax (size.height (), myCombo->iconSize ().height () + 6)); |
---|
138 | size.rwidth () += s->pixelMetric (QStyle::PM_FocusFrameHMargin, 0, myCombo); |
---|
139 | size.rwidth () += rect (option,index,TorrentCountStringRole).width (); |
---|
140 | size.rwidth () += hmargin * 4; |
---|
141 | return size; |
---|
142 | } |
---|
143 | } |
---|
144 | |
---|
145 | /** |
---|
146 | *** |
---|
147 | **/ |
---|
148 | |
---|
149 | FilterBarComboBox :: FilterBarComboBox (QWidget * parent): |
---|
150 | QComboBox (parent) |
---|
151 | { |
---|
152 | } |
---|
153 | |
---|
154 | int |
---|
155 | FilterBarComboBox :: currentCount () const |
---|
156 | { |
---|
157 | int count = 0; |
---|
158 | |
---|
159 | const QModelIndex modelIndex = model ()->index (currentIndex (), 0, rootModelIndex ()); |
---|
160 | if (modelIndex.isValid ()) |
---|
161 | count = modelIndex.data (TorrentCountRole).toInt (); |
---|
162 | |
---|
163 | return count; |
---|
164 | } |
---|
165 | |
---|
166 | void |
---|
167 | FilterBarComboBox :: paintEvent (QPaintEvent * e) |
---|
168 | { |
---|
169 | Q_UNUSED (e); |
---|
170 | |
---|
171 | QStylePainter painter (this); |
---|
172 | painter.setPen (palette ().color (QPalette::Text)); |
---|
173 | |
---|
174 | // draw the combobox frame, focusrect and selected etc. |
---|
175 | QStyleOptionComboBox opt; |
---|
176 | initStyleOption (&opt); |
---|
177 | painter.drawComplexControl (QStyle::CC_ComboBox, opt); |
---|
178 | |
---|
179 | // draw the icon and text |
---|
180 | const QModelIndex modelIndex = model ()->index (currentIndex (), 0, rootModelIndex ()); |
---|
181 | if (modelIndex.isValid ()) |
---|
182 | { |
---|
183 | QStyle * s = style (); |
---|
184 | QRect rect = s->subControlRect (QStyle::CC_ComboBox, &opt, QStyle::SC_ComboBoxEditField, this); |
---|
185 | const int hmargin = getHSpacing (this); |
---|
186 | rect.setRight (rect.right () - hmargin); |
---|
187 | |
---|
188 | // draw the icon |
---|
189 | QPixmap pixmap; |
---|
190 | QVariant variant = modelIndex.data (Qt::DecorationRole); |
---|
191 | switch (variant.type ()) |
---|
192 | { |
---|
193 | case QVariant::Pixmap: pixmap = qvariant_cast<QPixmap> (variant); break; |
---|
194 | case QVariant::Icon: pixmap = qvariant_cast<QIcon> (variant).pixmap (iconSize ()); break; |
---|
195 | default: break; |
---|
196 | } |
---|
197 | if (!pixmap.isNull ()) |
---|
198 | { |
---|
199 | s->drawItemPixmap (&painter, rect, Qt::AlignLeft|Qt::AlignVCenter, pixmap); |
---|
200 | rect.setLeft (rect.left () + pixmap.width () + hmargin); |
---|
201 | } |
---|
202 | |
---|
203 | // draw the count |
---|
204 | QString text = modelIndex.data (TorrentCountStringRole).toString (); |
---|
205 | if (!text.isEmpty ()) |
---|
206 | { |
---|
207 | const QPen pen = painter.pen (); |
---|
208 | painter.setPen (opt.palette.color (QPalette::Disabled, QPalette::Text)); |
---|
209 | QRect r = s->itemTextRect (painter.fontMetrics (), rect, Qt::AlignRight|Qt::AlignVCenter, false, text); |
---|
210 | painter.drawText (r, 0, text); |
---|
211 | rect.setRight (r.left () - hmargin); |
---|
212 | painter.setPen (pen); |
---|
213 | } |
---|
214 | |
---|
215 | // draw the text |
---|
216 | text = modelIndex.data (Qt::DisplayRole).toString (); |
---|
217 | text = painter.fontMetrics ().elidedText (text, Qt::ElideRight, rect.width ()); |
---|
218 | s->drawItemText (&painter, rect, Qt::AlignLeft|Qt::AlignVCenter, opt.palette, true, text); |
---|
219 | } |
---|
220 | } |
---|
221 | |
---|
222 | /**** |
---|
223 | ***** |
---|
224 | ***** ACTIVITY |
---|
225 | ***** |
---|
226 | ****/ |
---|
227 | |
---|
228 | FilterBarComboBox * |
---|
229 | FilterBar :: createActivityCombo () |
---|
230 | { |
---|
231 | FilterBarComboBox * c = new FilterBarComboBox (this); |
---|
232 | FilterBarComboBoxDelegate * delegate = new FilterBarComboBoxDelegate (this, c); |
---|
233 | c->setItemDelegate (delegate); |
---|
234 | |
---|
235 | QPixmap blankPixmap (c->iconSize ()); |
---|
236 | blankPixmap.fill (Qt::transparent); |
---|
237 | QIcon blankIcon (blankPixmap); |
---|
238 | |
---|
239 | QStandardItemModel * model = new QStandardItemModel (this); |
---|
240 | |
---|
241 | QStandardItem * row = new QStandardItem (tr ("All")); |
---|
242 | row->setData (FilterMode::SHOW_ALL, ActivityRole); |
---|
243 | model->appendRow (row); |
---|
244 | |
---|
245 | model->appendRow (new QStandardItem); // separator |
---|
246 | delegate->setSeparator (model, model->index (1, 0)); |
---|
247 | |
---|
248 | row = new QStandardItem (QIcon::fromTheme ("system-run", blankIcon), tr ("Active")); |
---|
249 | row->setData (FilterMode::SHOW_ACTIVE, ActivityRole); |
---|
250 | model->appendRow (row); |
---|
251 | |
---|
252 | row = new QStandardItem (QIcon::fromTheme ("go-down", blankIcon), tr ("Downloading")); |
---|
253 | row->setData (FilterMode::SHOW_DOWNLOADING, ActivityRole); |
---|
254 | model->appendRow (row); |
---|
255 | |
---|
256 | row = new QStandardItem (QIcon::fromTheme ("go-up", blankIcon), tr ("Seeding")); |
---|
257 | row->setData (FilterMode::SHOW_SEEDING, ActivityRole); |
---|
258 | model->appendRow (row); |
---|
259 | |
---|
260 | row = new QStandardItem (QIcon::fromTheme ("media-playback-pause", blankIcon), tr ("Paused")); |
---|
261 | row->setData (FilterMode::SHOW_PAUSED, ActivityRole); |
---|
262 | model->appendRow (row); |
---|
263 | |
---|
264 | row = new QStandardItem (blankIcon, tr ("Finished")); |
---|
265 | row->setData (FilterMode::SHOW_FINISHED, ActivityRole); |
---|
266 | model->appendRow (row); |
---|
267 | |
---|
268 | row = new QStandardItem (QIcon::fromTheme ("view-refresh", blankIcon), tr ("Verifying")); |
---|
269 | row->setData (FilterMode::SHOW_VERIFYING, ActivityRole); |
---|
270 | model->appendRow (row); |
---|
271 | |
---|
272 | row = new QStandardItem (QIcon::fromTheme ("dialog-error", blankIcon), tr ("Error")); |
---|
273 | row->setData (FilterMode::SHOW_ERROR, ActivityRole); |
---|
274 | model->appendRow (row); |
---|
275 | |
---|
276 | c->setModel (model); |
---|
277 | return c; |
---|
278 | } |
---|
279 | |
---|
280 | /**** |
---|
281 | ***** |
---|
282 | ***** |
---|
283 | ***** |
---|
284 | ****/ |
---|
285 | |
---|
286 | namespace |
---|
287 | { |
---|
288 | QString readableHostName (const QString host) |
---|
289 | { |
---|
290 | // get the readable name... |
---|
291 | QString name = host; |
---|
292 | const int pos = name.lastIndexOf ('.'); |
---|
293 | if (pos >= 0) |
---|
294 | name.truncate (pos); |
---|
295 | if (!name.isEmpty ()) |
---|
296 | name[0] = name[0].toUpper (); |
---|
297 | return name; |
---|
298 | } |
---|
299 | } |
---|
300 | |
---|
301 | void |
---|
302 | FilterBar :: refreshTrackers () |
---|
303 | { |
---|
304 | Favicons& favicons = dynamic_cast<MyApp*> (QApplication::instance ())->favicons; |
---|
305 | const int firstTrackerRow = 2; // skip over the "All" and separator... |
---|
306 | |
---|
307 | // pull info from the tracker model... |
---|
308 | QSet<QString> oldHosts; |
---|
309 | for (int row=firstTrackerRow; ; ++row) |
---|
310 | { |
---|
311 | QModelIndex index = myTrackerModel->index (row, 0); |
---|
312 | if (!index.isValid ()) |
---|
313 | break; |
---|
314 | oldHosts << index.data (TrackerRole).toString (); |
---|
315 | } |
---|
316 | |
---|
317 | // pull the new stats from the torrent model... |
---|
318 | QSet<QString> newHosts; |
---|
319 | QMap<QString,int> torrentsPerHost; |
---|
320 | for (int row=0; ; ++row) |
---|
321 | { |
---|
322 | QModelIndex index = myTorrents.index (row, 0); |
---|
323 | if (!index.isValid ()) |
---|
324 | break; |
---|
325 | const Torrent * tor = index.data (TorrentModel::TorrentRole).value<const Torrent*> (); |
---|
326 | QSet<QString> torrentNames; |
---|
327 | foreach (QString host, tor->hosts ()) |
---|
328 | { |
---|
329 | newHosts.insert (host); |
---|
330 | torrentNames.insert (readableHostName (host)); |
---|
331 | } |
---|
332 | foreach (QString name, torrentNames) |
---|
333 | ++torrentsPerHost[name]; |
---|
334 | } |
---|
335 | |
---|
336 | // update the "All" row |
---|
337 | myTrackerModel->setData (myTrackerModel->index (0,0), myTorrents.rowCount (), TorrentCountRole); |
---|
338 | myTrackerModel->setData (myTrackerModel->index (0,0), getCountString (myTorrents.rowCount ()), TorrentCountStringRole); |
---|
339 | |
---|
340 | // rows to update |
---|
341 | foreach (QString host, oldHosts & newHosts) |
---|
342 | { |
---|
343 | const QString name = readableHostName (host); |
---|
344 | QStandardItem * row = myTrackerModel->findItems (name).front (); |
---|
345 | const int count = torrentsPerHost[name]; |
---|
346 | row->setData (count, TorrentCountRole); |
---|
347 | row->setData (getCountString (count), TorrentCountStringRole); |
---|
348 | row->setData (favicons.findFromHost (host), Qt::DecorationRole); |
---|
349 | } |
---|
350 | |
---|
351 | // rows to remove |
---|
352 | foreach (QString host, oldHosts - newHosts) |
---|
353 | { |
---|
354 | const QString name = readableHostName (host); |
---|
355 | QStandardItem * item = myTrackerModel->findItems (name).front (); |
---|
356 | if (!item->data (TrackerRole).toString ().isEmpty ()) // don't remove "All" |
---|
357 | myTrackerModel->removeRows (item->row (), 1); |
---|
358 | } |
---|
359 | |
---|
360 | // rows to add |
---|
361 | bool anyAdded = false; |
---|
362 | foreach (QString host, newHosts - oldHosts) |
---|
363 | { |
---|
364 | const QString name = readableHostName (host); |
---|
365 | |
---|
366 | if (!myTrackerModel->findItems (name).isEmpty ()) |
---|
367 | continue; |
---|
368 | |
---|
369 | // find the sorted position to add this row |
---|
370 | int i = firstTrackerRow; |
---|
371 | for (int n=myTrackerModel->rowCount (); i<n; ++i) |
---|
372 | { |
---|
373 | const QString rowName = myTrackerModel->index (i,0).data (Qt::DisplayRole).toString (); |
---|
374 | if (rowName >= name) |
---|
375 | break; |
---|
376 | } |
---|
377 | |
---|
378 | // add the row |
---|
379 | QStandardItem * row = new QStandardItem (favicons.findFromHost (host), name); |
---|
380 | const int count = torrentsPerHost[host]; |
---|
381 | row->setData (count, TorrentCountRole); |
---|
382 | row->setData (getCountString (count), TorrentCountStringRole); |
---|
383 | row->setData (favicons.findFromHost (host), Qt::DecorationRole); |
---|
384 | row->setData (host, TrackerRole); |
---|
385 | myTrackerModel->insertRow (i, row); |
---|
386 | anyAdded = true; |
---|
387 | } |
---|
388 | |
---|
389 | if (anyAdded) // the one added might match our filter... |
---|
390 | refreshPref (Prefs::FILTER_TRACKERS); |
---|
391 | } |
---|
392 | |
---|
393 | |
---|
394 | FilterBarComboBox * |
---|
395 | FilterBar :: createTrackerCombo (QStandardItemModel * model) |
---|
396 | { |
---|
397 | FilterBarComboBox * c = new FilterBarComboBox (this); |
---|
398 | FilterBarComboBoxDelegate * delegate = new FilterBarComboBoxDelegate (this, c); |
---|
399 | c->setItemDelegate (delegate); |
---|
400 | |
---|
401 | QStandardItem * row = new QStandardItem (tr ("All")); |
---|
402 | row->setData ("", TrackerRole); |
---|
403 | const int count = myTorrents.rowCount (); |
---|
404 | row->setData (count, TorrentCountRole); |
---|
405 | row->setData (getCountString (count), TorrentCountStringRole); |
---|
406 | model->appendRow (row); |
---|
407 | |
---|
408 | model->appendRow (new QStandardItem); // separator |
---|
409 | delegate->setSeparator (model, model->index (1, 0)); |
---|
410 | |
---|
411 | c->setModel (model); |
---|
412 | return c; |
---|
413 | } |
---|
414 | |
---|
415 | /**** |
---|
416 | ***** |
---|
417 | ***** |
---|
418 | ***** |
---|
419 | ****/ |
---|
420 | |
---|
421 | FilterBar :: FilterBar (Prefs& prefs, TorrentModel& torrents, TorrentFilter& filter, QWidget * parent): |
---|
422 | QWidget (parent), |
---|
423 | myPrefs (prefs), |
---|
424 | myTorrents (torrents), |
---|
425 | myFilter (filter), |
---|
426 | myRecountTimer (new QTimer (this)), |
---|
427 | myIsBootstrapping (true) |
---|
428 | { |
---|
429 | QHBoxLayout * h = new QHBoxLayout (this); |
---|
430 | const int hmargin = qMax (int (HIG::PAD), style ()->pixelMetric (QStyle::PM_LayoutHorizontalSpacing)); |
---|
431 | |
---|
432 | myCountLabel = new QLabel (this); |
---|
433 | h->setSpacing (0); |
---|
434 | h->setContentsMargins (2, 2, 2, 2); |
---|
435 | h->addWidget (myCountLabel); |
---|
436 | h->addSpacing (hmargin); |
---|
437 | |
---|
438 | myActivityCombo = createActivityCombo (); |
---|
439 | h->addWidget (myActivityCombo, 1); |
---|
440 | h->addSpacing (hmargin); |
---|
441 | |
---|
442 | myTrackerModel = new QStandardItemModel (this); |
---|
443 | myTrackerCombo = createTrackerCombo (myTrackerModel); |
---|
444 | h->addWidget (myTrackerCombo, 1); |
---|
445 | h->addSpacing (hmargin*2); |
---|
446 | |
---|
447 | myLineEdit = new QLineEdit (this); |
---|
448 | h->addWidget (myLineEdit); |
---|
449 | connect (myLineEdit, SIGNAL (textChanged (QString)), this, SLOT (onTextChanged (QString))); |
---|
450 | |
---|
451 | QPushButton * p = new QPushButton (this); |
---|
452 | QIcon icon = QIcon::fromTheme ("edit-clear", style ()->standardIcon (QStyle::SP_DialogCloseButton)); |
---|
453 | int iconSize = style ()->pixelMetric (QStyle::PM_SmallIconSize); |
---|
454 | p->setIconSize (QSize (iconSize, iconSize)); |
---|
455 | p->setIcon (icon); |
---|
456 | p->setFlat (true); |
---|
457 | h->addWidget (p); |
---|
458 | connect (p, SIGNAL (clicked (bool)), myLineEdit, SLOT (clear ())); |
---|
459 | |
---|
460 | // listen for changes from the other players |
---|
461 | connect (&myPrefs, SIGNAL (changed (int)), this, SLOT (refreshPref (int))); |
---|
462 | connect (myActivityCombo, SIGNAL (currentIndexChanged (int)), this, SLOT (onActivityIndexChanged (int))); |
---|
463 | connect (myTrackerCombo, SIGNAL (currentIndexChanged (int)), this, SLOT (onTrackerIndexChanged (int))); |
---|
464 | connect (&myFilter, SIGNAL (rowsInserted (const QModelIndex&,int,int)), this, SLOT (refreshCountLabel ())); |
---|
465 | connect (&myFilter, SIGNAL (rowsRemoved (const QModelIndex&,int,int)), this, SLOT (refreshCountLabel ())); |
---|
466 | connect (&myTorrents, SIGNAL (modelReset ()), this, SLOT (onTorrentModelReset ())); |
---|
467 | connect (&myTorrents, SIGNAL (rowsInserted (const QModelIndex&,int,int)), this, SLOT (onTorrentModelRowsInserted (const QModelIndex&,int,int))); |
---|
468 | connect (&myTorrents, SIGNAL (rowsRemoved (const QModelIndex&,int,int)), this, SLOT (onTorrentModelRowsRemoved (const QModelIndex&,int,int))); |
---|
469 | connect (&myTorrents, SIGNAL (dataChanged (const QModelIndex&,const QModelIndex&)), this, SLOT (onTorrentModelDataChanged (const QModelIndex&,const QModelIndex&))); |
---|
470 | connect (myRecountTimer, SIGNAL (timeout ()), this, SLOT (recount ())); |
---|
471 | |
---|
472 | recountSoon (); |
---|
473 | refreshTrackers (); |
---|
474 | refreshCountLabel (); |
---|
475 | myIsBootstrapping = false; |
---|
476 | |
---|
477 | // initialize our state |
---|
478 | QList<int> initKeys; |
---|
479 | initKeys << Prefs :: FILTER_MODE |
---|
480 | << Prefs :: FILTER_TRACKERS; |
---|
481 | foreach (int key, initKeys) |
---|
482 | refreshPref (key); |
---|
483 | } |
---|
484 | |
---|
485 | FilterBar :: ~FilterBar () |
---|
486 | { |
---|
487 | delete myRecountTimer; |
---|
488 | } |
---|
489 | |
---|
490 | /*** |
---|
491 | **** |
---|
492 | ***/ |
---|
493 | |
---|
494 | void |
---|
495 | FilterBar :: refreshPref (int key) |
---|
496 | { |
---|
497 | switch (key) |
---|
498 | { |
---|
499 | case Prefs :: FILTER_MODE: |
---|
500 | { |
---|
501 | const FilterMode m = myPrefs.get<FilterMode> (key); |
---|
502 | QAbstractItemModel * model = myActivityCombo->model (); |
---|
503 | QModelIndexList indices = model->match (model->index (0,0), ActivityRole, m.mode ()); |
---|
504 | myActivityCombo->setCurrentIndex (indices.isEmpty () ? 0 : indices.first ().row ()); |
---|
505 | break; |
---|
506 | } |
---|
507 | |
---|
508 | case Prefs :: FILTER_TRACKERS: |
---|
509 | { |
---|
510 | const QString tracker = myPrefs.getString (key); |
---|
511 | const QString name = readableHostName (tracker); |
---|
512 | QList<QStandardItem*> rows = myTrackerModel->findItems (name); |
---|
513 | if (!rows.isEmpty ()) |
---|
514 | { |
---|
515 | myTrackerCombo->setCurrentIndex (rows.front ()->row ()); |
---|
516 | } |
---|
517 | else // hm, we don't seem to have this tracker anymore... |
---|
518 | { |
---|
519 | const bool isBootstrapping = myTrackerModel->rowCount () <= 2; |
---|
520 | if (!isBootstrapping) |
---|
521 | myPrefs.set (key, ""); |
---|
522 | } |
---|
523 | break; |
---|
524 | } |
---|
525 | } |
---|
526 | } |
---|
527 | |
---|
528 | void |
---|
529 | FilterBar :: onTextChanged (const QString& str) |
---|
530 | { |
---|
531 | if (!myIsBootstrapping) |
---|
532 | myPrefs.set (Prefs::FILTER_TEXT, str.trimmed ()); |
---|
533 | } |
---|
534 | |
---|
535 | void |
---|
536 | FilterBar :: onTrackerIndexChanged (int i) |
---|
537 | { |
---|
538 | if (!myIsBootstrapping) |
---|
539 | { |
---|
540 | QString str; |
---|
541 | const bool isTracker = !myTrackerCombo->itemData (i,TrackerRole).toString ().isEmpty (); |
---|
542 | if (!isTracker) // show all |
---|
543 | { |
---|
544 | str = ""; |
---|
545 | } |
---|
546 | else |
---|
547 | { |
---|
548 | str = myTrackerCombo->itemData (i,TrackerRole).toString (); |
---|
549 | const int pos = str.lastIndexOf ('.'); |
---|
550 | if (pos >= 0) |
---|
551 | str.truncate (pos+1); |
---|
552 | } |
---|
553 | myPrefs.set (Prefs::FILTER_TRACKERS, str); |
---|
554 | } |
---|
555 | } |
---|
556 | |
---|
557 | void |
---|
558 | FilterBar :: onActivityIndexChanged (int i) |
---|
559 | { |
---|
560 | if (!myIsBootstrapping) |
---|
561 | { |
---|
562 | const FilterMode mode = myActivityCombo->itemData (i, ActivityRole).toInt (); |
---|
563 | myPrefs.set (Prefs::FILTER_MODE, mode); |
---|
564 | } |
---|
565 | } |
---|
566 | |
---|
567 | /*** |
---|
568 | **** |
---|
569 | ***/ |
---|
570 | |
---|
571 | void FilterBar :: onTorrentModelReset () { recountSoon (); } |
---|
572 | void FilterBar :: onTorrentModelRowsInserted (const QModelIndex&, int, int) { recountSoon (); } |
---|
573 | void FilterBar :: onTorrentModelRowsRemoved (const QModelIndex&, int, int) { recountSoon (); } |
---|
574 | void FilterBar :: onTorrentModelDataChanged (const QModelIndex&, const QModelIndex&) { recountSoon (); } |
---|
575 | |
---|
576 | void |
---|
577 | FilterBar :: recountSoon () |
---|
578 | { |
---|
579 | if (!myRecountTimer->isActive ()) |
---|
580 | { |
---|
581 | myRecountTimer->setSingleShot (true); |
---|
582 | myRecountTimer->start (800); |
---|
583 | } |
---|
584 | } |
---|
585 | void |
---|
586 | FilterBar :: recount () |
---|
587 | { |
---|
588 | QAbstractItemModel * model = myActivityCombo->model (); |
---|
589 | |
---|
590 | int torrentsPerMode[FilterMode::NUM_MODES] = {}; |
---|
591 | myFilter.countTorrentsPerMode (torrentsPerMode); |
---|
592 | |
---|
593 | for (int row=0, n=model->rowCount (); row<n; ++row) |
---|
594 | { |
---|
595 | QModelIndex index = model->index (row, 0); |
---|
596 | const int mode = index.data (ActivityRole).toInt (); |
---|
597 | const int count = torrentsPerMode [mode]; |
---|
598 | model->setData (index, count, TorrentCountRole); |
---|
599 | model->setData (index, getCountString (count), TorrentCountStringRole); |
---|
600 | } |
---|
601 | |
---|
602 | refreshTrackers (); |
---|
603 | refreshCountLabel (); |
---|
604 | } |
---|
605 | |
---|
606 | QString |
---|
607 | FilterBar :: getCountString (int n) const |
---|
608 | { |
---|
609 | return QString ("%L1").arg (n); |
---|
610 | } |
---|
611 | |
---|
612 | void |
---|
613 | FilterBar :: refreshCountLabel () |
---|
614 | { |
---|
615 | const int visibleCount = myFilter.rowCount (); |
---|
616 | const int trackerCount = myTrackerCombo->currentCount (); |
---|
617 | const int activityCount = myActivityCombo->currentCount (); |
---|
618 | |
---|
619 | if ((visibleCount == activityCount) || (visibleCount == trackerCount)) |
---|
620 | myCountLabel->setText (tr("Show:")); |
---|
621 | else |
---|
622 | myCountLabel->setText (tr("Show %Ln of:", 0, visibleCount)); |
---|
623 | } |
---|