Transmission 2.33
[tomato.git] / release / src / router / transmission / qt / mainwin.cc
blobc4c13bfed9e32d19d6959184bee33357fe97688d
1 /*
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 12557 2011-07-19 21:19:18Z jordan $
13 #include <cassert>
14 #include <iostream>
16 #include <QtGui>
18 #include <libtransmission/transmission.h>
19 #include <libtransmission/utils.h>
20 #include <libtransmission/version.h>
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"
47 #define PREFS_KEY "prefs-key";
49 QIcon
50 TrMainWindow :: getStockIcon( const QString& name, int fallback )
52 QIcon icon = QIcon::fromTheme( name );
54 if( icon.isNull( ) && ( fallback >= 0 ) )
55 icon = style()->standardIcon( QStyle::StandardPixmap( fallback ), 0, this );
57 return icon;
60 namespace
62 QSize calculateTextButtonSizeHint( QPushButton * button )
64 QStyleOptionButton opt;
65 opt.initFrom( button );
66 QString s( button->text( ) );
67 if( s.isEmpty( ) )
68 s = QString::fromLatin1( "XXXX" );
69 QFontMetrics fm = button->fontMetrics( );
70 QSize sz = fm.size( Qt::TextShowMnemonic, s );
71 return button->style()->sizeFromContents( QStyle::CT_PushButton, &opt, sz, button ).expandedTo( QApplication::globalStrut( ) );
76 TrMainWindow :: TrMainWindow( Session& session, Prefs& prefs, TorrentModel& model, bool minimized ):
77 myLastFullUpdateTime( 0 ),
78 mySessionDialog( new SessionDialog( session, prefs, this ) ),
79 myPrefsDialog( 0 ),
80 myAboutDialog( new AboutDialog( this ) ),
81 myStatsDialog( new StatsDialog( session, this ) ),
82 myDetailsDialog( 0 ),
83 myFilterModel( prefs ),
84 myTorrentDelegate( new TorrentDelegate( this ) ),
85 myTorrentDelegateMin( new TorrentDelegateMin( this ) ),
86 mySession( session ),
87 myPrefs( prefs ),
88 myModel( model ),
89 mySpeedModeOffIcon( ":/icons/alt-limit-off.png" ),
90 mySpeedModeOnIcon( ":/icons/alt-limit-on.png" ),
91 myLastSendTime( 0 ),
92 myLastReadTime( 0 ),
93 myNetworkTimer( this ),
94 myRefreshTrayIconTimer( this ),
95 myRefreshActionSensitivityTimer( this )
97 setAcceptDrops( true );
99 QAction * sep = new QAction( this );
100 sep->setSeparator( true );
102 ui.setupUi( this );
104 QStyle * style = this->style();
106 int i = style->pixelMetric( QStyle::PM_SmallIconSize, 0, this );
107 const QSize smallIconSize( i, i );
109 // icons
110 ui.action_OpenFile->setIcon( getStockIcon( "folder-open", QStyle::SP_DialogOpenButton ) );
111 ui.action_New->setIcon( getStockIcon( "document-new", QStyle::SP_DesktopIcon ) );
112 ui.action_Properties->setIcon( getStockIcon( "document-properties", QStyle::SP_DesktopIcon ) );
113 ui.action_OpenFolder->setIcon( getStockIcon( "folder-open", QStyle::SP_DirOpenIcon ) );
114 ui.action_Start->setIcon( getStockIcon( "media-playback-start", QStyle::SP_MediaPlay ) );
115 ui.action_Announce->setIcon( getStockIcon( "network-transmit-receive" ) );
116 ui.action_Pause->setIcon( getStockIcon( "media-playback-pause", QStyle::SP_MediaPause ) );
117 ui.action_Remove->setIcon( getStockIcon( "list-remove", QStyle::SP_TrashIcon ) );
118 ui.action_Delete->setIcon( getStockIcon( "edit-delete", QStyle::SP_TrashIcon ) );
119 ui.action_StartAll->setIcon( getStockIcon( "media-playback-start", QStyle::SP_MediaPlay ) );
120 ui.action_PauseAll->setIcon( getStockIcon( "media-playback-pause", QStyle::SP_MediaPause ) );
121 ui.action_Quit->setIcon( getStockIcon( "application-exit" ) );
122 ui.action_SelectAll->setIcon( getStockIcon( "edit-select-all" ) );
123 ui.action_ReverseSortOrder->setIcon( getStockIcon( "view-sort-ascending", QStyle::SP_ArrowDown ) );
124 ui.action_Preferences->setIcon( getStockIcon( "preferences-system" ) );
125 ui.action_Contents->setIcon( getStockIcon( "help-contents", QStyle::SP_DialogHelpButton ) );
126 ui.action_About->setIcon( getStockIcon( "help-about" ) );
128 // ui signals
129 connect( ui.action_Toolbar, SIGNAL(toggled(bool)), this, SLOT(setToolbarVisible(bool)));
130 connect( ui.action_Filterbar, SIGNAL(toggled(bool)), this, SLOT(setFilterbarVisible(bool)));
131 connect( ui.action_Statusbar, SIGNAL(toggled(bool)), this, SLOT(setStatusbarVisible(bool)));
132 connect( ui.action_CompactView, SIGNAL(toggled(bool)), this, SLOT(setCompactView(bool)));
133 connect( ui.action_SortByActivity, SIGNAL(toggled(bool)), this, SLOT(onSortByActivityToggled(bool)));
134 connect( ui.action_SortByAge, SIGNAL(toggled(bool)), this, SLOT(onSortByAgeToggled(bool)));
135 connect( ui.action_SortByETA, SIGNAL(toggled(bool)), this, SLOT(onSortByETAToggled(bool)));
136 connect( ui.action_SortByName, SIGNAL(toggled(bool)), this, SLOT(onSortByNameToggled(bool)));
137 connect( ui.action_SortByProgress, SIGNAL(toggled(bool)), this, SLOT(onSortByProgressToggled(bool)));
138 connect( ui.action_SortByRatio, SIGNAL(toggled(bool)), this, SLOT(onSortByRatioToggled(bool)));
139 connect( ui.action_SortBySize, SIGNAL(toggled(bool)), this, SLOT(onSortBySizeToggled(bool)));
140 connect( ui.action_SortByState, SIGNAL(toggled(bool)), this, SLOT(onSortByStateToggled(bool)));
141 connect( ui.action_ReverseSortOrder, SIGNAL(toggled(bool)), this, SLOT(setSortAscendingPref(bool)));
142 connect( ui.action_Start, SIGNAL(triggered()), this, SLOT(startSelected()));
143 connect( ui.action_Pause, SIGNAL(triggered()), this, SLOT(pauseSelected()));
144 connect( ui.action_Remove, SIGNAL(triggered()), this, SLOT(removeSelected()));
145 connect( ui.action_Delete, SIGNAL(triggered()), this, SLOT(deleteSelected()));
146 connect( ui.action_Verify, SIGNAL(triggered()), this, SLOT(verifySelected()) );
147 connect( ui.action_Announce, SIGNAL(triggered()), this, SLOT(reannounceSelected()) );
148 connect( ui.action_StartAll, SIGNAL(triggered()), this, SLOT(startAll()));
149 connect( ui.action_PauseAll, SIGNAL(triggered()), this, SLOT(pauseAll()));
150 connect( ui.action_OpenFile, SIGNAL(triggered()), this, SLOT(openTorrent()));
151 connect( ui.action_AddURL, SIGNAL(triggered()), this, SLOT(openURL()));
152 connect( ui.action_New, SIGNAL(triggered()), this, SLOT(newTorrent()));
153 connect( ui.action_Preferences, SIGNAL(triggered()), this, SLOT(openPreferences()));
154 connect( ui.action_Statistics, SIGNAL(triggered()), myStatsDialog, SLOT(show()));
155 connect( ui.action_Donate, SIGNAL(triggered()), this, SLOT(openDonate()));
156 connect( ui.action_About, SIGNAL(triggered()), myAboutDialog, SLOT(show()));
157 connect( ui.action_Contents, SIGNAL(triggered()), this, SLOT(openHelp()));
158 connect( ui.action_OpenFolder, SIGNAL(triggered()), this, SLOT(openFolder()));
159 connect( ui.action_CopyMagnetToClipboard, SIGNAL(triggered()), this, SLOT(copyMagnetLinkToClipboard()));
160 connect( ui.action_SetLocation, SIGNAL(triggered()), this, SLOT(setLocation()));
161 connect( ui.action_Properties, SIGNAL(triggered()), this, SLOT(openProperties()));
162 connect( ui.action_SessionDialog, SIGNAL(triggered()), mySessionDialog, SLOT(show()));
163 connect( ui.listView, SIGNAL(activated(const QModelIndex&)), ui.action_Properties, SLOT(trigger()));
165 QAction * sep2 = new QAction( this );
166 sep2->setSeparator( true );
167 QAction * sep3 = new QAction( this );
168 sep3->setSeparator( true );
170 // context menu
171 QList<QAction*> actions;
172 actions << ui.action_Properties
173 << ui.action_OpenFolder
174 << sep2
175 << ui.action_Start
176 << ui.action_Announce
177 << ui.action_Pause
178 << ui.action_CopyMagnetToClipboard
179 << sep3
180 << ui.action_Verify
181 << ui.action_SetLocation
182 << sep
183 << ui.action_Remove
184 << ui.action_Delete;
185 addActions( actions );
186 setContextMenuPolicy( Qt::ActionsContextMenu );
188 // signals
189 connect( ui.action_SelectAll, SIGNAL(triggered()), ui.listView, SLOT(selectAll()));
190 connect( ui.action_DeselectAll, SIGNAL(triggered()), ui.listView, SLOT(clearSelection()));
192 connect( &myFilterModel, SIGNAL(rowsInserted(const QModelIndex&,int,int)), this, SLOT(refreshVisibleCount()));
193 connect( &myFilterModel, SIGNAL(rowsRemoved(const QModelIndex&,int,int)), this, SLOT(refreshVisibleCount()));
194 connect( &myFilterModel, SIGNAL(rowsInserted(const QModelIndex&,int,int)), this, SLOT(refreshActionSensitivitySoon()));
195 connect( &myFilterModel, SIGNAL(rowsRemoved(const QModelIndex&,int,int)), this, SLOT(refreshActionSensitivitySoon()));
197 connect( ui.action_Quit, SIGNAL(triggered()), QCoreApplication::instance(), SLOT(quit()) );
199 // torrent view
200 myFilterModel.setSourceModel( &myModel );
201 connect( &myModel, SIGNAL(modelReset()), this, SLOT(onModelReset()));
202 connect( &myModel, SIGNAL(rowsRemoved(const QModelIndex&,int,int)), this, SLOT(onModelReset()));
203 connect( &myModel, SIGNAL(rowsInserted(const QModelIndex&,int,int)), this, SLOT(onModelReset()));
204 connect( &myModel, SIGNAL(dataChanged(const QModelIndex&,const QModelIndex&)), this, SLOT(refreshTrayIconSoon()));
206 ui.listView->setModel( &myFilterModel );
207 connect( ui.listView->selectionModel(), SIGNAL(selectionChanged(const QItemSelection&,const QItemSelection&)), this, SLOT(refreshActionSensitivitySoon()));
209 QActionGroup * actionGroup = new QActionGroup( this );
210 actionGroup->addAction( ui.action_SortByActivity );
211 actionGroup->addAction( ui.action_SortByAge );
212 actionGroup->addAction( ui.action_SortByETA );
213 actionGroup->addAction( ui.action_SortByName );
214 actionGroup->addAction( ui.action_SortByProgress );
215 actionGroup->addAction( ui.action_SortByRatio );
216 actionGroup->addAction( ui.action_SortBySize );
217 actionGroup->addAction( ui.action_SortByState );
219 QMenu * menu = new QMenu( );
220 menu->addAction( ui.action_OpenFile );
221 menu->addAction( ui.action_AddURL );
222 menu->addSeparator( );
223 menu->addAction( ui.action_ShowMainWindow );
224 menu->addAction( ui.action_ShowMessageLog );
225 menu->addAction( ui.action_About );
226 menu->addSeparator( );
227 menu->addAction( ui.action_StartAll );
228 menu->addAction( ui.action_PauseAll );
229 menu->addSeparator( );
230 menu->addAction( ui.action_Quit );
231 myTrayIcon.setContextMenu( menu );
232 myTrayIcon.setIcon( QApplication::windowIcon( ) );
234 connect( &myPrefs, SIGNAL(changed(int)), this, SLOT(refreshPref(int)) );
235 connect( ui.action_ShowMainWindow, SIGNAL(toggled(bool)), this, SLOT(toggleWindows(bool)));
236 connect( &myTrayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
237 this, SLOT(trayActivated(QSystemTrayIcon::ActivationReason)));
239 ui.action_ShowMainWindow->setChecked( !minimized );
240 ui.action_TrayIcon->setChecked( minimized || prefs.getBool( Prefs::SHOW_TRAY_ICON ) );
242 ui.verticalLayout->addWidget( createStatusBar( ) );
243 ui.verticalLayout->insertWidget( 0, myFilterBar = new FilterBar( myPrefs, myModel, myFilterModel ) );
245 QList<int> initKeys;
246 initKeys << Prefs :: MAIN_WINDOW_X
247 << Prefs :: SHOW_TRAY_ICON
248 << Prefs :: SORT_REVERSED
249 << Prefs :: SORT_MODE
250 << Prefs :: FILTERBAR
251 << Prefs :: STATUSBAR
252 << Prefs :: STATUSBAR_STATS
253 << Prefs :: TOOLBAR
254 << Prefs :: ALT_SPEED_LIMIT_ENABLED
255 << Prefs :: COMPACT_VIEW
256 << Prefs :: DSPEED
257 << Prefs :: DSPEED_ENABLED
258 << Prefs :: USPEED
259 << Prefs :: USPEED_ENABLED
260 << Prefs :: RATIO
261 << Prefs :: RATIO_ENABLED;
262 foreach( int key, initKeys )
263 refreshPref( key );
265 connect( &mySession, SIGNAL(sourceChanged()), this, SLOT(onSessionSourceChanged()) );
266 connect( &mySession, SIGNAL(statsUpdated()), this, SLOT(refreshStatusBar()) );
267 connect( &mySession, SIGNAL(dataReadProgress()), this, SLOT(dataReadProgress()) );
268 connect( &mySession, SIGNAL(dataSendProgress()), this, SLOT(dataSendProgress()) );
269 connect( &mySession, SIGNAL(httpAuthenticationRequired()), this, SLOT(wrongAuthentication()) );
271 if( mySession.isServer( ) )
272 myNetworkLabel->hide( );
273 else {
274 connect( &myNetworkTimer, SIGNAL(timeout()), this, SLOT(onNetworkTimer()));
275 myNetworkTimer.start( 1000 );
278 connect( &myRefreshTrayIconTimer, SIGNAL(timeout()), this, SLOT(refreshTrayIcon()) );
279 connect( &myRefreshActionSensitivityTimer, SIGNAL(timeout()), this, SLOT(refreshActionSensitivity()) );
282 refreshActionSensitivitySoon( );
283 refreshTrayIconSoon( );
284 refreshStatusBar( );
285 refreshTitle( );
286 refreshVisibleCount( );
289 TrMainWindow :: ~TrMainWindow( )
293 /****
294 *****
295 ****/
297 void
298 TrMainWindow :: closeEvent( QCloseEvent * event )
300 // if they're using a tray icon, close to the tray
301 // instead of exiting
302 if( !myPrefs.getBool( Prefs :: SHOW_TRAY_ICON ) )
303 event->accept( );
304 else {
305 toggleWindows( false );
306 event->ignore( );
310 /****
311 *****
312 ****/
314 void
315 TrMainWindow :: onSessionSourceChanged( )
317 myModel.clear( );
320 void
321 TrMainWindow :: onModelReset( )
323 refreshTitle( );
324 refreshVisibleCount( );
325 refreshActionSensitivitySoon( );
326 refreshStatusBar( );
327 refreshTrayIconSoon( );
330 /****
331 *****
332 ****/
334 #define PREF_VARIANTS_KEY "pref-variants-list"
336 void
337 TrMainWindow :: onSetPrefs( )
339 const QVariantList p = sender()->property( PREF_VARIANTS_KEY ).toList( );
340 assert( ( p.size( ) % 2 ) == 0 );
341 for( int i=0, n=p.size(); i<n; i+=2 )
342 myPrefs.set( p[i].toInt(), p[i+1] );
345 void
346 TrMainWindow :: onSetPrefs( bool isChecked )
348 if( isChecked )
349 onSetPrefs( );
352 #define SHOW_KEY "show-mode"
354 QWidget *
355 TrMainWindow :: createStatusBar( )
357 QMenu * m;
358 QLabel * l;
359 QHBoxLayout * h;
360 QPushButton * p;
361 QActionGroup * a;
362 const int i = style( )->pixelMetric( QStyle::PM_SmallIconSize, 0, this );
363 const QSize smallIconSize( i, i );
365 QWidget * top = myStatusBar = new QWidget;
366 h = new QHBoxLayout( top );
367 h->setContentsMargins( HIG::PAD_SMALL, HIG::PAD_SMALL, HIG::PAD_SMALL, HIG::PAD_SMALL );
368 h->setSpacing( HIG::PAD_SMALL );
370 p = myOptionsButton = new TrIconPushButton( this );
371 p->setIcon( QIcon( ":/icons/utilities.png" ) );
372 p->setIconSize( QPixmap( ":/icons/utilities.png" ).size() );
373 p->setFlat( true );
374 p->setMenu( createOptionsMenu( ) );
375 h->addWidget( p );
377 p = myAltSpeedButton = new QPushButton( this );
378 p->setIcon( myPrefs.get<bool>(Prefs::ALT_SPEED_LIMIT_ENABLED) ? mySpeedModeOnIcon : mySpeedModeOffIcon );
379 p->setIconSize( QPixmap( ":/icons/alt-limit-on.png" ).size() );
380 p->setCheckable( true );
381 p->setFixedWidth( p->height() );
382 p->setFlat( true );
383 h->addWidget( p );
384 connect( p, SIGNAL(clicked()), this, SLOT(toggleSpeedMode()));
386 l = myNetworkLabel = new QLabel;
387 h->addWidget( l );
389 h->addStretch( 1 );
391 l = myVisibleCountLabel = new QLabel( this );
392 h->addWidget( l );
394 h->addStretch( 1 );
396 a = new QActionGroup( this );
397 a->addAction( ui.action_TotalRatio );
398 a->addAction( ui.action_TotalTransfer );
399 a->addAction( ui.action_SessionRatio );
400 a->addAction( ui.action_SessionTransfer );
401 m = new QMenu( );
402 m->addAction( ui.action_TotalRatio );
403 m->addAction( ui.action_TotalTransfer );
404 m->addAction( ui.action_SessionRatio );
405 m->addAction( ui.action_SessionTransfer );
406 connect( ui.action_TotalRatio, SIGNAL(triggered()), this, SLOT(showTotalRatio()));
407 connect( ui.action_TotalTransfer, SIGNAL(triggered()), this, SLOT(showTotalTransfer()));
408 connect( ui.action_SessionRatio, SIGNAL(triggered()), this, SLOT(showSessionRatio()));
409 connect( ui.action_SessionTransfer, SIGNAL(triggered()), this, SLOT(showSessionTransfer()));
410 p = myStatsModeButton = new TrIconPushButton( this );
411 p->setIcon( QIcon( ":/icons/ratio.png" ) );
412 p->setIconSize( QPixmap( ":/icons/ratio.png" ).size() );
413 p->setFlat( true );
414 p->setMenu( m );
415 h->addWidget( p );
416 l = myStatsLabel = new QLabel( this );
417 h->addWidget( l );
419 h->addStretch( 1 );
421 l = myDownloadSpeedLabel = new QLabel( this );
422 const int minimumSpeedWidth = l->fontMetrics().width( Formatter::speedToString(Speed::fromKBps(999.99)));
423 l->setMinimumWidth( minimumSpeedWidth );
424 l->setAlignment( Qt::AlignRight|Qt::AlignVCenter );
425 h->addWidget( l );
426 l = new QLabel( this );
427 l->setPixmap( getStockIcon( "go-down", QStyle::SP_ArrowDown ).pixmap( smallIconSize ) );
428 h->addWidget( l );
430 h->addStretch( 1 );
432 l = myUploadSpeedLabel = new QLabel;
433 l->setMinimumWidth( minimumSpeedWidth );
434 l->setAlignment( Qt::AlignRight|Qt::AlignVCenter );
435 h->addWidget( l );
436 l = new QLabel;
437 l->setPixmap( getStockIcon( "go-up", QStyle::SP_ArrowUp ).pixmap( smallIconSize ) );
438 h->addWidget( l );
440 return top;
443 QMenu *
444 TrMainWindow :: createOptionsMenu( )
446 QMenu * menu;
447 QMenu * sub;
448 QAction * a;
449 QActionGroup * g;
451 QList<int> stockSpeeds;
452 stockSpeeds << 5 << 10 << 20 << 30 << 40 << 50 << 75 << 100 << 150 << 200 << 250 << 500 << 750;
453 QList<double> stockRatios;
454 stockRatios << 0.25 << 0.50 << 0.75 << 1 << 1.5 << 2 << 3;
456 menu = new QMenu;
457 sub = menu->addMenu( tr( "Limit Download Speed" ) );
458 int currentVal = myPrefs.get<int>( Prefs::DSPEED );
459 g = new QActionGroup( this );
460 a = myDlimitOffAction = sub->addAction( tr( "Unlimited" ) );
461 a->setCheckable( true );
462 a->setProperty( PREF_VARIANTS_KEY, QVariantList() << Prefs::DSPEED_ENABLED << false );
463 g->addAction( a );
464 connect( a, SIGNAL(triggered(bool)), this, SLOT(onSetPrefs(bool)) );
465 a = myDlimitOnAction = sub->addAction( tr( "Limited at %1" ).arg( Formatter::speedToString( Speed::fromKBps( currentVal ) ) ) );
466 a->setCheckable( true );
467 a->setProperty( PREF_VARIANTS_KEY, QVariantList() << Prefs::DSPEED << currentVal << Prefs::DSPEED_ENABLED << true );
468 g->addAction( a );
469 connect( a, SIGNAL(triggered(bool)), this, SLOT(onSetPrefs(bool)) );
470 sub->addSeparator( );
471 foreach( int i, stockSpeeds ) {
472 a = sub->addAction( Formatter::speedToString( Speed::fromKBps( i ) ) );
473 a->setProperty( PREF_VARIANTS_KEY, QVariantList() << Prefs::DSPEED << i << Prefs::DSPEED_ENABLED << true );
474 connect( a, SIGNAL(triggered(bool)), this, SLOT(onSetPrefs()));
477 sub = menu->addMenu( tr( "Limit Upload Speed" ) );
478 currentVal = myPrefs.get<int>( Prefs::USPEED );
479 g = new QActionGroup( this );
480 a = myUlimitOffAction = sub->addAction( tr( "Unlimited" ) );
481 a->setCheckable( true );
482 a->setProperty( PREF_VARIANTS_KEY, QVariantList() << Prefs::USPEED_ENABLED << false );
483 g->addAction( a );
484 connect( a, SIGNAL(triggered(bool)), this, SLOT(onSetPrefs(bool)) );
485 a = myUlimitOnAction = sub->addAction( tr( "Limited at %1" ).arg( Formatter::speedToString( Speed::fromKBps( currentVal ) ) ) );
486 a->setCheckable( true );
487 a->setProperty( PREF_VARIANTS_KEY, QVariantList() << Prefs::USPEED << currentVal << Prefs::USPEED_ENABLED << true );
488 g->addAction( a );
489 connect( a, SIGNAL(triggered(bool)), this, SLOT(onSetPrefs(bool)) );
490 sub->addSeparator( );
491 foreach( int i, stockSpeeds ) {
492 a = sub->addAction( Formatter::speedToString( Speed::fromKBps( i ) ) );
493 a->setProperty( PREF_VARIANTS_KEY, QVariantList() << Prefs::USPEED << i << Prefs::USPEED_ENABLED << true );
494 connect( a, SIGNAL(triggered(bool)), this, SLOT(onSetPrefs()));
497 menu->addSeparator( );
498 sub = menu->addMenu( tr( "Stop Seeding at Ratio" ) );
500 double d = myPrefs.get<double>( Prefs::RATIO );
501 g = new QActionGroup( this );
502 a = myRatioOffAction = sub->addAction( tr( "Seed Forever" ) );
503 a->setCheckable( true );
504 a->setProperty( PREF_VARIANTS_KEY, QVariantList() << Prefs::RATIO_ENABLED << false );
505 g->addAction( a );
506 connect( a, SIGNAL(triggered(bool)), this, SLOT(onSetPrefs(bool)) );
507 a = myRatioOnAction = sub->addAction( tr( "Stop at Ratio (%1)" ).arg( Formatter::ratioToString( d ) ) );
508 a->setCheckable( true );
509 a->setProperty( PREF_VARIANTS_KEY, QVariantList() << Prefs::RATIO << d << Prefs::RATIO_ENABLED << true );
510 g->addAction( a );
511 connect( a, SIGNAL(triggered(bool)), this, SLOT(onSetPrefs(bool)) );
512 sub->addSeparator( );
513 foreach( double i, stockRatios ) {
514 a = sub->addAction( Formatter::ratioToString( i ) );
515 a->setProperty( PREF_VARIANTS_KEY, QVariantList() << Prefs::RATIO << i << Prefs::RATIO_ENABLED << true );
516 connect( a, SIGNAL(triggered(bool)), this, SLOT(onSetPrefs()));
519 return menu;
522 /****
523 *****
524 ****/
526 void
527 TrMainWindow :: setSortPref( int i )
529 myPrefs.set( Prefs::SORT_MODE, SortMode( i ) );
531 void TrMainWindow :: onSortByActivityToggled ( bool b ) { if( b ) setSortPref( SortMode::SORT_BY_ACTIVITY ); }
532 void TrMainWindow :: onSortByAgeToggled ( bool b ) { if( b ) setSortPref( SortMode::SORT_BY_AGE ); }
533 void TrMainWindow :: onSortByETAToggled ( bool b ) { if( b ) setSortPref( SortMode::SORT_BY_ETA ); }
534 void TrMainWindow :: onSortByNameToggled ( bool b ) { if( b ) setSortPref( SortMode::SORT_BY_NAME ); }
535 void TrMainWindow :: onSortByProgressToggled ( bool b ) { if( b ) setSortPref( SortMode::SORT_BY_PROGRESS ); }
536 void TrMainWindow :: onSortByRatioToggled ( bool b ) { if( b ) setSortPref( SortMode::SORT_BY_RATIO ); }
537 void TrMainWindow :: onSortBySizeToggled ( bool b ) { if( b ) setSortPref( SortMode::SORT_BY_SIZE ); }
538 void TrMainWindow :: onSortByStateToggled ( bool b ) { if( b ) setSortPref( SortMode::SORT_BY_STATE ); }
540 void
541 TrMainWindow :: setSortAscendingPref( bool b )
543 myPrefs.set( Prefs::SORT_REVERSED, b );
546 /****
547 *****
548 ****/
550 void
551 TrMainWindow :: onPrefsDestroyed( )
553 myPrefsDialog = 0;
556 void
557 TrMainWindow :: openPreferences( )
559 if( myPrefsDialog == 0 ) {
560 myPrefsDialog = new PrefsDialog( mySession, myPrefs, this );
561 connect( myPrefsDialog, SIGNAL(destroyed(QObject*)), this, SLOT(onPrefsDestroyed()));
564 myPrefsDialog->show( );
567 void
568 TrMainWindow :: onDetailsDestroyed( )
570 myDetailsDialog = 0;
573 void
574 TrMainWindow :: openProperties( )
576 if( myDetailsDialog == 0 ) {
577 myDetailsDialog = new Details( mySession, myPrefs, myModel, this );
578 connect( myDetailsDialog, SIGNAL(destroyed(QObject*)), this, SLOT(onDetailsDestroyed()));
581 myDetailsDialog->setIds( getSelectedTorrents( ) );
582 myDetailsDialog->show( );
585 void
586 TrMainWindow :: setLocation( )
588 QDialog * d = new RelocateDialog( mySession, myModel, getSelectedTorrents(), this );
589 d->show( );
592 void
593 TrMainWindow :: openFolder( )
595 const int torrentId( *getSelectedTorrents().begin() );
596 const Torrent * tor( myModel.getTorrentFromId( torrentId ) );
597 const QString path( tor->getPath( ) );
598 QDesktopServices :: openUrl( QUrl::fromLocalFile( path ) );
601 void
602 TrMainWindow :: copyMagnetLinkToClipboard( )
604 const int id( *getSelectedTorrents().begin() );
605 mySession.copyMagnetLinkToClipboard( id );
608 void
609 TrMainWindow :: openDonate( )
611 QDesktopServices :: openUrl( QUrl( "http://www.transmissionbt.com/donate.php" ) );
614 void
615 TrMainWindow :: openHelp( )
617 const char * fmt = "http://www.transmissionbt.com/help/gtk/%d.%dx";
618 int major, minor;
619 sscanf( SHORT_VERSION_STRING, "%d.%d", &major, &minor );
620 char url[128];
621 tr_snprintf( url, sizeof( url ), fmt, major, minor/10 );
622 QDesktopServices :: openUrl( QUrl( url ) );
625 void
626 TrMainWindow :: refreshTitle( )
628 QString title( "Transmission" );
629 const QUrl url( mySession.getRemoteUrl( ) );
630 if( !url.isEmpty() )
631 title += tr( " - %1:%2" ).arg( url.host() ).arg( url.port() );
632 setWindowTitle( title );
635 void
636 TrMainWindow :: refreshVisibleCount( )
638 const int visibleCount( myFilterModel.rowCount( ) );
639 const int totalCount( visibleCount + myFilterModel.hiddenRowCount( ) );
640 QString str;
641 if( visibleCount == totalCount )
642 str = tr( "%Ln Torrent(s)", 0, totalCount );
643 else
644 str = tr( "%L1 of %Ln Torrent(s)", 0, totalCount ).arg( visibleCount );
645 myVisibleCountLabel->setText( str );
646 myVisibleCountLabel->setVisible( totalCount > 0 );
649 void
650 TrMainWindow :: refreshTrayIconSoon( )
652 if( !myRefreshTrayIconTimer.isActive( ) )
654 myRefreshTrayIconTimer.setSingleShot( true );
655 myRefreshTrayIconTimer.start( 500 );
658 void
659 TrMainWindow :: refreshTrayIcon( )
661 Speed u, d;
662 const QString idle = tr( "Idle" );
664 foreach( int id, myModel.getIds( ) ) {
665 const Torrent * tor = myModel.getTorrentFromId( id );
666 u += tor->uploadSpeed( );
667 d += tor->downloadSpeed( );
670 myTrayIcon.setToolTip( tr( "Transmission\nUp: %1\nDown: %2" )
671 .arg( u.isZero() ? idle : Formatter::speedToString( u ) )
672 .arg( d.isZero() ? idle : Formatter::speedToString( d ) ) );
675 void
676 TrMainWindow :: refreshStatusBar( )
678 const Speed up( myModel.getUploadSpeed( ) );
679 const Speed down( myModel.getDownloadSpeed( ) );
680 myUploadSpeedLabel->setText( Formatter:: speedToString( up ) );
681 myDownloadSpeedLabel->setText( Formatter:: speedToString( down ) );
683 myNetworkLabel->setVisible( !mySession.isServer( ) );
685 const QString mode( myPrefs.getString( Prefs::STATUSBAR_STATS ) );
686 QString str;
688 if( mode == "session-ratio" )
690 str = tr( "Ratio: %1" ).arg( Formatter:: ratioToString( mySession.getStats().ratio ) );
692 else if( mode == "session-transfer" )
694 const tr_session_stats& stats( mySession.getStats( ) );
695 str = tr( "Down: %1, Up: %2" ).arg( Formatter:: sizeToString( stats.downloadedBytes ) )
696 .arg( Formatter:: sizeToString( stats.uploadedBytes ) );
698 else if( mode == "total-transfer" )
700 const tr_session_stats& stats( mySession.getCumulativeStats( ) );
701 str = tr( "Down: %1, Up: %2" ).arg( Formatter:: sizeToString( stats.downloadedBytes ) )
702 .arg( Formatter:: sizeToString( stats.uploadedBytes ) );
704 else // default is "total-ratio"
706 str = tr( "Ratio: %1" ).arg( Formatter:: ratioToString( mySession.getCumulativeStats().ratio ) );
709 myStatsLabel->setText( str );
714 void
715 TrMainWindow :: refreshActionSensitivitySoon( )
717 if( !myRefreshActionSensitivityTimer.isActive( ) )
719 myRefreshActionSensitivityTimer.setSingleShot( true );
720 myRefreshActionSensitivityTimer.start( 500 );
723 void
724 TrMainWindow :: refreshActionSensitivity( )
726 int selected( 0 );
727 int paused( 0 );
728 int selectedAndPaused( 0 );
729 int canAnnounce( 0 );
730 const QAbstractItemModel * model( ui.listView->model( ) );
731 const QItemSelectionModel * selectionModel( ui.listView->selectionModel( ) );
732 const int rowCount( model->rowCount( ) );
734 // count how many torrents are selected, paused, etc
735 for( int row=0; row<rowCount; ++row ) {
736 const QModelIndex modelIndex( model->index( row, 0 ) );
737 assert( model == modelIndex.model( ) );
738 const Torrent * tor( model->data( modelIndex, TorrentModel::TorrentRole ).value<const Torrent*>( ) );
739 if( tor ) {
740 const bool isSelected( selectionModel->isSelected( modelIndex ) );
741 const bool isPaused( tor->isPaused( ) );
742 if( isSelected )
743 ++selected;
744 if( isPaused )
745 ++ paused;
746 if( isSelected && isPaused )
747 ++selectedAndPaused;
748 if( tor->canManualAnnounce( ) )
749 ++canAnnounce;
753 const bool haveSelection( selected > 0 );
754 ui.action_Verify->setEnabled( haveSelection );
755 ui.action_Remove->setEnabled( haveSelection );
756 ui.action_Delete->setEnabled( haveSelection );
757 ui.action_Properties->setEnabled( haveSelection );
758 ui.action_DeselectAll->setEnabled( haveSelection );
759 ui.action_SetLocation->setEnabled( haveSelection );
761 const bool oneSelection( selected == 1 );
762 ui.action_OpenFolder->setEnabled( oneSelection && mySession.isLocal( ) );
763 ui.action_CopyMagnetToClipboard->setEnabled( oneSelection );
765 ui.action_SelectAll->setEnabled( selected < rowCount );
766 ui.action_StartAll->setEnabled( paused > 0 );
767 ui.action_PauseAll->setEnabled( paused < rowCount );
768 ui.action_Start->setEnabled( selectedAndPaused > 0 );
769 ui.action_Pause->setEnabled( selectedAndPaused < selected );
770 ui.action_Announce->setEnabled( selected > 0 && ( canAnnounce == selected ) );
772 if( myDetailsDialog )
773 myDetailsDialog->setIds( getSelectedTorrents( ) );
780 void
781 TrMainWindow :: clearSelection( )
783 ui.action_DeselectAll->trigger( );
786 QSet<int>
787 TrMainWindow :: getSelectedTorrents( ) const
789 QSet<int> ids;
791 foreach( QModelIndex index, ui.listView->selectionModel( )->selectedRows( ) )
793 const Torrent * tor( index.data( TorrentModel::TorrentRole ).value<const Torrent*>( ) );
794 ids.insert( tor->id( ) );
797 return ids;
800 void
801 TrMainWindow :: startSelected( )
803 mySession.startTorrents( getSelectedTorrents( ) );
805 void
806 TrMainWindow :: pauseSelected( )
808 mySession.pauseTorrents( getSelectedTorrents( ) );
810 void
811 TrMainWindow :: startAll( )
813 mySession.startTorrents( );
815 void
816 TrMainWindow :: pauseAll( )
818 mySession.pauseTorrents( );
820 void
821 TrMainWindow :: removeSelected( )
823 removeTorrents( false );
825 void
826 TrMainWindow :: deleteSelected( )
828 removeTorrents( true );
830 void
831 TrMainWindow :: verifySelected( )
833 mySession.verifyTorrents( getSelectedTorrents( ) );
835 void
836 TrMainWindow :: reannounceSelected( )
838 mySession.reannounceTorrents( getSelectedTorrents( ) );
845 void TrMainWindow :: showTotalRatio ( ) { myPrefs.set( Prefs::STATUSBAR_STATS, "total-ratio"); }
846 void TrMainWindow :: showTotalTransfer ( ) { myPrefs.set( Prefs::STATUSBAR_STATS, "total-transfer"); }
847 void TrMainWindow :: showSessionRatio ( ) { myPrefs.set( Prefs::STATUSBAR_STATS, "session-ratio"); }
848 void TrMainWindow :: showSessionTransfer ( ) { myPrefs.set( Prefs::STATUSBAR_STATS, "session-transfer"); }
854 void
855 TrMainWindow :: setCompactView( bool visible )
857 myPrefs.set( Prefs :: COMPACT_VIEW, visible );
859 void
860 TrMainWindow :: toggleSpeedMode( )
862 myPrefs.toggleBool( Prefs :: ALT_SPEED_LIMIT_ENABLED );
864 void
865 TrMainWindow :: setToolbarVisible( bool visible )
867 myPrefs.set( Prefs::TOOLBAR, visible );
869 void
870 TrMainWindow :: setFilterbarVisible( bool visible )
872 myPrefs.set( Prefs::FILTERBAR, visible );
874 void
875 TrMainWindow :: setStatusbarVisible( bool visible )
877 myPrefs.set( Prefs::STATUSBAR, visible );
884 void
885 TrMainWindow :: toggleWindows( bool doShow )
887 if( !doShow )
889 hide( );
891 else
893 if ( !isVisible( ) ) show( );
894 if ( isMinimized( ) ) showNormal( );
895 //activateWindow( );
896 raise( );
897 QApplication::setActiveWindow( this );
901 void
902 TrMainWindow :: trayActivated( QSystemTrayIcon::ActivationReason reason )
904 if( reason == QSystemTrayIcon::Trigger )
906 if( isMinimized ( ) )
907 toggleWindows( true );
908 else
909 ui.action_ShowMainWindow->toggle( );
914 void
915 TrMainWindow :: refreshPref( int key )
917 bool b;
918 int i;
919 QString str;
921 switch( key )
923 case Prefs::STATUSBAR_STATS:
924 str = myPrefs.getString( key );
925 ui.action_TotalRatio->setChecked ( str == "total-ratio" );
926 ui.action_TotalTransfer->setChecked ( str == "total-transfer" );
927 ui.action_SessionRatio->setChecked ( str == "session-ratio" );
928 ui.action_SessionTransfer->setChecked( str == "session-transfer" );
929 refreshStatusBar( );
930 break;
932 case Prefs::SORT_REVERSED:
933 ui.action_ReverseSortOrder->setChecked( myPrefs.getBool( key ) );
934 break;
936 case Prefs::SORT_MODE:
937 i = myPrefs.get<SortMode>(key).mode( );
938 ui.action_SortByActivity->setChecked ( i == SortMode::SORT_BY_ACTIVITY );
939 ui.action_SortByAge->setChecked ( i == SortMode::SORT_BY_AGE );
940 ui.action_SortByETA->setChecked ( i == SortMode::SORT_BY_ETA );
941 ui.action_SortByName->setChecked ( i == SortMode::SORT_BY_NAME );
942 ui.action_SortByProgress->setChecked ( i == SortMode::SORT_BY_PROGRESS );
943 ui.action_SortByRatio->setChecked ( i == SortMode::SORT_BY_RATIO );
944 ui.action_SortBySize->setChecked ( i == SortMode::SORT_BY_SIZE );
945 ui.action_SortByState->setChecked ( i == SortMode::SORT_BY_STATE );
946 break;
948 case Prefs::DSPEED_ENABLED:
949 (myPrefs.get<bool>(key) ? myDlimitOnAction : myDlimitOffAction)->setChecked( true );
950 break;
952 case Prefs::DSPEED:
953 myDlimitOnAction->setText( tr( "Limited at %1" ).arg( Formatter::speedToString( Speed::fromKBps( myPrefs.get<int>(key) ) ) ) );
954 break;
956 case Prefs::USPEED_ENABLED:
957 (myPrefs.get<bool>(key) ? myUlimitOnAction : myUlimitOffAction)->setChecked( true );
958 break;
960 case Prefs::USPEED:
961 myUlimitOnAction->setText( tr( "Limited at %1" ).arg( Formatter::speedToString( Speed::fromKBps( myPrefs.get<int>(key) ) ) ) );
962 break;
964 case Prefs::RATIO_ENABLED:
965 (myPrefs.get<bool>(key) ? myRatioOnAction : myRatioOffAction)->setChecked( true );
966 break;
968 case Prefs::RATIO:
969 myRatioOnAction->setText( tr( "Stop at Ratio (%1)" ).arg( Formatter::ratioToString( myPrefs.get<double>(key) ) ) );
970 break;
972 case Prefs::FILTERBAR:
973 b = myPrefs.getBool( key );
974 myFilterBar->setVisible( b );
975 ui.action_Filterbar->setChecked( b );
976 break;
978 case Prefs::STATUSBAR:
979 b = myPrefs.getBool( key );
980 myStatusBar->setVisible( b );
981 ui.action_Statusbar->setChecked( b );
982 break;
984 case Prefs::TOOLBAR:
985 b = myPrefs.getBool( key );
986 ui.toolBar->setVisible( b );
987 ui.action_Toolbar->setChecked( b );
988 break;
990 case Prefs::SHOW_TRAY_ICON:
991 b = myPrefs.getBool( key );
992 ui.action_TrayIcon->setChecked( b );
993 myTrayIcon.setVisible( b );
994 refreshTrayIconSoon( );
995 break;
997 case Prefs::COMPACT_VIEW: {
998 QItemSelectionModel * selectionModel( ui.listView->selectionModel( ) );
999 const QItemSelection selection( selectionModel->selection( ) );
1000 const QModelIndex currentIndex( selectionModel->currentIndex( ) );
1001 b = myPrefs.getBool( key );
1002 ui.action_CompactView->setChecked( b );
1003 ui.listView->setItemDelegate( b ? myTorrentDelegateMin : myTorrentDelegate );
1004 selectionModel->clear( );
1005 ui.listView->reset( ); // force the rows to resize
1006 selectionModel->select( selection, QItemSelectionModel::Select );
1007 selectionModel->setCurrentIndex( currentIndex, QItemSelectionModel::NoUpdate );
1008 break;
1011 case Prefs::MAIN_WINDOW_X:
1012 case Prefs::MAIN_WINDOW_Y:
1013 case Prefs::MAIN_WINDOW_WIDTH:
1014 case Prefs::MAIN_WINDOW_HEIGHT:
1015 setGeometry( myPrefs.getInt( Prefs::MAIN_WINDOW_X ),
1016 myPrefs.getInt( Prefs::MAIN_WINDOW_Y ),
1017 myPrefs.getInt( Prefs::MAIN_WINDOW_WIDTH ),
1018 myPrefs.getInt( Prefs::MAIN_WINDOW_HEIGHT ) );
1019 break;
1021 case Prefs :: ALT_SPEED_LIMIT_ENABLED:
1022 case Prefs :: ALT_SPEED_LIMIT_UP:
1023 case Prefs :: ALT_SPEED_LIMIT_DOWN: {
1024 b = myPrefs.getBool( Prefs :: ALT_SPEED_LIMIT_ENABLED );
1025 myAltSpeedButton->setChecked( b );
1026 myAltSpeedButton->setIcon( b ? mySpeedModeOnIcon : mySpeedModeOffIcon );
1027 const QString fmt = b ? tr( "Click to disable Temporary Speed Limits\n(%1 down, %2 up)" )
1028 : tr( "Click to enable Temporary Speed Limits\n(%1 down, %2 up)" );
1029 const Speed d = Speed::fromKBps( myPrefs.getInt( Prefs::ALT_SPEED_LIMIT_DOWN ) );
1030 const Speed u = Speed::fromKBps( myPrefs.getInt( Prefs::ALT_SPEED_LIMIT_UP ) );
1031 myAltSpeedButton->setToolTip( fmt.arg( Formatter::speedToString( d ) )
1032 .arg( Formatter::speedToString( u ) ) );
1033 break;
1036 default:
1037 break;
1041 /***
1042 ****
1043 ***/
1045 void
1046 TrMainWindow :: newTorrent( )
1048 MakeDialog * dialog = new MakeDialog( mySession, this );
1049 dialog->show( );
1052 void
1053 TrMainWindow :: openTorrent( )
1055 QFileDialog * myFileDialog;
1056 myFileDialog = new QFileDialog( this,
1057 tr( "Open Torrent" ),
1058 myPrefs.getString( Prefs::OPEN_DIALOG_FOLDER ),
1059 tr( "Torrent Files (*.torrent);;All Files (*.*)" ) );
1060 myFileDialog->setFileMode( QFileDialog::ExistingFiles );
1062 QCheckBox * button = new QCheckBox( tr( "Show &options dialog" ) );
1063 button->setChecked( myPrefs.getBool( Prefs::OPTIONS_PROMPT ) );
1064 QGridLayout * layout = dynamic_cast<QGridLayout*>(myFileDialog->layout());
1065 layout->addWidget( button, layout->rowCount( ), 0, 1, -1, Qt::AlignLeft );
1066 myFileDialogOptionsCheck = button;
1068 connect( myFileDialog, SIGNAL(filesSelected(const QStringList&)),
1069 this, SLOT(addTorrents(const QStringList&)));
1071 myFileDialog->show( );
1074 void
1075 TrMainWindow :: openURL( )
1077 QString str = QApplication::clipboard()->text( QClipboard::Selection );
1079 if( !AddData::isSupported( str ) )
1080 str = QApplication::clipboard()->text( QClipboard::Clipboard );
1082 if( !AddData::isSupported( str ) )
1083 str.clear();
1085 openURL( str );
1088 void
1089 TrMainWindow :: openURL( QString url )
1091 bool ok;
1092 const QString key = QInputDialog::getText( this,
1093 tr( "Open URL or Magnet Link" ),
1094 tr( "Open URL or Magnet Link" ),
1095 QLineEdit::Normal,
1096 url,
1097 &ok );
1098 if( ok && !key.isEmpty( ) )
1099 mySession.addTorrent( key );
1102 void
1103 TrMainWindow :: addTorrents( const QStringList& filenames )
1105 foreach( const QString& filename, filenames )
1106 addTorrent( filename );
1109 void
1110 TrMainWindow :: addTorrent( const QString& filename )
1112 if( !myFileDialogOptionsCheck->isChecked( ) ) {
1113 mySession.addTorrent( filename );
1114 QApplication :: alert ( this );
1115 } else {
1116 Options * o = new Options( mySession, myPrefs, filename, this );
1117 o->show( );
1118 QApplication :: alert( o );
1122 void
1123 TrMainWindow :: removeTorrents( const bool deleteFiles )
1125 QSet<int> ids;
1126 QMessageBox msgBox( this );
1127 QString primary_text, secondary_text;
1128 int incomplete = 0;
1129 int connected = 0;
1130 int count;
1132 foreach( QModelIndex index, ui.listView->selectionModel( )->selectedRows( ) )
1134 const Torrent * tor( index.data( TorrentModel::TorrentRole ).value<const Torrent*>( ) );
1135 ids.insert( tor->id( ) );
1136 if( tor->connectedPeers( ) )
1137 ++connected;
1138 if( !tor->isDone( ) )
1139 ++incomplete;
1142 if( ids.isEmpty() )
1143 return;
1144 count = ids.size();
1146 if( !deleteFiles )
1148 primary_text = ( count == 1 )
1149 ? tr( "Remove torrent?" )
1150 : tr( "Remove %1 torrents?" ).arg( count );
1152 else
1154 primary_text = ( count == 1 )
1155 ? tr( "Delete this torrent's downloaded files?" )
1156 : tr( "Delete these %1 torrents' downloaded files?" ).arg( count );
1159 if( !incomplete && !connected )
1161 secondary_text = ( count == 1 )
1162 ? tr( "Once removed, continuing the transfer will require the torrent file or magnet link." )
1163 : tr( "Once removed, continuing the transfers will require the torrent files or magnet links." );
1165 else if( count == incomplete )
1167 secondary_text = ( count == 1 )
1168 ? tr( "This torrent has not finished downloading." )
1169 : tr( "These torrents have not finished downloading." );
1171 else if( count == connected )
1173 secondary_text = ( count == 1 )
1174 ? tr( "This torrent is connected to peers." )
1175 : tr( "These torrents are connected to peers." );
1177 else
1179 if( connected )
1181 secondary_text = ( connected == 1 )
1182 ? tr( "One of these torrents is connected to peers." )
1183 : tr( "Some of these torrents are connected to peers." );
1186 if( connected && incomplete )
1188 secondary_text += "\n";
1191 if( incomplete )
1193 secondary_text += ( incomplete == 1 )
1194 ? tr( "One of these torrents has not finished downloading." )
1195 : tr( "Some of these torrents have not finished downloading." );
1199 msgBox.setWindowTitle( QString(" ") );
1200 msgBox.setText( QString( "<big><b>%1</big></b>" ).arg( primary_text ) );
1201 msgBox.setInformativeText( secondary_text );
1202 msgBox.setStandardButtons( QMessageBox::Ok | QMessageBox::Cancel );
1203 msgBox.setDefaultButton( QMessageBox::Cancel );
1204 msgBox.setIcon( QMessageBox::Question );
1205 /* hack needed to keep the dialog from being too narrow */
1206 QGridLayout* layout = (QGridLayout*)msgBox.layout();
1207 QSpacerItem* spacer = new QSpacerItem( 450, 0, QSizePolicy::Minimum, QSizePolicy::Expanding );
1208 layout->addItem( spacer, layout->rowCount(), 0, 1, layout->columnCount() );
1210 if( msgBox.exec() == QMessageBox::Ok )
1212 ui.listView->selectionModel()->clear();
1213 mySession.removeTorrents( ids, deleteFiles );
1217 /***
1218 ****
1219 ***/
1221 void
1222 TrMainWindow :: updateNetworkIcon( )
1224 const time_t now = time( NULL );
1225 const int period = 3;
1226 const bool isSending = now - myLastSendTime <= period;
1227 const bool isReading = now - myLastReadTime <= period;
1228 const char * key;
1230 if( isSending && isReading )
1231 key = "network-transmit-receive";
1232 else if( isSending )
1233 key = "network-transmit";
1234 else if( isReading )
1235 key = "network-receive";
1236 else
1237 key = "network-idle";
1239 QIcon icon = getStockIcon( key, QStyle::SP_DriveNetIcon );
1240 QPixmap pixmap = icon.pixmap ( 16, 16 );
1241 myNetworkLabel->setPixmap( pixmap );
1242 myNetworkLabel->setToolTip( isSending || isReading
1243 ? tr( "Transmission server is responding" )
1244 : tr( "Last response from server was %1 ago" ).arg( Formatter::timeToString( now-std::max(myLastReadTime,myLastSendTime))));
1247 void
1248 TrMainWindow :: onNetworkTimer( )
1250 updateNetworkIcon( );
1253 void
1254 TrMainWindow :: dataReadProgress( )
1256 myLastReadTime = time( NULL );
1257 updateNetworkIcon( );
1260 void
1261 TrMainWindow :: dataSendProgress( )
1263 myLastSendTime = time( NULL );
1264 updateNetworkIcon( );
1267 void
1268 TrMainWindow :: wrongAuthentication( )
1270 mySession.stop( );
1271 mySessionDialog->show( );
1274 /***
1275 ****
1276 ***/
1278 void
1279 TrMainWindow :: dragEnterEvent( QDragEnterEvent * event )
1281 const QMimeData * mime = event->mimeData( );
1283 if( mime->hasFormat("application/x-bittorrent")
1284 || mime->text().trimmed().endsWith(".torrent", Qt::CaseInsensitive) )
1285 event->acceptProposedAction();
1288 void
1289 TrMainWindow :: dropEvent( QDropEvent * event )
1291 QString key = event->mimeData()->text().trimmed();
1293 const QUrl url( key );
1294 if( url.scheme() == "file" )
1295 key = QUrl::fromPercentEncoding( url.path().toUtf8( ) );
1297 dynamic_cast<MyApp*>(QApplication::instance())->addTorrent( key );