source: branches/2.7x/qt/mainwin.cc @ 13750

Last change on this file since 13750 was 13750, checked in by jordan, 9 years ago

(2.7x, qt) backport r13733 for #5206

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