source: trunk/qt/mainwin.cc @ 13927

Last change on this file since 13927 was 13927, checked in by jordan, 10 years ago

in the Qt and GTK+ clients, move the torrent count indicator from the statusbar to the filterbar

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