source: trunk/qt/mainwin.cc @ 14380

Last change on this file since 14380 was 14380, checked in by mikedld, 8 years ago

Strip needless const& specifiers on SIGNAL and SLOT arguments (Qt client)

  • Property svn:keywords set to Date Rev Author Id
File size: 47.4 KB
Line 
1/*
2 * This file Copyright (C) 2009-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: mainwin.cc 14380 2014-12-13 09:04:10Z mikedld $
8 */
9
10#include <cassert>
11#include <iostream>
12
13#include <QtGui>
14#include <QCheckBox>
15#include <QIcon>
16#include <QProxyStyle>
17#include <QLabel>
18#include <QFileDialog>
19#include <QMessageBox>
20
21#include <libtransmission/transmission.h>
22#include <libtransmission/utils.h>
23#include <libtransmission/version.h>
24
25#include "about.h"
26#include "add-data.h"
27#include "app.h"
28#include "details.h"
29#include "filterbar.h"
30#include "filters.h"
31#include "formatter.h"
32#include "hig.h"
33#include "mainwin.h"
34#include "make-dialog.h"
35#include "options.h"
36#include "prefs.h"
37#include "prefs-dialog.h"
38#include "relocate.h"
39#include "session.h"
40#include "session-dialog.h"
41#include "speed.h"
42#include "stats-dialog.h"
43#include "torrent-delegate.h"
44#include "torrent-delegate-min.h"
45#include "torrent-filter.h"
46#include "torrent-model.h"
47#include "triconpushbutton.h"
48#include "ui_mainwin.h"
49
50#define PREFS_KEY "prefs-key";
51
52
53/**
54 * This is a proxy-style for that forces it to be always disabled.
55 * We use this to make our torrent list view behave consistently on
56 * both GTK and Qt implementations.
57 */
58class ListViewProxyStyle: public QProxyStyle
59{
60  public:
61
62    int styleHint (StyleHint            hint,
63                   const QStyleOption * option = 0,
64                   const QWidget      * widget = 0,
65                   QStyleHintReturn   * returnData = 0) const
66    {
67      if (hint == QStyle::SH_ItemView_ActivateItemOnSingleClick)
68        return 0;
69      return QProxyStyle::styleHint (hint, option, widget, returnData);
70    }
71};
72
73
74QIcon
75TrMainWindow::getStockIcon (const QString& name, int fallback)
76{
77  QIcon icon = QIcon::fromTheme (name);
78
79  if (icon.isNull () && (fallback >= 0))
80    icon = style ()->standardIcon (QStyle::StandardPixmap (fallback), 0, this);
81
82  return icon;
83}
84
85TrMainWindow::TrMainWindow (Session& session, Prefs& prefs, TorrentModel& model, bool minimized):
86  myLastFullUpdateTime (0),
87  mySessionDialog (new SessionDialog (session, prefs, this)),
88  myPrefsDialog (0),
89  myAboutDialog (new AboutDialog (this)),
90  myStatsDialog (new StatsDialog (session, this)),
91  myDetailsDialog (0),
92  myFilterModel (prefs),
93  myTorrentDelegate (new TorrentDelegate (this)),
94  myTorrentDelegateMin (new TorrentDelegateMin (this)),
95  mySession (session),
96  myPrefs (prefs),
97  myModel (model),
98  mySpeedModeOffIcon (":/icons/alt-limit-off.png"),
99  mySpeedModeOnIcon (":/icons/alt-limit-on.png"),
100  myLastSendTime (0),
101  myLastReadTime (0),
102  myNetworkTimer (this),
103  myNetworkError (false),
104  myRefreshTrayIconTimer (this),
105  myRefreshActionSensitivityTimer (this)
106{
107  setAcceptDrops (true);
108
109  QAction * sep = new QAction (this);
110  sep->setSeparator (true);
111
112  ui.setupUi (this);
113
114  QStyle * style = this->style ();
115
116  int i = style->pixelMetric (QStyle::PM_SmallIconSize, 0, this);
117  const QSize smallIconSize (i, i);
118
119  ui.listView->setStyle (new ListViewProxyStyle);
120
121  // icons
122  ui.action_OpenFile->setIcon (getStockIcon ("folder-open", QStyle::SP_DialogOpenButton));
123  ui.action_New->setIcon (getStockIcon ("document-new", QStyle::SP_DesktopIcon));
124  ui.action_Properties->setIcon (getStockIcon ("document-properties", QStyle::SP_DesktopIcon));
125  ui.action_OpenFolder->setIcon (getStockIcon ("folder-open", QStyle::SP_DirOpenIcon));
126  ui.action_Start->setIcon (getStockIcon ("media-playback-start", QStyle::SP_MediaPlay));
127  ui.action_StartNow->setIcon (getStockIcon ("media-playback-start", QStyle::SP_MediaPlay));
128  ui.action_Announce->setIcon (getStockIcon ("network-transmit-receive"));
129  ui.action_Pause->setIcon (getStockIcon ("media-playback-pause", QStyle::SP_MediaPause));
130  ui.action_Remove->setIcon (getStockIcon ("list-remove", QStyle::SP_TrashIcon));
131  ui.action_Delete->setIcon (getStockIcon ("edit-delete", QStyle::SP_TrashIcon));
132  ui.action_StartAll->setIcon (getStockIcon ("media-playback-start", QStyle::SP_MediaPlay));
133  ui.action_PauseAll->setIcon (getStockIcon ("media-playback-pause", QStyle::SP_MediaPause));
134  ui.action_Quit->setIcon (getStockIcon ("application-exit"));
135  ui.action_SelectAll->setIcon (getStockIcon ("edit-select-all"));
136  ui.action_ReverseSortOrder->setIcon (getStockIcon ("view-sort-ascending", QStyle::SP_ArrowDown));
137  ui.action_Preferences->setIcon (getStockIcon ("preferences-system"));
138  ui.action_Contents->setIcon (getStockIcon ("help-contents", QStyle::SP_DialogHelpButton));
139  ui.action_About->setIcon (getStockIcon ("help-about"));
140  ui.action_QueueMoveTop->setIcon (getStockIcon ("go-top"));
141  ui.action_QueueMoveUp->setIcon (getStockIcon ("go-up", QStyle::SP_ArrowUp));
142  ui.action_QueueMoveDown->setIcon (getStockIcon ("go-down", QStyle::SP_ArrowDown));
143  ui.action_QueueMoveBottom->setIcon (getStockIcon ("go-bottom"));
144
145  // ui signals
146  connect (ui.action_Toolbar, SIGNAL (toggled (bool)), this, SLOT (setToolbarVisible (bool)));
147  connect (ui.action_Filterbar, SIGNAL (toggled (bool)), this, SLOT (setFilterbarVisible (bool)));
148  connect (ui.action_Statusbar, SIGNAL (toggled (bool)), this, SLOT (setStatusbarVisible (bool)));
149  connect (ui.action_CompactView, SIGNAL (toggled (bool)), this, SLOT (setCompactView (bool)));
150  connect (ui.action_SortByActivity, SIGNAL (toggled (bool)), this, SLOT (onSortByActivityToggled (bool)));
151  connect (ui.action_SortByAge,      SIGNAL (toggled (bool)), this, SLOT (onSortByAgeToggled (bool)));
152  connect (ui.action_SortByETA,      SIGNAL (toggled (bool)), this, SLOT (onSortByETAToggled (bool)));
153  connect (ui.action_SortByName,     SIGNAL (toggled (bool)), this, SLOT (onSortByNameToggled (bool)));
154  connect (ui.action_SortByProgress, SIGNAL (toggled (bool)), this, SLOT (onSortByProgressToggled (bool)));
155  connect (ui.action_SortByQueue,    SIGNAL (toggled (bool)), this, SLOT (onSortByQueueToggled (bool)));
156  connect (ui.action_SortByRatio,    SIGNAL (toggled (bool)), this, SLOT (onSortByRatioToggled (bool)));
157  connect (ui.action_SortBySize,     SIGNAL (toggled (bool)), this, SLOT (onSortBySizeToggled (bool)));
158  connect (ui.action_SortByState,    SIGNAL (toggled (bool)), this, SLOT (onSortByStateToggled (bool)));
159  connect (ui.action_ReverseSortOrder, SIGNAL (toggled (bool)), this, SLOT (setSortAscendingPref (bool)));
160  connect (ui.action_Start, SIGNAL (triggered ()), this, SLOT (startSelected ()));
161  connect (ui.action_QueueMoveTop,    SIGNAL (triggered ()), this, SLOT (queueMoveTop ()));
162  connect (ui.action_QueueMoveUp,     SIGNAL (triggered ()), this, SLOT (queueMoveUp ()));
163  connect (ui.action_QueueMoveDown,   SIGNAL (triggered ()), this, SLOT (queueMoveDown ()));
164  connect (ui.action_QueueMoveBottom, SIGNAL (triggered ()), this, SLOT (queueMoveBottom ()));
165  connect (ui.action_StartNow, SIGNAL (triggered ()), this, SLOT (startSelectedNow ()));
166  connect (ui.action_Pause, SIGNAL (triggered ()), this, SLOT (pauseSelected ()));
167  connect (ui.action_Remove, SIGNAL (triggered ()), this, SLOT (removeSelected ()));
168  connect (ui.action_Delete, SIGNAL (triggered ()), this, SLOT (deleteSelected ()));
169  connect (ui.action_Verify, SIGNAL (triggered ()), this, SLOT (verifySelected ()));
170  connect (ui.action_Announce, SIGNAL (triggered ()), this, SLOT (reannounceSelected ()));
171  connect (ui.action_StartAll, SIGNAL (triggered ()), this, SLOT (startAll ()));
172  connect (ui.action_PauseAll, SIGNAL (triggered ()), this, SLOT (pauseAll ()));
173  connect (ui.action_OpenFile, SIGNAL (triggered ()), this, SLOT (openTorrent ()));
174  connect (ui.action_AddURL, SIGNAL (triggered ()), this, SLOT (openURL ()));
175  connect (ui.action_New, SIGNAL (triggered ()), this, SLOT (newTorrent ()));
176  connect (ui.action_Preferences, SIGNAL (triggered ()), this, SLOT (openPreferences ()));
177  connect (ui.action_Statistics, SIGNAL (triggered ()), myStatsDialog, SLOT (show ()));
178  connect (ui.action_Donate, SIGNAL (triggered ()), this, SLOT (openDonate ()));
179  connect (ui.action_About, SIGNAL (triggered ()), myAboutDialog, SLOT (show ()));
180  connect (ui.action_Contents, SIGNAL (triggered ()), this, SLOT (openHelp ()));
181  connect (ui.action_OpenFolder, SIGNAL (triggered ()), this, SLOT (openFolder ()));
182  connect (ui.action_CopyMagnetToClipboard, SIGNAL (triggered ()), this, SLOT (copyMagnetLinkToClipboard ()));
183  connect (ui.action_SetLocation, SIGNAL (triggered ()), this, SLOT (setLocation ()));
184  connect (ui.action_Properties, SIGNAL (triggered ()), this, SLOT (openProperties ()));
185  connect (ui.action_SessionDialog, SIGNAL (triggered ()), mySessionDialog, SLOT (show ()));
186
187  connect (ui.listView, SIGNAL (activated (QModelIndex)), ui.action_Properties, SLOT (trigger ()));
188
189  // signals
190  connect (ui.action_SelectAll, SIGNAL (triggered ()), ui.listView, SLOT (selectAll ()));
191  connect (ui.action_DeselectAll, SIGNAL (triggered ()), ui.listView, SLOT (clearSelection ()));
192
193  connect (&myFilterModel, SIGNAL (rowsInserted (QModelIndex, int, int)), this, SLOT (refreshActionSensitivitySoon ()));
194  connect (&myFilterModel, SIGNAL (rowsRemoved (QModelIndex, int, int)), this, SLOT (refreshActionSensitivitySoon ()));
195
196  connect (ui.action_Quit, SIGNAL (triggered ()), QCoreApplication::instance (), SLOT (quit ()));
197
198  // torrent view
199  myFilterModel.setSourceModel (&myModel);
200  connect (&myModel, SIGNAL (modelReset ()), this, SLOT (onModelReset ()));
201  connect (&myModel, SIGNAL (rowsRemoved (QModelIndex, int, int)), this, SLOT (onModelReset ()));
202  connect (&myModel, SIGNAL (rowsInserted (QModelIndex, int, int)), this, SLOT (onModelReset ()));
203  connect (&myModel, SIGNAL (dataChanged (QModelIndex, QModelIndex)), this, SLOT (refreshTrayIconSoon ()));
204
205  ui.listView->setModel (&myFilterModel);
206  connect (ui.listView->selectionModel (), SIGNAL (selectionChanged (QItemSelection, QItemSelection)), this, SLOT (refreshActionSensitivitySoon ()));
207
208  QActionGroup * actionGroup = new QActionGroup (this);
209  actionGroup->addAction (ui.action_SortByActivity);
210  actionGroup->addAction (ui.action_SortByAge);
211  actionGroup->addAction (ui.action_SortByETA);
212  actionGroup->addAction (ui.action_SortByName);
213  actionGroup->addAction (ui.action_SortByProgress);
214  actionGroup->addAction (ui.action_SortByQueue);
215  actionGroup->addAction (ui.action_SortByRatio);
216  actionGroup->addAction (ui.action_SortBySize);
217  actionGroup->addAction (ui.action_SortByState);
218
219  myAltSpeedAction = new QAction (tr ("Speed Limits"), this);
220  myAltSpeedAction->setIcon (myPrefs.get<bool> (Prefs::ALT_SPEED_LIMIT_ENABLED) ? mySpeedModeOnIcon : mySpeedModeOffIcon);
221  connect (myAltSpeedAction, SIGNAL (triggered ()), this, SLOT (toggleSpeedMode ()));
222
223  QMenu * menu = new QMenu (this);
224  menu->addAction (ui.action_OpenFile);
225  menu->addAction (ui.action_AddURL);
226  menu->addSeparator ();
227  menu->addAction (ui.action_ShowMainWindow);
228  menu->addAction (ui.action_ShowMessageLog);
229  menu->addAction (ui.action_About);
230  menu->addSeparator ();
231  menu->addAction (ui.action_StartAll);
232  menu->addAction (ui.action_PauseAll);
233  menu->addAction (myAltSpeedAction);
234  menu->addSeparator ();
235  menu->addAction (ui.action_Quit);
236  myTrayIcon.setContextMenu (menu);
237  myTrayIcon.setIcon (QIcon::fromTheme ("transmission-tray-icon", QApplication::windowIcon ()));
238
239  connect (&myPrefs, SIGNAL (changed (int)), this, SLOT (refreshPref (int)));
240  connect (ui.action_ShowMainWindow, SIGNAL (triggered (bool)), this, SLOT (toggleWindows (bool)));
241  connect (&myTrayIcon, SIGNAL (activated (QSystemTrayIcon::ActivationReason)),
242           this, SLOT (trayActivated (QSystemTrayIcon::ActivationReason)));
243
244  toggleWindows (!minimized);
245  ui.action_TrayIcon->setChecked (minimized || prefs.getBool (Prefs::SHOW_TRAY_ICON));
246
247  ui.verticalLayout->addWidget (createStatusBar ());
248  ui.verticalLayout->insertWidget (0, myFilterBar = new FilterBar (myPrefs, myModel, myFilterModel));
249
250  QList<int> initKeys;
251  initKeys << Prefs::MAIN_WINDOW_X
252           << Prefs::SHOW_TRAY_ICON
253           << Prefs::SORT_REVERSED
254           << Prefs::SORT_MODE
255           << Prefs::FILTERBAR
256           << Prefs::STATUSBAR
257           << Prefs::STATUSBAR_STATS
258           << Prefs::TOOLBAR
259           << Prefs::ALT_SPEED_LIMIT_ENABLED
260           << Prefs::COMPACT_VIEW
261           << Prefs::DSPEED
262           << Prefs::DSPEED_ENABLED
263           << Prefs::USPEED
264           << Prefs::USPEED_ENABLED
265           << Prefs::RATIO
266           << Prefs::RATIO_ENABLED;
267  foreach (int key, initKeys)
268    refreshPref (key);
269
270  connect (&mySession, SIGNAL (sourceChanged ()), this, SLOT (onSessionSourceChanged ()));
271  connect (&mySession, SIGNAL (statsUpdated ()), this, SLOT (refreshStatusBar ()));
272  connect (&mySession, SIGNAL (dataReadProgress ()), this, SLOT (dataReadProgress ()));
273  connect (&mySession, SIGNAL (dataSendProgress ()), this, SLOT (dataSendProgress ()));
274  connect (&mySession, SIGNAL (httpAuthenticationRequired ()), this, SLOT (wrongAuthentication ()));
275  connect (&mySession, SIGNAL (error (QNetworkReply::NetworkError)), this, SLOT (onError (QNetworkReply::NetworkError)));
276  connect (&mySession, SIGNAL (errorMessage (QString)), this, SLOT (errorMessage(QString)));
277
278  if (mySession.isServer ())
279    {
280      myNetworkLabel->hide ();
281    }
282  else
283    {
284      connect (&myNetworkTimer, SIGNAL (timeout ()), this, SLOT (onNetworkTimer ()));
285      myNetworkTimer.start (1000);
286    }
287
288  connect (&myRefreshTrayIconTimer, SIGNAL (timeout ()), this, SLOT (refreshTrayIcon ()));
289  connect (&myRefreshActionSensitivityTimer, SIGNAL (timeout ()), this, SLOT (refreshActionSensitivity ()));
290
291
292  refreshActionSensitivitySoon ();
293  refreshTrayIconSoon ();
294  refreshStatusBar ();
295  refreshTitle ();
296}
297
298TrMainWindow::~TrMainWindow ()
299{
300}
301
302/****
303*****
304****/
305
306void
307TrMainWindow::onSessionSourceChanged ()
308{
309  myModel.clear ();
310}
311
312void
313TrMainWindow::onModelReset ()
314{
315  refreshTitle ();
316  refreshActionSensitivitySoon ();
317  refreshStatusBar ();
318  refreshTrayIconSoon ();
319}
320
321/****
322*****
323****/
324
325#define PREF_VARIANTS_KEY "pref-variants-list"
326
327void
328TrMainWindow::onSetPrefs ()
329{
330  const QVariantList p = sender ()->property (PREF_VARIANTS_KEY).toList ();
331  assert ( (p.size () % 2) == 0);
332  for (int i=0, n=p.size (); i<n; i+=2)
333    myPrefs.set (p[i].toInt (), p[i+1]);
334}
335
336void
337TrMainWindow::onSetPrefs (bool isChecked)
338{
339  if (isChecked)
340    onSetPrefs ();
341}
342
343#define SHOW_KEY "show-mode"
344
345QWidget *
346TrMainWindow::createStatusBar ()
347{
348  QMenu * m;
349  QLabel * l;
350  QHBoxLayout * h;
351  QPushButton * p;
352  QActionGroup * a;
353  const int i = style ()->pixelMetric (QStyle::PM_SmallIconSize, 0, this);
354  const QSize smallIconSize (i, i);
355
356  QWidget * top = myStatusBar = new QWidget;
357  h = new QHBoxLayout (top);
358  h->setContentsMargins (HIG::PAD_SMALL, HIG::PAD_SMALL, HIG::PAD_SMALL, HIG::PAD_SMALL);
359  h->setSpacing (HIG::PAD_SMALL);
360
361    p = myOptionsButton = new TrIconPushButton (this);
362    p->setIcon (QIcon (":/icons/utilities.png"));
363    p->setIconSize (QPixmap (":/icons/utilities.png").size ());
364    p->setFlat (true);
365    p->setMenu (createOptionsMenu ());
366    h->addWidget (p);
367
368    p = myAltSpeedButton = new QPushButton (this);
369    p->setIcon (myPrefs.get<bool> (Prefs::ALT_SPEED_LIMIT_ENABLED) ? mySpeedModeOnIcon : mySpeedModeOffIcon);
370    p->setIconSize (QPixmap (":/icons/alt-limit-on.png").size ());
371    p->setCheckable (true);
372    p->setFixedWidth (p->height ());
373    p->setFlat (true);
374    h->addWidget (p);
375    connect (p, SIGNAL (clicked ()), this, SLOT (toggleSpeedMode ()));
376
377    l = myNetworkLabel = new QLabel;
378    h->addWidget (l);
379
380  h->addStretch (1);
381
382    l = myDownloadSpeedLabel = new QLabel (this);
383    const int minimumSpeedWidth = l->fontMetrics ().width (Formatter::uploadSpeedToString (Speed::fromKBps (999.99)));
384    l->setMinimumWidth (minimumSpeedWidth);
385    l->setAlignment (Qt::AlignRight|Qt::AlignVCenter);
386    h->addWidget (l);
387
388  h->addSpacing (HIG::PAD);
389
390    l = myUploadSpeedLabel = new QLabel;
391    l->setMinimumWidth (minimumSpeedWidth);
392    l->setAlignment (Qt::AlignRight|Qt::AlignVCenter);
393    h->addWidget (l);
394
395  h->addSpacing (HIG::PAD);
396
397    l = myStatsLabel = new QLabel (this);
398    h->addWidget (l);
399    a = new QActionGroup (this);
400    a->addAction (ui.action_TotalRatio);
401    a->addAction (ui.action_TotalTransfer);
402    a->addAction (ui.action_SessionRatio);
403    a->addAction (ui.action_SessionTransfer);
404    m = new QMenu (this);
405    m->addAction (ui.action_TotalRatio);
406    m->addAction (ui.action_TotalTransfer);
407    m->addAction (ui.action_SessionRatio);
408    m->addAction (ui.action_SessionTransfer);
409    connect (ui.action_TotalRatio, SIGNAL (triggered ()), this, SLOT (showTotalRatio ()));
410    connect (ui.action_TotalTransfer, SIGNAL (triggered ()), this, SLOT (showTotalTransfer ()));
411    connect (ui.action_SessionRatio, SIGNAL (triggered ()), this, SLOT (showSessionRatio ()));
412    connect (ui.action_SessionTransfer, SIGNAL (triggered ()), this, SLOT (showSessionTransfer ()));
413    p = myStatsModeButton = new TrIconPushButton (this);
414    p->setIcon (QIcon (":/icons/ratio.png"));
415    p->setIconSize (QPixmap (":/icons/ratio.png").size ());
416    p->setFlat (true);
417    p->setMenu (m);
418    h->addWidget (p);
419
420  return top;
421}
422
423QMenu *
424TrMainWindow::createOptionsMenu ()
425{
426  QMenu * menu;
427  QMenu * sub;
428  QAction * a;
429  QActionGroup * g;
430
431  QList<int> stockSpeeds;
432  stockSpeeds << 5 << 10 << 20 << 30 << 40 << 50 << 75 << 100 << 150 << 200 << 250 << 500 << 750;
433  QList<double> stockRatios;
434  stockRatios << 0.25 << 0.50 << 0.75 << 1 << 1.5 << 2 << 3;
435
436  menu = new QMenu (this);
437  sub = menu->addMenu (tr ("Limit Download Speed"));
438
439    int currentVal = myPrefs.get<int> (Prefs::DSPEED);
440    g = new QActionGroup (this);
441    a = myDlimitOffAction = sub->addAction (tr ("Unlimited"));
442    a->setCheckable (true);
443    a->setProperty (PREF_VARIANTS_KEY, QVariantList () << Prefs::DSPEED_ENABLED << false);
444    g->addAction (a);
445    connect (a, SIGNAL (triggered (bool)), this, SLOT (onSetPrefs (bool)));
446    a = myDlimitOnAction = sub->addAction (tr ("Limited at %1").arg (Formatter::speedToString (Speed::fromKBps (currentVal))));
447    a->setCheckable (true);
448    a->setProperty (PREF_VARIANTS_KEY, QVariantList () << Prefs::DSPEED << currentVal << Prefs::DSPEED_ENABLED << true);
449    g->addAction (a);
450    connect (a, SIGNAL (triggered (bool)), this, SLOT (onSetPrefs (bool)));
451    sub->addSeparator ();
452    foreach (int i, stockSpeeds)
453      {
454        a = sub->addAction (Formatter::speedToString (Speed::fromKBps (i)));
455        a->setProperty (PREF_VARIANTS_KEY, QVariantList () << Prefs::DSPEED << i << Prefs::DSPEED_ENABLED << true);
456        connect (a, SIGNAL (triggered (bool)), this, SLOT (onSetPrefs ()));
457      }
458
459  sub = menu->addMenu (tr ("Limit Upload Speed"));
460
461    currentVal = myPrefs.get<int> (Prefs::USPEED);
462    g = new QActionGroup (this);
463    a = myUlimitOffAction = sub->addAction (tr ("Unlimited"));
464    a->setCheckable (true);
465    a->setProperty (PREF_VARIANTS_KEY, QVariantList () << Prefs::USPEED_ENABLED << false);
466    g->addAction (a);
467    connect (a, SIGNAL (triggered (bool)), this, SLOT (onSetPrefs (bool)));
468    a = myUlimitOnAction = sub->addAction (tr ("Limited at %1").arg (Formatter::speedToString (Speed::fromKBps (currentVal))));
469    a->setCheckable (true);
470    a->setProperty (PREF_VARIANTS_KEY, QVariantList () << Prefs::USPEED << currentVal << Prefs::USPEED_ENABLED << true);
471    g->addAction (a);
472    connect (a, SIGNAL (triggered (bool)), this, SLOT (onSetPrefs (bool)));
473    sub->addSeparator ();
474    foreach (int i, stockSpeeds)
475      {
476        a = sub->addAction (Formatter::speedToString (Speed::fromKBps (i)));
477        a->setProperty (PREF_VARIANTS_KEY, QVariantList () << Prefs::USPEED << i << Prefs::USPEED_ENABLED << true);
478        connect (a, SIGNAL (triggered (bool)), this, SLOT (onSetPrefs ()));
479      }
480
481  menu->addSeparator ();
482  sub = menu->addMenu (tr ("Stop Seeding at Ratio"));
483
484    double d = myPrefs.get<double> (Prefs::RATIO);
485    g = new QActionGroup (this);
486    a = myRatioOffAction = sub->addAction (tr ("Seed Forever"));
487    a->setCheckable (true);
488    a->setProperty (PREF_VARIANTS_KEY, QVariantList () << Prefs::RATIO_ENABLED << false);
489    g->addAction (a);
490    connect (a, SIGNAL (triggered (bool)), this, SLOT (onSetPrefs (bool)));
491    a = myRatioOnAction = sub->addAction (tr ("Stop at Ratio (%1)").arg (Formatter::ratioToString (d)));
492    a->setCheckable (true);
493    a->setProperty (PREF_VARIANTS_KEY, QVariantList () << Prefs::RATIO << d << Prefs::RATIO_ENABLED << true);
494    g->addAction (a);
495    connect (a, SIGNAL (triggered (bool)), this, SLOT (onSetPrefs (bool)));
496    sub->addSeparator ();
497    foreach (double i, stockRatios)
498      {
499        a = sub->addAction (Formatter::ratioToString (i));
500        a->setProperty (PREF_VARIANTS_KEY, QVariantList () << Prefs::RATIO << i << Prefs::RATIO_ENABLED << true);
501        connect (a, SIGNAL (triggered (bool)), this, SLOT (onSetPrefs ()));
502      }
503
504  return menu;
505}
506
507/****
508*****
509****/
510
511void
512TrMainWindow::setSortPref (int i)
513{
514  myPrefs.set (Prefs::SORT_MODE, SortMode (i));
515}
516void TrMainWindow::onSortByActivityToggled (bool b) { if (b) setSortPref (SortMode::SORT_BY_ACTIVITY); }
517void TrMainWindow::onSortByAgeToggled (bool b) { if (b) setSortPref (SortMode::SORT_BY_AGE); }
518void TrMainWindow::onSortByETAToggled (bool b) { if (b) setSortPref (SortMode::SORT_BY_ETA); }
519void TrMainWindow::onSortByNameToggled (bool b) { if (b) setSortPref (SortMode::SORT_BY_NAME); }
520void TrMainWindow::onSortByProgressToggled (bool b) { if (b) setSortPref (SortMode::SORT_BY_PROGRESS); }
521void TrMainWindow::onSortByQueueToggled (bool b) { if (b) setSortPref (SortMode::SORT_BY_QUEUE); }
522void TrMainWindow::onSortByRatioToggled (bool b) { if (b) setSortPref (SortMode::SORT_BY_RATIO); }
523void TrMainWindow::onSortBySizeToggled (bool b) { if (b) setSortPref (SortMode::SORT_BY_SIZE); }
524void TrMainWindow::onSortByStateToggled (bool b) { if (b) setSortPref (SortMode::SORT_BY_STATE); }
525
526void
527TrMainWindow::setSortAscendingPref (bool b)
528{
529  myPrefs.set (Prefs::SORT_REVERSED, b);
530}
531
532/****
533*****
534****/
535
536void
537TrMainWindow::showEvent (QShowEvent * event)
538{
539  Q_UNUSED (event);
540
541  ui.action_ShowMainWindow->setChecked (true);
542}
543
544/****
545*****
546****/
547
548void
549TrMainWindow::hideEvent (QHideEvent * event)
550{
551  Q_UNUSED (event);
552
553  if (!isVisible ())
554    ui.action_ShowMainWindow->setChecked (false);
555}
556
557/****
558*****
559****/
560
561void
562TrMainWindow::onPrefsDestroyed ()
563{
564  myPrefsDialog = 0;
565}
566
567void
568TrMainWindow::openPreferences ()
569{
570  if (myPrefsDialog == 0)
571    {
572      myPrefsDialog = new PrefsDialog (mySession, myPrefs, this);
573      connect (myPrefsDialog, SIGNAL (destroyed (QObject*)), this, SLOT (onPrefsDestroyed ()));
574    }
575
576  myPrefsDialog->show ();
577}
578
579void
580TrMainWindow::onDetailsDestroyed ()
581{
582  myDetailsDialog = 0;
583}
584
585void
586TrMainWindow::openProperties ()
587{
588  if (myDetailsDialog == 0)
589    {
590      myDetailsDialog = new Details (mySession, myPrefs, myModel, this);
591      connect (myDetailsDialog, SIGNAL (destroyed (QObject*)), this, SLOT (onDetailsDestroyed ()));
592    }
593
594  myDetailsDialog->setIds (getSelectedTorrents ());
595  myDetailsDialog->show ();
596}
597
598void
599TrMainWindow::setLocation ()
600{
601  QDialog * d = new RelocateDialog (mySession, myModel, getSelectedTorrents (), this);
602  d->show ();
603}
604
605// Open Folder & select torrent's file or top folder
606#undef HAVE_OPEN_SELECT
607#if defined (Q_OS_WIN)
608# define HAVE_OPEN_SELECT
609static
610void openSelect (const QString& path)
611{
612  const QString explorer = "explorer";
613  QString param;
614  if (!QFileInfo (path).isDir ())
615    param = QLatin1String ("/select,");
616  param += QDir::toNativeSeparators (path);
617  QProcess::startDetached (explorer, QStringList (param));
618}
619#elif defined (Q_OS_MAC)
620# define HAVE_OPEN_SELECT
621static
622void openSelect (const QString& path)
623{
624  QStringList scriptArgs;
625  scriptArgs << QLatin1String ("-e")
626             << QString::fromLatin1 ("tell application \"Finder\" to reveal POSIX file \"%1\"").arg (path);
627  QProcess::execute (QLatin1String ("/usr/bin/osascript"), scriptArgs);
628
629  scriptArgs.clear ();
630  scriptArgs << QLatin1String ("-e")
631             << QLatin1String ("tell application \"Finder\" to activate");
632  QProcess::execute ("/usr/bin/osascript", scriptArgs);
633}
634#endif
635
636void
637TrMainWindow::openFolder ()
638{
639  const int torrentId (*getSelectedTorrents ().begin ());
640  const Torrent * tor (myModel.getTorrentFromId (torrentId));
641  QString path (tor->getPath ());
642  const FileList files = tor->files ();
643  const QString firstfile = files.at (0).filename;
644  int slashIndex = firstfile.indexOf ('/');
645  if (slashIndex > -1)
646    {
647      path = path + "/" + firstfile.left (slashIndex);
648    }
649#ifdef HAVE_OPEN_SELECT
650  else
651    {
652      openSelect (path + "/" + firstfile);
653      return;
654    }
655#endif
656  QDesktopServices::openUrl (QUrl::fromLocalFile (path));
657}
658
659void
660TrMainWindow::copyMagnetLinkToClipboard ()
661{
662  const int id (*getSelectedTorrents ().begin ());
663  mySession.copyMagnetLinkToClipboard (id);
664}
665
666void
667TrMainWindow::openDonate ()
668{
669  QDesktopServices::openUrl (QUrl ("http://www.transmissionbt.com/donate.php"));
670}
671
672void
673TrMainWindow::openHelp ()
674{
675  const char * fmt = "http://www.transmissionbt.com/help/gtk/%d.%dx";
676  int major, minor;
677  sscanf (SHORT_VERSION_STRING, "%d.%d", &major, &minor);
678  char url[128];
679  tr_snprintf (url, sizeof (url), fmt, major, minor/10);
680  QDesktopServices::openUrl (QUrl (url));
681}
682
683void
684TrMainWindow::refreshTitle ()
685{
686  QString title ("Transmission");
687  const QUrl url (mySession.getRemoteUrl ());
688  if (!url.isEmpty ())
689    title += tr (" - %1:%2").arg (url.host ()).arg (url.port ());
690  setWindowTitle (title);
691}
692
693void
694TrMainWindow::refreshTrayIconSoon ()
695{
696  if (!myRefreshTrayIconTimer.isActive ())
697    {
698      myRefreshTrayIconTimer.setSingleShot (true);
699      myRefreshTrayIconTimer.start (100);
700    }
701}
702void
703TrMainWindow::refreshTrayIcon ()
704{
705  Speed upSpeed, downSpeed;
706  size_t upCount, downCount;
707  QString tip;
708
709  myModel.getTransferSpeed (upSpeed, upCount, downSpeed, downCount);
710
711  if (myNetworkError)
712    {
713      tip  = tr ("Network Error");
714    }
715  else if (!upCount && !downCount)
716    {
717      tip = tr ("Idle");
718    }
719  else if (downCount)
720    {
721      tip  = tr( "%1   %2" ).arg(Formatter::downloadSpeedToString(downSpeed))
722                            .arg(Formatter::uploadSpeedToString(upSpeed));
723    }
724  else if (upCount)
725    {
726      tip = Formatter::uploadSpeedToString(upSpeed);
727    }
728
729  myTrayIcon.setToolTip (tip);
730}
731
732void
733TrMainWindow::refreshStatusBar ()
734{
735  Speed upSpeed, downSpeed;
736  size_t upCount, downCount;
737  myModel.getTransferSpeed (upSpeed, upCount, downSpeed, downCount);
738
739  myUploadSpeedLabel->setText (Formatter::uploadSpeedToString(upSpeed));
740  myUploadSpeedLabel->setVisible (downCount || upCount);
741  myDownloadSpeedLabel->setText (Formatter::downloadSpeedToString(downSpeed));
742  myDownloadSpeedLabel->setVisible (downCount);
743
744  myNetworkLabel->setVisible (!mySession.isServer ());
745
746  const QString mode (myPrefs.getString (Prefs::STATUSBAR_STATS));
747  QString str;
748
749  if (mode == "session-ratio")
750    {
751      str = tr ("Ratio: %1").arg (Formatter:: ratioToString (mySession.getStats ().ratio));
752    }
753  else if (mode == "session-transfer")
754    {
755      const tr_session_stats& stats (mySession.getStats ());
756      str = tr ("Down: %1, Up: %2").arg (Formatter:: sizeToString (stats.downloadedBytes))
757                                      .arg (Formatter:: sizeToString (stats.uploadedBytes));
758    }
759  else if (mode == "total-transfer")
760    {
761      const tr_session_stats& stats (mySession.getCumulativeStats ());
762      str = tr ("Down: %1, Up: %2").arg (Formatter:: sizeToString (stats.downloadedBytes))
763                                   .arg (Formatter:: sizeToString (stats.uploadedBytes));
764    }
765  else // default is "total-ratio"
766    {
767      str = tr ("Ratio: %1").arg (Formatter:: ratioToString (mySession.getCumulativeStats ().ratio));
768    }
769
770  myStatsLabel->setText (str);
771}
772
773
774
775void
776TrMainWindow::refreshActionSensitivitySoon ()
777{
778  if (!myRefreshActionSensitivityTimer.isActive ())
779    {
780      myRefreshActionSensitivityTimer.setSingleShot (true);
781      myRefreshActionSensitivityTimer.start (100);
782    }
783}
784void
785TrMainWindow::refreshActionSensitivity ()
786{
787  int selected (0);
788  int paused (0);
789  int queued (0);
790  int selectedAndPaused (0);
791  int selectedAndQueued (0);
792  int canAnnounce (0);
793  const QAbstractItemModel * model (ui.listView->model ());
794  const QItemSelectionModel * selectionModel (ui.listView->selectionModel ());
795  const int rowCount (model->rowCount ());
796
797  // count how many torrents are selected, paused, etc
798  for (int row=0; row<rowCount; ++row)
799    {
800      const QModelIndex modelIndex (model->index (row, 0));
801      assert (model == modelIndex.model ());
802      const Torrent * tor (model->data (modelIndex, TorrentModel::TorrentRole).value<const Torrent*> ());
803      if (tor)
804        {
805          const bool isSelected (selectionModel->isSelected (modelIndex));
806          const bool isPaused (tor->isPaused ());
807          const bool isQueued (tor->isQueued ());
808          if (isSelected) ++selected;
809          if (isQueued) ++queued;
810          if (isPaused) ++ paused;
811          if (isSelected && isPaused) ++selectedAndPaused;
812          if (isSelected && isQueued) ++selectedAndQueued;
813          if (tor->canManualAnnounce ()) ++canAnnounce;
814        }
815    }
816
817  const bool haveSelection (selected > 0);
818  ui.action_Verify->setEnabled (haveSelection);
819  ui.action_Remove->setEnabled (haveSelection);
820  ui.action_Delete->setEnabled (haveSelection);
821  ui.action_Properties->setEnabled (haveSelection);
822  ui.action_DeselectAll->setEnabled (haveSelection);
823  ui.action_SetLocation->setEnabled (haveSelection);
824
825  const bool oneSelection (selected == 1);
826  ui.action_OpenFolder->setEnabled (oneSelection && mySession.isLocal ());
827  ui.action_CopyMagnetToClipboard->setEnabled (oneSelection);
828
829  ui.action_SelectAll->setEnabled (selected < rowCount);
830  ui.action_StartAll->setEnabled (paused > 0);
831  ui.action_PauseAll->setEnabled (paused < rowCount);
832  ui.action_Start->setEnabled (selectedAndPaused > 0);
833  ui.action_StartNow->setEnabled (selectedAndPaused + selectedAndQueued > 0);
834  ui.action_Pause->setEnabled (selectedAndPaused < selected);
835  ui.action_Announce->setEnabled (selected > 0 && (canAnnounce == selected));
836
837  ui.action_QueueMoveTop->setEnabled (haveSelection);
838  ui.action_QueueMoveUp->setEnabled (haveSelection);
839  ui.action_QueueMoveDown->setEnabled (haveSelection);
840  ui.action_QueueMoveBottom->setEnabled (haveSelection);
841
842  if (myDetailsDialog)
843    myDetailsDialog->setIds (getSelectedTorrents ());
844}
845
846/**
847***
848**/
849
850void
851TrMainWindow::clearSelection ()
852{
853  ui.action_DeselectAll->trigger ();
854}
855
856QSet<int>
857TrMainWindow::getSelectedTorrents () const
858{
859  QSet<int> ids;
860
861  foreach (QModelIndex index, ui.listView->selectionModel ()->selectedRows ())
862    {
863      const Torrent * tor (index.data (TorrentModel::TorrentRole).value<const Torrent*> ());
864      ids.insert (tor->id ());
865    }
866
867  return ids;
868}
869
870void
871TrMainWindow::startSelected ()
872{
873  mySession.startTorrents (getSelectedTorrents ());
874}
875void
876TrMainWindow::startSelectedNow ()
877{
878  mySession.startTorrentsNow (getSelectedTorrents ());
879}
880void
881TrMainWindow::pauseSelected ()
882{
883  mySession.pauseTorrents (getSelectedTorrents ());
884}
885void
886TrMainWindow::queueMoveTop ()
887{
888  mySession.queueMoveTop (getSelectedTorrents ());
889}
890void
891TrMainWindow::queueMoveUp ()
892{
893  mySession.queueMoveUp (getSelectedTorrents ());
894}
895void
896TrMainWindow::queueMoveDown ()
897{
898  mySession.queueMoveDown (getSelectedTorrents ());
899}
900void
901TrMainWindow::queueMoveBottom ()
902{
903  mySession.queueMoveBottom (getSelectedTorrents ());
904}
905void
906TrMainWindow::startAll ()
907{
908  mySession.startTorrents ();
909}
910void
911TrMainWindow::pauseAll ()
912{
913  mySession.pauseTorrents ();
914}
915void
916TrMainWindow::removeSelected ()
917{
918  removeTorrents (false);
919}
920void
921TrMainWindow::deleteSelected ()
922{
923  removeTorrents (true);
924}
925void
926TrMainWindow::verifySelected ()
927{
928  mySession.verifyTorrents (getSelectedTorrents ());
929}
930void
931TrMainWindow::reannounceSelected ()
932{
933  mySession.reannounceTorrents (getSelectedTorrents ());
934}
935
936/**
937***
938**/
939
940void TrMainWindow::showTotalRatio () { myPrefs.set (Prefs::STATUSBAR_STATS, "total-ratio"); }
941void TrMainWindow::showTotalTransfer () { myPrefs.set (Prefs::STATUSBAR_STATS, "total-transfer"); }
942void TrMainWindow::showSessionRatio () { myPrefs.set (Prefs::STATUSBAR_STATS, "session-ratio"); }
943void TrMainWindow::showSessionTransfer () { myPrefs.set (Prefs::STATUSBAR_STATS, "session-transfer"); }
944
945/**
946***
947**/
948
949void
950TrMainWindow::setCompactView (bool visible)
951{
952  myPrefs.set (Prefs::COMPACT_VIEW, visible);
953}
954void
955TrMainWindow::toggleSpeedMode ()
956{
957  myPrefs.toggleBool (Prefs::ALT_SPEED_LIMIT_ENABLED);
958  const bool mode = myPrefs.get<bool> (Prefs::ALT_SPEED_LIMIT_ENABLED);
959  myAltSpeedAction->setIcon (mode ? mySpeedModeOnIcon : mySpeedModeOffIcon);
960}
961void
962TrMainWindow::setToolbarVisible (bool visible)
963{
964  myPrefs.set (Prefs::TOOLBAR, visible);
965}
966void
967TrMainWindow::setFilterbarVisible (bool visible)
968{
969  myPrefs.set (Prefs::FILTERBAR, visible);
970}
971void
972TrMainWindow::setStatusbarVisible (bool visible)
973{
974  myPrefs.set (Prefs::STATUSBAR, visible);
975}
976
977/**
978***
979**/
980
981void
982TrMainWindow::toggleWindows (bool doShow)
983{
984  if (!doShow)
985    {
986      hide ();
987    }
988  else
989    {
990      if (!isVisible ()) show ();
991      if (isMinimized ()) showNormal ();
992      //activateWindow ();
993      raise ();
994      QApplication::setActiveWindow (this);
995    }
996}
997
998void
999TrMainWindow::trayActivated (QSystemTrayIcon::ActivationReason reason)
1000{
1001  if ((reason == QSystemTrayIcon::Trigger) ||
1002      (reason == QSystemTrayIcon::DoubleClick))
1003    {
1004      if (isMinimized ())
1005        toggleWindows (true);
1006      else
1007        toggleWindows (!isVisible ());
1008    }
1009}
1010
1011
1012void
1013TrMainWindow::refreshPref (int key)
1014{
1015  bool b;
1016  int i;
1017  QString str;
1018
1019  switch (key)
1020    {
1021      case Prefs::STATUSBAR_STATS:
1022        str = myPrefs.getString (key);
1023        ui.action_TotalRatio->setChecked (str == "total-ratio");
1024        ui.action_TotalTransfer->setChecked (str == "total-transfer");
1025        ui.action_SessionRatio->setChecked (str == "session-ratio");
1026        ui.action_SessionTransfer->setChecked (str == "session-transfer");
1027        refreshStatusBar ();
1028        break;
1029
1030      case Prefs::SORT_REVERSED:
1031        ui.action_ReverseSortOrder->setChecked (myPrefs.getBool (key));
1032        break;
1033
1034      case Prefs::SORT_MODE:
1035        i = myPrefs.get<SortMode> (key).mode ();
1036        ui.action_SortByActivity->setChecked (i == SortMode::SORT_BY_ACTIVITY);
1037        ui.action_SortByAge->setChecked (i == SortMode::SORT_BY_AGE);
1038        ui.action_SortByETA->setChecked (i == SortMode::SORT_BY_ETA);
1039        ui.action_SortByName->setChecked (i == SortMode::SORT_BY_NAME);
1040        ui.action_SortByProgress->setChecked (i == SortMode::SORT_BY_PROGRESS);
1041        ui.action_SortByQueue->setChecked (i == SortMode::SORT_BY_QUEUE);
1042        ui.action_SortByRatio->setChecked (i == SortMode::SORT_BY_RATIO);
1043        ui.action_SortBySize->setChecked (i == SortMode::SORT_BY_SIZE);
1044        ui.action_SortByState->setChecked (i == SortMode::SORT_BY_STATE);
1045        break;
1046
1047      case Prefs::DSPEED_ENABLED:
1048        (myPrefs.get<bool> (key) ? myDlimitOnAction : myDlimitOffAction)->setChecked (true);
1049        break;
1050
1051      case Prefs::DSPEED:
1052        myDlimitOnAction->setText (tr ("Limited at %1").arg (Formatter::speedToString (Speed::fromKBps (myPrefs.get<int> (key)))));
1053        break;
1054
1055      case Prefs::USPEED_ENABLED:
1056        (myPrefs.get<bool> (key) ? myUlimitOnAction : myUlimitOffAction)->setChecked (true);
1057        break;
1058
1059      case Prefs::USPEED:
1060        myUlimitOnAction->setText (tr ("Limited at %1").arg (Formatter::speedToString (Speed::fromKBps (myPrefs.get<int> (key)))));
1061        break;
1062
1063      case Prefs::RATIO_ENABLED:
1064        (myPrefs.get<bool> (key) ? myRatioOnAction : myRatioOffAction)->setChecked (true);
1065        break;
1066
1067      case Prefs::RATIO:
1068        myRatioOnAction->setText (tr ("Stop at Ratio (%1)").arg (Formatter::ratioToString (myPrefs.get<double> (key))));
1069        break;
1070
1071      case Prefs::FILTERBAR:
1072        b = myPrefs.getBool (key);
1073        myFilterBar->setVisible (b);
1074        ui.action_Filterbar->setChecked (b);
1075        break;
1076
1077      case Prefs::STATUSBAR:
1078        b = myPrefs.getBool (key);
1079        myStatusBar->setVisible (b);
1080        ui.action_Statusbar->setChecked (b);
1081        break;
1082
1083      case Prefs::TOOLBAR:
1084        b = myPrefs.getBool (key);
1085        ui.toolBar->setVisible (b);
1086        ui.action_Toolbar->setChecked (b);
1087        break;
1088
1089      case Prefs::SHOW_TRAY_ICON:
1090        b = myPrefs.getBool (key);
1091        ui.action_TrayIcon->setChecked (b);
1092        myTrayIcon.setVisible (b);
1093        dynamic_cast<MyApp*> (QCoreApplication::instance ())->setQuitOnLastWindowClosed (!b);
1094        refreshTrayIconSoon ();
1095        break;
1096
1097      case Prefs::COMPACT_VIEW: {
1098            QItemSelectionModel * selectionModel (ui.listView->selectionModel ());
1099            const QItemSelection selection (selectionModel->selection ());
1100            const QModelIndex currentIndex (selectionModel->currentIndex ());
1101            b = myPrefs.getBool (key);
1102            ui.action_CompactView->setChecked (b);
1103            ui.listView->setItemDelegate (b ? myTorrentDelegateMin : myTorrentDelegate);
1104            selectionModel->clear ();
1105            ui.listView->reset (); // force the rows to resize
1106            selectionModel->select (selection, QItemSelectionModel::Select);
1107            selectionModel->setCurrentIndex (currentIndex, QItemSelectionModel::NoUpdate);
1108            break;
1109        }
1110
1111      case Prefs::MAIN_WINDOW_X:
1112      case Prefs::MAIN_WINDOW_Y:
1113      case Prefs::MAIN_WINDOW_WIDTH:
1114      case Prefs::MAIN_WINDOW_HEIGHT:
1115        setGeometry (myPrefs.getInt (Prefs::MAIN_WINDOW_X),
1116                     myPrefs.getInt (Prefs::MAIN_WINDOW_Y),
1117                     myPrefs.getInt (Prefs::MAIN_WINDOW_WIDTH),
1118                     myPrefs.getInt (Prefs::MAIN_WINDOW_HEIGHT));
1119        break;
1120
1121      case Prefs::ALT_SPEED_LIMIT_ENABLED:
1122      case Prefs::ALT_SPEED_LIMIT_UP:
1123      case Prefs::ALT_SPEED_LIMIT_DOWN:
1124        {
1125          b = myPrefs.getBool (Prefs::ALT_SPEED_LIMIT_ENABLED);
1126          myAltSpeedButton->setChecked (b);
1127          myAltSpeedButton->setIcon (b ? mySpeedModeOnIcon : mySpeedModeOffIcon);
1128          const QString fmt = b ? tr ("Click to disable Temporary Speed Limits\n (%1 down, %2 up)")
1129                                : tr ("Click to enable Temporary Speed Limits\n (%1 down, %2 up)");
1130          const Speed d = Speed::fromKBps (myPrefs.getInt (Prefs::ALT_SPEED_LIMIT_DOWN));
1131          const Speed u = Speed::fromKBps (myPrefs.getInt (Prefs::ALT_SPEED_LIMIT_UP));
1132          myAltSpeedButton->setToolTip (fmt.arg (Formatter::speedToString (d))
1133                                           .arg (Formatter::speedToString (u)));
1134          break;
1135        }
1136
1137      default:
1138        break;
1139    }
1140}
1141
1142/***
1143****
1144***/
1145
1146#define SHOW_OPTIONS_CHECKBOX_NAME "show-options-checkbox"
1147
1148void
1149TrMainWindow::newTorrent ()
1150{
1151  MakeDialog * dialog = new MakeDialog (mySession, this);
1152  dialog->show ();
1153}
1154
1155void
1156TrMainWindow::openTorrent ()
1157{
1158  QFileDialog * d;
1159  d = new QFileDialog (this,
1160                       tr ("Open Torrent"),
1161                       myPrefs.getString (Prefs::OPEN_DIALOG_FOLDER),
1162                       tr ("Torrent Files (*.torrent);;All Files (*.*)"));
1163  d->setFileMode (QFileDialog::ExistingFiles);
1164  d->setAttribute (Qt::WA_DeleteOnClose);
1165
1166  QCheckBox * b = new QCheckBox (tr ("Show &options dialog"));
1167  b->setChecked (myPrefs.getBool (Prefs::OPTIONS_PROMPT));
1168  b->setObjectName (SHOW_OPTIONS_CHECKBOX_NAME);
1169  auto l = dynamic_cast<QGridLayout*> (d->layout ());
1170  if (l == nullptr)
1171    {
1172      l = new QGridLayout;
1173      d->setLayout (l);
1174    }
1175  l->addWidget (b, l->rowCount(), 0, 1, -1, Qt::AlignLeft);
1176
1177  connect (d, SIGNAL (filesSelected (QStringList)),
1178           this, SLOT (addTorrents (QStringList)));
1179
1180  d->show ();
1181}
1182
1183void
1184TrMainWindow::openURL ()
1185{
1186  QString str = QApplication::clipboard ()->text (QClipboard::Selection);
1187
1188  if (!AddData::isSupported (str))
1189    str = QApplication::clipboard ()->text (QClipboard::Clipboard);
1190
1191  if (!AddData::isSupported (str))
1192    str.clear ();
1193
1194  addTorrent (str, true);
1195}
1196
1197void
1198TrMainWindow::addTorrents (const QStringList& filenames)
1199{
1200  bool showOptions = myPrefs.getBool (Prefs::OPTIONS_PROMPT);
1201
1202  const QFileDialog * const fileDialog = qobject_cast<const QFileDialog*> (sender ());
1203  if (fileDialog != NULL)
1204    {
1205      const QCheckBox * const b = fileDialog->findChild<const QCheckBox*> (SHOW_OPTIONS_CHECKBOX_NAME);
1206      if (b != NULL)
1207        showOptions = b->isChecked ();
1208    }
1209
1210  foreach (const QString& filename, filenames)
1211    addTorrent (filename, showOptions);
1212}
1213
1214void
1215TrMainWindow::addTorrent (const AddData& addMe, bool showOptions)
1216{
1217  if (showOptions)
1218    {
1219      Options * o = new Options (mySession, myPrefs, addMe, this);
1220      o->show ();
1221      QApplication::alert (o);
1222    }
1223  else
1224    {
1225      mySession.addTorrent (addMe);
1226      QApplication::alert (this);
1227    }
1228}
1229
1230void
1231TrMainWindow::removeTorrents (const bool deleteFiles)
1232{
1233  QSet<int> ids;
1234  QMessageBox msgBox (this);
1235  QString primary_text, secondary_text;
1236  int incomplete = 0;
1237  int connected  = 0;
1238  int count;
1239
1240  foreach (QModelIndex index, ui.listView->selectionModel ()->selectedRows ())
1241    {
1242      const Torrent * tor (index.data (TorrentModel::TorrentRole).value<const Torrent*> ());
1243      ids.insert (tor->id ());
1244
1245      if (tor->connectedPeers ())
1246        ++connected;
1247
1248      if (!tor->isDone ())
1249        ++incomplete;
1250    }
1251
1252  if (ids.isEmpty ())
1253    return;
1254
1255  count = ids.size ();
1256
1257  if (!deleteFiles)
1258    {
1259      primary_text = (count == 1)
1260        ? tr ("Remove torrent?")
1261        : tr ("Remove %1 torrents?").arg (count);
1262    }
1263  else
1264    {
1265      primary_text = (count == 1)
1266        ? tr ("Delete this torrent's downloaded files?")
1267        : tr ("Delete these %1 torrents' downloaded files?").arg (count);
1268    }
1269
1270  if (!incomplete && !connected)
1271    {
1272      secondary_text = (count == 1)
1273        ? tr ("Once removed, continuing the transfer will require the torrent file or magnet link.")
1274        : tr ("Once removed, continuing the transfers will require the torrent files or magnet links.");
1275    }
1276  else if (count == incomplete)
1277    {
1278      secondary_text = (count == 1)
1279        ? tr ("This torrent has not finished downloading.")
1280        : tr ("These torrents have not finished downloading.");
1281    }
1282  else if (count == connected)
1283    {
1284      secondary_text = (count == 1)
1285        ? tr ("This torrent is connected to peers.")
1286        : tr ("These torrents are connected to peers.");
1287    }
1288  else
1289    {
1290      if (connected)
1291        {
1292          secondary_text = (connected == 1)
1293            ? tr ("One of these torrents is connected to peers.")
1294            : tr ("Some of these torrents are connected to peers.");
1295        }
1296
1297      if (connected && incomplete)
1298        {
1299          secondary_text += "\n";
1300        }
1301
1302      if (incomplete)
1303        {
1304          secondary_text += (incomplete == 1)
1305            ? tr ("One of these torrents has not finished downloading.")
1306            : tr ("Some of these torrents have not finished downloading.");
1307        }
1308    }
1309
1310  msgBox.setWindowTitle (QString (" "));
1311  msgBox.setText (QString ("<big><b>%1</big></b>").arg (primary_text));
1312  msgBox.setInformativeText (secondary_text);
1313  msgBox.setStandardButtons (QMessageBox::Ok | QMessageBox::Cancel);
1314  msgBox.setDefaultButton (QMessageBox::Cancel);
1315  msgBox.setIcon (QMessageBox::Question);
1316  // hack needed to keep the dialog from being too narrow
1317  auto layout = dynamic_cast<QGridLayout*>(msgBox.layout());
1318  if (layout == nullptr)
1319    {
1320      layout = new QGridLayout;
1321      msgBox.setLayout (layout);
1322    }
1323  QSpacerItem* spacer = new QSpacerItem (450, 0, QSizePolicy::Minimum, QSizePolicy::Expanding);
1324  layout->addItem (spacer, layout->rowCount (), 0, 1, layout->columnCount ());
1325
1326  if (msgBox.exec () == QMessageBox::Ok)
1327    {
1328      ui.listView->selectionModel ()->clear ();
1329      mySession.removeTorrents (ids, deleteFiles);
1330    }
1331}
1332
1333/***
1334****
1335***/
1336
1337void
1338TrMainWindow::updateNetworkIcon ()
1339{
1340  const time_t now = time (NULL);
1341  const int period = 3;
1342  const time_t secondsSinceLastSend = now - myLastSendTime;
1343  const time_t secondsSinceLastRead = now - myLastReadTime;
1344  const bool isSending = secondsSinceLastSend <= period;
1345  const bool isReading = secondsSinceLastRead <= period;
1346  const char * key;
1347
1348  if (myNetworkError)
1349    key = "network-error";
1350  else if (isSending && isReading)
1351    key = "network-transmit-receive";
1352  else if (isSending)
1353    key = "network-transmit";
1354  else if (isReading)
1355    key = "network-receive";
1356  else
1357    key = "network-idle";
1358  const QIcon icon = getStockIcon (key, QStyle::SP_DriveNetIcon);
1359  const QPixmap pixmap = icon.pixmap (16, 16);
1360
1361  QString tip;
1362  const QString url = mySession.getRemoteUrl ().host ();
1363  if (!myLastReadTime)
1364    tip = tr ("%1 has not responded yet").arg (url);
1365  else if (myNetworkError)
1366    tip = tr (myErrorMessage.toLatin1 ().constData ());
1367  else if (secondsSinceLastRead < 30)
1368    tip = tr ("%1 is responding").arg (url);
1369  else if (secondsSinceLastRead < (60*2))
1370    tip = tr ("%1 last responded %2 ago").arg (url).arg (Formatter::timeToString (secondsSinceLastRead));
1371  else
1372    tip = tr ("%1 is not responding").arg (url);
1373
1374  myNetworkLabel->setPixmap (pixmap);
1375  myNetworkLabel->setToolTip (tip);
1376}
1377
1378void
1379TrMainWindow::onNetworkTimer ()
1380{
1381  updateNetworkIcon ();
1382}
1383
1384void
1385TrMainWindow::dataReadProgress ()
1386{
1387  if (!myNetworkError)
1388  myLastReadTime = time (NULL);
1389}
1390
1391void
1392TrMainWindow::dataSendProgress ()
1393{
1394  myLastSendTime = time (NULL);
1395}
1396
1397void
1398TrMainWindow::onError (QNetworkReply::NetworkError code)
1399{
1400  const bool hadError = myNetworkError;
1401  const bool haveError = (code != QNetworkReply::NoError)
1402                      && (code != QNetworkReply::UnknownContentError);
1403
1404  myNetworkError = haveError;
1405  refreshTrayIconSoon();
1406  updateNetworkIcon();
1407
1408  // Refresh our model if we've just gotten a clean connection to the session.
1409  // That way we can rebuild after a restart of transmission-daemon
1410  if (hadError && !haveError)
1411    myModel.clear();
1412}
1413
1414void
1415TrMainWindow::errorMessage (const QString& msg)
1416{
1417    myErrorMessage = msg;
1418}
1419
1420void
1421TrMainWindow::wrongAuthentication ()
1422{
1423  mySession.stop ();
1424  mySessionDialog->show ();
1425}
1426
1427/***
1428****
1429***/
1430
1431void
1432TrMainWindow::dragEnterEvent (QDragEnterEvent * event)
1433{
1434  const QMimeData * mime = event->mimeData ();
1435
1436  if (mime->hasFormat ("application/x-bittorrent")
1437        || mime->hasUrls()
1438        || mime->text ().trimmed ().endsWith (".torrent", Qt::CaseInsensitive)
1439        || mime->text ().startsWith ("magnet:", Qt::CaseInsensitive))
1440    event->acceptProposedAction ();
1441}
1442
1443void
1444TrMainWindow::dropEvent (QDropEvent * event)
1445{
1446  QStringList list;
1447
1448  if (event->mimeData()->hasText())
1449    {
1450      list = event->mimeData()->text().trimmed().split('\n');
1451    }
1452  else if (event->mimeData()->hasUrls())
1453    {
1454      foreach (QUrl url, event->mimeData()->urls())
1455        list.append(url.toLocalFile());
1456    }
1457
1458  foreach (QString entry, list)
1459    {
1460      QString key = entry.trimmed();
1461
1462      if (!key.isEmpty())
1463        {
1464          const QUrl url (key);
1465
1466          if (url.scheme () == "file")
1467            key = QUrl::fromPercentEncoding (url.path().toUtf8());
1468
1469          dynamic_cast<MyApp*> (QApplication::instance ())->addTorrent (key);
1470        }
1471    }
1472}
1473
1474/***
1475****
1476***/
1477
1478void
1479TrMainWindow::contextMenuEvent (QContextMenuEvent * event)
1480{
1481  QMenu * menu = new QMenu (this);
1482
1483  menu->addAction (ui.action_Properties);
1484  menu->addAction (ui.action_OpenFolder);
1485
1486  QAction * sep = new QAction (menu);
1487  sep->setSeparator (true);
1488  menu->addAction (sep);
1489  menu->addAction (ui.action_Start);
1490  menu->addAction (ui.action_StartNow);
1491  menu->addAction (ui.action_Announce);
1492  QMenu * queueMenu = menu->addMenu (tr ("Queue"));
1493    queueMenu->addAction (ui.action_QueueMoveTop);
1494    queueMenu->addAction (ui.action_QueueMoveUp);
1495    queueMenu->addAction (ui.action_QueueMoveDown);
1496    queueMenu->addAction (ui.action_QueueMoveBottom);
1497  menu->addAction (ui.action_Pause);
1498
1499  sep = new QAction (menu);
1500  sep->setSeparator (true);
1501  menu->addAction (sep);
1502  menu->addAction (ui.action_Verify);
1503  menu->addAction (ui.action_SetLocation);
1504  menu->addAction (ui.action_CopyMagnetToClipboard);
1505
1506  sep = new QAction (menu);
1507  sep->setSeparator (true);
1508  menu->addAction (sep);
1509  menu->addAction (ui.action_Remove);
1510  menu->addAction (ui.action_Delete);
1511
1512  menu->popup (event->globalPos ());
1513}
Note: See TracBrowser for help on using the repository browser.