(trunk qt) handle multiple torrent selection in the properties dialog

This commit is contained in:
Charles Kerr 2009-04-18 23:18:28 +00:00
parent 03fa0b3e7d
commit d82b4a5fc6
9 changed files with 451 additions and 134 deletions

View File

@ -15,6 +15,7 @@
#include <iostream> #include <iostream>
#include <QCheckBox> #include <QCheckBox>
#include <QComboBox>
#include <QEvent> #include <QEvent>
#include <QHeaderView> #include <QHeaderView>
#include <QResizeEvent> #include <QResizeEvent>
@ -47,6 +48,7 @@
#include "session.h" #include "session.h"
#include "squeezelabel.h" #include "squeezelabel.h"
#include "torrent.h" #include "torrent.h"
#include "torrent-model.h"
#include "utils.h" #include "utils.h"
class Prefs; class Prefs;
@ -119,30 +121,36 @@ class PeerItem: public QTreeWidgetItem
**** ****
***/ ***/
Details :: Details( Session& session, Torrent& torrent, QWidget * parent ): Details :: Details( Session& session, TorrentModel& model, QWidget * parent ):
QDialog( parent, Qt::Dialog ), QDialog( parent, Qt::Dialog ),
mySession( session ), mySession( session ),
myTorrent( torrent ) myModel( model ),
myHavePendingRefresh( false )
{ {
QVBoxLayout * layout = new QVBoxLayout( this ); QVBoxLayout * layout = new QVBoxLayout( this );
setWindowTitle( tr( "%1 Properties" ).arg( torrent.name( ) ) ); setWindowTitle( tr( "Torrent Properties" ) );
QTabWidget * t = new QTabWidget( this ); QTabWidget * t = new QTabWidget( this );
t->addTab( createActivityTab( ), tr( "Activity" ) ); QWidget * w;
t->addTab( createPeersTab( ), tr( "Peers" ) ); t->addTab( w = createActivityTab( ), tr( "Activity" ) );
t->addTab( createTrackerTab( ), tr( "Tracker" ) ); myWidgets << w;
t->addTab( createInfoTab( ), tr( "Information" ) ); t->addTab( w = createPeersTab( ), tr( "Peers" ) );
t->addTab( createFilesTab( ), tr( "Files" ) ); myWidgets << w;
t->addTab( createOptionsTab( ), tr( "Options" ) ); t->addTab( w = createTrackerTab( ), tr( "Tracker" ) );
myWidgets << w;
t->addTab( w = createInfoTab( ), tr( "Information" ) );
myWidgets << w;
t->addTab( w = createFilesTab( ), tr( "Files" ) );
myWidgets << w;
t->addTab( w = createOptionsTab( ), tr( "Options" ) );
myWidgets << w;
layout->addWidget( t ); layout->addWidget( t );
QDialogButtonBox * buttons = new QDialogButtonBox( QDialogButtonBox::Close, Qt::Horizontal, this ); QDialogButtonBox * buttons = new QDialogButtonBox( QDialogButtonBox::Close, Qt::Horizontal, this );
connect( buttons, SIGNAL(rejected()), this, SLOT(deleteLater()) ); // "close" triggers rejected connect( buttons, SIGNAL(rejected()), this, SLOT(deleteLater()) ); // "close" triggers rejected
layout->addWidget( buttons ); layout->addWidget( buttons );
connect( &myTorrent, SIGNAL(torrentChanged(int)), this, SLOT(onTorrentChanged()) );
connect( &myTorrent, SIGNAL(destroyed(QObject*)), this, SLOT(deleteLater()) );
connect( &myTimer, SIGNAL(timeout()), this, SLOT(onTimer()) ); connect( &myTimer, SIGNAL(timeout()), this, SLOT(onTimer()) );
onTimer( ); onTimer( );
@ -154,6 +162,34 @@ Details :: ~Details( )
{ {
} }
void
Details :: setIds( const QSet<int>& ids )
{
if( ids == myIds )
return;
// stop listening to the old torrents
foreach( int id, myIds ) {
const Torrent * tor = myModel.getTorrentFromId( id );
if( tor )
disconnect( tor, SIGNAL(torrentChanged(int)), this, SLOT(onTorrentChanged()) );
}
myIds = ids;
// listen to the new torrents
foreach( int id, myIds ) {
const Torrent * tor = myModel.getTorrentFromId( id );
if( tor )
connect( tor, SIGNAL(torrentChanged(int)), this, SLOT(onTorrentChanged()) );
}
foreach( QWidget * w, myWidgets )
w->setEnabled( false );
onTimer( );
}
/*** /***
**** ****
***/ ***/
@ -161,85 +197,292 @@ Details :: ~Details( )
void void
Details :: onTimer( ) Details :: onTimer( )
{ {
mySession.refreshExtraStats( myTorrent.id( ) ); if( !myIds.empty( ) )
mySession.refreshExtraStats( myIds );
} }
void void
Details :: onTorrentChanged( ) Details :: onTorrentChanged( )
{ {
if( !myHavePendingRefresh ) {
myHavePendingRefresh = true;
QTimer::singleShot( 100, this, SLOT(refresh()));
}
}
void
Details :: refresh( )
{
int i;
QLocale locale; QLocale locale;
const int n = myIds.size( );
const bool single = n == 1;
const QString blank;
const QFontMetrics fm( fontMetrics( ) ); const QFontMetrics fm( fontMetrics( ) );
QSet<const Torrent*> torrents;
const Torrent * tor;
QSet<QString> strings;
QString string;
// activity tab // build a list of torrents
myStateLabel->setText( myTorrent.activityString( ) ); foreach( int id, myIds ) {
myProgressLabel->setText( locale.toString( myTorrent.percentDone( )*100.0, 'f', 2 ) ); const Torrent * tor = myModel.getTorrentFromId( id );
if( tor )
torrents << tor;
}
///
/// activity tab
///
// myStateLabel
if( torrents.empty( ) )
string = tr( "None" );
else {
strings.clear( );
foreach( tor, torrents ) strings.insert( tor->activityString( ) );
string = strings.size()==1 ? *strings.begin() : blank;
}
myStateLabel->setText( string );
// myProgressLabel
if( torrents.empty( ) )
string = tr( "None" );
else {
double sizeWhenDone = 0;
double leftUntilDone = 0;
foreach( tor, torrents ) {
sizeWhenDone += tor->sizeWhenDone( );
leftUntilDone += tor->leftUntilDone( );
}
string = locale.toString( 100.0*((sizeWhenDone-leftUntilDone)/sizeWhenDone), 'f', 2 );
}
myProgressLabel->setText( string );
// myHaveLabel
int64_t haveTotal = 0;
int64_t haveVerified = 0;
int64_t verifiedPieces = 0;
foreach( tor, torrents ) {
haveTotal += tor->haveTotal( );
haveVerified += tor->haveVerified( );
verifiedPieces += tor->haveVerified( ) / tor->pieceSize( );
}
myHaveLabel->setText( tr( "%1 (%2 verified in %L3 pieces)" ) myHaveLabel->setText( tr( "%1 (%2 verified in %L3 pieces)" )
.arg( Utils::sizeToString( myTorrent.haveTotal( ) ) ) .arg( Utils::sizeToString( haveTotal ) )
.arg( Utils::sizeToString( myTorrent.haveVerified( ) ) ) .arg( Utils::sizeToString( haveVerified ) )
.arg( myTorrent.haveVerified()/myTorrent.pieceSize() ) ); .arg( verifiedPieces ) );
myDownloadedLabel->setText( Utils::sizeToString( myTorrent.downloadedEver( ) ) );
myUploadedLabel->setText( Utils::sizeToString( myTorrent.uploadedEver( ) ) );
myFailedLabel->setText( Utils::sizeToString( myTorrent.failedEver( ) ) );
myRatioLabel->setText( Utils :: ratioToString( myTorrent.ratio( ) ) );
mySwarmSpeedLabel->setText( Utils::speedToString( myTorrent.swarmSpeed( ) ) );
myAddedDateLabel->setText( myTorrent.dateAdded().toString() );
QDateTime dt = myTorrent.lastActivity( ); int64_t num = 0;
myActivityLabel->setText( dt.isNull() ? tr("Never") : dt.toString() ); foreach( tor, torrents ) num += tor->downloadedEver( );
QString s = myTorrent.getError( ); myDownloadedLabel->setText( Utils::sizeToString( num ) );
myErrorLabel->setText( s.isEmpty() ? tr("None") : s );
// information tab num = 0;
myPiecesLabel->setText( tr( "%L1 Pieces @ %2" ).arg( myTorrent.pieceCount() ) foreach( tor, torrents ) num += tor->uploadedEver( );
.arg( Utils::sizeToString(myTorrent.pieceSize()) ) ); myUploadedLabel->setText( Utils::sizeToString( num ) );
myHashLabel->setText( myTorrent.hashString( ) );
myPrivacyLabel->setText( myTorrent.isPrivate( ) ? tr( "Private to this tracker -- PEX disabled" ) num = 0;
foreach( tor, torrents ) num += tor->failedEver( );
myFailedLabel->setText( Utils::sizeToString( num ) );
double d = 0;
foreach( tor, torrents ) d += tor->ratio( );
myRatioLabel->setText( Utils :: ratioToString( d / n ) );
Speed speed;
foreach( tor, torrents ) speed += tor->swarmSpeed( );
mySwarmSpeedLabel->setText( Utils::speedToString( speed ) );
strings.clear( );
foreach( tor, torrents ) strings.insert( tor->dateAdded().toString() );
string = strings.size()==1 ? *strings.begin() : blank;
myAddedDateLabel->setText( string );
strings.clear( );
foreach( tor, torrents ) {
QDateTime dt = tor->lastActivity( );
strings.insert( dt.isNull() ? tr("Never") : dt.toString() );
}
string = strings.size()==1 ? *strings.begin() : blank;
myActivityLabel->setText( string );
if( torrents.empty( ) )
string = tr( "None" );
else {
strings.clear( );
foreach( tor, torrents ) strings.insert( tor->getError( ) );
string = strings.size()==1 ? *strings.begin() : blank;
}
myErrorLabel->setText( string );
///
/// information tab
///
// myPiecesLabel
int64_t pieceCount = 0;
int64_t pieceSize = 0;
foreach( tor, torrents ) {
pieceCount += tor->pieceCount( );
pieceSize += tor->pieceSize( );
}
myPiecesLabel->setText( tr( "%L1 Pieces @ %2" ).arg( pieceCount )
.arg( Utils::sizeToString( pieceSize ) ) );
// myHashLabel
strings.clear( );
foreach( tor, torrents ) strings.insert( tor->hashString( ) );
string = strings.size()==1 ? *strings.begin() : blank;
myHashLabel->setText( string );
// myPrivacyLabel
strings.clear( );
foreach( tor, torrents )
strings.insert( tor->isPrivate( ) ? tr( "Private to this tracker -- PEX disabled" )
: tr( "Public torrent" ) ); : tr( "Public torrent" ) );
myCommentBrowser->setText( myTorrent.comment( ) ); string = strings.size()==1 ? *strings.begin() : blank;
QString str = myTorrent.creator( ); myPrivacyLabel->setText( string );
if( str.isEmpty( ) )
str = tr( "Unknown" );
myCreatorLabel->setText( str );
myDateCreatedLabel->setText( myTorrent.dateCreated( ).toString( ) );
myDestinationLabel->setText( myTorrent.getPath( ) );
myTorrentFileLabel->setText( myTorrent.torrentFile( ) );
// options tab // myCommentBrowser
mySessionLimitCheck->setChecked( myTorrent.honorsSessionLimits( ) ); strings.clear( );
mySingleDownCheck->setChecked( myTorrent.downloadIsLimited( ) ); foreach( tor, torrents ) strings.insert( tor->comment( ) );
mySingleUpCheck->setChecked( myTorrent.uploadIsLimited( ) ); string = strings.size()==1 ? *strings.begin() : blank;
mySingleDownSpin->setValue( (int)myTorrent.downloadLimit().kbps() ); myCommentBrowser->setText( string );
mySingleUpSpin->setValue( (int)myTorrent.uploadLimit().kbps() );
myPeerLimitSpin->setValue( myTorrent.peerLimit( ) );
// myCreatorLabel
strings.clear( );
foreach( tor, torrents ) strings.insert( tor->creator().isEmpty() ? tr( "Unknown" ) : tor->creator() );
string = strings.size()==1 ? *strings.begin() : blank;
myCreatorLabel->setText( string );
// myDateCreatedLabel
strings.clear( );
foreach( tor, torrents ) strings.insert( tor->dateCreated().toString() );
string = strings.size()==1 ? *strings.begin() : blank;
myDateCreatedLabel->setText( string );
// myDestinationLabel
strings.clear( );
foreach( tor, torrents ) strings.insert( tor->getPath( ) );
string = strings.size()==1 ? *strings.begin() : blank;
myDestinationLabel->setText( string );
// myTorrentFileLabel
strings.clear( );
foreach( tor, torrents ) strings.insert( tor->torrentFile( ) );
string = strings.size()==1 ? *strings.begin() : blank;
myTorrentFileLabel->setText( string );
///
/// Options Tab
///
if( !torrents.empty( ) )
{
int i;
const Torrent * baseline = *torrents.begin();
bool uniform;
bool baselineFlag;
int baselineInt;
// mySessionLimitCheck
uniform = true;
baselineFlag = baseline->honorsSessionLimits( );
foreach( tor, torrents ) if( baselineFlag != tor->honorsSessionLimits( ) ) { uniform = false; break; }
mySessionLimitCheck->setChecked( uniform && baselineFlag );
// mySingleDownCheck
uniform = true;
baselineFlag = baseline->downloadIsLimited( );
foreach( tor, torrents ) if( baselineFlag != tor->downloadIsLimited( ) ) { uniform = false; break; }
mySingleDownCheck->setChecked( uniform && baselineFlag );
// mySingleUpCheck
uniform = true;
baselineFlag = baseline->uploadIsLimited( );
foreach( tor, torrents ) if( baselineFlag != tor->uploadIsLimited( ) ) { uniform = false; break; }
mySingleUpCheck->setChecked( uniform && baselineFlag );
// myBandwidthPriorityCombo
uniform = true;
baselineInt = baseline->getBandwidthPriority( );
foreach( tor, torrents ) if ( baselineInt != tor->getBandwidthPriority( ) ) { uniform = false; break; }
if( uniform )
i = myBandwidthPriorityCombo->findData( baselineInt );
else
i = -1;
myBandwidthPriorityCombo->blockSignals( true );
myBandwidthPriorityCombo->setCurrentIndex( i );
myBandwidthPriorityCombo->blockSignals( false );
mySingleDownSpin->blockSignals( true );
mySingleDownSpin->setValue( tor->downloadLimit().kbps() );
mySingleDownSpin->blockSignals( false );
mySingleUpSpin->blockSignals( true );
mySingleUpSpin->setValue( tor->uploadLimit().kbps() );
mySingleUpSpin->blockSignals( false );
myPeerLimitSpin->blockSignals( true );
myPeerLimitSpin->setValue( tor->peerLimit() );
myPeerLimitSpin->blockSignals( false );
// ratio radios
uniform = true;
baselineInt = tor->seedRatioMode( );
foreach( tor, torrents ) if( baselineInt != tor->seedRatioMode( ) ) { uniform = false; break; }
if( !uniform ) {
mySeedGlobalRadio->setChecked( false );
mySeedCustomRadio->setChecked( false );
mySeedForeverRadio->setChecked( false );
} else {
QRadioButton * rb; QRadioButton * rb;
switch( myTorrent.seedRatioMode( ) ) { switch( baselineInt ) {
case TR_RATIOLIMIT_GLOBAL: rb = mySeedGlobalRadio; break; case TR_RATIOLIMIT_GLOBAL: rb = mySeedGlobalRadio; break;
case TR_RATIOLIMIT_SINGLE: rb = mySeedCustomRadio; break; case TR_RATIOLIMIT_SINGLE: rb = mySeedCustomRadio; break;
case TR_RATIOLIMIT_UNLIMITED: rb = mySeedForeverRadio; break; case TR_RATIOLIMIT_UNLIMITED: rb = mySeedForeverRadio; break;
} }
rb->setChecked( true ); rb->setChecked( true );
mySeedCustomSpin->setValue( myTorrent.seedRatioLimit( ) ); }
mySeedCustomSpin->setValue( tor->seedRatioLimit( ) );
}
// tracker tab // tracker tab
const time_t now( time( 0 ) ); const time_t now( time( 0 ) );
myScrapeTimePrevLabel->setText( myTorrent.lastScrapeTime().toString() ); myScrapeTimePrevLabel->setText( tor ? tor->lastScrapeTime().toString() : blank );
myScrapeResponseLabel->setText( myTorrent.scrapeResponse() ); myScrapeResponseLabel->setText( tor ? tor->scrapeResponse() : blank );
myScrapeTimeNextLabel->setText( Utils :: timeToString( myTorrent.nextScrapeTime().toTime_t() - now ) ); myScrapeTimeNextLabel->setText( Utils :: timeToString( tor ? tor->nextScrapeTime().toTime_t() - now : 0 ) );
myAnnounceTimePrevLabel->setText( myTorrent.lastScrapeTime().toString() ); myAnnounceTimePrevLabel->setText( tor ? tor->lastScrapeTime().toString() : blank );
myAnnounceTimeNextLabel->setText( Utils :: timeToString( myTorrent.nextAnnounceTime().toTime_t() - now ) ); myAnnounceTimeNextLabel->setText( Utils :: timeToString( tor ? tor->nextAnnounceTime().toTime_t() - now : 0 ) );
myAnnounceManualLabel->setText( Utils :: timeToString( myTorrent.manualAnnounceTime().toTime_t() - now ) ); myAnnounceManualLabel->setText( Utils :: timeToString( tor ? tor->manualAnnounceTime().toTime_t() - now : 0 ) );
myAnnounceResponseLabel->setText( myTorrent.announceResponse( ) ); myAnnounceResponseLabel->setText( tor ? tor->announceResponse( ) : blank );
const QUrl url( myTorrent.announceUrl( ) );
myTrackerLabel->setText( url.host( ) );
// peers tab // myTrackerLabel
mySeedersLabel->setText( locale.toString( myTorrent.seeders( ) ) ); strings.clear( );
myLeechersLabel->setText( locale.toString( myTorrent.leechers( ) ) ); foreach( tor, torrents ) strings.insert( QUrl(tor->announceUrl()).host() );
myTimesCompletedLabel->setText( locale.toString( myTorrent.timesCompleted( ) ) ); string = strings.size()==1 ? *strings.begin() : blank;
const PeerList peers( myTorrent.peers( ) ); myTrackerLabel->setText( string );
///
/// Peers tab
///
i = 0;
foreach( tor, torrents ) i += tor->seeders( );
mySeedersLabel->setText( locale.toString( i ) );
i = 0;
foreach( tor, torrents ) i += tor->leechers( );
myLeechersLabel->setText( locale.toString( i ) );
i = 0;
foreach( tor, torrents ) i += tor->timesCompleted( );
myTimesCompletedLabel->setText( locale.toString( i ) );
PeerList peers;
foreach( tor, torrents ) peers << tor->peers( );
QMap<QString,QTreeWidgetItem*> peers2; QMap<QString,QTreeWidgetItem*> peers2;
QList<QTreeWidgetItem*> newItems; QList<QTreeWidgetItem*> newItems;
static const QIcon myEncryptionIcon( ":/icons/encrypted.png" ); static const QIcon myEncryptionIcon( ":/icons/encrypted.png" );
@ -310,7 +553,16 @@ Details :: onTorrentChanged( )
} }
myPeers = peers2; myPeers = peers2;
myFileTreeView->update( myTorrent.files( ) ); if( single ) {
tor = *torrents.begin();
myFileTreeView->update( tor->files( ) );
} else {
myFileTreeView->clear( );
}
myHavePendingRefresh = false;
foreach( QWidget * w, myWidgets )
w->setEnabled( true );
} }
void void
@ -357,27 +609,28 @@ Details :: createActivityTab( )
void void
Details :: onHonorsSessionLimitsToggled( bool val ) Details :: onHonorsSessionLimitsToggled( bool val )
{ {
mySession.torrentSet( myTorrent.id(), "honorsSessionLimits", val ); std::cerr << " honorsSessionLimits clicked to " << val << std::endl;
mySession.torrentSet( myIds, "honorsSessionLimits", val );
} }
void void
Details :: onDownloadLimitedToggled( bool val ) Details :: onDownloadLimitedToggled( bool val )
{ {
mySession.torrentSet( myTorrent.id(), "downloadLimited", val ); mySession.torrentSet( myIds, "downloadLimited", val );
} }
void void
Details :: onDownloadLimitChanged( int val ) Details :: onDownloadLimitChanged( int val )
{ {
mySession.torrentSet( myTorrent.id(), "downloadLimit", val ); mySession.torrentSet( myIds, "downloadLimit", val );
} }
void void
Details :: onUploadLimitedToggled( bool val ) Details :: onUploadLimitedToggled( bool val )
{ {
mySession.torrentSet( myTorrent.id(), "uploadLimited", val ); mySession.torrentSet( myIds, "uploadLimited", val );
} }
void void
Details :: onUploadLimitChanged( int val ) Details :: onUploadLimitChanged( int val )
{ {
mySession.torrentSet( myTorrent.id(), "uploadLimit", val ); mySession.torrentSet( myIds, "uploadLimit", val );
} }
#define RATIO_KEY "seedRatioMode" #define RATIO_KEY "seedRatioMode"
@ -386,19 +639,29 @@ void
Details :: onSeedUntilChanged( bool b ) Details :: onSeedUntilChanged( bool b )
{ {
if( b ) if( b )
mySession.torrentSet( myTorrent.id(), RATIO_KEY, sender()->property(RATIO_KEY).toInt() ); mySession.torrentSet( myIds, RATIO_KEY, sender()->property(RATIO_KEY).toInt() );
} }
void void
Details :: onSeedRatioLimitChanged( double val ) Details :: onSeedRatioLimitChanged( double val )
{ {
mySession.torrentSet( myTorrent.id(), "seedRatioLimit", val ); mySession.torrentSet( myIds, "seedRatioLimit", val );
} }
void void
Details :: onMaxPeersChanged( int val ) Details :: onMaxPeersChanged( int val )
{ {
mySession.torrentSet( myTorrent.id(), "peer-limit", val ); mySession.torrentSet( myIds, "peer-limit", val );
}
void
Details :: onBandwidthPriorityChanged( int index )
{
if( index != -1 )
{
const int priority = myBandwidthPriorityCombo->itemData(index).toInt( );
mySession.torrentSet( myIds, "bandwidthPriority", priority );
}
} }
QWidget * QWidget *
@ -407,17 +670,18 @@ Details :: createOptionsTab( )
//QWidget * l; //QWidget * l;
QSpinBox * s; QSpinBox * s;
QCheckBox * c; QCheckBox * c;
QComboBox * m;
QHBoxLayout * h; QHBoxLayout * h;
QRadioButton * r; QRadioButton * r;
QDoubleSpinBox * ds; QDoubleSpinBox * ds;
HIG * hig = new HIG( this ); HIG * hig = new HIG( this );
hig->addSectionTitle( tr( "Speed Limits" ) ); hig->addSectionTitle( tr( "Speed" ) );
c = new QCheckBox( tr( "Honor global &limits" ) ); c = new QCheckBox( tr( "Honor global &limits" ) );
mySessionLimitCheck = c; mySessionLimitCheck = c;
hig->addWideControl( c ); hig->addWideControl( c );
connect( c, SIGNAL(toggled(bool)), this, SLOT(onHonorsSessionLimitsToggled(bool)) ); connect( c, SIGNAL(clicked(bool)), this, SLOT(onHonorsSessionLimitsToggled(bool)) );
c = new QCheckBox( tr( "Limit &download speed (KB/s)" ) ); c = new QCheckBox( tr( "Limit &download speed (KB/s)" ) );
mySingleDownCheck = c; mySingleDownCheck = c;
@ -426,7 +690,7 @@ Details :: createOptionsTab( )
s->setRange( 0, INT_MAX ); s->setRange( 0, INT_MAX );
hig->addRow( c, s ); hig->addRow( c, s );
enableWhenChecked( c, s ); enableWhenChecked( c, s );
connect( c, SIGNAL(toggled(bool)), this, SLOT(onDownloadLimitedToggled(bool)) ); connect( c, SIGNAL(clicked(bool)), this, SLOT(onDownloadLimitedToggled(bool)) );
connect( s, SIGNAL(valueChanged(int)), this, SLOT(onDownloadLimitChanged(int))); connect( s, SIGNAL(valueChanged(int)), this, SLOT(onDownloadLimitChanged(int)));
c = new QCheckBox( tr( "Limit &upload speed (KB/s)" ) ); c = new QCheckBox( tr( "Limit &upload speed (KB/s)" ) );
@ -436,21 +700,30 @@ Details :: createOptionsTab( )
s->setRange( 0, INT_MAX ); s->setRange( 0, INT_MAX );
hig->addRow( c, s ); hig->addRow( c, s );
enableWhenChecked( c, s ); enableWhenChecked( c, s );
connect( c, SIGNAL(toggled(bool)), this, SLOT(onUploadLimitedToggled(bool)) ); connect( c, SIGNAL(clicked(bool)), this, SLOT(onUploadLimitedToggled(bool)) );
connect( s, SIGNAL(valueChanged(int)), this, SLOT(onUploadLimitChanged(int))); connect( s, SIGNAL(valueChanged(int)), this, SLOT(onUploadLimitChanged(int)));
m = new QComboBox;
m->addItem( tr( "Low" ), TR_PRI_LOW );
m->addItem( tr( "Normal" ), TR_PRI_NORMAL );
m->addItem( tr( "High" ), TR_PRI_HIGH );
connect( m, SIGNAL(currentIndexChanged(int)), this, SLOT(onBandwidthPriorityChanged(int)));
hig->addRow( tr( "Bandwidth priority:" ), m );
myBandwidthPriorityCombo = m;
hig->addSectionDivider( ); hig->addSectionDivider( );
hig->addSectionTitle( tr( "Seed-Until Ratio" ) ); hig->addSectionTitle( tr( "Seed-Until Ratio" ) );
r = new QRadioButton( tr( "Use &global setting" ) ); r = new QRadioButton( tr( "Use &global setting" ) );
r->setProperty( RATIO_KEY, TR_RATIOLIMIT_GLOBAL ); r->setProperty( RATIO_KEY, TR_RATIOLIMIT_GLOBAL );
connect( r, SIGNAL(toggled(bool)), this, SLOT(onSeedUntilChanged(bool))); connect( r, SIGNAL(clicked(bool)), this, SLOT(onSeedUntilChanged(bool)));
mySeedGlobalRadio = r; mySeedGlobalRadio = r;
hig->addWideControl( r ); hig->addWideControl( r );
r = new QRadioButton( tr( "Seed &regardless of ratio" ) ); r = new QRadioButton( tr( "Seed &regardless of ratio" ) );
r->setProperty( RATIO_KEY, TR_RATIOLIMIT_UNLIMITED ); r->setProperty( RATIO_KEY, TR_RATIOLIMIT_UNLIMITED );
connect( r, SIGNAL(toggled(bool)), this, SLOT(onSeedUntilChanged(bool))); connect( r, SIGNAL(clicked(bool)), this, SLOT(onSeedUntilChanged(bool)));
mySeedForeverRadio = r; mySeedForeverRadio = r;
hig->addWideControl( r ); hig->addWideControl( r );
@ -458,7 +731,7 @@ Details :: createOptionsTab( )
h->setSpacing( HIG :: PAD ); h->setSpacing( HIG :: PAD );
r = new QRadioButton( tr( "&Stop seeding when a torrent's ratio reaches" ) ); r = new QRadioButton( tr( "&Stop seeding when a torrent's ratio reaches" ) );
r->setProperty( RATIO_KEY, TR_RATIOLIMIT_SINGLE ); r->setProperty( RATIO_KEY, TR_RATIOLIMIT_SINGLE );
connect( r, SIGNAL(toggled(bool)), this, SLOT(onSeedUntilChanged(bool))); connect( r, SIGNAL(clicked(bool)), this, SLOT(onSeedUntilChanged(bool)));
mySeedCustomRadio = r; mySeedCustomRadio = r;
h->addWidget( r ); h->addWidget( r );
ds = new QDoubleSpinBox( ); ds = new QDoubleSpinBox( );
@ -633,12 +906,12 @@ Details :: onFilePriorityChanged( const QSet<int>& indices, int priority )
case TR_PRI_HIGH: key = "priority-high"; break; case TR_PRI_HIGH: key = "priority-high"; break;
default: key = "priority-normal"; break; default: key = "priority-normal"; break;
} }
mySession.torrentSet( myTorrent.id( ), key, indices.toList( ) ); mySession.torrentSet( myIds, key, indices.toList( ) );
} }
void void
Details :: onFileWantedChanged( const QSet<int>& indices, bool wanted ) Details :: onFileWantedChanged( const QSet<int>& indices, bool wanted )
{ {
QString key( wanted ? "files-wanted" : "files-unwanted" ); QString key( wanted ? "files-wanted" : "files-unwanted" );
mySession.torrentSet( myTorrent.id( ), key, indices.toList( ) ); mySession.torrentSet( myIds, key, indices.toList( ) );
} }

