source: trunk/qt/mainwin.cc @ 13888

Last change on this file since 13888 was 13888, checked in by jordan, 8 years ago

use 'folder' instead of 'directory' in the freespace tooltips

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