source: trunk/qt/mainwin.cc @ 8236

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

(trunk qt) #1522: add upload/download/ratio limits popup menu at the bottom of the main window

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