View File

@ -18,11 +18,13 @@
#include <QMap> #include <QMap>
#include <QSet> #include <QSet>
#include <QTimer> #include <QTimer>
#include <QWidgetList>
#include "prefs.h" #include "prefs.h"
class FileTreeView; class FileTreeView;
class QTreeView; class QTreeView;
class QComboBox;
class QCheckBox; class QCheckBox;
class QDoubleSpinBox; class QDoubleSpinBox;
class QLabel; class QLabel;
@ -33,6 +35,7 @@ class QTreeWidget;
class QTreeWidgetItem; class QTreeWidgetItem;
class Session; class Session;
class Torrent; class Torrent;
class TorrentModel;
class Details: public QDialog class Details: public QDialog
{ {
@ -43,8 +46,9 @@ class Details: public QDialog
void onTimer( ); void onTimer( );
public: public:
Details( Session&, Torrent&, QWidget * parent = 0 ); Details( Session&, TorrentModel&, QWidget * parent = 0 );
~Details( ); ~Details( );
void setIds( const QSet<int>& ids );
private: private:
QWidget * createActivityTab( ); QWidget * createActivityTab( );
@ -61,8 +65,10 @@ class Details: public QDialog
private: private:
Session& mySession; Session& mySession;
Torrent& myTorrent; TorrentModel& myModel;
QSet<int> myIds;
QTimer myTimer; QTimer myTimer;
bool myHavePendingRefresh;
QLabel * myStateLabel; QLabel * myStateLabel;
QLabel * myProgressLabel; QLabel * myProgressLabel;
@ -86,6 +92,7 @@ class Details: public QDialog
QRadioButton * mySeedCustomRadio; QRadioButton * mySeedCustomRadio;
QDoubleSpinBox * mySeedCustomSpin; QDoubleSpinBox * mySeedCustomSpin;
QSpinBox * myPeerLimitSpin; QSpinBox * myPeerLimitSpin;
QComboBox * myBandwidthPriorityCombo;
QLabel * myPiecesLabel; QLabel * myPiecesLabel;
QLabel * myHashLabel; QLabel * myHashLabel;
@ -110,10 +117,12 @@ class Details: public QDialog
QLabel * myTimesCompletedLabel; QLabel * myTimesCompletedLabel;
QTreeWidget * myPeerTree; QTreeWidget * myPeerTree;
QMap<QString,QTreeWidgetItem*> myPeers; QMap<QString,QTreeWidgetItem*> myPeers;
QWidgetList myWidgets;
FileTreeView * myFileTreeView; FileTreeView * myFileTreeView;
private slots: private slots:
void onBandwidthPriorityChanged( int );
void onFilePriorityChanged( const QSet<int>& fileIndices, int ); void onFilePriorityChanged( const QSet<int>& fileIndices, int );
void onFileWantedChanged( const QSet<int>& fileIndices, bool ); void onFileWantedChanged( const QSet<int>& fileIndices, bool );
void onHonorsSessionLimitsToggled( bool ); void onHonorsSessionLimitsToggled( bool );
@ -124,6 +133,7 @@ class Details: public QDialog
void onSeedUntilChanged( bool ); void onSeedUntilChanged( bool );
void onSeedRatioLimitChanged( double ); void onSeedRatioLimitChanged( double );
void onMaxPeersChanged( int ); void onMaxPeersChanged( int );
void refresh( );
}; };
#endif #endif

