(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 <QCheckBox>
#include <QComboBox>
#include <QEvent>
#include <QHeaderView>
#include <QResizeEvent>
@ -47,6 +48,7 @@
#include "session.h"
#include "squeezelabel.h"
#include "torrent.h"
#include "torrent-model.h"
#include "utils.h"
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 ),
mySession( session ),
myTorrent( torrent )
myModel( model ),
myHavePendingRefresh( false )
{
QVBoxLayout * layout = new QVBoxLayout( this );
setWindowTitle( tr( "%1 Properties" ).arg( torrent.name( ) ) );
setWindowTitle( tr( "Torrent Properties" ) );
QTabWidget * t = new QTabWidget( this );
t->addTab( createActivityTab( ), tr( "Activity" ) );
t->addTab( createPeersTab( ), tr( "Peers" ) );
t->addTab( createTrackerTab( ), tr( "Tracker" ) );
t->addTab( createInfoTab( ), tr( "Information" ) );
t->addTab( createFilesTab( ), tr( "Files" ) );
t->addTab( createOptionsTab( ), tr( "Options" ) );
QWidget * w;
t->addTab( w = createActivityTab( ), tr( "Activity" ) );
myWidgets << w;
t->addTab( w = createPeersTab( ), tr( "Peers" ) );
myWidgets << w;
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 );
QDialogButtonBox * buttons = new QDialogButtonBox( QDialogButtonBox::Close, Qt::Horizontal, this );
connect( buttons, SIGNAL(rejected()), this, SLOT(deleteLater()) ); // "close" triggers rejected
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()) );
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
Details :: onTimer( )
{
mySession.refreshExtraStats( myTorrent.id( ) );
if( !myIds.empty( ) )
mySession.refreshExtraStats( myIds );
}
void
Details :: onTorrentChanged( )
{
QLocale locale;
const QFontMetrics fm( fontMetrics( ) );
// activity tab
myStateLabel->setText( myTorrent.activityString( ) );
myProgressLabel->setText( locale.toString( myTorrent.percentDone( )*100.0, 'f', 2 ) );
myHaveLabel->setText( tr( "%1 (%2 verified in %L3 pieces)" )
.arg( Utils::sizeToString( myTorrent.haveTotal( ) ) )
.arg( Utils::sizeToString( myTorrent.haveVerified( ) ) )
.arg( myTorrent.haveVerified()/myTorrent.pieceSize() ) );
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( );
myActivityLabel->setText( dt.isNull() ? tr("Never") : dt.toString() );
QString s = myTorrent.getError( );
myErrorLabel->setText( s.isEmpty() ? tr("None") : s );
// information tab
myPiecesLabel->setText( tr( "%L1 Pieces @ %2" ).arg( myTorrent.pieceCount() )
.arg( Utils::sizeToString(myTorrent.pieceSize()) ) );
myHashLabel->setText( myTorrent.hashString( ) );
myPrivacyLabel->setText( myTorrent.isPrivate( ) ? tr( "Private to this tracker -- PEX disabled" )
: tr( "Public torrent" ) );
myCommentBrowser->setText( myTorrent.comment( ) );
QString str = myTorrent.creator( );
if( str.isEmpty( ) )
str = tr( "Unknown" );
myCreatorLabel->setText( str );
myDateCreatedLabel->setText( myTorrent.dateCreated( ).toString( ) );
myDestinationLabel->setText( myTorrent.getPath( ) );
myTorrentFileLabel->setText( myTorrent.torrentFile( ) );
// options tab
mySessionLimitCheck->setChecked( myTorrent.honorsSessionLimits( ) );
mySingleDownCheck->setChecked( myTorrent.downloadIsLimited( ) );
mySingleUpCheck->setChecked( myTorrent.uploadIsLimited( ) );
mySingleDownSpin->setValue( (int)myTorrent.downloadLimit().kbps() );
mySingleUpSpin->setValue( (int)myTorrent.uploadLimit().kbps() );
myPeerLimitSpin->setValue( myTorrent.peerLimit( ) );
QRadioButton * rb;
switch( myTorrent.seedRatioMode( ) ) {
case TR_RATIOLIMIT_GLOBAL: rb = mySeedGlobalRadio; break;
case TR_RATIOLIMIT_SINGLE: rb = mySeedCustomRadio; break;
case TR_RATIOLIMIT_UNLIMITED: rb = mySeedForeverRadio; break;
if( !myHavePendingRefresh ) {
myHavePendingRefresh = true;
QTimer::singleShot( 100, this, SLOT(refresh()));
}
rb->setChecked( true );
mySeedCustomSpin->setValue( myTorrent.seedRatioLimit( ) );
}
void
Details :: refresh( )
{
int i;
QLocale locale;
const int n = myIds.size( );
const bool single = n == 1;
const QString blank;
const QFontMetrics fm( fontMetrics( ) );
QSet<const Torrent*> torrents;
const Torrent * tor;
QSet<QString> strings;
QString string;
// build a list of torrents
foreach( int id, myIds ) {
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)" )
.arg( Utils::sizeToString( haveTotal ) )
.arg( Utils::sizeToString( haveVerified ) )
.arg( verifiedPieces ) );
int64_t num = 0;
foreach( tor, torrents ) num += tor->downloadedEver( );
myDownloadedLabel->setText( Utils::sizeToString( num ) );
num = 0;
foreach( tor, torrents ) num += tor->uploadedEver( );
myUploadedLabel->setText( Utils::sizeToString( num ) );
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" ) );
string = strings.size()==1 ? *strings.begin() : blank;
myPrivacyLabel->setText( string );
// myCommentBrowser
strings.clear( );
foreach( tor, torrents ) strings.insert( tor->comment( ) );
string = strings.size()==1 ? *strings.begin() : blank;
myCommentBrowser->setText( string );
// 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;
switch( baselineInt ) {
case TR_RATIOLIMIT_GLOBAL: rb = mySeedGlobalRadio; break;
case TR_RATIOLIMIT_SINGLE: rb = mySeedCustomRadio; break;
case TR_RATIOLIMIT_UNLIMITED: rb = mySeedForeverRadio; break;
}
rb->setChecked( true );
}
mySeedCustomSpin->setValue( tor->seedRatioLimit( ) );
}
// tracker tab
const time_t now( time( 0 ) );
myScrapeTimePrevLabel->setText( myTorrent.lastScrapeTime().toString() );
myScrapeResponseLabel->setText( myTorrent.scrapeResponse() );
myScrapeTimeNextLabel->setText( Utils :: timeToString( myTorrent.nextScrapeTime().toTime_t() - now ) );
myAnnounceTimePrevLabel->setText( myTorrent.lastScrapeTime().toString() );
myAnnounceTimeNextLabel->setText( Utils :: timeToString( myTorrent.nextAnnounceTime().toTime_t() - now ) );
myAnnounceManualLabel->setText( Utils :: timeToString( myTorrent.manualAnnounceTime().toTime_t() - now ) );
myAnnounceResponseLabel->setText( myTorrent.announceResponse( ) );
const QUrl url( myTorrent.announceUrl( ) );
myTrackerLabel->setText( url.host( ) );
myScrapeTimePrevLabel->setText( tor ? tor->lastScrapeTime().toString() : blank );
myScrapeResponseLabel->setText( tor ? tor->scrapeResponse() : blank );
myScrapeTimeNextLabel->setText( Utils :: timeToString( tor ? tor->nextScrapeTime().toTime_t() - now : 0 ) );
myAnnounceTimePrevLabel->setText( tor ? tor->lastScrapeTime().toString() : blank );
myAnnounceTimeNextLabel->setText( Utils :: timeToString( tor ? tor->nextAnnounceTime().toTime_t() - now : 0 ) );
myAnnounceManualLabel->setText( Utils :: timeToString( tor ? tor->manualAnnounceTime().toTime_t() - now : 0 ) );
myAnnounceResponseLabel->setText( tor ? tor->announceResponse( ) : blank );
// peers tab
mySeedersLabel->setText( locale.toString( myTorrent.seeders( ) ) );
myLeechersLabel->setText( locale.toString( myTorrent.leechers( ) ) );
myTimesCompletedLabel->setText( locale.toString( myTorrent.timesCompleted( ) ) );
const PeerList peers( myTorrent.peers( ) );
// myTrackerLabel
strings.clear( );
foreach( tor, torrents ) strings.insert( QUrl(tor->announceUrl()).host() );
string = strings.size()==1 ? *strings.begin() : blank;
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;
QList<QTreeWidgetItem*> newItems;
static const QIcon myEncryptionIcon( ":/icons/encrypted.png" );
@ -310,7 +553,16 @@ Details :: onTorrentChanged( )
}
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
@ -357,27 +609,28 @@ Details :: createActivityTab( )
void
Details :: onHonorsSessionLimitsToggled( bool val )
{
mySession.torrentSet( myTorrent.id(), "honorsSessionLimits", val );
std::cerr << " honorsSessionLimits clicked to " << val << std::endl;
mySession.torrentSet( myIds, "honorsSessionLimits", val );
}
void
Details :: onDownloadLimitedToggled( bool val )
{
mySession.torrentSet( myTorrent.id(), "downloadLimited", val );
mySession.torrentSet( myIds, "downloadLimited", val );
}
void
Details :: onDownloadLimitChanged( int val )
{
mySession.torrentSet( myTorrent.id(), "downloadLimit", val );
mySession.torrentSet( myIds, "downloadLimit", val );
}
void
Details :: onUploadLimitedToggled( bool val )
{
mySession.torrentSet( myTorrent.id(), "uploadLimited", val );
mySession.torrentSet( myIds, "uploadLimited", val );
}
void
Details :: onUploadLimitChanged( int val )
{
mySession.torrentSet( myTorrent.id(), "uploadLimit", val );
mySession.torrentSet( myIds, "uploadLimit", val );
}
#define RATIO_KEY "seedRatioMode"
@ -386,19 +639,29 @@ void
Details :: onSeedUntilChanged( bool 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
Details :: onSeedRatioLimitChanged( double val )
{
mySession.torrentSet( myTorrent.id(), "seedRatioLimit", val );
mySession.torrentSet( myIds, "seedRatioLimit", val );
}
void
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 *
@ -407,17 +670,18 @@ Details :: createOptionsTab( )
//QWidget * l;
QSpinBox * s;
QCheckBox * c;
QComboBox * m;
QHBoxLayout * h;
QRadioButton * r;
QDoubleSpinBox * ds;
HIG * hig = new HIG( this );
hig->addSectionTitle( tr( "Speed Limits" ) );
hig->addSectionTitle( tr( "Speed" ) );
c = new QCheckBox( tr( "Honor global &limits" ) );
mySessionLimitCheck = 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)" ) );
mySingleDownCheck = c;
@ -426,7 +690,7 @@ Details :: createOptionsTab( )
s->setRange( 0, INT_MAX );
hig->addRow( 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)));
c = new QCheckBox( tr( "Limit &upload speed (KB/s)" ) );
@ -436,21 +700,30 @@ Details :: createOptionsTab( )
s->setRange( 0, INT_MAX );
hig->addRow( 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)));
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->addSectionTitle( tr( "Seed-Until Ratio" ) );
r = new QRadioButton( tr( "Use &global setting" ) );
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;
hig->addWideControl( r );
r = new QRadioButton( tr( "Seed &regardless of ratio" ) );
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;
hig->addWideControl( r );
@ -458,7 +731,7 @@ Details :: createOptionsTab( )
h->setSpacing( HIG :: PAD );
r = new QRadioButton( tr( "&Stop seeding when a torrent's ratio reaches" ) );
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;
h->addWidget( r );
ds = new QDoubleSpinBox( );
@ -633,12 +906,12 @@ Details :: onFilePriorityChanged( const QSet<int>& indices, int priority )
case TR_PRI_HIGH: key = "priority-high"; break;
default: key = "priority-normal"; break;
}
mySession.torrentSet( myTorrent.id( ), key, indices.toList( ) );
mySession.torrentSet( myIds, key, indices.toList( ) );
}
void
Details :: onFileWantedChanged( const QSet<int>& indices, bool wanted )
{
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 <QSet>
#include <QTimer>
#include <QWidgetList>
#include "prefs.h"
class FileTreeView;
class QTreeView;
class QComboBox;
class QCheckBox;
class QDoubleSpinBox;
class QLabel;
@ -33,6 +35,7 @@ class QTreeWidget;
class QTreeWidgetItem;
class Session;
class Torrent;
class TorrentModel;
class Details: public QDialog
{
@ -43,8 +46,9 @@ class Details: public QDialog
void onTimer( );
public:
Details( Session&, Torrent&, QWidget * parent = 0 );
Details( Session&, TorrentModel&, QWidget * parent = 0 );
~Details( );
void setIds( const QSet<int>& ids );
private:
QWidget * createActivityTab( );
@ -61,8 +65,10 @@ class Details: public QDialog
private:
Session& mySession;
Torrent& myTorrent;
TorrentModel& myModel;
QSet<int> myIds;
QTimer myTimer;
bool myHavePendingRefresh;
QLabel * myStateLabel;
QLabel * myProgressLabel;
@ -86,6 +92,7 @@ class Details: public QDialog
QRadioButton * mySeedCustomRadio;
QDoubleSpinBox * mySeedCustomSpin;
QSpinBox * myPeerLimitSpin;
QComboBox * myBandwidthPriorityCombo;
QLabel * myPiecesLabel;
QLabel * myHashLabel;
@ -110,10 +117,12 @@ class Details: public QDialog
QLabel * myTimesCompletedLabel;
QTreeWidget * myPeerTree;
QMap<QString,QTreeWidgetItem*> myPeers;
QWidgetList myWidgets;
FileTreeView * myFileTreeView;
private slots:
void onBandwidthPriorityChanged( int );
void onFilePriorityChanged( const QSet<int>& fileIndices, int );
void onFileWantedChanged( const QSet<int>& fileIndices, bool );
void onHonorsSessionLimitsToggled( bool );
@ -124,6 +133,7 @@ class Details: public QDialog
void onSeedUntilChanged( bool );
void onSeedRatioLimitChanged( double );
void onMaxPeersChanged( int );
void refresh( );
};
#endif

View File

@ -80,6 +80,7 @@ TrMainWindow :: TrMainWindow( Session& session, Prefs& prefs, TorrentModel& mode
myPrefsDialog( new PrefsDialog( session, prefs, this ) ),
myAboutDialog( new AboutDialog( this ) ),
myStatsDialog( new StatsDialog( session, this ) ),
myDetailsDialog( 0 ),
myFileDialog( 0 ),
myFilterModel( prefs ),
myTorrentDelegate( new TorrentDelegate( this ) ),
@ -549,14 +550,22 @@ TrMainWindow :: setSortAscendingPref( bool b )
*****
****/
void
TrMainWindow :: onDetailsDestroyed( )
{
myDetailsDialog = 0;
}
void
TrMainWindow :: openProperties( )
{
const int id( *getSelectedTorrents().begin() );
Torrent * torrent( myModel.getTorrentFromId( id ) );
assert( torrent != 0 );
QDialog * d( new Details( mySession, *torrent, this ) );
d->show( );
if( myDetailsDialog == 0 ) {
myDetailsDialog = new Details( mySession, myModel, this );
connect( myDetailsDialog, SIGNAL(destroyed(QObject*)), this, SLOT(onDetailsDestroyed()));
}
myDetailsDialog->setIds( getSelectedTorrents( ) );
myDetailsDialog->show( );
}
void
@ -658,10 +667,10 @@ TrMainWindow :: refreshActionSensitivity( )
ui.action_Verify->setEnabled( haveSelection );
ui.action_Remove->setEnabled( haveSelection );
ui.action_Delete->setEnabled( haveSelection );
ui.action_Properties->setEnabled( haveSelection );
ui.action_DeselectAll->setEnabled( haveSelection );
const bool oneSelection( selected == 1 );
ui.action_Properties->setEnabled( oneSelection );
ui.action_OpenFolder->setEnabled( oneSelection );
ui.action_SelectAll->setEnabled( selected < rowCount );
@ -670,6 +679,9 @@ TrMainWindow :: refreshActionSensitivity( )
ui.action_Start->setEnabled( selectedAndPaused > 0 );
ui.action_Pause->setEnabled( selectedAndPaused < 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 Prefs;
class Details;
class Session;
class TorrentDelegate;
class TorrentDelegateMin;
@ -54,6 +55,7 @@ class TrMainWindow: public QMainWindow
QDialog * myPrefsDialog;
QDialog * myAboutDialog;
QDialog * myStatsDialog;
Details * myDetailsDialog;
QFileDialog * myFileDialog;
QCheckBox * myFileDialogOptionsCheck;
QSystemTrayIcon myTrayIcon;
@ -86,6 +88,7 @@ class TrMainWindow: public QMainWindow
QWidgetList myHidden;
private slots:
void onDetailsDestroyed( );
void onShowModeClicked( );
void showAll( );
void showActive( );

View File

@ -297,6 +297,9 @@ PrefsDialog :: createBandwidthTab( )
r = spinBoxNew( Prefs :: ALT_SPEED_LIMIT_UP, 0, INT_MAX, 5 );
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 );
h = new QHBoxLayout( );
h->setSpacing( HIG::PAD );

View File

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

View File

@ -77,10 +77,11 @@ class Session: public QObject
void refreshTorrents( const QSet<int>& torrentIds );
public:
void torrentSet( int id, const QString& key, bool val );
void torrentSet( int id, const QString& key, int val );
void torrentSet( int id, 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, bool val );
void torrentSet( const QSet<int>& ids, const QString& key, int val );
void torrentSet( const QSet<int>& ids, const QString& key, double val );
void torrentSet( const QSet<int>& ids, const QString& key, const QList<int>& val );
public slots:
void pause( const QSet<int>& torrentIds = QSet<int>() );
@ -98,7 +99,7 @@ class Session: public QObject
void updatePref( int key );
/** 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:
void onRequestStarted( int id );

View File

@ -108,7 +108,8 @@ Torrent :: myProperties[] =
{ LEECHERS, "leechers", QVariant::Int, STAT_EXTRA },
{ TIMES_COMPLETED, "timesCompleted", QVariant::Int, 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

View File

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