]> granicus.if.org Git - transmission/commitdiff
(qt) #3362:Edit trackers in transmission-qt
authorDaniel Lee <Longinus00@gmail.com>
Wed, 30 Jun 2010 05:55:46 +0000 (05:55 +0000)
committerDaniel Lee <Longinus00@gmail.com>
Wed, 30 Jun 2010 05:55:46 +0000 (05:55 +0000)
qt/details.cc
qt/details.h
qt/prefs.cc
qt/prefs.h
qt/session.cc
qt/session.h

index 2b932e21fdcedfc316fb4f0b4d8b991902c8d33d..246c6848a60fd8a8a88acdb366dba4c720cd53ae 100644 (file)
@@ -25,7 +25,9 @@
 #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"
@@ -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 );
-
-            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
@@ -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;
 }
 
index 3702ffd53fb84ec68039e13f66a9e4a9d56062ef..3917b042e307d2efbc9275af57e20b6fa909b348 100644 (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( );
 };
index 73b94ea6fd2a3da09612cbdfa484918845152287..1ffc5f6591358bd4c38c1ac3631c540d16578d88 100644 (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 );
index 01aeb5d0a5b0875c93bd0824a4ffaa950f928a3f..dde37f1621423d82d3fd8c0b748ebdd422c9e155 100644 (file)
@@ -45,7 +45,6 @@ class Prefs: public QObject
             SORT_MODE,
             SORT_REVERSED,
             COMPACT_VIEW,
-            SHOW_BACKUP_TRACKERS,
             FILTERBAR,
             STATUSBAR,
             STATUSBAR_STATS,
index 0a1c29b07cb03738cba8d9d762cc094b6fa6ffd2..a96bd88453b13a2b3c422288867b140d4d498ec4 100644 (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 )
 {
index 4065f0bda546a4667632b2c1f5226e074c6df243..461852d456403af7aa3a1e9f9bfda4d7ebe84758 100644 (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 );