(trunk qt) handle multiple torrent selection in the properties dialog
This commit is contained in:
parent
03fa0b3e7d
commit
d82b4a5fc6
467
qt/details.cc
467
qt/details.cc
|
@ -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 ®ardless 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( ) );
|
||||
}
|
||||
|
|
14
qt/details.h
14
qt/details.h
|
@ -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
|
||||
|
|
|
@ -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( ) );
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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( );
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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 );
|
||||
|
|
11
qt/session.h
11
qt/session.h
|
@ -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 );
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 ); }
|
||||
|
|
Loading…
Reference in New Issue