View File

@ -80,6 +80,7 @@ TrMainWindow :: TrMainWindow( Session& session, Prefs& prefs, TorrentModel& mode
myPrefsDialog( new PrefsDialog( session, prefs, this ) ), myPrefsDialog( new PrefsDialog( session, prefs, this ) ),
myAboutDialog( new AboutDialog( this ) ), myAboutDialog( new AboutDialog( this ) ),
myStatsDialog( new StatsDialog( session, this ) ), myStatsDialog( new StatsDialog( session, this ) ),
myDetailsDialog( 0 ),
myFileDialog( 0 ), myFileDialog( 0 ),
myFilterModel( prefs ), myFilterModel( prefs ),
myTorrentDelegate( new TorrentDelegate( this ) ), myTorrentDelegate( new TorrentDelegate( this ) ),
@ -549,14 +550,22 @@ TrMainWindow :: setSortAscendingPref( bool b )
***** *****
****/ ****/
void
TrMainWindow :: onDetailsDestroyed( )
{
myDetailsDialog = 0;
}
void void
TrMainWindow :: openProperties( ) TrMainWindow :: openProperties( )
{ {
const int id( *getSelectedTorrents().begin() ); if( myDetailsDialog == 0 ) {
Torrent * torrent( myModel.getTorrentFromId( id ) ); myDetailsDialog = new Details( mySession, myModel, this );
assert( torrent != 0 ); connect( myDetailsDialog, SIGNAL(destroyed(QObject*)), this, SLOT(onDetailsDestroyed()));
QDialog * d( new Details( mySession, *torrent, this ) ); }
d->show( );
myDetailsDialog->setIds( getSelectedTorrents( ) );
myDetailsDialog->show( );
} }
void void
@ -658,10 +667,10 @@ TrMainWindow :: refreshActionSensitivity( )
ui.action_Verify->setEnabled( haveSelection ); ui.action_Verify->setEnabled( haveSelection );
ui.action_Remove->setEnabled( haveSelection ); ui.action_Remove->setEnabled( haveSelection );
ui.action_Delete->setEnabled( haveSelection ); ui.action_Delete->setEnabled( haveSelection );
ui.action_Properties->setEnabled( haveSelection );
ui.action_DeselectAll->setEnabled( haveSelection ); ui.action_DeselectAll->setEnabled( haveSelection );
const bool oneSelection( selected == 1 ); const bool oneSelection( selected == 1 );
ui.action_Properties->setEnabled( oneSelection );
ui.action_OpenFolder->setEnabled( oneSelection ); ui.action_OpenFolder->setEnabled( oneSelection );
ui.action_SelectAll->setEnabled( selected < rowCount ); ui.action_SelectAll->setEnabled( selected < rowCount );
@ -670,6 +679,9 @@ TrMainWindow :: refreshActionSensitivity( )
ui.action_Start->setEnabled( selectedAndPaused > 0 ); ui.action_Start->setEnabled( selectedAndPaused > 0 );
ui.action_Pause->setEnabled( selectedAndPaused < selected ); ui.action_Pause->setEnabled( selectedAndPaused < selected );
ui.action_Announce->setEnabled( selected > 0 && ( canAnnounce == selected ) ); ui.action_Announce->setEnabled( selected > 0 && ( canAnnounce == selected ) );
if( myDetailsDialog )
myDetailsDialog->setIds( getSelectedTorrents( ) );
} }
/** /**

View File

@ -35,6 +35,7 @@ extern "C" {
class ActionDelegator; class ActionDelegator;
class Prefs; class Prefs;
class Details;
class Session; class Session;
class TorrentDelegate; class TorrentDelegate;
class TorrentDelegateMin; class TorrentDelegateMin;
@ -54,6 +55,7 @@ class TrMainWindow: public QMainWindow
QDialog * myPrefsDialog; QDialog * myPrefsDialog;
QDialog * myAboutDialog; QDialog * myAboutDialog;
QDialog * myStatsDialog; QDialog * myStatsDialog;
Details * myDetailsDialog;
QFileDialog * myFileDialog; QFileDialog * myFileDialog;
QCheckBox * myFileDialogOptionsCheck; QCheckBox * myFileDialogOptionsCheck;
QSystemTrayIcon myTrayIcon; QSystemTrayIcon myTrayIcon;
@ -86,6 +88,7 @@ class TrMainWindow: public QMainWindow
QWidgetList myHidden; QWidgetList myHidden;
private slots: private slots:
void onDetailsDestroyed( );
void onShowModeClicked( ); void onShowModeClicked( );
void showAll( ); void showAll( );
void showActive( ); void showActive( );

View File

@ -297,6 +297,9 @@ PrefsDialog :: createBandwidthTab( )
r = spinBoxNew( Prefs :: ALT_SPEED_LIMIT_UP, 0, INT_MAX, 5 ); r = spinBoxNew( Prefs :: ALT_SPEED_LIMIT_UP, 0, INT_MAX, 5 );
hig->addRow( s, r ); hig->addRow( s, r );
s = tr( "<small>When enabled, Speed Limit Mode overrides the Global Bandwidth Limits</small>" );
hig->addWideControl( new QLabel( s ) );
QCheckBox * c = checkBoxNew( tr( "Use Speed Limit Mode &between" ), Prefs::ALT_SPEED_LIMIT_TIME_ENABLED ); QCheckBox * c = checkBoxNew( tr( "Use Speed Limit Mode &between" ), Prefs::ALT_SPEED_LIMIT_TIME_ENABLED );
h = new QHBoxLayout( ); h = new QHBoxLayout( );
h->setSpacing( HIG::PAD ); h->setSpacing( HIG::PAD );

View File

@ -280,49 +280,61 @@ namespace
const int Session :: ADD_TORRENT_TAG = TAG_ADD_TORRENT; const int Session :: ADD_TORRENT_TAG = TAG_ADD_TORRENT;
void void
Session :: torrentSet( int id, const QString& key, double value ) Session :: torrentSet( const QSet<int>& ids, const QString& key, double value )
{ {
QString s; tr_benc top;
QTextStream( &s ) << "{ \"method\": \"torrent-set\", \"arguments\": { \"ids\": "<<id<<", \""<<key<<"\": "<<value<<" } }"; tr_bencInitDict( &top, 2 );
std::cerr << qPrintable(s) << std::endl; tr_bencDictAddStr( &top, "method", "torrent-set" );
exec( s.toUtf8().constData() ); tr_benc * args = tr_bencDictAddDict( &top, "arguments", 2 );
refreshExtraStats( id ); tr_bencDictAddReal( args, key.toUtf8().constData(), value );
addOptionalIds( args, ids );
std::cerr << tr_bencToJSON(&top) << std::endl;
exec( &top );
tr_bencFree( &top );
} }
void void
Session :: torrentSet( int id, const QString& key, int value ) Session :: torrentSet( const QSet<int>& ids, const QString& key, int value )
{ {
QString s; tr_benc top;
QTextStream( &s ) << "{ \"method\": \"torrent-set\", \"arguments\": { \"ids\": "<<id<<", \""<<key<<"\": "<<value<<" } }"; tr_bencInitDict( &top, 2 );
std::cerr << qPrintable(s) << std::endl; tr_bencDictAddStr( &top, "method", "torrent-set" );
exec( s.toUtf8().constData() ); tr_benc * args = tr_bencDictAddDict( &top, "arguments", 2 );
refreshExtraStats( id ); tr_bencDictAddInt( args, key.toUtf8().constData(), value );
addOptionalIds( args, ids );
std::cerr << tr_bencToJSON(&top) << std::endl;
exec( &top );
tr_bencFree( &top );
} }
void void
Session :: torrentSet( int id, const QString& key, bool value ) Session :: torrentSet( const QSet<int>& ids, const QString& key, bool value )
{ {
QString s; tr_benc top;
QTextStream( &s ) << "{ \"method\": \"torrent-set\", \"arguments\": { \"ids\": "<<id<<", \""<<key<<"\": "<<(value?"true":"false")<<" } }"; tr_bencInitDict( &top, 2 );
std::cerr << qPrintable(s) << std::endl; tr_bencDictAddStr( &top, "method", "torrent-set" );
exec( s.toUtf8().constData() ); tr_benc * args = tr_bencDictAddDict( &top, "arguments", 2 );
refreshExtraStats( id ); tr_bencDictAddBool( args, key.toUtf8().constData(), value );
addOptionalIds( args, ids );
std::cerr << tr_bencToJSON(&top) << std::endl;
exec( &top );
tr_bencFree( &top );
} }
void void
Session :: torrentSet( int id, const QString& key, const QList<int>& value ) Session :: torrentSet( const QSet<int>& ids, const QString& key, const QList<int>& value )
{ {
tr_benc top; tr_benc top;
tr_bencInitDict( &top, 2 ); tr_bencInitDict( &top, 2 );
tr_bencDictAddStr( &top, "method", "torrent-set" ); tr_bencDictAddStr( &top, "method", "torrent-set" );
tr_benc * args( tr_bencDictAddDict( &top, "arguments", 2 ) ); tr_benc * args( tr_bencDictAddDict( &top, "arguments", 2 ) );
tr_bencListAddInt( tr_bencDictAddList( args, "ids", 1 ), id ); addOptionalIds( args, ids );
tr_benc * list( tr_bencDictAddList( args, key.toUtf8().constData(), value.size( ) ) ); tr_benc * list( tr_bencDictAddList( args, key.toUtf8().constData(), value.size( ) ) );
foreach( int i, value ) foreach( int i, value )
tr_bencListAddInt( list, i ); tr_bencListAddInt( list, i );
std::cerr << tr_bencToJSON(&top) << std::endl;
exec( &top ); exec( &top );
tr_bencFree( &top ); tr_bencFree( &top );
refreshExtraStats( id );
} }
@ -348,14 +360,14 @@ Session :: refreshTorrents( const QSet<int>& ids )
} }
void void
Session :: refreshExtraStats( int id ) Session :: refreshExtraStats( const QSet<int>& ids )
{ {
tr_benc top; tr_benc top;
tr_bencInitDict( &top, 3 ); tr_bencInitDict( &top, 3 );
tr_bencDictAddStr( &top, "method", "torrent-get" ); tr_bencDictAddStr( &top, "method", "torrent-get" );
tr_bencDictAddInt( &top, "tag", TAG_SOME_TORRENTS ); tr_bencDictAddInt( &top, "tag", TAG_SOME_TORRENTS );
tr_benc * args( tr_bencDictAddDict( &top, "arguments", 2 ) ); tr_benc * args( tr_bencDictAddDict( &top, "arguments", 2 ) );
tr_bencListAddInt( tr_bencDictAddList( args, "ids", 1 ), id ); addOptionalIds( args, ids );
addList( tr_bencDictAddList( args, "fields", 0 ), getStatKeys( ) + getExtraStatKeys( )); addList( tr_bencDictAddList( args, "fields", 0 ), getStatKeys( ) + getExtraStatKeys( ));
exec( &top ); exec( &top );
tr_bencFree( &top ); tr_bencFree( &top );

View File

@ -77,10 +77,11 @@ class Session: public QObject
void refreshTorrents( const QSet<int>& torrentIds ); void refreshTorrents( const QSet<int>& torrentIds );
public: public:
void torrentSet( int id, const QString& key, bool val ); void torrentSet( const QSet<int>& ids, const QString& key, bool val );
void torrentSet( int id, const QString& key, int val ); void torrentSet( const QSet<int>& ids, const QString& key, int val );
void torrentSet( int id, const QString& key, double val ); void torrentSet( const QSet<int>& ids, const QString& key, double val );
void torrentSet( int id, const QString& key, const QList<int>& val ); void torrentSet( const QSet<int>& ids, const QString& key, const QList<int>& val );
public slots: public slots:
void pause( const QSet<int>& torrentIds = QSet<int>() ); void pause( const QSet<int>& torrentIds = QSet<int>() );
@ -98,7 +99,7 @@ class Session: public QObject
void updatePref( int key ); void updatePref( int key );
/** request a refresh for statistics, including the ones only used by the properties dialog, for a specific torrent */ /** request a refresh for statistics, including the ones only used by the properties dialog, for a specific torrent */
void refreshExtraStats( int torrent ); void refreshExtraStats( const QSet<int>& ids );
private slots: private slots:
void onRequestStarted( int id ); void onRequestStarted( int id );

