2 * This file Copyright (C) Mnemosyne LLC
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.
8 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
10 * $Id: mainwin.cc 11263 2010-09-24 15:22:53Z charles $
18 #include <libtransmission/transmission.h>
19 #include <libtransmission/utils.h>
20 #include <libtransmission/version.h>
25 #include "filterbar.h"
27 #include "formatter.h"
30 #include "make-dialog.h"
33 #include "prefs-dialog.h"
36 #include "session-dialog.h"
38 #include "stats-dialog.h"
39 #include "torrent-delegate.h"
40 #include "torrent-delegate-min.h"
41 #include "torrent-filter.h"
42 #include "torrent-model.h"
43 #include "triconpushbutton.h"
44 #include "ui_mainwin.h"
46 #define PREFS_KEY "prefs-key";
49 TrMainWindow :: getStockIcon( const QString
& name
, int fallback
)
51 QIcon icon
= QIcon::fromTheme( name
);
53 if( icon
.isNull( ) && ( fallback
>= 0 ) )
54 icon
= style()->standardIcon( QStyle::StandardPixmap( fallback
), 0, this );
61 QSize
calculateTextButtonSizeHint( QPushButton
* button
)
63 QStyleOptionButton opt
;
64 opt
.initFrom( button
);
65 QString
s( button
->text( ) );
67 s
= QString::fromLatin1( "XXXX" );
68 QFontMetrics fm
= button
->fontMetrics( );
69 QSize sz
= fm
.size( Qt::TextShowMnemonic
, s
);
70 return button
->style()->sizeFromContents( QStyle::CT_PushButton
, &opt
, sz
, button
).expandedTo( QApplication::globalStrut( ) );
75 TrMainWindow :: TrMainWindow( Session
& session
, Prefs
& prefs
, TorrentModel
& model
, bool minimized
):
76 myLastFullUpdateTime( 0 ),
77 mySessionDialog( new SessionDialog( session
, prefs
, this ) ),
79 myAboutDialog( new AboutDialog( this ) ),
80 myStatsDialog( new StatsDialog( session
, this ) ),
82 myFilterModel( prefs
),
83 myTorrentDelegate( new TorrentDelegate( this ) ),
84 myTorrentDelegateMin( new TorrentDelegateMin( this ) ),
88 mySpeedModeOffIcon( ":/icons/alt-limit-off.png" ),
89 mySpeedModeOnIcon( ":/icons/alt-limit-on.png" ),
92 myNetworkTimer( this )
94 setAcceptDrops( true );
96 QAction
* sep
= new QAction( this );
97 sep
->setSeparator( true );
101 QStyle
* style
= this->style();
103 int i
= style
->pixelMetric( QStyle::PM_SmallIconSize
, 0, this );
104 const QSize
smallIconSize( i
, i
);
107 ui
.action_AddFile
->setIcon( getStockIcon( "list-add", QStyle::SP_DialogOpenButton
) );
108 ui
.action_New
->setIcon( getStockIcon( "document-new", QStyle::SP_DesktopIcon
) );
109 ui
.action_Properties
->setIcon( getStockIcon( "document-properties", QStyle::SP_DesktopIcon
) );
110 ui
.action_OpenFolder
->setIcon( getStockIcon( "folder-open", QStyle::SP_DirOpenIcon
) );
111 ui
.action_Start
->setIcon( getStockIcon( "media-playback-start", QStyle::SP_MediaPlay
) );
112 ui
.action_Announce
->setIcon( getStockIcon( "network-transmit-receive" ) );
113 ui
.action_Pause
->setIcon( getStockIcon( "media-playback-pause", QStyle::SP_MediaPause
) );
114 ui
.action_Remove
->setIcon( getStockIcon( "list-remove", QStyle::SP_TrashIcon
) );
115 ui
.action_Delete
->setIcon( getStockIcon( "edit-delete", QStyle::SP_TrashIcon
) );
116 ui
.action_StartAll
->setIcon( getStockIcon( "media-playback-start", QStyle::SP_MediaPlay
) );
117 ui
.action_PauseAll
->setIcon( getStockIcon( "media-playback-pause", QStyle::SP_MediaPause
) );
118 ui
.action_Quit
->setIcon( getStockIcon( "application-exit" ) );
119 ui
.action_SelectAll
->setIcon( getStockIcon( "edit-select-all" ) );
120 ui
.action_ReverseSortOrder
->setIcon( getStockIcon( "view-sort-ascending", QStyle::SP_ArrowDown
) );
121 ui
.action_Preferences
->setIcon( getStockIcon( "preferences-system" ) );
122 ui
.action_Contents
->setIcon( getStockIcon( "help-contents", QStyle::SP_DialogHelpButton
) );
123 ui
.action_About
->setIcon( getStockIcon( "help-about" ) );
126 connect( ui
.action_Toolbar
, SIGNAL(toggled(bool)), this, SLOT(setToolbarVisible(bool)));
127 connect( ui
.action_Filterbar
, SIGNAL(toggled(bool)), this, SLOT(setFilterbarVisible(bool)));
128 connect( ui
.action_Statusbar
, SIGNAL(toggled(bool)), this, SLOT(setStatusbarVisible(bool)));
129 connect( ui
.action_CompactView
, SIGNAL(toggled(bool)), this, SLOT(setCompactView(bool)));
130 connect( ui
.action_SortByActivity
, SIGNAL(toggled(bool)), this, SLOT(onSortByActivityToggled(bool)));
131 connect( ui
.action_SortByAge
, SIGNAL(toggled(bool)), this, SLOT(onSortByAgeToggled(bool)));
132 connect( ui
.action_SortByETA
, SIGNAL(toggled(bool)), this, SLOT(onSortByETAToggled(bool)));
133 connect( ui
.action_SortByName
, SIGNAL(toggled(bool)), this, SLOT(onSortByNameToggled(bool)));
134 connect( ui
.action_SortByProgress
, SIGNAL(toggled(bool)), this, SLOT(onSortByProgressToggled(bool)));
135 connect( ui
.action_SortByRatio
, SIGNAL(toggled(bool)), this, SLOT(onSortByRatioToggled(bool)));
136 connect( ui
.action_SortBySize
, SIGNAL(toggled(bool)), this, SLOT(onSortBySizeToggled(bool)));
137 connect( ui
.action_SortByState
, SIGNAL(toggled(bool)), this, SLOT(onSortByStateToggled(bool)));
138 connect( ui
.action_ReverseSortOrder
, SIGNAL(toggled(bool)), this, SLOT(setSortAscendingPref(bool)));
139 connect( ui
.action_Start
, SIGNAL(triggered()), this, SLOT(startSelected()));
140 connect( ui
.action_Pause
, SIGNAL(triggered()), this, SLOT(pauseSelected()));
141 connect( ui
.action_Remove
, SIGNAL(triggered()), this, SLOT(removeSelected()));
142 connect( ui
.action_Delete
, SIGNAL(triggered()), this, SLOT(deleteSelected()));
143 connect( ui
.action_Verify
, SIGNAL(triggered()), this, SLOT(verifySelected()) );
144 connect( ui
.action_Announce
, SIGNAL(triggered()), this, SLOT(reannounceSelected()) );
145 connect( ui
.action_StartAll
, SIGNAL(triggered()), this, SLOT(startAll()));
146 connect( ui
.action_PauseAll
, SIGNAL(triggered()), this, SLOT(pauseAll()));
147 connect( ui
.action_AddFile
, SIGNAL(triggered()), this, SLOT(openTorrent()));
148 connect( ui
.action_AddURL
, SIGNAL(triggered()), this, SLOT(openURL()));
149 connect( ui
.action_New
, SIGNAL(triggered()), this, SLOT(newTorrent()));
150 connect( ui
.action_Preferences
, SIGNAL(triggered()), this, SLOT(openPreferences()));
151 connect( ui
.action_Statistics
, SIGNAL(triggered()), myStatsDialog
, SLOT(show()));
152 connect( ui
.action_Donate
, SIGNAL(triggered()), this, SLOT(openDonate()));
153 connect( ui
.action_About
, SIGNAL(triggered()), myAboutDialog
, SLOT(show()));
154 connect( ui
.action_Contents
, SIGNAL(triggered()), this, SLOT(openHelp()));
155 connect( ui
.action_OpenFolder
, SIGNAL(triggered()), this, SLOT(openFolder()));
156 connect( ui
.action_CopyMagnetToClipboard
, SIGNAL(triggered()), this, SLOT(copyMagnetLinkToClipboard()));
157 connect( ui
.action_SetLocation
, SIGNAL(triggered()), this, SLOT(setLocation()));
158 connect( ui
.action_Properties
, SIGNAL(triggered()), this, SLOT(openProperties()));
159 connect( ui
.action_SessionDialog
, SIGNAL(triggered()), mySessionDialog
, SLOT(show()));
160 connect( ui
.listView
, SIGNAL(activated(const QModelIndex
&)), ui
.action_Properties
, SLOT(trigger()));
162 QAction
* sep2
= new QAction( this );
163 sep2
->setSeparator( true );
164 QAction
* sep3
= new QAction( this );
165 sep3
->setSeparator( true );
168 QList
<QAction
*> actions
;
169 actions
<< ui
.action_Properties
170 << ui
.action_OpenFolder
173 << ui
.action_Announce
175 << ui
.action_CopyMagnetToClipboard
178 << ui
.action_SetLocation
182 addActions( actions
);
183 setContextMenuPolicy( Qt::ActionsContextMenu
);
186 connect( ui
.action_SelectAll
, SIGNAL(triggered()), ui
.listView
, SLOT(selectAll()));
187 connect( ui
.action_DeselectAll
, SIGNAL(triggered()), ui
.listView
, SLOT(clearSelection()));
189 connect( &myFilterModel
, SIGNAL(rowsInserted(const QModelIndex
&,int,int)), this, SLOT(refreshVisibleCount()));
190 connect( &myFilterModel
, SIGNAL(rowsRemoved(const QModelIndex
&,int,int)), this, SLOT(refreshVisibleCount()));
191 connect( &myFilterModel
, SIGNAL(rowsInserted(const QModelIndex
&,int,int)), this, SLOT(refreshActionSensitivity()));
192 connect( &myFilterModel
, SIGNAL(rowsRemoved(const QModelIndex
&,int,int)), this, SLOT(refreshActionSensitivity()));
194 connect( ui
.action_Quit
, SIGNAL(triggered()), QCoreApplication::instance(), SLOT(quit()) );
197 myFilterModel
.setSourceModel( &myModel
);
198 connect( &myModel
, SIGNAL(modelReset()), this, SLOT(onModelReset()));
199 connect( &myModel
, SIGNAL(rowsRemoved(const QModelIndex
&,int,int)), this, SLOT(onModelReset()));
200 connect( &myModel
, SIGNAL(rowsInserted(const QModelIndex
&,int,int)), this, SLOT(onModelReset()));
201 connect( &myModel
, SIGNAL(dataChanged(const QModelIndex
&,const QModelIndex
&)), this, SLOT(refreshTrayIcon()));
203 ui
.listView
->setModel( &myFilterModel
);
204 connect( ui
.listView
->selectionModel(), SIGNAL(selectionChanged(const QItemSelection
&,const QItemSelection
&)), this, SLOT(refreshActionSensitivity()));
206 QActionGroup
* actionGroup
= new QActionGroup( this );
207 actionGroup
->addAction( ui
.action_SortByActivity
);
208 actionGroup
->addAction( ui
.action_SortByAge
);
209 actionGroup
->addAction( ui
.action_SortByETA
);
210 actionGroup
->addAction( ui
.action_SortByName
);
211 actionGroup
->addAction( ui
.action_SortByProgress
);
212 actionGroup
->addAction( ui
.action_SortByRatio
);
213 actionGroup
->addAction( ui
.action_SortBySize
);
214 actionGroup
->addAction( ui
.action_SortByState
);
216 QMenu
* menu
= new QMenu( );
217 menu
->addAction( ui
.action_AddFile
);
218 menu
->addAction( ui
.action_AddURL
);
219 menu
->addSeparator( );
220 menu
->addAction( ui
.action_ShowMainWindow
);
221 menu
->addAction( ui
.action_ShowMessageLog
);
222 menu
->addAction( ui
.action_About
);
223 menu
->addSeparator( );
224 menu
->addAction( ui
.action_StartAll
);
225 menu
->addAction( ui
.action_PauseAll
);
226 menu
->addSeparator( );
227 menu
->addAction( ui
.action_Quit
);
228 myTrayIcon
.setContextMenu( menu
);
229 myTrayIcon
.setIcon( QApplication::windowIcon( ) );
231 connect( &myPrefs
, SIGNAL(changed(int)), this, SLOT(refreshPref(int)) );
232 connect( ui
.action_ShowMainWindow
, SIGNAL(toggled(bool)), this, SLOT(toggleWindows(bool)));
233 connect( &myTrayIcon
, SIGNAL(activated(QSystemTrayIcon::ActivationReason
)),
234 this, SLOT(trayActivated(QSystemTrayIcon::ActivationReason
)));
236 ui
.action_ShowMainWindow
->setChecked( !minimized
);
237 ui
.action_TrayIcon
->setChecked( minimized
|| prefs
.getBool( Prefs::SHOW_TRAY_ICON
) );
239 ui
.verticalLayout
->addWidget( createStatusBar( ) );
240 ui
.verticalLayout
->insertWidget( 0, myFilterBar
= new FilterBar( myPrefs
, myModel
, myFilterModel
) );
243 initKeys
<< Prefs :: MAIN_WINDOW_X
244 << Prefs :: SHOW_TRAY_ICON
245 << Prefs :: SORT_REVERSED
246 << Prefs :: SORT_MODE
247 << Prefs :: FILTERBAR
248 << Prefs :: STATUSBAR
249 << Prefs :: STATUSBAR_STATS
251 << Prefs :: ALT_SPEED_LIMIT_ENABLED
252 << Prefs :: COMPACT_VIEW
254 << Prefs :: DSPEED_ENABLED
256 << Prefs :: USPEED_ENABLED
258 << Prefs :: RATIO_ENABLED
;
259 foreach( int key
, initKeys
)
262 connect( &mySession
, SIGNAL(sourceChanged()), this, SLOT(onSessionSourceChanged()) );
263 connect( &mySession
, SIGNAL(statsUpdated()), this, SLOT(refreshStatusBar()) );
264 connect( &mySession
, SIGNAL(dataReadProgress()), this, SLOT(dataReadProgress()) );
265 connect( &mySession
, SIGNAL(dataSendProgress()), this, SLOT(dataSendProgress()) );
266 connect( &mySession
, SIGNAL(httpAuthenticationRequired()), this, SLOT(wrongAuthentication()) );
268 if( mySession
.isServer( ) )
269 myNetworkLabel
->hide( );
271 connect( &myNetworkTimer
, SIGNAL(timeout()), this, SLOT(onNetworkTimer()));
272 myNetworkTimer
.start( 1000 );
275 refreshActionSensitivity( );
279 refreshVisibleCount( );
282 TrMainWindow :: ~TrMainWindow( )
291 TrMainWindow :: closeEvent( QCloseEvent
* event
)
293 // if they're using a tray icon, close to the tray
294 // instead of exiting
295 if( !myPrefs
.getBool( Prefs :: SHOW_TRAY_ICON
) )
298 toggleWindows( false );
308 TrMainWindow :: onSessionSourceChanged( )
314 TrMainWindow :: onModelReset( )
317 refreshVisibleCount( );
318 refreshActionSensitivity( );
327 #define PREF_VARIANTS_KEY "pref-variants-list"
330 TrMainWindow :: onSetPrefs( )
332 const QVariantList p
= sender()->property( PREF_VARIANTS_KEY
).toList( );
333 assert( ( p
.size( ) % 2 ) == 0 );
334 for( int i
=0, n
=p
.size(); i
<n
; i
+=2 )
335 myPrefs
.set( p
[i
].toInt(), p
[i
+1] );
339 TrMainWindow :: onSetPrefs( bool isChecked
)
345 #define SHOW_KEY "show-mode"
348 TrMainWindow :: createStatusBar( )
355 const int i
= style( )->pixelMetric( QStyle::PM_SmallIconSize
, 0, this );
356 const QSize
smallIconSize( i
, i
);
358 QWidget
* top
= myStatusBar
= new QWidget
;
359 h
= new QHBoxLayout( top
);
360 h
->setContentsMargins( HIG::PAD_SMALL
, HIG::PAD_SMALL
, HIG::PAD_SMALL
, HIG::PAD_SMALL
);
361 h
->setSpacing( HIG::PAD_SMALL
);
363 p
= myOptionsButton
= new TrIconPushButton( this );
364 p
->setIcon( QIcon( ":/icons/utilities.png" ) );
365 p
->setIconSize( QPixmap( ":/icons/utilities.png" ).size() );
367 p
->setMenu( createOptionsMenu( ) );
370 p
= myAltSpeedButton
= new QPushButton( this );
371 p
->setIcon( myPrefs
.get
<bool>(Prefs::ALT_SPEED_LIMIT_ENABLED
) ? mySpeedModeOnIcon
: mySpeedModeOffIcon
);
372 p
->setIconSize( QPixmap( ":/icons/alt-limit-on.png" ).size() );
373 p
->setCheckable( true );
374 p
->setFixedWidth( p
->height() );
377 connect( p
, SIGNAL(clicked()), this, SLOT(toggleSpeedMode()));
379 l
= myNetworkLabel
= new QLabel
;
384 l
= myVisibleCountLabel
= new QLabel( this );
389 a
= new QActionGroup( this );
390 a
->addAction( ui
.action_TotalRatio
);
391 a
->addAction( ui
.action_TotalTransfer
);
392 a
->addAction( ui
.action_SessionRatio
);
393 a
->addAction( ui
.action_SessionTransfer
);
395 m
->addAction( ui
.action_TotalRatio
);
396 m
->addAction( ui
.action_TotalTransfer
);
397 m
->addAction( ui
.action_SessionRatio
);
398 m
->addAction( ui
.action_SessionTransfer
);
399 connect( ui
.action_TotalRatio
, SIGNAL(triggered()), this, SLOT(showTotalRatio()));
400 connect( ui
.action_TotalTransfer
, SIGNAL(triggered()), this, SLOT(showTotalTransfer()));
401 connect( ui
.action_SessionRatio
, SIGNAL(triggered()), this, SLOT(showSessionRatio()));
402 connect( ui
.action_SessionTransfer
, SIGNAL(triggered()), this, SLOT(showSessionTransfer()));
403 p
= myStatsModeButton
= new TrIconPushButton( this );
404 p
->setIcon( QIcon( ":/icons/ratio.png" ) );
405 p
->setIconSize( QPixmap( ":/icons/ratio.png" ).size() );
409 l
= myStatsLabel
= new QLabel( this );
414 l
= myDownloadSpeedLabel
= new QLabel( this );
415 const int minimumSpeedWidth
= l
->fontMetrics().width( Formatter::speedToString(Speed::fromKBps(999.99)));
416 l
->setMinimumWidth( minimumSpeedWidth
);
417 l
->setAlignment( Qt::AlignRight
|Qt::AlignVCenter
);
419 l
= new QLabel( this );
420 l
->setPixmap( getStockIcon( "go-down", QStyle::SP_ArrowDown
).pixmap( smallIconSize
) );
425 l
= myUploadSpeedLabel
= new QLabel
;
426 l
->setMinimumWidth( minimumSpeedWidth
);
427 l
->setAlignment( Qt::AlignRight
|Qt::AlignVCenter
);
430 l
->setPixmap( getStockIcon( "go-up", QStyle::SP_ArrowUp
).pixmap( smallIconSize
) );
437 TrMainWindow :: createOptionsMenu( )
444 QList
<int> stockSpeeds
;
445 stockSpeeds
<< 5 << 10 << 20 << 30 << 40 << 50 << 75 << 100 << 150 << 200 << 250 << 500 << 750;
446 QList
<double> stockRatios
;
447 stockRatios
<< 0.25 << 0.50 << 0.75 << 1 << 1.5 << 2 << 3;
450 sub
= menu
->addMenu( tr( "Limit Download Speed" ) );
451 int currentVal
= myPrefs
.get
<int>( Prefs::DSPEED
);
452 g
= new QActionGroup( this );
453 a
= myDlimitOffAction
= sub
->addAction( tr( "Unlimited" ) );
454 a
->setCheckable( true );
455 a
->setProperty( PREF_VARIANTS_KEY
, QVariantList() << Prefs::DSPEED_ENABLED
<< false );
457 connect( a
, SIGNAL(triggered(bool)), this, SLOT(onSetPrefs(bool)) );
458 a
= myDlimitOnAction
= sub
->addAction( tr( "Limited at %1" ).arg( Formatter::speedToString( Speed::fromKBps( currentVal
) ) ) );
459 a
->setCheckable( true );
460 a
->setProperty( PREF_VARIANTS_KEY
, QVariantList() << Prefs::DSPEED
<< currentVal
<< Prefs::DSPEED_ENABLED
<< true );
462 connect( a
, SIGNAL(triggered(bool)), this, SLOT(onSetPrefs(bool)) );
463 sub
->addSeparator( );
464 foreach( int i
, stockSpeeds
) {
465 a
= sub
->addAction( Formatter::speedToString( Speed::fromKBps( i
) ) );
466 a
->setProperty( PREF_VARIANTS_KEY
, QVariantList() << Prefs::DSPEED
<< i
<< Prefs::DSPEED_ENABLED
<< true );
467 connect( a
, SIGNAL(triggered(bool)), this, SLOT(onSetPrefs()));
470 sub
= menu
->addMenu( tr( "Limit Upload Speed" ) );
471 currentVal
= myPrefs
.get
<int>( Prefs::USPEED
);
472 g
= new QActionGroup( this );
473 a
= myUlimitOffAction
= sub
->addAction( tr( "Unlimited" ) );
474 a
->setCheckable( true );
475 a
->setProperty( PREF_VARIANTS_KEY
, QVariantList() << Prefs::USPEED_ENABLED
<< false );
477 connect( a
, SIGNAL(triggered(bool)), this, SLOT(onSetPrefs(bool)) );
478 a
= myUlimitOnAction
= sub
->addAction( tr( "Limited at %1" ).arg( Formatter::speedToString( Speed::fromKBps( currentVal
) ) ) );
479 a
->setCheckable( true );
480 a
->setProperty( PREF_VARIANTS_KEY
, QVariantList() << Prefs::USPEED
<< currentVal
<< Prefs::USPEED_ENABLED
<< true );
482 connect( a
, SIGNAL(triggered(bool)), this, SLOT(onSetPrefs(bool)) );
483 sub
->addSeparator( );
484 foreach( int i
, stockSpeeds
) {
485 a
= sub
->addAction( Formatter::speedToString( Speed::fromKBps( i
) ) );
486 a
->setProperty( PREF_VARIANTS_KEY
, QVariantList() << Prefs::USPEED
<< i
<< Prefs::USPEED_ENABLED
<< true );
487 connect( a
, SIGNAL(triggered(bool)), this, SLOT(onSetPrefs()));
490 menu
->addSeparator( );
491 sub
= menu
->addMenu( tr( "Stop Seeding at Ratio" ) );
493 double d
= myPrefs
.get
<double>( Prefs::RATIO
);
494 g
= new QActionGroup( this );
495 a
= myRatioOffAction
= sub
->addAction( tr( "Seed Forever" ) );
496 a
->setCheckable( true );
497 a
->setProperty( PREF_VARIANTS_KEY
, QVariantList() << Prefs::RATIO_ENABLED
<< false );
499 connect( a
, SIGNAL(triggered(bool)), this, SLOT(onSetPrefs(bool)) );
500 a
= myRatioOnAction
= sub
->addAction( tr( "Stop at Ratio (%1)" ).arg( Formatter::ratioToString( d
) ) );
501 a
->setCheckable( true );
502 a
->setProperty( PREF_VARIANTS_KEY
, QVariantList() << Prefs::RATIO
<< d
<< Prefs::RATIO_ENABLED
<< true );
504 connect( a
, SIGNAL(triggered(bool)), this, SLOT(onSetPrefs(bool)) );
505 sub
->addSeparator( );
506 foreach( double i
, stockRatios
) {
507 a
= sub
->addAction( Formatter::ratioToString( i
) );
508 a
->setProperty( PREF_VARIANTS_KEY
, QVariantList() << Prefs::RATIO
<< i
<< Prefs::RATIO_ENABLED
<< true );
509 connect( a
, SIGNAL(triggered(bool)), this, SLOT(onSetPrefs()));
520 TrMainWindow :: setSortPref( int i
)
522 myPrefs
.set( Prefs::SORT_MODE
, SortMode( i
) );
524 void TrMainWindow :: onSortByActivityToggled ( bool b
) { if( b
) setSortPref( SortMode::SORT_BY_ACTIVITY
); }
525 void TrMainWindow :: onSortByAgeToggled ( bool b
) { if( b
) setSortPref( SortMode::SORT_BY_AGE
); }
526 void TrMainWindow :: onSortByETAToggled ( bool b
) { if( b
) setSortPref( SortMode::SORT_BY_ETA
); }
527 void TrMainWindow :: onSortByNameToggled ( bool b
) { if( b
) setSortPref( SortMode::SORT_BY_NAME
); }
528 void TrMainWindow :: onSortByProgressToggled ( bool b
) { if( b
) setSortPref( SortMode::SORT_BY_PROGRESS
); }
529 void TrMainWindow :: onSortByRatioToggled ( bool b
) { if( b
) setSortPref( SortMode::SORT_BY_RATIO
); }
530 void TrMainWindow :: onSortBySizeToggled ( bool b
) { if( b
) setSortPref( SortMode::SORT_BY_SIZE
); }
531 void TrMainWindow :: onSortByStateToggled ( bool b
) { if( b
) setSortPref( SortMode::SORT_BY_STATE
); }
534 TrMainWindow :: setSortAscendingPref( bool b
)
536 myPrefs
.set( Prefs::SORT_REVERSED
, b
);
544 TrMainWindow :: onPrefsDestroyed( )
550 TrMainWindow :: openPreferences( )
552 if( myPrefsDialog
== 0 ) {
553 myPrefsDialog
= new PrefsDialog( mySession
, myPrefs
, this );
554 connect( myPrefsDialog
, SIGNAL(destroyed(QObject
*)), this, SLOT(onPrefsDestroyed()));
557 myPrefsDialog
->show( );
561 TrMainWindow :: onDetailsDestroyed( )
567 TrMainWindow :: openProperties( )
569 if( myDetailsDialog
== 0 ) {
570 myDetailsDialog
= new Details( mySession
, myPrefs
, myModel
, this );
571 connect( myDetailsDialog
, SIGNAL(destroyed(QObject
*)), this, SLOT(onDetailsDestroyed()));
574 myDetailsDialog
->setIds( getSelectedTorrents( ) );
575 myDetailsDialog
->show( );
579 TrMainWindow :: setLocation( )
581 QDialog
* d
= new RelocateDialog( mySession
, myModel
, getSelectedTorrents(), this );
586 TrMainWindow :: openFolder( )
588 const int torrentId( *getSelectedTorrents().begin() );
589 const Torrent
* tor( myModel
.getTorrentFromId( torrentId
) );
590 const QString
path( tor
->getPath( ) );
591 QDesktopServices :: openUrl( QUrl::fromLocalFile( path
) );
595 TrMainWindow :: copyMagnetLinkToClipboard( )
597 const int id( *getSelectedTorrents().begin() );
598 mySession
.copyMagnetLinkToClipboard( id
);
602 TrMainWindow :: openDonate( )
604 QDesktopServices :: openUrl( QUrl( "http://www.transmissionbt.com/donate.php" ) );
608 TrMainWindow :: openHelp( )
610 const char * fmt
= "http://www.transmissionbt.com/help/gtk/%d.%dx";
612 sscanf( SHORT_VERSION_STRING
, "%d.%d", &major
, &minor
);
614 tr_snprintf( url
, sizeof( url
), fmt
, major
, minor
/10 );
615 QDesktopServices :: openUrl( QUrl( url
) );
619 TrMainWindow :: refreshTitle( )
621 QString
title( "Transmission" );
622 const QUrl
url( mySession
.getRemoteUrl( ) );
624 title
+= tr( " - %1:%2" ).arg( url
.host() ).arg( url
.port() );
625 setWindowTitle( title
);
629 TrMainWindow :: refreshVisibleCount( )
631 const int visibleCount( myFilterModel
.rowCount( ) );
632 const int totalCount( visibleCount
+ myFilterModel
.hiddenRowCount( ) );
634 if( visibleCount
== totalCount
)
635 str
= tr( "%Ln Torrent(s)", 0, totalCount
);
637 str
= tr( "%L1 of %Ln Torrent(s)", 0, totalCount
).arg( visibleCount
);
638 myVisibleCountLabel
->setText( str
);
639 myVisibleCountLabel
->setVisible( totalCount
> 0 );
643 TrMainWindow :: refreshTrayIcon( )
646 const QString idle
= tr( "Idle" );
648 foreach( int id
, myModel
.getIds( ) ) {
649 const Torrent
* tor
= myModel
.getTorrentFromId( id
);
650 u
+= tor
->uploadSpeed( );
651 d
+= tor
->downloadSpeed( );
654 myTrayIcon
.setToolTip( tr( "Transmission\nUp: %1\nDown: %2" )
655 .arg( u
.isZero() ? idle
: Formatter::speedToString( u
) )
656 .arg( d
.isZero() ? idle
: Formatter::speedToString( d
) ) );
660 TrMainWindow :: refreshStatusBar( )
662 const Speed
up( myModel
.getUploadSpeed( ) );
663 const Speed
down( myModel
.getDownloadSpeed( ) );
664 myUploadSpeedLabel
->setText( Formatter:: speedToString( up
) );
665 myDownloadSpeedLabel
->setText( Formatter:: speedToString( down
) );
667 myNetworkLabel
->setVisible( !mySession
.isServer( ) );
669 const QString
mode( myPrefs
.getString( Prefs::STATUSBAR_STATS
) );
672 if( mode
== "session-ratio" )
674 str
= tr( "Ratio: %1" ).arg( Formatter:: ratioToString( mySession
.getStats().ratio
) );
676 else if( mode
== "session-transfer" )
678 const tr_session_stats
& stats( mySession
.getStats( ) );
679 str
= tr( "Down: %1, Up: %2" ).arg( Formatter:: sizeToString( stats
.downloadedBytes
) )
680 .arg( Formatter:: sizeToString( stats
.uploadedBytes
) );
682 else if( mode
== "total-transfer" )
684 const tr_session_stats
& stats( mySession
.getCumulativeStats( ) );
685 str
= tr( "Down: %1, Up: %2" ).arg( Formatter:: sizeToString( stats
.downloadedBytes
) )
686 .arg( Formatter:: sizeToString( stats
.uploadedBytes
) );
688 else // default is "total-ratio"
690 str
= tr( "Ratio: %1" ).arg( Formatter:: ratioToString( mySession
.getCumulativeStats().ratio
) );
693 myStatsLabel
->setText( str
);
697 TrMainWindow :: refreshActionSensitivity( )
701 int selectedAndPaused( 0 );
702 int canAnnounce( 0 );
703 const QAbstractItemModel
* model( ui
.listView
->model( ) );
704 const QItemSelectionModel
* selectionModel( ui
.listView
->selectionModel( ) );
705 const int rowCount( model
->rowCount( ) );
707 // count how many torrents are selected, paused, etc
708 for( int row
=0; row
<rowCount
; ++row
) {
709 const QModelIndex
modelIndex( model
->index( row
, 0 ) );
710 assert( model
== modelIndex
.model( ) );
711 const Torrent
* tor( model
->data( modelIndex
, TorrentModel::TorrentRole
).value
<const Torrent
*>( ) );
713 const bool isSelected( selectionModel
->isSelected( modelIndex
) );
714 const bool isPaused( tor
->isPaused( ) );
719 if( isSelected
&& isPaused
)
721 if( tor
->canManualAnnounce( ) )
726 const bool haveSelection( selected
> 0 );
727 ui
.action_Verify
->setEnabled( haveSelection
);
728 ui
.action_Remove
->setEnabled( haveSelection
);
729 ui
.action_Delete
->setEnabled( haveSelection
);
730 ui
.action_Properties
->setEnabled( haveSelection
);
731 ui
.action_DeselectAll
->setEnabled( haveSelection
);
732 ui
.action_SetLocation
->setEnabled( haveSelection
);
734 const bool oneSelection( selected
== 1 );
735 ui
.action_OpenFolder
->setEnabled( oneSelection
&& mySession
.isLocal( ) );
736 ui
.action_CopyMagnetToClipboard
->setEnabled( oneSelection
);
738 ui
.action_SelectAll
->setEnabled( selected
< rowCount
);
739 ui
.action_StartAll
->setEnabled( paused
> 0 );
740 ui
.action_PauseAll
->setEnabled( paused
< rowCount
);
741 ui
.action_Start
->setEnabled( selectedAndPaused
> 0 );
742 ui
.action_Pause
->setEnabled( selectedAndPaused
< selected
);
743 ui
.action_Announce
->setEnabled( selected
> 0 && ( canAnnounce
== selected
) );
745 if( myDetailsDialog
)
746 myDetailsDialog
->setIds( getSelectedTorrents( ) );
754 TrMainWindow :: clearSelection( )
756 ui
.action_DeselectAll
->trigger( );
760 TrMainWindow :: getSelectedTorrents( ) const
764 foreach( QModelIndex index
, ui
.listView
->selectionModel( )->selectedRows( ) )
766 const Torrent
* tor( index
.data( TorrentModel::TorrentRole
).value
<const Torrent
*>( ) );
767 ids
.insert( tor
->id( ) );
774 TrMainWindow :: startSelected( )
776 mySession
.startTorrents( getSelectedTorrents( ) );
779 TrMainWindow :: pauseSelected( )
781 mySession
.pauseTorrents( getSelectedTorrents( ) );
784 TrMainWindow :: startAll( )
786 mySession
.startTorrents( );
789 TrMainWindow :: pauseAll( )
791 mySession
.pauseTorrents( );
794 TrMainWindow :: removeSelected( )
796 removeTorrents( false );
799 TrMainWindow :: deleteSelected( )
801 removeTorrents( true );
804 TrMainWindow :: verifySelected( )
806 mySession
.verifyTorrents( getSelectedTorrents( ) );
809 TrMainWindow :: reannounceSelected( )
811 mySession
.reannounceTorrents( getSelectedTorrents( ) );
818 void TrMainWindow :: showTotalRatio ( ) { myPrefs
.set( Prefs::STATUSBAR_STATS
, "total-ratio"); }
819 void TrMainWindow :: showTotalTransfer ( ) { myPrefs
.set( Prefs::STATUSBAR_STATS
, "total-transfer"); }
820 void TrMainWindow :: showSessionRatio ( ) { myPrefs
.set( Prefs::STATUSBAR_STATS
, "session-ratio"); }
821 void TrMainWindow :: showSessionTransfer ( ) { myPrefs
.set( Prefs::STATUSBAR_STATS
, "session-transfer"); }
828 TrMainWindow :: setCompactView( bool visible
)
830 myPrefs
.set( Prefs :: COMPACT_VIEW
, visible
);
833 TrMainWindow :: toggleSpeedMode( )
835 myPrefs
.toggleBool( Prefs :: ALT_SPEED_LIMIT_ENABLED
);
838 TrMainWindow :: setToolbarVisible( bool visible
)
840 myPrefs
.set( Prefs::TOOLBAR
, visible
);
843 TrMainWindow :: setFilterbarVisible( bool visible
)
845 myPrefs
.set( Prefs::FILTERBAR
, visible
);
848 TrMainWindow :: setStatusbarVisible( bool visible
)
850 myPrefs
.set( Prefs::STATUSBAR
, visible
);
858 TrMainWindow :: toggleWindows( bool doShow
)
866 if ( !isVisible( ) ) show( );
867 if ( isMinimized( ) ) showNormal( );
870 QApplication::setActiveWindow( this );
875 TrMainWindow :: trayActivated( QSystemTrayIcon::ActivationReason reason
)
877 if( reason
== QSystemTrayIcon::Trigger
)
879 if( isMinimized ( ) )
880 toggleWindows( true );
882 ui
.action_ShowMainWindow
->toggle( );
888 TrMainWindow :: refreshPref( int key
)
896 case Prefs::STATUSBAR_STATS
:
897 str
= myPrefs
.getString( key
);
898 ui
.action_TotalRatio
->setChecked ( str
== "total-ratio" );
899 ui
.action_TotalTransfer
->setChecked ( str
== "total-transfer" );
900 ui
.action_SessionRatio
->setChecked ( str
== "session-ratio" );
901 ui
.action_SessionTransfer
->setChecked( str
== "session-transfer" );
905 case Prefs::SORT_REVERSED
:
906 ui
.action_ReverseSortOrder
->setChecked( myPrefs
.getBool( key
) );
909 case Prefs::SORT_MODE
:
910 i
= myPrefs
.get
<SortMode
>(key
).mode( );
911 ui
.action_SortByActivity
->setChecked ( i
== SortMode::SORT_BY_ACTIVITY
);
912 ui
.action_SortByAge
->setChecked ( i
== SortMode::SORT_BY_AGE
);
913 ui
.action_SortByETA
->setChecked ( i
== SortMode::SORT_BY_ETA
);
914 ui
.action_SortByName
->setChecked ( i
== SortMode::SORT_BY_NAME
);
915 ui
.action_SortByProgress
->setChecked ( i
== SortMode::SORT_BY_PROGRESS
);
916 ui
.action_SortByRatio
->setChecked ( i
== SortMode::SORT_BY_RATIO
);
917 ui
.action_SortBySize
->setChecked ( i
== SortMode::SORT_BY_SIZE
);
918 ui
.action_SortByState
->setChecked ( i
== SortMode::SORT_BY_STATE
);
921 case Prefs::DSPEED_ENABLED
:
922 (myPrefs
.get
<bool>(key
) ? myDlimitOnAction
: myDlimitOffAction
)->setChecked( true );
926 myDlimitOnAction
->setText( tr( "Limited at %1" ).arg( Formatter::speedToString( Speed::fromKBps( myPrefs
.get
<int>(key
) ) ) ) );
929 case Prefs::USPEED_ENABLED
:
930 (myPrefs
.get
<bool>(key
) ? myUlimitOnAction
: myUlimitOffAction
)->setChecked( true );
934 myUlimitOnAction
->setText( tr( "Limited at %1" ).arg( Formatter::speedToString( Speed::fromKBps( myPrefs
.get
<int>(key
) ) ) ) );
937 case Prefs::RATIO_ENABLED
:
938 (myPrefs
.get
<bool>(key
) ? myRatioOnAction
: myRatioOffAction
)->setChecked( true );
942 myRatioOnAction
->setText( tr( "Stop at Ratio (%1)" ).arg( Formatter::ratioToString( myPrefs
.get
<double>(key
) ) ) );
945 case Prefs::FILTERBAR
:
946 b
= myPrefs
.getBool( key
);
947 myFilterBar
->setVisible( b
);
948 ui
.action_Filterbar
->setChecked( b
);
951 case Prefs::STATUSBAR
:
952 b
= myPrefs
.getBool( key
);
953 myStatusBar
->setVisible( b
);
954 ui
.action_Statusbar
->setChecked( b
);
958 b
= myPrefs
.getBool( key
);
959 ui
.toolBar
->setVisible( b
);
960 ui
.action_Toolbar
->setChecked( b
);
963 case Prefs::SHOW_TRAY_ICON
:
964 b
= myPrefs
.getBool( key
);
965 ui
.action_TrayIcon
->setChecked( b
);
966 myTrayIcon
.setVisible( b
);
970 case Prefs::COMPACT_VIEW
: {
971 QItemSelectionModel
* selectionModel( ui
.listView
->selectionModel( ) );
972 const QItemSelection
selection( selectionModel
->selection( ) );
973 const QModelIndex
currentIndex( selectionModel
->currentIndex( ) );
974 b
= myPrefs
.getBool( key
);
975 ui
.action_CompactView
->setChecked( b
);
976 ui
.listView
->setItemDelegate( b
? myTorrentDelegateMin
: myTorrentDelegate
);
977 selectionModel
->clear( );
978 ui
.listView
->reset( ); // force the rows to resize
979 selectionModel
->select( selection
, QItemSelectionModel::Select
);
980 selectionModel
->setCurrentIndex( currentIndex
, QItemSelectionModel::NoUpdate
);
984 case Prefs::MAIN_WINDOW_X
:
985 case Prefs::MAIN_WINDOW_Y
:
986 case Prefs::MAIN_WINDOW_WIDTH
:
987 case Prefs::MAIN_WINDOW_HEIGHT
:
988 setGeometry( myPrefs
.getInt( Prefs::MAIN_WINDOW_X
),
989 myPrefs
.getInt( Prefs::MAIN_WINDOW_Y
),
990 myPrefs
.getInt( Prefs::MAIN_WINDOW_WIDTH
),
991 myPrefs
.getInt( Prefs::MAIN_WINDOW_HEIGHT
) );
994 case Prefs :: ALT_SPEED_LIMIT_ENABLED
:
995 case Prefs :: ALT_SPEED_LIMIT_UP
:
996 case Prefs :: ALT_SPEED_LIMIT_DOWN
: {
997 b
= myPrefs
.getBool( Prefs :: ALT_SPEED_LIMIT_ENABLED
);
998 myAltSpeedButton
->setChecked( b
);
999 myAltSpeedButton
->setIcon( b
? mySpeedModeOnIcon
: mySpeedModeOffIcon
);
1000 const QString fmt
= b
? tr( "Click to disable Temporary Speed Limits\n(%1 down, %2 up)" )
1001 : tr( "Click to enable Temporary Speed Limits\n(%1 down, %2 up)" );
1002 const Speed d
= Speed::fromKBps( myPrefs
.getInt( Prefs::ALT_SPEED_LIMIT_DOWN
) );
1003 const Speed u
= Speed::fromKBps( myPrefs
.getInt( Prefs::ALT_SPEED_LIMIT_UP
) );
1004 myAltSpeedButton
->setToolTip( fmt
.arg( Formatter::speedToString( d
) )
1005 .arg( Formatter::speedToString( u
) ) );
1019 TrMainWindow :: newTorrent( )
1021 MakeDialog
* dialog
= new MakeDialog( mySession
, this );
1026 TrMainWindow :: openTorrent( )
1028 QFileDialog
* myFileDialog
;
1029 myFileDialog
= new QFileDialog( this,
1030 tr( "Add Torrent" ),
1031 myPrefs
.getString( Prefs::OPEN_DIALOG_FOLDER
),
1032 tr( "Torrent Files (*.torrent);;All Files (*.*)" ) );
1033 myFileDialog
->setFileMode( QFileDialog::ExistingFiles
);
1035 QCheckBox
* button
= new QCheckBox( tr( "Show &options dialog" ) );
1036 button
->setChecked( myPrefs
.getBool( Prefs::OPTIONS_PROMPT
) );
1037 QGridLayout
* layout
= dynamic_cast<QGridLayout
*>(myFileDialog
->layout());
1038 layout
->addWidget( button
, layout
->rowCount( ), 0, 1, -1, Qt::AlignLeft
);
1039 myFileDialogOptionsCheck
= button
;
1041 connect( myFileDialog
, SIGNAL(filesSelected(const QStringList
&)),
1042 this, SLOT(addTorrents(const QStringList
&)));
1044 myFileDialog
->show( );
1048 TrMainWindow :: openURL( )
1055 TrMainWindow :: openURL( QString url
)
1058 const QString key
= QInputDialog::getText( this,
1059 tr( "Add URL or Magnet Link" ),
1060 tr( "Add URL or Magnet Link" ),
1064 if( ok
&& !key
.isEmpty( ) )
1065 mySession
.addTorrent( key
);
1069 TrMainWindow :: addTorrents( const QStringList
& filenames
)
1071 foreach( const QString
& filename
, filenames
)
1072 addTorrent( filename
);
1076 TrMainWindow :: addTorrent( const QString
& filename
)
1078 if( !myFileDialogOptionsCheck
->isChecked( ) ) {
1079 mySession
.addTorrent( filename
);
1080 QApplication :: alert ( this );
1082 Options
* o
= new Options( mySession
, myPrefs
, filename
, this );
1084 QApplication :: alert( o
);
1089 TrMainWindow :: removeTorrents( const bool deleteFiles
)
1092 QMessageBox
msgBox( this );
1093 QString primary_text
, secondary_text
;
1098 foreach( QModelIndex index
, ui
.listView
->selectionModel( )->selectedRows( ) )
1100 const Torrent
* tor( index
.data( TorrentModel::TorrentRole
).value
<const Torrent
*>( ) );
1101 ids
.insert( tor
->id( ) );
1102 if( tor
->connectedPeers( ) )
1104 if( !tor
->isDone( ) )
1114 primary_text
= ( count
== 1 )
1115 ? tr( "Remove torrent?" )
1116 : tr( "Remove %1 torrents?" ).arg( count
);
1120 primary_text
= ( count
== 1 )
1121 ? tr( "Delete this torrent's downloaded files?" )
1122 : tr( "Delete these %1 torrents' downloaded files?" ).arg( count
);
1125 if( !incomplete
&& !connected
)
1127 secondary_text
= ( count
== 1 )
1128 ? tr( "Once removed, continuing the transfer will require the torrent file or magnet link." )
1129 : tr( "Once removed, continuing the transfers will require the torrent files or magnet links." );
1131 else if( count
== incomplete
)
1133 secondary_text
= ( count
== 1 )
1134 ? tr( "This torrent has not finished downloading." )
1135 : tr( "These torrents have not finished downloading." );
1137 else if( count
== connected
)
1139 secondary_text
= ( count
== 1 )
1140 ? tr( "This torrent is connected to peers." )
1141 : tr( "These torrents are connected to peers." );
1147 secondary_text
= ( connected
== 1 )
1148 ? tr( "One of these torrents is connected to peers." )
1149 : tr( "Some of these torrents are connected to peers." );
1152 if( connected
&& incomplete
)
1154 secondary_text
+= "\n";
1159 secondary_text
+= ( incomplete
== 1 )
1160 ? tr( "One of these torrents has not finished downloading." )
1161 : tr( "Some of these torrents have not finished downloading." );
1165 msgBox
.setWindowTitle( QString(" ") );
1166 msgBox
.setText( QString( "<big><b>%1</big></b>" ).arg( primary_text
) );
1167 msgBox
.setInformativeText( secondary_text
);
1168 msgBox
.setStandardButtons( QMessageBox::Ok
| QMessageBox::Cancel
);
1169 msgBox
.setDefaultButton( QMessageBox::Cancel
);
1170 msgBox
.setIcon( QMessageBox::Question
);
1171 /* hack needed to keep the dialog from being too narrow */
1172 QGridLayout
* layout
= (QGridLayout
*)msgBox
.layout();
1173 QSpacerItem
* spacer
= new QSpacerItem( 450, 0, QSizePolicy::Minimum
, QSizePolicy::Expanding
);
1174 layout
->addItem( spacer
, layout
->rowCount(), 0, 1, layout
->columnCount() );
1176 if( msgBox
.exec() == QMessageBox::Ok
)
1178 ui
.listView
->selectionModel()->clear();
1179 mySession
.removeTorrents( ids
, deleteFiles
);
1188 TrMainWindow :: updateNetworkIcon( )
1190 const time_t now
= time( NULL
);
1191 const int period
= 3;
1192 const bool isSending
= now
- myLastSendTime
<= period
;
1193 const bool isReading
= now
- myLastReadTime
<= period
;
1196 if( isSending
&& isReading
)
1197 key
= "network-transmit-receive";
1198 else if( isSending
)
1199 key
= "network-transmit";
1200 else if( isReading
)
1201 key
= "network-receive";
1203 key
= "network-idle";
1205 QIcon icon
= getStockIcon( key
, QStyle::SP_DriveNetIcon
);
1206 QPixmap pixmap
= icon
.pixmap ( 16, 16 );
1207 myNetworkLabel
->setPixmap( pixmap
);
1208 myNetworkLabel
->setToolTip( isSending
|| isReading
1209 ? tr( "Transmission server is responding" )
1210 : tr( "Last response from server was %1 ago" ).arg( Formatter::timeToString( now
-std::max(myLastReadTime
,myLastSendTime
))));
1214 TrMainWindow :: onNetworkTimer( )
1216 updateNetworkIcon( );
1220 TrMainWindow :: dataReadProgress( )
1222 myLastReadTime
= time( NULL
);
1223 updateNetworkIcon( );
1227 TrMainWindow :: dataSendProgress( )
1229 myLastSendTime
= time( NULL
);
1230 updateNetworkIcon( );
1234 TrMainWindow :: wrongAuthentication( )
1237 mySessionDialog
->show( );
1245 TrMainWindow :: dragEnterEvent( QDragEnterEvent
* event
)
1247 const QMimeData
* mime
= event
->mimeData( );
1249 if( mime
->hasFormat("application/x-bittorrent")
1250 || mime
->text().trimmed().endsWith(".torrent", Qt::CaseInsensitive
) )
1251 event
->acceptProposedAction();
1255 TrMainWindow :: dropEvent( QDropEvent
* event
)
1257 QString key
= event
->mimeData()->text().trimmed();
1259 const QUrl
url( key
);
1260 if( url
.scheme() == "file" )
1261 key
= QUrl::fromPercentEncoding( url
.path().toUtf8( ) );
1263 dynamic_cast<MyApp
*>(QApplication::instance())->addTorrent( key
);