source: trunk/qt/mainwin.cc @ 12594

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

(trunk qt) reduce the timer delay between model changes and UI updates

  • Property svn:keywords set to Date Rev Author Id
File size: 44.1 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 12594 2011-07-26 02:56:06Z 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
49QIcon
50TrMainWindow :: getStockIcon( const QString& name, int fallback )
51{
52    QIcon icon = QIcon::fromTheme( name );
53
54    if( icon.isNull( ) && ( fallback >= 0 ) )
55        icon = style()->standardIcon( QStyle::StandardPixmap( fallback ), 0, this );
56
57    return icon;
58}
59
60namespace
61{
62    QSize calculateTextButtonSizeHint( QPushButton * button )
63    {
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( ) );
72    }
73}
74
75
76TrMainWindow :: 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 )
96{
97    setAcceptDrops( true );
98
99    QAction * sep = new QAction( this );
100    sep->setSeparator( true );
101
102    ui.setupUi( this );
103
104    QStyle * style = this->style();
105
106    int i = style->pixelMetric( QStyle::PM_SmallIconSize, 0, this );
107    const QSize smallIconSize( i, i );
108
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" ) );
127
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()));
164
165    QAction * sep2 = new QAction( this );
166    sep2->setSeparator( true );
167    QAction * sep3 = new QAction( this );
168    sep3->setSeparator( true );
169
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 );
187
188    // signals
189    connect( ui.action_SelectAll, SIGNAL(triggered()), ui.listView, SLOT(selectAll()));
190    connect( ui.action_DeselectAll, SIGNAL(triggered()), ui.listView, SLOT(clearSelection()));
191
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()));
196
197    connect( ui.action_Quit, SIGNAL(triggered()), QCoreApplication::instance(), SLOT(quit()) );
198
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()));
205
206    ui.listView->setModel( &myFilterModel );
207    connect( ui.listView->selectionModel(), SIGNAL(selectionChanged(const QItemSelection&,const QItemSelection&)), this, SLOT(refreshActionSensitivitySoon()));
208
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 );
218
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( ) );
233
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)));
238
239    ui.action_ShowMainWindow->setChecked( !minimized );
240    ui.action_TrayIcon->setChecked( minimized || prefs.getBool( Prefs::SHOW_TRAY_ICON ) );
241
242    ui.verticalLayout->addWidget( createStatusBar( ) );
243    ui.verticalLayout->insertWidget( 0, myFilterBar = new FilterBar( myPrefs, myModel, myFilterModel ) );
244
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 );
264
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()) );
270
271    if( mySession.isServer( ) )
272        myNetworkLabel->hide( );
273    else {
274        connect( &myNetworkTimer, SIGNAL(timeout()), this, SLOT(onNetworkTimer()));
275        myNetworkTimer.start( 1000 );
276    }
277
278    connect( &myRefreshTrayIconTimer, SIGNAL(timeout()), this, SLOT(refreshTrayIcon()) );
279    connect( &myRefreshActionSensitivityTimer, SIGNAL(timeout()), this, SLOT(refreshActionSensitivity()) );
280
281
282    refreshActionSensitivitySoon( );
283    refreshTrayIconSoon( );
284    refreshStatusBar( );
285    refreshTitle( );
286    refreshVisibleCount( );
287}
288
289TrMainWindow :: ~TrMainWindow( )
290{
291}
292
293/****
294*****
295****/
296
297void
298TrMainWindow :: closeEvent( QCloseEvent * event )
299{
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( );
307    }
308}
309
310/****
311*****
312****/
313
314void
315TrMainWindow :: onSessionSourceChanged( )
316{
317    myModel.clear( );
318}
319
320void
321TrMainWindow :: onModelReset( )
322{
323    refreshTitle( );
324    refreshVisibleCount( );
325    refreshActionSensitivitySoon( );
326    refreshStatusBar( );
327    refreshTrayIconSoon( );
328}
329
330/****
331*****
332****/
333
334#define PREF_VARIANTS_KEY "pref-variants-list"
335
336void
337TrMainWindow :: onSetPrefs( )
338{
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] );
343}
344
345void
346TrMainWindow :: onSetPrefs( bool isChecked )
347{
348    if( isChecked )
349        onSetPrefs( );
350}
351
352#define SHOW_KEY "show-mode"
353
354QWidget *
355TrMainWindow :: createStatusBar( )
356{
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 );
364
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 );
369
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 );
376
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()));
385
386        l = myNetworkLabel = new QLabel;
387        h->addWidget( l );
388
389    h->addStretch( 1 );
390
391        l = myVisibleCountLabel = new QLabel( this );
392        h->addWidget( l );
393
394    h->addStretch( 1 );
395
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 );
418
419    h->addStretch( 1 );
420
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 );
429
430    h->addStretch( 1 );
431
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 );
439
440    return top;
441}
442
443QMenu *
444TrMainWindow :: createOptionsMenu( )
445{
446    QMenu * menu;
447    QMenu * sub;
448    QAction * a;
449    QActionGroup * g;
450
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;
455
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()));
475        }
476
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()));
495        }
496
497    menu->addSeparator( );
498    sub = menu->addMenu( tr( "Stop Seeding at Ratio" ) );
499
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()));
517        }
518
519    return menu;
520}
521
522/****
523*****
524****/
525
526void
527TrMainWindow :: setSortPref( int i )
528{
529    myPrefs.set( Prefs::SORT_MODE, SortMode( i ) );
530}
531void TrMainWindow :: onSortByActivityToggled ( bool b ) { if( b ) setSortPref( SortMode::SORT_BY_ACTIVITY ); }
532void TrMainWindow :: onSortByAgeToggled      ( bool b ) { if( b ) setSortPref( SortMode::SORT_BY_AGE );      }
533void TrMainWindow :: onSortByETAToggled      ( bool b ) { if( b ) setSortPref( SortMode::SORT_BY_ETA );      }
534void TrMainWindow :: onSortByNameToggled     ( bool b ) { if( b ) setSortPref( SortMode::SORT_BY_NAME );     }
535void TrMainWindow :: onSortByProgressToggled ( bool b ) { if( b ) setSortPref( SortMode::SORT_BY_PROGRESS ); }
536void TrMainWindow :: onSortByRatioToggled    ( bool b ) { if( b ) setSortPref( SortMode::SORT_BY_RATIO );    }
537void TrMainWindow :: onSortBySizeToggled     ( bool b ) { if( b ) setSortPref( SortMode::SORT_BY_SIZE );     }
538void TrMainWindow :: onSortByStateToggled    ( bool b ) { if( b ) setSortPref( SortMode::SORT_BY_STATE );    }
539
540void
541TrMainWindow :: setSortAscendingPref( bool b )
542{
543    myPrefs.set( Prefs::SORT_REVERSED, b );
544}
545
546/****
547*****
548****/
549
550void
551TrMainWindow :: onPrefsDestroyed( )
552{
553    myPrefsDialog = 0;
554}
555
556void
557TrMainWindow :: openPreferences( )
558{
559    if( myPrefsDialog == 0 ) {
560        myPrefsDialog = new PrefsDialog( mySession, myPrefs, this );
561        connect( myPrefsDialog, SIGNAL(destroyed(QObject*)), this, SLOT(onPrefsDestroyed()));
562    }
563
564    myPrefsDialog->show( );
565}
566
567void
568TrMainWindow :: onDetailsDestroyed( )
569{
570    myDetailsDialog = 0;
571}
572
573void
574TrMainWindow :: openProperties( )
575{
576    if( myDetailsDialog == 0 ) {
577        myDetailsDialog = new Details( mySession, myPrefs, myModel, this );
578        connect( myDetailsDialog, SIGNAL(destroyed(QObject*)), this, SLOT(onDetailsDestroyed()));
579    }
580
581    myDetailsDialog->setIds( getSelectedTorrents( ) );
582    myDetailsDialog->show( );
583}
584
585void
586TrMainWindow :: setLocation( )
587{
588    QDialog * d = new RelocateDialog( mySession, myModel, getSelectedTorrents(), this );
589    d->show( );
590}
591
592void
593TrMainWindow :: openFolder( )
594{
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 ) );
599}
600
601void
602TrMainWindow :: copyMagnetLinkToClipboard( )
603{
604    const int id( *getSelectedTorrents().begin() );
605    mySession.copyMagnetLinkToClipboard( id );
606}
607
608void
609TrMainWindow :: openDonate( )
610{
611    QDesktopServices :: openUrl( QUrl( "http://www.transmissionbt.com/donate.php" ) );
612}
613
614void
615TrMainWindow :: openHelp( )
616{
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 ) );
623}
624
625void
626TrMainWindow :: refreshTitle( )
627{
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 );
633}
634
635void
636TrMainWindow :: refreshVisibleCount( )
637{
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 );
647}
648
649void
650TrMainWindow :: refreshTrayIconSoon( )
651{
652    if( !myRefreshTrayIconTimer.isActive( ) )
653    {
654        myRefreshTrayIconTimer.setSingleShot( true );
655        myRefreshTrayIconTimer.start( 100 );
656    }
657}
658void
659TrMainWindow :: refreshTrayIcon( )
660{
661    Speed u, d;
662    const QString idle = tr( "Idle" );
663
664    foreach( int id, myModel.getIds( ) ) {
665        const Torrent * tor = myModel.getTorrentFromId( id );
666        u += tor->uploadSpeed( );
667        d += tor->downloadSpeed( );
668    }
669
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 ) ) );
673}
674
675void
676TrMainWindow :: refreshStatusBar( )
677{
678    const Speed up( myModel.getUploadSpeed( ) );
679    const Speed down( myModel.getDownloadSpeed( ) );
680    myUploadSpeedLabel->setText( Formatter:: speedToString( up ) );
681    myDownloadSpeedLabel->setText( Formatter:: speedToString( down ) );
682
683    myNetworkLabel->setVisible( !mySession.isServer( ) );
684
685    const QString mode( myPrefs.getString( Prefs::STATUSBAR_STATS ) );
686    QString str;
687
688    if( mode == "session-ratio" )
689    {
690        str = tr( "Ratio: %1" ).arg( Formatter:: ratioToString( mySession.getStats().ratio ) );
691    }
692    else if( mode == "session-transfer" )
693    {
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 ) );
697    }
698    else if( mode == "total-transfer" )
699    {
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 ) );
703    }
704    else // default is "total-ratio"
705    {
706        str = tr( "Ratio: %1" ).arg( Formatter:: ratioToString( mySession.getCumulativeStats().ratio ) );
707    }
708
709    myStatsLabel->setText( str );
710}
711
712
713
714void
715TrMainWindow :: refreshActionSensitivitySoon( )
716{
717    if( !myRefreshActionSensitivityTimer.isActive( ) )
718    {
719        myRefreshActionSensitivityTimer.setSingleShot( true );
720        myRefreshActionSensitivityTimer.start( 100 );
721    }
722}
723void
724TrMainWindow :: refreshActionSensitivity( )
725{
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( ) );
733
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;
750        }
751    }
752
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 );
760
761    const bool oneSelection( selected == 1 );
762    ui.action_OpenFolder->setEnabled( oneSelection && mySession.isLocal( ) );
763    ui.action_CopyMagnetToClipboard->setEnabled( oneSelection );
764
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 ) );
771
772    if( myDetailsDialog )
773        myDetailsDialog->setIds( getSelectedTorrents( ) );
774}
775
776/**
777***
778**/
779
780void
781TrMainWindow :: clearSelection( )
782{
783    ui.action_DeselectAll->trigger( );
784}
785
786QSet<int>
787TrMainWindow :: getSelectedTorrents( ) const
788{
789    QSet<int> ids;
790
791    foreach( QModelIndex index, ui.listView->selectionModel( )->selectedRows( ) )
792    {
793        const Torrent * tor( index.data( TorrentModel::TorrentRole ).value<const Torrent*>( ) );
794        ids.insert( tor->id( ) );
795    }
796
797    return ids;
798}
799
800void
801TrMainWindow :: startSelected( )
802{
803    mySession.startTorrents( getSelectedTorrents( ) );
804}
805void
806TrMainWindow :: pauseSelected( )
807{
808    mySession.pauseTorrents( getSelectedTorrents( ) );
809}
810void
811TrMainWindow :: startAll( )
812{
813    mySession.startTorrents( );
814}
815void
816TrMainWindow :: pauseAll( )
817{
818    mySession.pauseTorrents( );
819}
820void
821TrMainWindow :: removeSelected( )
822{
823    removeTorrents( false );
824}
825void
826TrMainWindow :: deleteSelected( )
827{
828    removeTorrents( true );
829}
830void
831TrMainWindow :: verifySelected( )
832{
833    mySession.verifyTorrents( getSelectedTorrents( ) );
834}
835void
836TrMainWindow :: reannounceSelected( )
837{
838    mySession.reannounceTorrents( getSelectedTorrents( ) );
839}
840
841/**
842***
843**/
844
845void TrMainWindow :: showTotalRatio      ( ) { myPrefs.set( Prefs::STATUSBAR_STATS, "total-ratio"); }
846void TrMainWindow :: showTotalTransfer   ( ) { myPrefs.set( Prefs::STATUSBAR_STATS, "total-transfer"); }
847void TrMainWindow :: showSessionRatio    ( ) { myPrefs.set( Prefs::STATUSBAR_STATS, "session-ratio"); }
848void TrMainWindow :: showSessionTransfer ( ) { myPrefs.set( Prefs::STATUSBAR_STATS, "session-transfer"); }
849
850/**
851***
852**/
853
854void
855TrMainWindow :: setCompactView( bool visible )
856{
857    myPrefs.set( Prefs :: COMPACT_VIEW, visible );
858}
859void
860TrMainWindow :: toggleSpeedMode( )
861{
862    myPrefs.toggleBool( Prefs :: ALT_SPEED_LIMIT_ENABLED );
863}
864void
865TrMainWindow :: setToolbarVisible( bool visible )
866{
867    myPrefs.set( Prefs::TOOLBAR, visible );
868}
869void
870TrMainWindow :: setFilterbarVisible( bool visible )
871{
872    myPrefs.set( Prefs::FILTERBAR, visible );
873}
874void
875TrMainWindow :: setStatusbarVisible( bool visible )
876{
877    myPrefs.set( Prefs::STATUSBAR, visible );
878}
879
880/**
881***
882**/
883
884void
885TrMainWindow :: toggleWindows( bool doShow )
886{
887    if( !doShow )
888    {
889        hide( );
890    }
891    else
892    {
893        if ( !isVisible( ) ) show( );
894        if ( isMinimized( ) ) showNormal( );
895        //activateWindow( );
896        raise( );
897        QApplication::setActiveWindow( this );
898    }
899}
900
901void
902TrMainWindow :: trayActivated( QSystemTrayIcon::ActivationReason reason )
903{
904    if( reason == QSystemTrayIcon::Trigger )
905    {
906        if( isMinimized ( ) )
907            toggleWindows( true );
908        else
909            ui.action_ShowMainWindow->toggle( );
910    }
911}
912
913
914void
915TrMainWindow :: refreshPref( int key )
916{
917    bool b;
918    int i;
919    QString str;
920
921    switch( key )
922    {
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;
931
932        case Prefs::SORT_REVERSED:
933            ui.action_ReverseSortOrder->setChecked( myPrefs.getBool( key ) );
934            break;
935
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;
947
948        case Prefs::DSPEED_ENABLED:
949            (myPrefs.get<bool>(key) ? myDlimitOnAction : myDlimitOffAction)->setChecked( true );
950            break;
951
952        case Prefs::DSPEED:
953            myDlimitOnAction->setText( tr( "Limited at %1" ).arg( Formatter::speedToString( Speed::fromKBps( myPrefs.get<int>(key) ) ) ) );
954            break;
955
956        case Prefs::USPEED_ENABLED:
957            (myPrefs.get<bool>(key) ? myUlimitOnAction : myUlimitOffAction)->setChecked( true );
958            break;
959
960        case Prefs::USPEED:
961            myUlimitOnAction->setText( tr( "Limited at %1" ).arg( Formatter::speedToString( Speed::fromKBps( myPrefs.get<int>(key) ) ) ) );
962            break;
963
964        case Prefs::RATIO_ENABLED:
965            (myPrefs.get<bool>(key) ? myRatioOnAction : myRatioOffAction)->setChecked( true );
966            break;
967
968        case Prefs::RATIO:
969            myRatioOnAction->setText( tr( "Stop at Ratio (%1)" ).arg( Formatter::ratioToString( myPrefs.get<double>(key) ) ) );
970            break;
971
972        case Prefs::FILTERBAR:
973            b = myPrefs.getBool( key );
974            myFilterBar->setVisible( b );
975            ui.action_Filterbar->setChecked( b );
976            break;
977
978        case Prefs::STATUSBAR:
979            b = myPrefs.getBool( key );
980            myStatusBar->setVisible( b );
981            ui.action_Statusbar->setChecked( b );
982            break;
983
984        case Prefs::TOOLBAR:
985            b = myPrefs.getBool( key );
986            ui.toolBar->setVisible( b );
987            ui.action_Toolbar->setChecked( b );
988            break;
989
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;
996
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;
1009        }
1010
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;
1020
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;
1034        }
1035
1036        default:
1037            break;
1038    }
1039}
1040
1041/***
1042****
1043***/
1044
1045void
1046TrMainWindow :: newTorrent( )
1047{
1048    MakeDialog * dialog = new MakeDialog( mySession, this );
1049    dialog->show( );
1050}
1051
1052void
1053TrMainWindow :: openTorrent( )
1054{
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 );
1061
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;
1067
1068    connect( myFileDialog, SIGNAL(filesSelected(const QStringList&)),
1069             this, SLOT(addTorrents(const QStringList&)));
1070
1071    myFileDialog->show( );
1072}
1073
1074void
1075TrMainWindow :: openURL( )
1076{
1077    QString str = QApplication::clipboard()->text( QClipboard::Selection );
1078
1079    if( !AddData::isSupported( str ) )
1080        str = QApplication::clipboard()->text( QClipboard::Clipboard );
1081
1082    if( !AddData::isSupported( str ) )
1083        str.clear();
1084
1085    openURL( str );
1086}
1087
1088void
1089TrMainWindow :: openURL( QString url )
1090{
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 );
1100}
1101
1102void
1103TrMainWindow :: addTorrents( const QStringList& filenames )
1104{
1105    foreach( const QString& filename, filenames )
1106        addTorrent( filename );
1107}
1108
1109void
1110TrMainWindow :: addTorrent( const QString& filename )
1111{
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 );
1119    }
1120}
1121
1122void
1123TrMainWindow :: removeTorrents( const bool deleteFiles )
1124{
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;
1131
1132    foreach( QModelIndex index, ui.listView->selectionModel( )->selectedRows( ) )
1133    {
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;
1140    }
1141
1142    if( ids.isEmpty() )
1143        return;
1144    count = ids.size();
1145
1146    if( !deleteFiles )
1147    {
1148        primary_text = ( count == 1 )
1149            ? tr( "Remove torrent?" )
1150            : tr( "Remove %1 torrents?" ).arg( count );
1151    }
1152    else
1153    {
1154        primary_text = ( count == 1 )
1155            ? tr( "Delete this torrent's downloaded files?" )
1156            : tr( "Delete these %1 torrents' downloaded files?" ).arg( count );
1157    }
1158
1159    if( !incomplete && !connected )
1160    {
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." );
1164    }
1165    else if( count == incomplete )
1166    {
1167        secondary_text = ( count == 1 )
1168            ? tr( "This torrent has not finished downloading." )
1169            : tr( "These torrents have not finished downloading." );
1170    }
1171    else if( count == connected )
1172    {
1173        secondary_text = ( count == 1 )
1174            ? tr( "This torrent is connected to peers." )
1175            : tr( "These torrents are connected to peers." );
1176    }
1177    else
1178    {
1179        if( connected )
1180        {
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." );
1184        }
1185
1186        if( connected && incomplete )
1187        {
1188            secondary_text += "\n";
1189        }
1190
1191        if( incomplete )
1192        {
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." );
1196        }
1197    }
1198
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() );
1209
1210    if( msgBox.exec() == QMessageBox::Ok )
1211    {
1212        ui.listView->selectionModel()->clear();
1213        mySession.removeTorrents( ids, deleteFiles );
1214    }
1215}
1216
1217/***
1218****
1219***/
1220
1221void
1222TrMainWindow :: updateNetworkIcon( )
1223{
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;
1229
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";
1238
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))));
1245}
1246
1247void
1248TrMainWindow :: onNetworkTimer( )
1249{
1250    updateNetworkIcon( );
1251}
1252
1253void
1254TrMainWindow :: dataReadProgress( )
1255{
1256    myLastReadTime = time( NULL );
1257    updateNetworkIcon( );
1258}
1259
1260void
1261TrMainWindow :: dataSendProgress( )
1262{
1263    myLastSendTime = time( NULL );
1264    updateNetworkIcon( );
1265}
1266
1267void
1268TrMainWindow :: wrongAuthentication( )
1269{
1270    mySession.stop( );
1271    mySessionDialog->show( );
1272}
1273
1274/***
1275****
1276***/
1277
1278void
1279TrMainWindow :: dragEnterEvent( QDragEnterEvent * event )
1280{
1281    const QMimeData * mime = event->mimeData( );
1282
1283    if( mime->hasFormat("application/x-bittorrent")
1284            || mime->text().trimmed().endsWith(".torrent", Qt::CaseInsensitive) )
1285        event->acceptProposedAction();
1286}
1287
1288void
1289TrMainWindow :: dropEvent( QDropEvent * event )
1290{
1291    QString key = event->mimeData()->text().trimmed();
1292
1293    const QUrl url( key );
1294    if( url.scheme() == "file" )
1295        key = QUrl::fromPercentEncoding( url.path().toUtf8( ) );
1296
1297    dynamic_cast<MyApp*>(QApplication::instance())->addTorrent( key );
1298}
Note: See TracBrowser for help on using the repository browser.