source: trunk/qt/mainwin.cc @ 9261

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

(trunk Qt) #2298: qtr client crashes when expired torrent is removed

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