source: trunk/qt/mainwin.cc @ 14389

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

Move status bar creation to mainwin.ui

Among other things,

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