source: trunk/qt/mainwin.cc @ 9671

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

(trunk) update the copyright notices

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