#include <QHBoxLayout>
#include <QHBoxLayout>
#include <QHeaderView>
+#include <QInputDialog>
#include <QLabel>
+#include <QMessageBox>
#include <QPushButton>
#include <QRadioButton>
#include <QResizeEvent>
#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"
****
***/
+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 ),
// 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 ) );
}
}
}
-
item->setText( 0, str );
-
- trackers2.insert( key, item );
+ 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() );
+
+ 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
****
***/
-void
-Details :: onShowBackupTrackersToggled( bool val )
-{
- myPrefs.set( Prefs::SHOW_BACKUP_TRACKERS, val );
-}
-
void
Details :: onShowTrackerScrapesToggled( bool val )
{
}
}
+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( )
{
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 ) );
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;
}