source: trunk/qt/mainwin.cc @ 8322

Last change on this file since 8322 was 8322, checked in by charles, 13 years ago

(trunk qt) privacy guard to the url in the titlebar. patch from kjg

File size: 38.3 KB
Line 
1/*
2 * This file Copyright (C) 2009 Charles Kerr <charles@transmissionbt.com>
3 *
4 * This file is licensed by the GPL version 2.  Works owned by the
5 * Transmission project are granted a special exemption to clause 2(b)
6 * so that the bulk of its code can remain under the MIT license.
7 * This exemption does not extend to derived works not owned by
8 * the Transmission project.
9 *
10 * $Id:$
11 */
12
13#include <cassert>
14#include <iostream>
15
16#include <QCheckBox>
17#include <QDesktopServices>
18#include <QFileDialog>
19#include <QLabel>
20#include <QSize>
21#include <QStyle>
22#include <QHBoxLayout>
23#include <QSystemTrayIcon>
24#include <QUrl>
25#include <QSignalMapper>
26
27#include <libtransmission/version.h>
28
29#include "about.h"
30#include "details.h"
31#include "filters.h"
32#include "hig.h"
33#include "mainwin.h"
34#include "make-dialog.h"
35#include "options.h"
36#include "prefs.h"
37#include "prefs-dialog.h"
38#include "session.h"
39#include "speed.h"
40#include "stats-dialog.h"
41#include "torrent-delegate.h"
42#include "torrent-delegate-min.h"
43#include "torrent-filter.h"
44#include "torrent-model.h"
45#include "triconpushbutton.h"
46#include "ui_mainwin.h"
47#include "utils.h"
48#include "qticonloader.h"
49
50#define PREFS_KEY "prefs-key";
51
52QIcon
53TrMainWindow :: getStockIcon( const QString& freedesktop_name, int fallback )
54{
55    QIcon fallbackIcon;
56
57    if( fallback > 0 )
58        fallbackIcon = style()->standardIcon( QStyle::StandardPixmap( fallback ), 0, this );
59
60    return QtIconLoader::icon( freedesktop_name, fallbackIcon );
61}
62
63namespace
64{
65    QSize calculateTextButtonSizeHint( QPushButton * button )
66    {
67        QStyleOptionButton opt;
68        opt.initFrom( button );
69        QString s( button->text( ) );
70        if( s.isEmpty( ) )
71            s = QString::fromLatin1( "XXXX" );
72        QFontMetrics fm = button->fontMetrics( );
73        QSize sz = fm.size( Qt::TextShowMnemonic, s );
74        return button->style()->sizeFromContents( QStyle::CT_PushButton, &opt, sz, button ).expandedTo( QApplication::globalStrut( ) );
75    }
76}
77
78
79TrMainWindow :: TrMainWindow( Session& session, Prefs& prefs, TorrentModel& model, bool minimized ):
80    myLastFullUpdateTime( 0 ),
81    myPrefsDialog( new PrefsDialog( session, prefs, this ) ),
82    myAboutDialog( new AboutDialog( this ) ),
83    myStatsDialog( new StatsDialog( session, this ) ),
84    myDetailsDialog( 0 ),
85    myFileDialog( 0 ),
86    myFilterModel( prefs ),
87    myTorrentDelegate( new TorrentDelegate( this ) ),
88    myTorrentDelegateMin( new TorrentDelegateMin( this ) ),
89    mySession( session ),
90    myPrefs( prefs ),
91    myModel( model ),
92    mySpeedModeOffIcon( ":/icons/alt-limit-off.png" ),
93    mySpeedModeOnIcon( ":/icons/alt-limit-on.png" ),
94    myLastSendTime( 0 ),
95    myLastReadTime( 0 ),
96    myNetworkTimer( this )
97{
98    QAction * sep = new QAction( this );
99    sep->setSeparator( true );
100
101    ui.setupUi( this );
102
103    QString title( "Transmission" );
104    const QUrl remoteUrl( session.getRemoteUrl( ) );
105    if( !remoteUrl.isEmpty( ) )
106        title += tr( " - %1" ).arg( remoteUrl.toString(QUrl::RemoveUserInfo) );
107    setWindowTitle( title );
108
109    QStyle * style = this->style();
110
111    int i = style->pixelMetric( QStyle::PM_SmallIconSize, 0, this );
112    const QSize smallIconSize( i, i );
113
114    // icons
115    ui.action_Add->setIcon( getStockIcon( "list-add", QStyle::SP_DialogOpenButton ) );
116    ui.action_New->setIcon( getStockIcon( "document-new", QStyle::SP_DesktopIcon ) );
117    ui.action_Properties->setIcon( getStockIcon( "document-properties", QStyle::SP_DesktopIcon ) );
118    ui.action_OpenFolder->setIcon( getStockIcon( "folder-open", QStyle::SP_DirOpenIcon ) );
119    ui.action_Start->setIcon( getStockIcon( "media-playback-start", QStyle::SP_MediaPlay ) );
120    ui.action_Announce->setIcon( getStockIcon( "network-transmit-receive" ) );
121    ui.action_Pause->setIcon( getStockIcon( "media-playback-pause", QStyle::SP_MediaPause ) );
122    ui.action_Remove->setIcon( getStockIcon( "list-remove", QStyle::SP_TrashIcon ) );
123    ui.action_Delete->setIcon( getStockIcon( "edit-delete", QStyle::SP_TrashIcon ) );
124    ui.action_StartAll->setIcon( getStockIcon( "media-playback-start", QStyle::SP_MediaPlay ) );
125    ui.action_PauseAll->setIcon( getStockIcon( "media-playback-pause", QStyle::SP_MediaPause ) );
126    ui.action_Quit->setIcon( getStockIcon( "application-exit" ) );
127    ui.action_SelectAll->setIcon( getStockIcon( "edit-select-all" ) );
128    ui.action_ReverseSortOrder->setIcon( getStockIcon( "view-sort-ascending", QStyle::SP_ArrowDown ) );
129    ui.action_Preferences->setIcon( getStockIcon( "preferences-system" ) );
130    ui.action_Contents->setIcon( getStockIcon( "help-contents", QStyle::SP_DialogHelpButton ) );
131    ui.action_About->setIcon( getStockIcon( "help-about" ) );
132
133    // ui signals
134    connect( ui.action_Toolbar, SIGNAL(toggled(bool)), this, SLOT(setToolbarVisible(bool)));
135    connect( ui.action_TrayIcon, SIGNAL(toggled(bool)), this, SLOT(setTrayIconVisible(bool)));
136    connect( ui.action_Filterbar, SIGNAL(toggled(bool)), this, SLOT(setFilterbarVisible(bool)));
137    connect( ui.action_Statusbar, SIGNAL(toggled(bool)), this, SLOT(setStatusbarVisible(bool)));
138    connect( ui.action_MinimalView, SIGNAL(toggled(bool)), this, SLOT(setMinimalView(bool)));
139    connect( ui.action_SortByActivity, SIGNAL(toggled(bool)), this, SLOT(onSortByActivityToggled(bool)));
140    connect( ui.action_SortByAge,      SIGNAL(toggled(bool)), this, SLOT(onSortByAgeToggled(bool)));
141    connect( ui.action_SortByETA,      SIGNAL(toggled(bool)), this, SLOT(onSortByETAToggled(bool)));
142    connect( ui.action_SortByName,     SIGNAL(toggled(bool)), this, SLOT(onSortByNameToggled(bool)));
143    connect( ui.action_SortByProgress, SIGNAL(toggled(bool)), this, SLOT(onSortByProgressToggled(bool)));
144    connect( ui.action_SortByRatio,    SIGNAL(toggled(bool)), this, SLOT(onSortByRatioToggled(bool)));
145    connect( ui.action_SortBySize,     SIGNAL(toggled(bool)), this, SLOT(onSortBySizeToggled(bool)));
146    connect( ui.action_SortByState,    SIGNAL(toggled(bool)), this, SLOT(onSortByStateToggled(bool)));
147    connect( ui.action_SortByTracker,  SIGNAL(toggled(bool)), this, SLOT(onSortByTrackerToggled(bool)));
148    connect( ui.action_ReverseSortOrder, SIGNAL(toggled(bool)), this, SLOT(setSortAscendingPref(bool)));
149    connect( ui.action_Start, SIGNAL(triggered()), this, SLOT(startSelected()));
150    connect( ui.action_Pause, SIGNAL(triggered()), this, SLOT(pauseSelected()));
151    connect( ui.action_Remove, SIGNAL(triggered()), this, SLOT(removeSelected()));
152    connect( ui.action_Delete, SIGNAL(triggered()), this, SLOT(deleteSelected()));
153    connect( ui.action_Verify, SIGNAL(triggered()), this, SLOT(verifySelected()) );
154    connect( ui.action_Announce, SIGNAL(triggered()), this, SLOT(reannounceSelected()) );
155    connect( ui.action_StartAll, SIGNAL(triggered()), this, SLOT(startAll()));
156    connect( ui.action_PauseAll, SIGNAL(triggered()), this, SLOT(pauseAll()));
157    connect( ui.action_Add, SIGNAL(triggered()), this, SLOT(openTorrent()));
158    connect( ui.action_New, SIGNAL(triggered()), this, SLOT(newTorrent()));
159    connect( ui.action_Preferences, SIGNAL(triggered()), myPrefsDialog, SLOT(show()));
160    connect( ui.action_Statistics, SIGNAL(triggered()), myStatsDialog, SLOT(show()));
161    connect( ui.action_About, SIGNAL(triggered()), myAboutDialog, SLOT(show()));
162    connect( ui.action_Contents, SIGNAL(triggered()), this, SLOT(openHelp()));
163    connect( ui.action_OpenFolder, SIGNAL(triggered()), this, SLOT(openFolder()));
164    connect( ui.action_Properties, SIGNAL(triggered()), this, SLOT(openProperties()));
165    connect( ui.listView, SIGNAL(activated(const QModelIndex&)), ui.action_Properties, SLOT(trigger()));
166
167    // context menu
168    QList<QAction*> actions;
169    actions << ui.action_Properties
170            << ui.action_OpenFolder
171            << sep
172            << ui.action_Start
173            << ui.action_Pause
174            << ui.action_Verify
175            << ui.action_Announce
176            << sep
177            << ui.action_Remove
178            << ui.action_Delete;
179    addActions( actions );
180    setContextMenuPolicy( Qt::ActionsContextMenu );
181
182    // signals
183    connect( ui.action_SelectAll, SIGNAL(triggered()), ui.listView, SLOT(selectAll()));
184    connect( ui.action_DeselectAll, SIGNAL(triggered()), ui.listView, SLOT(clearSelection()));
185
186    connect( &myFilterModel, SIGNAL(rowsInserted(const QModelIndex&,int,int)), this, SLOT(refreshVisibleCount()));
187    connect( &myFilterModel, SIGNAL(rowsRemoved(const QModelIndex&,int,int)), this, SLOT(refreshVisibleCount()));
188    connect( &myFilterModel, SIGNAL(rowsInserted(const QModelIndex&,int,int)), this, SLOT(refreshActionSensitivity()));
189    connect( &myFilterModel, SIGNAL(rowsRemoved(const QModelIndex&,int,int)), this, SLOT(refreshActionSensitivity()));
190
191    connect( ui.action_Quit, SIGNAL(triggered()), QCoreApplication::instance(), SLOT(quit()) );
192
193    // torrent view
194    myFilterModel.setSourceModel( &myModel );
195    ui.listView->setModel( &myFilterModel );
196    connect( ui.listView->selectionModel(), SIGNAL(selectionChanged(const QItemSelection&,const QItemSelection&)), this, SLOT(refreshActionSensitivity()));
197
198    QActionGroup * actionGroup = new QActionGroup( this );
199    actionGroup->addAction( ui.action_SortByActivity );
200    actionGroup->addAction( ui.action_SortByAge );
201    actionGroup->addAction( ui.action_SortByETA );
202    actionGroup->addAction( ui.action_SortByName );
203    actionGroup->addAction( ui.action_SortByProgress );
204    actionGroup->addAction( ui.action_SortByRatio );
205    actionGroup->addAction( ui.action_SortBySize );
206    actionGroup->addAction( ui.action_SortByState );
207    actionGroup->addAction( ui.action_SortByTracker );
208
209    QMenu * menu = new QMenu( );
210    menu->addAction( ui.action_Add );
211    menu->addSeparator( );
212    menu->addAction( ui.action_ShowMainWindow );
213    menu->addAction( ui.action_ShowMessageLog );
214    menu->addAction( ui.action_About );
215    menu->addSeparator( );
216    menu->addAction( ui.action_StartAll );
217    menu->addAction( ui.action_PauseAll );
218    menu->addSeparator( );
219    menu->addAction( ui.action_Quit );
220    myTrayIcon.setContextMenu( menu );
221    myTrayIcon.setIcon( QApplication::windowIcon( ) );
222
223    connect( &myPrefs, SIGNAL(changed(int)), this, SLOT(refreshPref(int)) );
224    connect( ui.action_ShowMainWindow, SIGNAL(toggled(bool)), this, SLOT(toggleWindows()));
225    connect( &myTrayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
226             this, SLOT(trayActivated(QSystemTrayIcon::ActivationReason)));
227
228    ui.action_ShowMainWindow->setChecked( !minimized );
229    ui.action_TrayIcon->setChecked( minimized || prefs.getBool( Prefs::SHOW_TRAY_ICON ) );
230
231    ui.verticalLayout->addWidget( createStatusBar( ) );
232    ui.verticalLayout->insertWidget( 0, createFilterBar( ) );
233
234    QList<int> initKeys;
235    initKeys << Prefs :: MAIN_WINDOW_X
236             << Prefs :: SHOW_TRAY_ICON
237             << Prefs :: SORT_REVERSED
238             << Prefs :: SORT_MODE
239             << Prefs :: FILTER_MODE
240             << Prefs :: FILTERBAR
241             << Prefs :: STATUSBAR
242             << Prefs :: STATUSBAR_STATS
243             << Prefs :: TOOLBAR
244             << Prefs :: ALT_SPEED_LIMIT_ENABLED
245             << Prefs :: MINIMAL_VIEW
246             << Prefs :: DSPEED
247             << Prefs :: DSPEED_ENABLED
248             << Prefs :: USPEED
249             << Prefs :: USPEED_ENABLED
250             << Prefs :: RATIO
251             << Prefs :: RATIO_ENABLED;
252    foreach( int key, initKeys )
253        refreshPref( key );
254
255    connect( &mySession, SIGNAL(statsUpdated()), this, SLOT(refreshStatusBar()) );
256    connect( &mySession, SIGNAL(dataReadProgress()), this, SLOT(dataReadProgress()) );
257    connect( &mySession, SIGNAL(dataSendProgress()), this, SLOT(dataSendProgress()) );
258
259    if( mySession.isServer( ) )
260        myNetworkLabel->hide( );
261    else {
262        connect( &myNetworkTimer, SIGNAL(timeout()), this, SLOT(onNetworkTimer()));
263        myNetworkTimer.start( 1000 );
264    }
265
266    refreshActionSensitivity( );
267    refreshStatusBar( );
268    refreshVisibleCount( );
269}
270
271TrMainWindow :: ~TrMainWindow( )
272{
273}
274
275/****
276*****
277****/
278
279#define PREF_VARIANTS_KEY "pref-variants-list"
280
281void
282TrMainWindow :: onSetPrefs( )
283{
284    const QVariantList p = sender()->property( PREF_VARIANTS_KEY ).toList( );
285    assert( ( p.size( ) % 2 ) == 0 );
286    for( int i=0, n=p.size(); i<n; i+=2 )
287        myPrefs.set( p[i].toInt(), p[i+1] );
288}
289
290void
291TrMainWindow :: onSetPrefs( bool isChecked )
292{
293    if( isChecked )
294        onSetPrefs( );
295}
296
297#define SHOW_KEY "show-mode"
298
299void
300TrMainWindow :: onShowModeClicked( )
301{
302    setShowMode( sender()->property(SHOW_KEY).toInt() );
303}
304
305QWidget *
306TrMainWindow :: createFilterBar( )
307{
308    int i;
309    QMenu * m;
310    QLineEdit * e;
311    QPushButton * p;
312    QHBoxLayout * h;
313    QActionGroup * a;
314    const int smallSize = style( )->pixelMetric( QStyle::PM_SmallIconSize, 0, this );
315    const QSize smallIconSize( smallSize, smallSize );
316
317    QWidget * top = myFilterBar = new QWidget;
318    h = new QHBoxLayout( top );
319    h->setContentsMargins( HIG::PAD_SMALL, HIG::PAD_SMALL, HIG::PAD_SMALL, HIG::PAD_SMALL );
320    h->setSpacing( HIG::PAD_SMALL );
321
322        QList<QString> titles;
323        titles << tr( "A&ll" ) << tr( "&Active" ) << tr( "&Downloading" ) << tr( "&Seeding" ) << tr( "&Paused" );
324        for( i=0; i<titles.size(); ++i ) {
325            p = myFilterButtons[i] = new QPushButton( titles[i] );
326            p->setProperty( SHOW_KEY, i );
327            p->setFlat( true );
328            p->setCheckable( true );
329            p->setMaximumSize( calculateTextButtonSizeHint( p ) );
330            connect( p, SIGNAL(clicked()), this, SLOT(onShowModeClicked()));
331            h->addWidget( p );
332        }
333
334    h->addStretch( 1 );
335
336        a = new QActionGroup( this );
337        a->addAction( ui.action_FilterByName );
338        a->addAction( ui.action_FilterByFiles );
339        a->addAction( ui.action_FilterByTracker );
340        m = new QMenu( );
341        m->addAction( ui.action_FilterByName );
342        m->addAction( ui.action_FilterByFiles );
343        m->addAction( ui.action_FilterByTracker );
344        connect( ui.action_FilterByName, SIGNAL(triggered()), this, SLOT(filterByName()));
345        connect( ui.action_FilterByFiles, SIGNAL(triggered()), this, SLOT(filterByFiles()));
346        connect( ui.action_FilterByTracker, SIGNAL(triggered()), this, SLOT(filterByTracker()));
347        ui.action_FilterByName->setChecked( true );
348        p = myFilterTextButton = new TrIconPushButton;
349        p->setIcon( getStockIcon( "edit-find", QStyle::SP_ArrowForward ) );
350        p->setFlat( true );
351        p->setMenu( m );
352        h->addWidget( p );
353
354        e = myFilterTextLineEdit = new QLineEdit;
355        connect( e, SIGNAL(textChanged(QString)), &myFilterModel, SLOT(setText(QString)));
356        h->addWidget( e );
357
358        p = myFilterTextButton = new TrIconPushButton;
359        p->setIcon( getStockIcon( "edit-clear", QStyle::SP_DialogCloseButton ) );
360        p->setFlat( true );
361        connect( p, SIGNAL(clicked()), myFilterTextLineEdit, SLOT(clear()));
362        h->addWidget( p );
363
364    return top;
365}
366
367QWidget *
368TrMainWindow :: createStatusBar( )
369{
370    QMenu * m;
371    QLabel *l, *l2;
372    QWidget *w;
373    QHBoxLayout * h;
374    QPushButton * p;
375    QActionGroup * a;
376    const int i = style( )->pixelMetric( QStyle::PM_SmallIconSize, 0, this );
377    const QSize smallIconSize( i, i );
378
379    QWidget * top = myStatusBar = new QWidget;
380    h = new QHBoxLayout( top );
381    h->setContentsMargins( HIG::PAD_SMALL, HIG::PAD_SMALL, HIG::PAD_SMALL, HIG::PAD_SMALL );
382    h->setSpacing( HIG::PAD_SMALL );
383
384        p = myOptionsButton = new TrIconPushButton( this );
385        p->setIcon( QIcon( ":/icons/options.png" ) );
386        p->setFlat( true );
387        p->setMenu( createOptionsMenu( ) );
388        h->addWidget( p );
389
390        p = myAltSpeedButton = new TrIconPushButton( this );
391        p->setIcon( myPrefs.get<bool>(Prefs::ALT_SPEED_LIMIT_ENABLED) ? mySpeedModeOnIcon : mySpeedModeOffIcon );
392        p->setFlat( true );
393        h->addWidget( p );
394        connect( p, SIGNAL(clicked()), this, SLOT(toggleSpeedMode()));
395
396        l = myNetworkLabel = new QLabel;
397        h->addWidget( l );
398
399    h->addStretch( 1 );
400
401        l = myVisibleCountLabel = new QLabel( this );
402        h->addWidget( l );
403
404    h->addStretch( 1 );
405 
406        a = new QActionGroup( this );
407        a->addAction( ui.action_TotalRatio );
408        a->addAction( ui.action_TotalTransfer );
409        a->addAction( ui.action_SessionRatio );
410        a->addAction( ui.action_SessionTransfer );
411        m = new QMenu( );
412        m->addAction( ui.action_TotalRatio );
413        m->addAction( ui.action_TotalTransfer );
414        m->addAction( ui.action_SessionRatio );
415        m->addAction( ui.action_SessionTransfer );
416        connect( ui.action_TotalRatio, SIGNAL(triggered()), this, SLOT(showTotalRatio()));
417        connect( ui.action_TotalTransfer, SIGNAL(triggered()), this, SLOT(showTotalTransfer()));
418        connect( ui.action_SessionRatio, SIGNAL(triggered()), this, SLOT(showSessionRatio()));
419        connect( ui.action_SessionTransfer, SIGNAL(triggered()), this, SLOT(showSessionTransfer()));
420        p = myStatsModeButton = new TrIconPushButton( this );
421        p->setIcon( getStockIcon( "view-refresh", QStyle::SP_BrowserReload ) );
422        p->setFlat( true );
423        p->setMenu( m );
424        h->addWidget( p ); 
425        l = myStatsLabel = new QLabel( this );
426        h->addWidget( l ); 
427   
428        w = new QWidget( this );
429        w->setMinimumSize( HIG::PAD_BIG, 1 );
430        w->setMaximumSize( HIG::PAD_BIG, 1 );
431        h->addWidget( w );
432        l = new QLabel( this );
433        l->setPixmap( getStockIcon( "go-down", QStyle::SP_ArrowDown ).pixmap( smallIconSize ) );
434        h->addWidget( l );
435        l2 = myDownloadSpeedLabel = new QLabel( this );
436        h->addWidget( l2 );
437        myDownStatusWidgets << w << l << l2;
438
439        w = new QWidget( this );
440        w->setMinimumSize( HIG::PAD_BIG, 1 );
441        w->setMaximumSize( HIG::PAD_BIG, 1 );
442        h->addWidget( w );
443        l = new QLabel;
444        l->setPixmap( getStockIcon( "go-up", QStyle::SP_ArrowUp ).pixmap( smallIconSize ) );
445        h->addWidget( l );
446        l2 = myUploadSpeedLabel = new QLabel;
447        h->addWidget( l2 );
448        myUpStatusWidgets << w << l << l2;
449
450    return top;
451}
452
453QMenu *
454TrMainWindow :: createOptionsMenu( )
455{
456    QMenu * menu;
457    QMenu * sub;
458    QAction * a;
459    QActionGroup * g;
460
461    QList<int> stockSpeeds;
462    stockSpeeds << 5 << 10 << 20 << 30 << 40 << 50 << 75 << 100 << 150 << 200 << 250 << 500 << 750;
463    QList<double> stockRatios;
464    stockRatios << 0.25 << 0.50 << 0.75 << 1 << 1.5 << 2 << 3;
465
466    menu = new QMenu;
467    sub = menu->addMenu( tr( "Limit Download Speed" ) );
468        int currentVal = myPrefs.get<int>( Prefs::DSPEED );
469        g = new QActionGroup( this );
470        a = myDlimitOffAction = sub->addAction( tr( "Unlimited" ) );
471        a->setCheckable( true );
472        a->setProperty( PREF_VARIANTS_KEY, QVariantList() << Prefs::DSPEED_ENABLED << false );
473        g->addAction( a );
474        connect( a, SIGNAL(triggered(bool)), this, SLOT(onSetPrefs(bool)) );
475        a = myDlimitOnAction = sub->addAction( tr( "Limited at %1" ).arg( Utils::speedToString( Speed::fromKbps( currentVal ) ) ) );
476        a->setCheckable( true );
477        a->setProperty( PREF_VARIANTS_KEY, QVariantList() << Prefs::DSPEED << currentVal << Prefs::DSPEED_ENABLED << true );
478        g->addAction( a );
479        connect( a, SIGNAL(triggered(bool)), this, SLOT(onSetPrefs(bool)) );
480        sub->addSeparator( );
481        foreach( int i, stockSpeeds ) {
482            a = sub->addAction( Utils::speedToString( Speed::fromKbps(i) ) );
483            a->setProperty( PREF_VARIANTS_KEY, QVariantList() << Prefs::DSPEED << i << Prefs::DSPEED_ENABLED << true );
484            connect( a, SIGNAL(triggered(bool)), this, SLOT(onSetPrefs()));
485        }
486
487    sub = menu->addMenu( tr( "Limit Upload Speed" ) );
488        currentVal = myPrefs.get<int>( Prefs::USPEED );
489        g = new QActionGroup( this );
490        a = myUlimitOffAction = sub->addAction( tr( "Unlimited" ) );
491        a->setCheckable( true );
492        a->setProperty( PREF_VARIANTS_KEY, QVariantList() << Prefs::USPEED_ENABLED << false );
493        g->addAction( a );
494        connect( a, SIGNAL(triggered(bool)), this, SLOT(onSetPrefs(bool)) );
495        a = myUlimitOnAction = sub->addAction( tr( "Limited at %1" ).arg( Utils::speedToString( Speed::fromKbps( currentVal ) ) ) );
496        a->setCheckable( true );
497        a->setProperty( PREF_VARIANTS_KEY, QVariantList() << Prefs::USPEED << currentVal << Prefs::USPEED_ENABLED << true );
498        g->addAction( a );
499        connect( a, SIGNAL(triggered(bool)), this, SLOT(onSetPrefs(bool)) );
500        sub->addSeparator( );
501        foreach( int i, stockSpeeds ) {
502            a = sub->addAction( Utils::speedToString( Speed::fromKbps(i) ) );
503            a->setProperty( PREF_VARIANTS_KEY, QVariantList() << Prefs::USPEED << i << Prefs::USPEED_ENABLED << true );
504            connect( a, SIGNAL(triggered(bool)), this, SLOT(onSetPrefs()));
505        }
506
507    menu->addSeparator( );
508    sub = menu->addMenu( tr( "Stop Seeding at Ratio" ) );
509
510        double d = myPrefs.get<double>( Prefs::RATIO );
511        g = new QActionGroup( this );
512        a = myRatioOffAction = sub->addAction( tr( "Seed Forever" ) );
513        a->setCheckable( true );
514        a->setProperty( PREF_VARIANTS_KEY, QVariantList() << Prefs::RATIO_ENABLED << false );
515        g->addAction( a );
516        connect( a, SIGNAL(triggered(bool)), this, SLOT(onSetPrefs(bool)) );
517        a = myRatioOnAction = sub->addAction( tr( "Stop at Ratio (%1)" ).arg( Utils::ratioToString( d ) ) );
518        a->setCheckable( true );
519        a->setProperty( PREF_VARIANTS_KEY, QVariantList() << Prefs::RATIO << d << Prefs::RATIO_ENABLED << true );
520        g->addAction( a );
521        connect( a, SIGNAL(triggered(bool)), this, SLOT(onSetPrefs(bool)) );
522        sub->addSeparator( );
523        foreach( double i, stockRatios ) {
524            a = sub->addAction( Utils::ratioToString( i ) );
525            a->setProperty( PREF_VARIANTS_KEY, QVariantList() << Prefs::RATIO << i << Prefs::RATIO_ENABLED << true );
526            connect( a, SIGNAL(triggered(bool)), this, SLOT(onSetPrefs()));
527        }
528
529    return menu;
530}
531
532/****
533*****
534****/
535
536void
537TrMainWindow :: setSortPref( int i )
538{
539    myPrefs.set( Prefs::SORT_MODE, SortMode( i ) );
540}
541void TrMainWindow :: onSortByActivityToggled ( bool b ) { if( b ) setSortPref( SortMode::SORT_BY_ACTIVITY ); }
542void TrMainWindow :: onSortByAgeToggled      ( bool b ) { if( b ) setSortPref( SortMode::SORT_BY_AGE );      }
543void TrMainWindow :: onSortByETAToggled      ( bool b ) { if( b ) setSortPref( SortMode::SORT_BY_ETA );      }
544void TrMainWindow :: onSortByNameToggled     ( bool b ) { if( b ) setSortPref( SortMode::SORT_BY_NAME );     }
545void TrMainWindow :: onSortByProgressToggled ( bool b ) { if( b ) setSortPref( SortMode::SORT_BY_PROGRESS ); }
546void TrMainWindow :: onSortByRatioToggled    ( bool b ) { if( b ) setSortPref( SortMode::SORT_BY_RATIO );    }
547void TrMainWindow :: onSortBySizeToggled     ( bool b ) { if( b ) setSortPref( SortMode::SORT_BY_SIZE );     }
548void TrMainWindow :: onSortByStateToggled    ( bool b ) { if( b ) setSortPref( SortMode::SORT_BY_STATE );    }
549void TrMainWindow :: onSortByTrackerToggled  ( bool b ) { if( b ) setSortPref( SortMode::SORT_BY_TRACKER );  }
550
551void
552TrMainWindow :: setSortAscendingPref( bool b )
553{
554    myPrefs.set( Prefs::SORT_REVERSED, b );
555}
556
557/****
558*****
559****/
560
561void
562TrMainWindow :: onDetailsDestroyed( )
563{
564    myDetailsDialog = 0;
565}
566
567void
568TrMainWindow :: openProperties( )
569{
570    if( myDetailsDialog == 0 ) {
571        myDetailsDialog = new Details( mySession, myModel, this );
572        connect( myDetailsDialog, SIGNAL(destroyed(QObject*)), this, SLOT(onDetailsDestroyed()));
573    }
574
575    myDetailsDialog->setIds( getSelectedTorrents( ) );
576    myDetailsDialog->show( );
577}
578
579void
580TrMainWindow :: openFolder( )
581{
582    const int torrentId( *getSelectedTorrents().begin() );
583    const Torrent * tor( myModel.getTorrentFromId( torrentId ) );
584    const QString path( tor->getPath( ) );
585    QDesktopServices :: openUrl( QUrl::fromLocalFile( path ) );
586}
587
588void
589TrMainWindow :: openHelp( )
590{
591    const char * fmt = "http://www.transmissionbt.com/help/gtk/%d.%dx";
592    int major, minor;
593    sscanf( SHORT_VERSION_STRING, "%d.%d", &major, &minor );
594    char url[128];
595    snprintf( url, sizeof( url ), fmt, major, minor/10 );
596    QDesktopServices :: openUrl( QUrl( QString( url ) ) );
597}
598
599void
600TrMainWindow :: refreshVisibleCount( )
601{
602    const int visibleCount( myFilterModel.rowCount( ) );
603    const int totalCount( visibleCount + myFilterModel.hiddenRowCount( ) );
604    QString str;
605    if( visibleCount == totalCount )
606        str = tr( "%Ln Torrent(s)", 0, totalCount );
607    else
608        str = tr( "%L1 of %Ln Torrent(s)", 0, totalCount ).arg( visibleCount );
609    myVisibleCountLabel->setText( str );
610    myVisibleCountLabel->setVisible( totalCount > 0 );
611}
612
613void
614TrMainWindow :: refreshStatusBar( )
615{
616    const Speed up( myModel.getUploadSpeed( ) );
617    const Speed down( myModel.getDownloadSpeed( ) );
618    myUploadSpeedLabel->setText( Utils :: speedToString( up ) );
619    myDownloadSpeedLabel->setText( Utils :: speedToString( down ) );
620    foreach( QWidget * w, myUpStatusWidgets ) w->setVisible( !up.isZero( ) );
621    foreach( QWidget * w, myDownStatusWidgets ) w->setVisible( !down.isZero( ) );
622
623    const QString mode( myPrefs.getString( Prefs::STATUSBAR_STATS ) );
624    QString str;
625
626    if( mode == "session-ratio" )
627    {
628        str = tr( "Ratio: %1" ).arg( Utils :: ratioToString( mySession.getStats().ratio ) );
629    }
630    else if( mode == "session-transfer" )
631    {
632        const tr_session_stats& stats( mySession.getStats( ) );
633        str = tr( "Down: %1, Up: %2" ).arg( Utils :: sizeToString( stats.downloadedBytes ) )
634                                      .arg( Utils :: sizeToString( stats.uploadedBytes ) );
635    }
636    else if( mode == "total-transfer" )
637    {
638        const tr_session_stats& stats( mySession.getCumulativeStats( ) );
639        str = tr( "Down: %1, Up: %2" ).arg( Utils :: sizeToString( stats.downloadedBytes ) )
640                                      .arg( Utils :: sizeToString( stats.uploadedBytes ) );
641    }
642    else /* default is "total-ratio" */
643    {
644        str = tr( "Ratio: %1" ).arg( Utils :: ratioToString( mySession.getCumulativeStats().ratio ) );
645    }
646
647    myStatsLabel->setText( str );
648}
649
650void
651TrMainWindow :: refreshActionSensitivity( )
652{
653    int selected( 0 );
654    int paused( 0 );
655    int selectedAndPaused( 0 );
656    int canAnnounce( 0 );
657    const QAbstractItemModel * model( ui.listView->model( ) );
658    const QItemSelectionModel * selectionModel( ui.listView->selectionModel( ) );
659    const int rowCount( model->rowCount( ) );
660
661    /* count how many torrents are selected, paused, etc */
662    for( int row=0; row<rowCount; ++row ) {
663        const QModelIndex modelIndex( model->index( row, 0 ) );
664        assert( model == modelIndex.model( ) );
665        const Torrent * tor( model->data( modelIndex, TorrentModel::TorrentRole ).value<const Torrent*>( ) );
666        const bool isSelected( selectionModel->isSelected( modelIndex ) );
667        const bool isPaused( tor->isPaused( ) );
668        if( isSelected )
669            ++selected;
670        if( isPaused )
671            ++ paused;
672        if( isSelected && isPaused )
673            ++selectedAndPaused;
674        if( tor->canManualAnnounce( ) )
675            ++canAnnounce;
676    }
677
678    const bool haveSelection( selected > 0 );
679    ui.action_Verify->setEnabled( haveSelection );
680    ui.action_Remove->setEnabled( haveSelection );
681    ui.action_Delete->setEnabled( haveSelection );
682    ui.action_Properties->setEnabled( haveSelection );
683    ui.action_DeselectAll->setEnabled( haveSelection );
684
685    const bool oneSelection( selected == 1 );
686    ui.action_OpenFolder->setEnabled( oneSelection );
687
688    ui.action_SelectAll->setEnabled( selected < rowCount );
689    ui.action_StartAll->setEnabled( paused > 0 );
690    ui.action_PauseAll->setEnabled( paused < rowCount );
691    ui.action_Start->setEnabled( selectedAndPaused > 0 );
692    ui.action_Pause->setEnabled( selectedAndPaused < selected );
693    ui.action_Announce->setEnabled( selected > 0 && ( canAnnounce == selected ) );
694
695    if( myDetailsDialog )
696        myDetailsDialog->setIds( getSelectedTorrents( ) );
697}
698
699/**
700***
701**/
702
703void
704TrMainWindow :: clearSelection( )
705{
706    ui.action_DeselectAll->trigger( );
707}
708
709QSet<int>
710TrMainWindow :: getSelectedTorrents( ) const
711{
712    QSet<int> ids;
713
714    foreach( QModelIndex index, ui.listView->selectionModel( )->selectedRows( ) )
715    {
716        const Torrent * tor( index.model()->data( index, TorrentModel::TorrentRole ).value<const Torrent*>( ) );
717        ids.insert( tor->id( ) );
718    }
719
720    return ids;
721}
722
723void
724TrMainWindow :: startSelected( )
725{
726    mySession.start( getSelectedTorrents( ) );
727}
728void
729TrMainWindow :: pauseSelected( )
730{
731    mySession.pause( getSelectedTorrents( ) );
732}
733void
734TrMainWindow :: startAll( )
735{
736    mySession.start( );
737}
738void
739TrMainWindow :: pauseAll( )
740{
741    mySession.pause( );
742}
743void
744TrMainWindow :: removeSelected( )
745{
746    mySession.removeTorrents( getSelectedTorrents( ), false );
747}
748void
749TrMainWindow :: deleteSelected( )
750{
751    mySession.removeTorrents( getSelectedTorrents( ), true );
752}
753void
754TrMainWindow :: verifySelected( )
755{
756    mySession.verifyTorrents( getSelectedTorrents( ) );
757}
758void
759TrMainWindow :: reannounceSelected( )
760{
761    mySession.reannounceTorrents( getSelectedTorrents( ) );
762}
763
764/**
765***
766**/
767
768void TrMainWindow :: setShowMode     ( int i ) { myPrefs.set( Prefs::FILTER_MODE, FilterMode( i ) ); }
769void TrMainWindow :: showAll         ( ) { setShowMode( FilterMode :: SHOW_ALL ); }
770void TrMainWindow :: showActive      ( ) { setShowMode( FilterMode :: SHOW_ACTIVE ); }
771void TrMainWindow :: showDownloading ( ) { setShowMode( FilterMode :: SHOW_DOWNLOADING ); }
772void TrMainWindow :: showSeeding     ( ) { setShowMode( FilterMode :: SHOW_SEEDING ); }
773void TrMainWindow :: showPaused      ( ) { setShowMode( FilterMode :: SHOW_PAUSED ); }
774
775void TrMainWindow :: filterByName    ( ) { myFilterModel.setTextMode( TorrentFilter :: FILTER_BY_NAME ); }
776void TrMainWindow :: filterByTracker ( ) { myFilterModel.setTextMode( TorrentFilter :: FILTER_BY_TRACKER ); }
777void TrMainWindow :: filterByFiles   ( ) { myFilterModel.setTextMode( TorrentFilter :: FILTER_BY_FILES ); }
778
779void TrMainWindow :: showTotalRatio      ( ) { myPrefs.set( Prefs::STATUSBAR_STATS, "total-ratio"); }
780void TrMainWindow :: showTotalTransfer   ( ) { myPrefs.set( Prefs::STATUSBAR_STATS, "total-transfer"); }
781void TrMainWindow :: showSessionRatio    ( ) { myPrefs.set( Prefs::STATUSBAR_STATS, "session-ratio"); }
782void TrMainWindow :: showSessionTransfer ( ) { myPrefs.set( Prefs::STATUSBAR_STATS, "session-transfer"); }
783
784/**
785***
786**/
787
788void
789TrMainWindow :: setMinimalView( bool visible )
790{
791    myPrefs.set( Prefs :: MINIMAL_VIEW, visible );
792}
793void
794TrMainWindow :: setTrayIconVisible( bool visible )
795{
796    myPrefs.set( Prefs :: SHOW_TRAY_ICON, visible );
797}
798void
799TrMainWindow :: toggleSpeedMode( )
800{
801    myPrefs.toggleBool( Prefs :: ALT_SPEED_LIMIT_ENABLED );
802}
803void
804TrMainWindow :: setToolbarVisible( bool visible )
805{
806    myPrefs.set( Prefs::TOOLBAR, visible );
807}
808void
809TrMainWindow :: setFilterbarVisible( bool visible )
810{
811    myPrefs.set( Prefs::FILTERBAR, visible );
812}
813void
814TrMainWindow :: setStatusbarVisible( bool visible )
815{
816    myPrefs.set( Prefs::STATUSBAR, visible );
817}
818
819/**
820***
821**/
822
823void
824TrMainWindow :: toggleWindows( )
825{
826    setVisible( !isVisible( ) );
827}
828
829void
830TrMainWindow :: trayActivated( QSystemTrayIcon::ActivationReason reason )
831{
832    if( reason == QSystemTrayIcon::Trigger )
833        ui.action_ShowMainWindow->toggle( );
834}
835
836
837void
838TrMainWindow :: refreshPref( int key )
839{
840    bool b;
841    int i;
842    QString str;
843
844    switch( key )
845    {
846        case Prefs::STATUSBAR_STATS:
847            str = myPrefs.getString( key );
848            ui.action_TotalRatio->setChecked     ( str == "total-ratio" );
849            ui.action_TotalTransfer->setChecked  ( str == "total-transfer" );
850            ui.action_SessionRatio->setChecked   ( str == "session-ratio" );
851            ui.action_SessionTransfer->setChecked( str == "session-transfer" );
852            refreshStatusBar( );
853            break;
854
855        case Prefs::SORT_REVERSED:
856            ui.action_ReverseSortOrder->setChecked( myPrefs.getBool( key ) );
857            break;
858
859        case Prefs::SORT_MODE:
860            i = myPrefs.get<SortMode>(key).mode( );
861            ui.action_SortByActivity->setChecked ( i == SortMode::SORT_BY_ACTIVITY );
862            ui.action_SortByAge->setChecked      ( i == SortMode::SORT_BY_AGE );
863            ui.action_SortByETA->setChecked      ( i == SortMode::SORT_BY_ETA );
864            ui.action_SortByName->setChecked     ( i == SortMode::SORT_BY_NAME );
865            ui.action_SortByProgress->setChecked ( i == SortMode::SORT_BY_PROGRESS );
866            ui.action_SortByRatio->setChecked    ( i == SortMode::SORT_BY_RATIO );
867            ui.action_SortBySize->setChecked     ( i == SortMode::SORT_BY_SIZE );
868            ui.action_SortByState->setChecked    ( i == SortMode::SORT_BY_STATE );
869            ui.action_SortByTracker->setChecked  ( i == SortMode::SORT_BY_TRACKER );
870            break;
871
872        case Prefs::DSPEED_ENABLED:
873            (myPrefs.get<bool>(key) ? myDlimitOnAction : myDlimitOffAction)->setChecked( true );
874            break;
875     
876        case Prefs::DSPEED:
877            myDlimitOnAction->setText( tr( "Limited at %1" ).arg( Utils::speedToString( Speed::fromKbps( myPrefs.get<int>(key) ) ) ) );
878            break;
879
880        case Prefs::USPEED_ENABLED:
881            (myPrefs.get<bool>(key) ? myUlimitOnAction : myUlimitOffAction)->setChecked( true );
882            break;
883     
884        case Prefs::USPEED:
885            myUlimitOnAction->setText( tr( "Limited at %1" ).arg( Utils::speedToString( Speed::fromKbps( myPrefs.get<int>(key) ) ) ) );
886            break;
887
888        case Prefs::RATIO_ENABLED:
889            (myPrefs.get<bool>(key) ? myRatioOnAction : myRatioOffAction)->setChecked( true );
890            break;
891
892        case Prefs::RATIO:
893            myRatioOnAction->setText( tr( "Stop at Ratio (%1)" ).arg( Utils::ratioToString( myPrefs.get<double>(key) ) ) );
894            break;
895
896        case Prefs::FILTER_MODE:
897            i = myPrefs.get<FilterMode>(key).mode( );
898            for( int j=0; j<FilterMode::NUM_MODES; ++j )
899                myFilterButtons[j]->setChecked( i==j );
900            break;
901
902        case Prefs::FILTERBAR:
903            b = myPrefs.getBool( key );
904            myFilterBar->setVisible( b );
905            ui.action_Filterbar->setChecked( b );
906            break;
907
908        case Prefs::STATUSBAR:
909            b = myPrefs.getBool( key );
910            myStatusBar->setVisible( b );
911            ui.action_Statusbar->setChecked( b );
912            break;
913
914        case Prefs::TOOLBAR:
915            b = myPrefs.getBool( key );
916            ui.toolBar->setVisible( b );
917            ui.action_Toolbar->setChecked( b );
918            break;
919
920        case Prefs::SHOW_TRAY_ICON:
921            b = myPrefs.getBool( key );
922            ui.action_TrayIcon->setChecked( b );
923            myTrayIcon.setVisible( b );
924            break;
925
926        case Prefs::MINIMAL_VIEW:
927            b = myPrefs.getBool( key );
928            ui.action_MinimalView->setChecked( b );
929            ui.listView->setItemDelegate( b ? myTorrentDelegateMin : myTorrentDelegate );
930            ui.listView->reset( ); // force the rows to resize
931            break;
932
933        case Prefs::MAIN_WINDOW_X:
934        case Prefs::MAIN_WINDOW_Y:
935        case Prefs::MAIN_WINDOW_WIDTH:
936        case Prefs::MAIN_WINDOW_HEIGHT:
937            setGeometry( myPrefs.getInt( Prefs::MAIN_WINDOW_X ),
938                         myPrefs.getInt( Prefs::MAIN_WINDOW_Y ),
939                         myPrefs.getInt( Prefs::MAIN_WINDOW_WIDTH ),
940                         myPrefs.getInt( Prefs::MAIN_WINDOW_HEIGHT ) );
941            break;
942
943        case Prefs :: ALT_SPEED_LIMIT_ENABLED:
944            b = myPrefs.getBool( key );
945            myAltSpeedButton->setChecked( b );
946            myAltSpeedButton->setIcon( b ? mySpeedModeOnIcon : mySpeedModeOffIcon );
947            myAltSpeedButton->setToolTip( b ? tr( "Click to disable Speed Limit Mode" )
948                                            : tr( "Click to enable Speed Limit Mode" ) );
949            break;
950
951        default:
952            break;
953    }
954}
955
956/***
957****
958***/
959
960void
961TrMainWindow :: newTorrent( )
962{
963    MakeDialog * d = new MakeDialog( mySession, this );
964    d->show( );
965}
966
967void
968TrMainWindow :: openTorrent( )
969{
970    if( myFileDialog == 0 )
971    {
972        myFileDialog = new QFileDialog( this,
973                                        tr( "Add Torrent" ),
974                                        myPrefs.getString( Prefs::OPEN_DIALOG_FOLDER ),
975                                        tr( "Torrent Files (*.torrent);;All Files (*.*)" ) );
976        myFileDialog->setFileMode( QFileDialog::ExistingFiles );
977
978
979        QCheckBox * button = new QCheckBox( tr( "Display &options dialog" ) );
980        button->setChecked( myPrefs.getBool( Prefs::OPTIONS_PROMPT ) );
981        QGridLayout * layout = dynamic_cast<QGridLayout*>(myFileDialog->layout());
982        layout->addWidget( button, layout->rowCount( ), 0, 1, -1, Qt::AlignLeft );
983        myFileDialogOptionsCheck = button;
984
985        connect( myFileDialog, SIGNAL(filesSelected(const QStringList&)), this, SLOT(addTorrents(const QStringList&)));
986    }
987
988    myFileDialog->show( );
989}
990
991void
992TrMainWindow :: addTorrents( const QStringList& filenames )
993{
994    foreach( const QString& filename, filenames )
995        addTorrent( filename );
996}
997
998void
999TrMainWindow :: addTorrent( const QString& filename )
1000{
1001    if( !myFileDialogOptionsCheck->isChecked( ) ) {
1002        mySession.addTorrent( filename );
1003        QApplication :: alert ( this );
1004    } else {
1005        Options * o = new Options( mySession, myPrefs, filename, this );
1006        o->show( );
1007        QApplication :: alert( o );
1008    }
1009}
1010
1011/***
1012****
1013***/
1014
1015void
1016TrMainWindow :: updateNetworkIcon( )
1017{
1018    const time_t now = time( NULL );
1019    const int period = 3;
1020    const bool isSending = now - myLastSendTime <= period;
1021    const bool isReading = now - myLastReadTime <= period;
1022    const char * key;
1023
1024    if( isSending && isReading )
1025        key = "network-transmit-receive";
1026    else if( isSending )
1027        key = "network-transmit";
1028    else if( isReading )
1029        key = "network-receive";
1030    else
1031        key = "network-idle";
1032
1033    QIcon icon = getStockIcon( key, QStyle::SP_DriveNetIcon );
1034    QPixmap pixmap = icon.pixmap ( 16, 16 );
1035    myNetworkLabel->setPixmap( pixmap );
1036    myNetworkLabel->setToolTip( isSending || isReading
1037        ? tr( "Transmission server is responding" )
1038        : tr( "Last response from server was %1 ago" ).arg( Utils::timeToString( now-std::max(myLastReadTime,myLastSendTime))));
1039}
1040
1041void
1042TrMainWindow :: onNetworkTimer( )
1043{
1044    updateNetworkIcon( );
1045}
1046
1047void
1048TrMainWindow :: dataReadProgress( )
1049{
1050    myLastReadTime = time( NULL );
1051    updateNetworkIcon( );
1052}
1053
1054void
1055TrMainWindow :: dataSendProgress( )
1056{
1057    myLastSendTime = time( NULL );
1058    updateNetworkIcon( );
1059}
Note: See TracBrowser for help on using the repository browser.