View File

@ -108,7 +108,8 @@ Torrent :: myProperties[] =
{ LEECHERS, "leechers", QVariant::Int, STAT_EXTRA }, { LEECHERS, "leechers", QVariant::Int, STAT_EXTRA },
{ TIMES_COMPLETED, "timesCompleted", QVariant::Int, STAT_EXTRA }, { TIMES_COMPLETED, "timesCompleted", QVariant::Int, STAT_EXTRA },
{ PEERS, "peers", TrTypes::PeerList, STAT_EXTRA }, { PEERS, "peers", TrTypes::PeerList, STAT_EXTRA },
{ TORRENT_FILE, "torrentFile", QVariant::String, STAT_EXTRA } { TORRENT_FILE, "torrentFile", QVariant::String, STAT_EXTRA },
{ BANDWIDTH_PRIORITY, "bandwidthPriority", QVariant::Int, STAT_EXTRA }
}; };
Torrent :: KeyList Torrent :: KeyList

View File

@ -139,6 +139,7 @@ class Torrent: public QObject
TIMES_COMPLETED, TIMES_COMPLETED,
PEERS, PEERS,
TORRENT_FILE, TORRENT_FILE,
BANDWIDTH_PRIORITY,
PROPERTY_COUNT PROPERTY_COUNT
}; };
@ -200,6 +201,7 @@ class Torrent: public QObject
bool setDateTime ( int key, const QDateTime& ); bool setDateTime ( int key, const QDateTime& );
public: public:
int getBandwidthPriority( ) const { return getInt( BANDWIDTH_PRIORITY ); }
int id( ) const { return getInt( ID ); } int id( ) const { return getInt( ID ); }
QString name( ) const { return getString( NAME ); } QString name( ) const { return getString( NAME ); }
QString creator( ) const { return getString( CREATOR ); } QString creator( ) const { return getString( CREATOR ); }