(qt) #3362:Edit trackers in transmission-qt

This commit is contained in:
Daniel Lee 2010-06-30 05:55:46 +00:00
parent 235dbf607b
commit c8408b755f
6 changed files with 266 additions and 40 deletions

View File

@ -25,7 +25,9 @@
#include <QHBoxLayout>
#include <QHBoxLayout>
#include <QHeaderView>
#include <QInputDialog>
#include <QLabel>
#include <QMessageBox>
#include <QPushButton>
#include <QRadioButton>
#include <QResizeEvent>
@ -39,11 +41,13 @@
#include <QVBoxLayout>
#include <libtransmission/transmission.h>
#include <libtransmission/bencode.h>
#include "details.h"
#include "file-tree.h"
#include "hig.h"
#include "prefs.h"
#include "qticonloader.h"
#include "session.h"
#include "squeezelabel.h"
#include "torrent.h"
@ -117,6 +121,17 @@ class PeerItem: public QTreeWidgetItem
****
***/
QIcon
Details :: getStockIcon( const QString& freedesktop_name, int fallback )
{
QIcon fallbackIcon;
if( fallback > 0 )
fallbackIcon = style()->standardIcon( QStyle::StandardPixmap( fallback ), 0, this );
return QtIconLoader::icon( freedesktop_name, fallbackIcon );
}
Details :: Details( Session& session, Prefs& prefs, TorrentModel& model, QWidget * parent ):
QDialog( parent, Qt::Dialog ),
mySession( session ),
@ -657,30 +672,57 @@ Details :: refresh( )
// tracker tab
//
QMap<QString,QTreeWidgetItem*> trackers2;
QList<QTreeWidgetItem*> newItems2;
QMap<QString,QTreeWidgetItem*> trackerTiers;
QMap<QString,QTreeWidgetItem*> trackerItems;
const time_t now( time( 0 ) );
const bool showBackup = myPrefs.getBool( Prefs::SHOW_BACKUP_TRACKERS );
const bool showScrape = myPrefs.getBool( Prefs::SHOW_TRACKER_SCRAPES );
foreach( const Torrent * t, torrents )
{
const QString idStr( QString::number( t->id( ) ) );
TrackerStatsList trackerStats = t->trackerStats( );
const TrackerStatsList trackerStats = t->trackerStats( );
foreach( const TrackerStat& trackerStat, trackerStats )
{
const QString key( idStr + ":" + QString::number(trackerStat.id) );
QTreeWidgetItem * item = (QTreeWidgetItem*) myTrackerStats.value( key, 0 );
QFont font;
QString str;
const QString tierKey( QString::number(trackerStat.tier) );
QTreeWidgetItem * tier = (QTreeWidgetItem*) myTrackerTiers.value( tierKey, 0 );
if( tier == 0 ) // new tier
{
QFont tierFont;
tier = new QTreeWidgetItem( myTrackerTree );
myTrackerTree->addTopLevelItem( tier );
str = "Tier: " + QString::number( trackerStat.tier + 1 );
tier->setText( 0, str );
tierFont.setBold( true );
tier->setFont( 0, tierFont );
}
const QString key( idStr + tierKey + ":" + QString::number( trackerStat.id ) );
QTreeWidgetItem * item = (QTreeWidgetItem*) myTrackerItems.value( key, 0 );
if( item == 0 ) // new tracker
{
item = new QTreeWidgetItem( myTrackerTree );
newItems2 << item;
item = new QTreeWidgetItem( tier );
tier->addChild( item );
if( tier->childCount() == 1 )
tier->setExpanded( true );
}
str = trackerStat.host;
if( showBackup || !trackerStat.isBackup)
if( trackerStat.isBackup )
{
font.setItalic( true );
if( showScrape )
{
str += "\n";
str += "Tracker will be used as a backup";
}
}
else
{
font.setItalic( false );
if( trackerStat.hasAnnounced )
{
const QString tstr( timeToStringRounded( now - trackerStat.lastAnnounceTime ) );
@ -778,21 +820,37 @@ Details :: refresh( )
}
}
}
item->setText( 0, str );
item->setFont( 0, font );
item->setData( 0, TRACKERID, trackerStat.id );
item->setData( 0, TRACKERURL, trackerStat.announce );
item->setData( 0, TRACKERTIER, trackerStat.tier );
item->setData( 0, TORRENTID, t->id() );
trackers2.insert( key, item );
tier->setData( 0, TRACKERID, -1 );
tier->setData( 0, TRACKERURL, QString() );
tier->setData( 0, TRACKERTIER, trackerStat.tier );
tier->setData( 0, TORRENTID, torrents.count() > 1 ? -1 : t->id() );
trackerTiers.insert( tierKey, tier );
trackerItems.insert( key, item );
}
}
myTrackerTree->addTopLevelItems( newItems2 );
foreach( QString key, myTrackerStats.keys() ) {
if( !trackers2.contains( key ) ) { // tracker has disappeared
QTreeWidgetItem * item = myTrackerStats.value( key, 0 );
myTrackerTree->takeTopLevelItem( myTrackerTree->indexOfTopLevelItem( item ) );
delete item;
QList<QTreeWidgetItem*> tierList = trackerTiers.values();
QList<QTreeWidgetItem*> itemList = trackerItems.values();
for( int i = 0; i < myTrackerTree->topLevelItemCount(); ++i )
{
QTreeWidgetItem * tier = myTrackerTree->topLevelItem( i );
for( int j = 0; j < tier->childCount(); ++j )
{
if( !itemList.contains( tier->child( j ) ) ) // tracker has disappeared
delete tier->takeChild( j-- );
}
if( !tierList.contains( tier ) ) // tier has disappeared
delete myTrackerTree->takeTopLevelItem( i-- );
}
myTrackerStats = trackers2;
myTrackerTiers = trackerTiers;
myTrackerItems = trackerItems;
///
/// Peers tab
@ -933,12 +991,6 @@ Details :: createInfoTab( )
****
***/
void
Details :: onShowBackupTrackersToggled( bool val )
{
myPrefs.set( Prefs::SHOW_BACKUP_TRACKERS, val );
}
void
Details :: onShowTrackerScrapesToggled( bool val )
{
@ -1011,6 +1063,115 @@ Details :: onBandwidthPriorityChanged( int index )
}
}
void
Details :: onTrackerSelectionChanged( )
{
const QList<QTreeWidgetItem*> items = myTrackerTree->selectedItems();
if( items.count() == 1 )
myEditTrackerButton->setEnabled( items.first()->data( 0, TRACKERID ).toInt() >= 0 );
else
myEditTrackerButton->setEnabled( false );
myRemoveTrackerButton->setEnabled( !items.isEmpty() );
}
bool
Details :: findTrackerByURL( const QString& url, int torId )
{
bool duplicate = false;
foreach( QTreeWidgetItem * tracker, myTrackerItems.values() )
{
if( tracker->data( 0, TRACKERURL ).toString() == url &&
( torId == -1 || tracker->data( 0, TORRENTID ).toInt() == torId ) )
{
duplicate = true;
break;
}
}
return duplicate;
}
void
Details :: onAddTrackerPushed( )
{
const QString urlString = QInputDialog::getText( this,
tr( "Add tracker announce URL " ),
NULL );
if( !urlString.isEmpty() )
{
if( !findTrackerByURL( urlString, -1 ) )
{
QByteArray url = urlString.toUtf8();
tr_benc top;
tr_bencInitDict( &top, 1 );
tr_bencDictAddStr( &top, "announce", url );
mySession.torrentSet( myIds, "trackerAdd", &top );
}
else
QMessageBox::warning( this, "Error", "Tracker already exists." );
}
}
void
Details :: onEditTrackerPushed( )
{
const QTreeWidgetItem * item = myTrackerTree->selectedItems().first();
const QString urlString = QInputDialog::getText( this,
tr( "Edit tracker announce URL " ),
NULL,
QLineEdit::Normal,
item->data( 0, TRACKERURL ).toString() );
if( !urlString.isEmpty() )
{
const int torId = item->data( 0, TORRENTID ).toInt();
if( !findTrackerByURL( urlString, torId ) )
{
QByteArray url = urlString.toUtf8();
QSet<int> ids;
tr_benc top;
ids << torId;
tr_bencInitDict( &top, 2 );
tr_bencDictAddStr( &top, "announce", item->data( 0, TRACKERURL ).toByteArray() );
tr_bencDictAddStr( &top, "announce-new", url );
mySession.torrentSet( ids, "trackerEdit", &top );
}
else
QMessageBox::warning( this, "Error", "Tracker already exists." );
}
}
void
Details :: removeTracker( const QTreeWidgetItem * item )
{
QByteArray url = item->data( 0, TRACKERURL ).toByteArray();
const int torId = item->data( 0, TORRENTID ).toInt();
QSet<int> ids;
tr_benc top;
ids << torId;
tr_bencInitDict( &top, 1 );
tr_bencDictAddStr( &top, "announce", url );
mySession.torrentSet( ids, "trackerRemove", &top );
}
void
Details :: onRemoveTrackerPushed( )
{
const QTreeWidgetItem * item = myTrackerTree->selectedItems().first();
const bool isTier = item->data( 0, TRACKERID ).toInt() == -1;
if( isTier )
{
for( int i = 0; i < item->childCount(); ++i )
removeTracker( item->child( i ) );
}
else
removeTracker( item );
}
QWidget *
Details :: createOptionsTab( )
{
@ -1109,21 +1270,63 @@ QWidget *
Details :: createTrackerTab( )
{
QCheckBox * c;
QPushButton * p;
QWidget * top = new QWidget;
QVBoxLayout * v = new QVBoxLayout( top );
QHBoxLayout * h = new QHBoxLayout();
QVBoxLayout * v2 = new QVBoxLayout();
v->setSpacing( HIG :: PAD_BIG );
v->setSpacing( HIG::PAD_BIG );
v->setContentsMargins( HIG::PAD_BIG, HIG::PAD_BIG, HIG::PAD_BIG, HIG::PAD_BIG );
h->setSpacing( HIG::PAD );
h->setContentsMargins( HIG::PAD_SMALL, HIG::PAD_SMALL, HIG::PAD_SMALL, HIG::PAD_SMALL );
v2->setSpacing( HIG::PAD );
QStringList headers;
headers << tr("Trackers");
myTrackerTree = new QTreeWidget;
myTrackerTree->setHeaderLabels( headers );
myTrackerTree->setSelectionMode( QTreeWidget::NoSelection );
myTrackerTree->setSelectionMode( QTreeWidget::SingleSelection );
myTrackerTree->setRootIsDecorated( false );
myTrackerTree->setIndentation( 2 );
myTrackerTree->setItemsExpandable( false );
myTrackerTree->setTextElideMode( Qt::ElideRight );
myTrackerTree->setAlternatingRowColors( true );
v->addWidget( myTrackerTree, 1 );
connect( myTrackerTree, SIGNAL(itemSelectionChanged()), this, SLOT(onTrackerSelectionChanged()));
h->addWidget( myTrackerTree, 1 );
p = new QPushButton();
p->setIcon( getStockIcon( "list-add", QStyle::SP_DialogOpenButton ) );
p->setToolTip( "Add Tracker" );
myAddTrackerButton = p;
v2->addWidget( p, 1 );
connect( p, SIGNAL(clicked(bool)), this, SLOT(onAddTrackerPushed()));
p = new QPushButton();
p->setIcon( getStockIcon( "document-properties", QStyle::SP_DesktopIcon ) );
p->setToolTip( "Edit Tracker" );
myAddTrackerButton = p;
p->setEnabled( false );
myEditTrackerButton = p;
v2->addWidget( p, 1 );
connect( p, SIGNAL(clicked(bool)), this, SLOT(onEditTrackerPushed()));
p = new QPushButton();
p->setIcon( getStockIcon( "list-remove", QStyle::SP_TrashIcon ) );
p->setToolTip( "Remove Trackers" );
p->setEnabled( false );
myRemoveTrackerButton = p;
v2->addWidget( p, 1 );
connect( p, SIGNAL(clicked(bool)), this, SLOT(onRemoveTrackerPushed()));
v2->addStretch( 1 );
h->addLayout( v2, 1 );
h->setStretch( 1, 0 );
v->addLayout( h, 1 );
c = new QCheckBox( tr( "Show &more details" ) );
c->setChecked( myPrefs.getBool( Prefs::SHOW_TRACKER_SCRAPES ) );
@ -1131,12 +1334,6 @@ Details :: createTrackerTab( )
v->addWidget( c, 1 );
connect( c, SIGNAL(clicked(bool)), this, SLOT(onShowTrackerScrapesToggled(bool)) );
c = new QCheckBox( tr( "Show &backup trackers" ) );
c->setChecked( myPrefs.getBool( Prefs::SHOW_BACKUP_TRACKERS ) );
myShowBackupTrackersCheck = c;
v->addWidget( c, 1 );
connect( c, SIGNAL(clicked(bool)), this, SLOT(onShowBackupTrackersToggled(bool)) );
return top;
}

View File

@ -41,6 +41,15 @@ class Details: public QDialog
{
Q_OBJECT
private:
enum
{
TRACKERID = Qt::UserRole,
TRACKERURL,
TRACKERTIER,
TORRENTID
};
private slots:
void onTorrentChanged( );
void onTimer( );
@ -58,12 +67,14 @@ class Details: public QDialog
QWidget * createOptionsTab( );
private:
QIcon getStockIcon( const QString& freedesktop_name, int fallback );
QString timeToStringRounded( int seconds );
QString trimToDesiredWidth( const QString& str );
void enableWhenChecked( QCheckBox *, QWidget * );
bool findTrackerByURL( const QString& url, int torId );
void removeTracker( const QTreeWidgetItem * item );
private:
Session& mySession;
Prefs& myPrefs;
TorrentModel& myModel;
@ -87,7 +98,9 @@ class Details: public QDialog
QCheckBox * mySingleDownCheck;
QCheckBox * mySingleUpCheck;
QCheckBox * myShowTrackerScrapesCheck;
QCheckBox * myShowBackupTrackersCheck;
QPushButton * myAddTrackerButton;
QPushButton * myEditTrackerButton;
QPushButton * myRemoveTrackerButton;
QSpinBox * mySingleDownSpin;
QSpinBox * mySingleUpSpin;
QRadioButton * mySeedGlobalRadio;
@ -115,7 +128,8 @@ class Details: public QDialog
QTreeWidget * myTrackerTree;
QTreeWidget * myPeerTree;
QMap<QString,QTreeWidgetItem*> myTrackerStats;
QMap<QString,QTreeWidgetItem*> myTrackerTiers;
QMap<QString,QTreeWidgetItem*> myTrackerItems;
QMap<QString,QTreeWidgetItem*> myPeers;
QWidgetList myWidgets;
@ -132,8 +146,11 @@ class Details: public QDialog
void onUploadLimitChanged( int );
void onSeedUntilChanged( bool );
void onSeedRatioLimitChanged( double );
void onShowBackupTrackersToggled( bool );
void onShowTrackerScrapesToggled( bool );
void onTrackerSelectionChanged( );
void onAddTrackerPushed( );
void onEditTrackerPushed( );
void onRemoveTrackerPushed( );
void onMaxPeersChanged( int );
void refresh( );
};

View File

@ -42,7 +42,6 @@ Prefs::PrefItem Prefs::myItems[] =
{ SORT_MODE, "sort-mode", TrTypes::SortModeType },
{ SORT_REVERSED, "sort-reversed", QVariant::Bool },
{ COMPACT_VIEW, "compact-view", QVariant::Bool },
{ SHOW_BACKUP_TRACKERS, "show-backup-trackers", QVariant::Bool },
{ FILTERBAR, "show-filterbar", QVariant::Bool },
{ STATUSBAR, "show-statusbar", QVariant::Bool },
{ STATUSBAR_STATS, "statusbar-stats", QVariant::String },
@ -244,7 +243,6 @@ Prefs :: initDefaults( tr_benc * d )
tr_bencDictAddInt( d, keyStr(BLOCKLIST_DATE), 0 );
tr_bencDictAddInt( d, keyStr(BLOCKLIST_UPDATES_ENABLED), true );
tr_bencDictAddStr( d, keyStr(OPEN_DIALOG_FOLDER), QDir::home().absolutePath().toLatin1() );
tr_bencDictAddInt( d, keyStr(SHOW_BACKUP_TRACKERS), false );
tr_bencDictAddInt( d, keyStr(SHOW_TRACKER_SCRAPES), false );
tr_bencDictAddInt( d, keyStr(TOOLBAR), true );
tr_bencDictAddInt( d, keyStr(FILTERBAR), true );

View File

@ -45,7 +45,6 @@ class Prefs: public QObject
SORT_MODE,
SORT_REVERSED,
COMPACT_VIEW,
SHOW_BACKUP_TRACKERS,
FILTERBAR,
STATUSBAR,
STATUSBAR_STATS,

View File

@ -418,6 +418,20 @@ Session :: torrentSet( const QSet<int>& ids, const QString& key, const QList<int
tr_bencFree( &top );
}
void
Session :: torrentSet( const QSet<int>& ids, const QString& key, const tr_benc * value )
{
tr_benc top;
tr_bencInitDict( &top, 2 );
tr_bencDictAddStr( &top, "method", "torrent-set" );
tr_benc * args( tr_bencDictAddDict( &top, "arguments", 2 ) );
addOptionalIds( args, ids );
tr_benc * child( tr_bencDictAdd( args, key.toUtf8().constData() ) );
memcpy( child, value, sizeof(tr_benc) );
exec( &top );
tr_bencFree( &top );
}
void
Session :: torrentSetLocation( const QSet<int>& ids, const QString& location, bool doMove )
{

View File

@ -100,6 +100,7 @@ class Session: public QObject
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 );
void torrentSet( const QSet<int>& ids, const QString& key, const tr_benc * value );
void torrentSetLocation( const QSet<int>& ids, const QString& path, bool doMove );