(trunk Qt) sync the "trackers" tab with the GTK+ client and more.
This commit is contained in:
parent
f9db0755c0
commit
914af1c9f8
|
@ -104,31 +104,11 @@
|
|||
"seedIdleMode" | number which seeding inactivity to use. See tr_inactvelimit
|
||||
"seedRatioLimit" | double torrent-level seeding ratio
|
||||
"seedRatioMode" | number which ratio to use. See tr_ratiolimit
|
||||
"trackerAdd" | object (see below)
|
||||
"trackerEdit" | object (see below)
|
||||
"trackerRemove" | object (see below)
|
||||
"trackerAdd" | array strings of URLs to add
|
||||
"trackerRemove" | array strings of URLs to remove
|
||||
"trackerReplace" | array pairs of old/new announce URLs
|
||||
"uploadLimit" | number maximum upload speed (KBps)
|
||||
"uploadLimited" | boolean true if "uploadLimit" is honored
|
||||
|
|
||||
----------------------+---------------------------------+
|
||||
trackerAdd | an object containing: |
|
||||
+-----------------------+---------+
|
||||
| announce | string | announce URL of the tracker
|
||||
| tier (optional) | number | tier to add the tracker to
|
||||
----------------------+---------------------------------+
|
||||
trackerEdit | an object containing: |
|
||||
+-----------------------+---------+
|
||||
| announce (or id) | string | announce URL of the tracker to modify
|
||||
| id (or announce) | number | trackerId of the tracker to modify (see trackerStats)
|
||||
+-----------------------+---------+
|
||||
| announce-new | string | new announce URL for the tracker
|
||||
| tier | number | tier to change the tracker to
|
||||
----------------------+---------------------------------+
|
||||
trackerRemove | an object containing: |
|
||||
+-----------------------+---------+
|
||||
| announce (or id) | string | announce URL of the tracker to remove
|
||||
| id (or announce) | number | trackerId of the tracker to remove (see trackerStats)
|
||||
+-----------------------+---------+
|
||||
|
||||
Just as an empty "ids" value is shorthand for "all ids", using an empty array
|
||||
for "files-wanted", "files-unwanted", "priority-high", "priority-low", or
|
||||
|
|
|
@ -757,20 +757,17 @@ setFileDLs( tr_torrent * tor,
|
|||
}
|
||||
|
||||
static tr_bool
|
||||
findTrackerById( const tr_info * inf,
|
||||
uint32_t id,
|
||||
int * index )
|
||||
findAnnounceUrl( const tr_tracker_info * t, int n, const char * url, int * pos )
|
||||
{
|
||||
int i;
|
||||
tr_bool found = FALSE;
|
||||
|
||||
for( i = 0; i < inf->trackerCount; ++i )
|
||||
for( i=0; i<n; ++i )
|
||||
{
|
||||
const tr_tracker_info * t = &inf->trackers[i];
|
||||
if( t->id == id )
|
||||
if( !strcmp( t[i].announce, url ) )
|
||||
{
|
||||
if( index ) *index = i;
|
||||
found = TRUE;
|
||||
if( pos ) *pos = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -778,196 +775,157 @@ findTrackerById( const tr_info * inf,
|
|||
return found;
|
||||
}
|
||||
|
||||
static tr_bool
|
||||
findTrackerByURL( const tr_info * inf,
|
||||
const char * url,
|
||||
int * index )
|
||||
static int
|
||||
copyTrackers( tr_tracker_info * tgt, const tr_tracker_info * src, int n )
|
||||
{
|
||||
int i;
|
||||
tr_bool found = FALSE;
|
||||
|
||||
for( i = 0; i < inf->trackerCount; ++i )
|
||||
int maxTier = -1;
|
||||
|
||||
for( i=0; i<n; ++i )
|
||||
{
|
||||
const tr_tracker_info * t = &inf->trackers[i];
|
||||
if( !strcmp( t->announce, url ) )
|
||||
{
|
||||
if( index ) *index = i;
|
||||
found = TRUE;
|
||||
break;
|
||||
}
|
||||
tgt[i].tier = src[i].tier;
|
||||
tgt[i].announce = tr_strdup( src[i].announce );
|
||||
maxTier = MAX( maxTier, src[i].tier );
|
||||
}
|
||||
|
||||
return found;
|
||||
return maxTier;
|
||||
}
|
||||
|
||||
static void
|
||||
freeTrackers( tr_tracker_info * trackers, int n )
|
||||
{
|
||||
int i;
|
||||
|
||||
for( i=0; i<n; ++i )
|
||||
tr_free( trackers[i].announce );
|
||||
|
||||
tr_free( trackers );
|
||||
}
|
||||
|
||||
static const char*
|
||||
addTracker( tr_torrent * tor,
|
||||
tr_benc * tracker )
|
||||
addTrackerUrls( tr_torrent * tor, tr_benc * urls )
|
||||
{
|
||||
int i;
|
||||
int64_t tmp;
|
||||
tr_bool duplicate = FALSE;
|
||||
const char * errmsg = NULL;
|
||||
const char * announce;
|
||||
int n;
|
||||
int tier;
|
||||
tr_benc * val;
|
||||
tr_tracker_info * trackers;
|
||||
tr_bool changed = FALSE;
|
||||
const tr_info * inf = tr_torrentInfo( tor );
|
||||
const char * errmsg = NULL;
|
||||
|
||||
if( !tr_bencDictFindStr( tracker, "announce", &announce ) )
|
||||
return "no announce url supplied";
|
||||
/* make a working copy of the existing announce list */
|
||||
n = inf->trackerCount;
|
||||
trackers = tr_new0( tr_tracker_info, n + tr_bencListSize( urls ) );
|
||||
tier = copyTrackers( trackers, inf->trackers, n );
|
||||
|
||||
duplicate = findTrackerByURL( inf, announce, NULL );
|
||||
|
||||
if( !duplicate )
|
||||
/* and add the new ones */
|
||||
i = 0;
|
||||
while(( val = tr_bencListChild( urls, i++ ) ))
|
||||
{
|
||||
int tier, trackerCount;
|
||||
tr_tracker_info * trackers = tr_new0( tr_tracker_info, inf->trackerCount + 1 );
|
||||
const char * announce = NULL;
|
||||
|
||||
if( tr_bencDictFindInt( tracker, "tier", &tmp ) )
|
||||
tier = (int)tmp;
|
||||
else
|
||||
tier = -1;
|
||||
|
||||
for( i = 0; i < inf->trackerCount; ++i )
|
||||
if( tr_bencGetStr( val, &announce )
|
||||
&& tr_urlIsValid( announce )
|
||||
&& !findAnnounceUrl( trackers, n, announce, NULL ) )
|
||||
{
|
||||
const tr_tracker_info * t = &inf->trackers[i];
|
||||
trackers[i].tier = t->tier;
|
||||
trackers[i].announce = tr_strdup( t->announce );
|
||||
trackers[n].tier = ++tier; /* add a new tier */
|
||||
trackers[n].announce = tr_strdup( announce );
|
||||
++n;
|
||||
changed = TRUE;
|
||||
}
|
||||
trackers[i].tier = tier < 0 ? trackers[i-1].tier + 1 : tier;
|
||||
trackers[i].announce = tr_strdup( announce );
|
||||
trackerCount = inf->trackerCount + 1;
|
||||
|
||||
if( !tr_torrentSetAnnounceList( tor, trackers, trackerCount ) )
|
||||
errmsg = "tracker URL was invalid";
|
||||
|
||||
for( i = 0; i < trackerCount; ++i )
|
||||
tr_free( trackers[i].announce );
|
||||
tr_free( trackers );
|
||||
}
|
||||
else
|
||||
errmsg = "tracker already exists";
|
||||
|
||||
if( !changed )
|
||||
errmsg = "invalid argument";
|
||||
else if( !tr_torrentSetAnnounceList( tor, trackers, n ) )
|
||||
errmsg = "error setting announce list";
|
||||
|
||||
freeTrackers( trackers, n );
|
||||
return errmsg;
|
||||
}
|
||||
|
||||
static const char*
|
||||
editTracker( tr_torrent * tor,
|
||||
tr_benc * tracker )
|
||||
replaceTrackerUrls( tr_torrent * tor, tr_benc * urls )
|
||||
{
|
||||
int trackerIndex;
|
||||
int64_t tmp;
|
||||
tr_bool found = FALSE;
|
||||
const char * errmsg = NULL;
|
||||
const char * announce;
|
||||
int i;
|
||||
tr_benc * pair[2];
|
||||
tr_tracker_info * trackers;
|
||||
tr_bool changed = FALSE;
|
||||
const tr_info * inf = tr_torrentInfo( tor );
|
||||
const int n = inf->trackerCount;
|
||||
const char * errmsg = NULL;
|
||||
|
||||
if( tr_bencDictFindInt( tracker, "id", &tmp ) )
|
||||
found = findTrackerById( inf, (uint32_t)tmp, &trackerIndex );
|
||||
else if( tr_bencDictFindStr( tracker, "announce", &announce ) )
|
||||
found = findTrackerByURL( inf, announce, &trackerIndex );
|
||||
else
|
||||
errmsg = "no tracker supplied";
|
||||
/* make a working copy of the existing announce list */
|
||||
trackers = tr_new0( tr_tracker_info, n );
|
||||
copyTrackers( trackers, inf->trackers, n );
|
||||
|
||||
if( found )
|
||||
/* make the substitutions... */
|
||||
i = 0;
|
||||
while(((pair[0] = tr_bencListChild(urls,i))) &&
|
||||
((pair[1] = tr_bencListChild(urls,i+1))))
|
||||
{
|
||||
int tier;
|
||||
const char * new;
|
||||
tr_bool rename = FALSE;
|
||||
tr_bool move = FALSE;
|
||||
const char * oldval;
|
||||
const char * newval;
|
||||
|
||||
if( tr_bencDictFindStr( tracker, "announce-new", &new ) )
|
||||
if( tr_bencGetStr( pair[0], &oldval )
|
||||
&& tr_bencGetStr( pair[1], &newval )
|
||||
&& strcmp( oldval, newval )
|
||||
&& tr_urlIsValid( newval )
|
||||
&& findAnnounceUrl( trackers, n, oldval, &i ) )
|
||||
{
|
||||
rename = !findTrackerByURL( inf, new, NULL );
|
||||
if( !rename )
|
||||
errmsg = "tracker already exists";
|
||||
}
|
||||
if( tr_bencDictFindInt( tracker, "tier", &tmp ) )
|
||||
{
|
||||
tier = (int)tmp;
|
||||
move = TRUE;
|
||||
tr_free( trackers[i].announce );
|
||||
trackers[i].announce = tr_strdup( newval );
|
||||
changed = TRUE;
|
||||
}
|
||||
|
||||
if( ( rename || move ) && !errmsg )
|
||||
{
|
||||
int i, trackerCount;
|
||||
tr_tracker_info * trackers = tr_new0( tr_tracker_info, inf->trackerCount );
|
||||
|
||||
for( i = 0; i < inf->trackerCount; ++i )
|
||||
{
|
||||
const tr_tracker_info * t = &inf->trackers[i];
|
||||
if( i != trackerIndex )
|
||||
{
|
||||
trackers[i].tier = t->tier;
|
||||
trackers[i].announce = tr_strdup( t->announce );
|
||||
}
|
||||
else
|
||||
{
|
||||
trackers[i].tier = move ? tier : t->tier;
|
||||
trackers[i].announce = tr_strdup( rename ? new : t->announce );
|
||||
}
|
||||
}
|
||||
trackerCount = i;
|
||||
|
||||
if( !tr_torrentSetAnnounceList( tor, trackers, trackerCount ) )
|
||||
errmsg = "error setting announce list";
|
||||
|
||||
for( i = 0; i < trackerCount; ++i )
|
||||
tr_free( trackers[i].announce );
|
||||
tr_free( trackers );
|
||||
}
|
||||
else if( !errmsg )
|
||||
errmsg = "no operation supplied";
|
||||
i += 2;
|
||||
}
|
||||
else
|
||||
errmsg = "tracker doesn't exists";
|
||||
|
||||
if( !changed )
|
||||
errmsg = "invalid argument";
|
||||
else if( !tr_torrentSetAnnounceList( tor, trackers, n ) )
|
||||
errmsg = "error setting announce list";
|
||||
|
||||
freeTrackers( trackers, n );
|
||||
return errmsg;
|
||||
}
|
||||
|
||||
static const char*
|
||||
removeTracker( tr_torrent * tor,
|
||||
tr_benc * tracker )
|
||||
removeTrackerUrls( tr_torrent * tor, tr_benc * urls )
|
||||
{
|
||||
int trackerIndex;
|
||||
int64_t tmp;
|
||||
tr_bool found = FALSE;
|
||||
const char * errmsg = NULL;
|
||||
const char * announce;
|
||||
int i;
|
||||
int n;
|
||||
tr_benc * val;
|
||||
tr_tracker_info * trackers;
|
||||
tr_bool changed = FALSE;
|
||||
const tr_info * inf = tr_torrentInfo( tor );
|
||||
const char * errmsg = NULL;
|
||||
|
||||
if( tr_bencDictFindInt( tracker, "id", &tmp ) )
|
||||
found = findTrackerById( inf, (uint32_t)tmp, &trackerIndex );
|
||||
else if( tr_bencDictFindStr( tracker, "announce", &announce ) )
|
||||
found = findTrackerByURL( inf, announce, &trackerIndex );
|
||||
else
|
||||
errmsg = "no tracker supplied";
|
||||
/* make a working copy of the existing announce list */
|
||||
n = inf->trackerCount;
|
||||
trackers = tr_new0( tr_tracker_info, n );
|
||||
copyTrackers( trackers, inf->trackers, n );
|
||||
|
||||
if( found )
|
||||
/* remove the ones specified in the urls list */
|
||||
i = 0;
|
||||
while(( val = tr_bencListChild( urls, i++ )))
|
||||
{
|
||||
int i, j, trackerCount;
|
||||
tr_tracker_info * trackers = tr_new0( tr_tracker_info, inf->trackerCount - 1 );
|
||||
|
||||
for( i = 0, j = 0; i < inf->trackerCount; ++i )
|
||||
int pos;
|
||||
const char * url;
|
||||
if( tr_bencGetStr( val, &url ) && findAnnounceUrl( trackers, n, url, &pos ) )
|
||||
{
|
||||
if( i != trackerIndex )
|
||||
{
|
||||
const tr_tracker_info * t = &inf->trackers[i];
|
||||
trackers[j].tier = t->tier;
|
||||
trackers[j].announce = tr_strdup( t->announce );
|
||||
++j;
|
||||
}
|
||||
tr_removeElementFromArray( trackers, pos, sizeof( tr_tracker_info ), n-- );
|
||||
changed = TRUE;
|
||||
}
|
||||
trackerCount = j;
|
||||
|
||||
if( !tr_torrentSetAnnounceList( tor, trackers, trackerCount ) )
|
||||
errmsg = "error setting announce list";
|
||||
|
||||
for( i = 0; i < trackerCount; ++i )
|
||||
tr_free( trackers[i].announce );
|
||||
tr_free( trackers );
|
||||
}
|
||||
else
|
||||
errmsg = "tracker doesn't exists";
|
||||
|
||||
if( !changed )
|
||||
errmsg = "invalid argument";
|
||||
else if( !tr_torrentSetAnnounceList( tor, trackers, n ) )
|
||||
errmsg = "error setting announce list";
|
||||
|
||||
freeTrackers( trackers, n );
|
||||
return errmsg;
|
||||
}
|
||||
|
||||
|
@ -988,7 +946,7 @@ torrentSet( tr_session * session,
|
|||
int64_t tmp;
|
||||
double d;
|
||||
tr_benc * files;
|
||||
tr_benc * tracker;
|
||||
tr_benc * urls;
|
||||
tr_bool boolVal;
|
||||
tr_torrent * tor = torrents[i];
|
||||
|
||||
|
@ -1025,12 +983,12 @@ torrentSet( tr_session * session,
|
|||
tr_torrentSetRatioLimit( tor, d );
|
||||
if( tr_bencDictFindInt( args_in, "seedRatioMode", &tmp ) )
|
||||
tr_torrentSetRatioMode( tor, tmp );
|
||||
if( !errmsg && tr_bencDictFindDict( args_in, "trackerAdd", &tracker ) )
|
||||
errmsg = addTracker( tor, tracker );
|
||||
if( !errmsg && tr_bencDictFindDict( args_in, "trackerEdit", &tracker ) )
|
||||
errmsg = editTracker( tor, tracker );
|
||||
if( !errmsg && tr_bencDictFindDict( args_in, "trackerRemove", &tracker ) )
|
||||
errmsg = removeTracker( tor, tracker );
|
||||
if( !errmsg && tr_bencDictFindList( args_in, "trackerAdd", &urls ) )
|
||||
errmsg = addTrackerUrls( tor, urls );
|
||||
if( !errmsg && tr_bencDictFindList( args_in, "trackerRemove", &urls ) )
|
||||
errmsg = removeTrackerUrls( tor, urls );
|
||||
if( !errmsg && tr_bencDictFindList( args_in, "trackerReplace", &urls ) )
|
||||
errmsg = replaceTrackerUrls( tor, urls );
|
||||
notify( session, TR_RPC_TORRENT_CHANGED, tor );
|
||||
}
|
||||
|
||||
|
|
437
qt/details.cc
437
qt/details.cc
|
@ -12,7 +12,6 @@
|
|||
|
||||
#include <cassert>
|
||||
#include <ctime>
|
||||
#include <iostream>
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QComboBox>
|
||||
|
@ -26,12 +25,16 @@
|
|||
#include <QHBoxLayout>
|
||||
#include <QHeaderView>
|
||||
#include <QInputDialog>
|
||||
#include <QItemSelectionModel>
|
||||
#include <QLabel>
|
||||
#include <QList>
|
||||
#include <QMap>
|
||||
#include <QMessageBox>
|
||||
#include <QPushButton>
|
||||
#include <QRadioButton>
|
||||
#include <QResizeEvent>
|
||||
#include <QSpinBox>
|
||||
#include <QStringList>
|
||||
#include <QStyle>
|
||||
#include <QTabWidget>
|
||||
#include <QTextBrowser>
|
||||
|
@ -53,6 +56,8 @@
|
|||
#include "squeezelabel.h"
|
||||
#include "torrent.h"
|
||||
#include "torrent-model.h"
|
||||
#include "tracker-delegate.h"
|
||||
#include "tracker-model.h"
|
||||
|
||||
class Prefs;
|
||||
class Session;
|
||||
|
@ -163,7 +168,13 @@ Details :: Details( Session& session, Prefs& prefs, TorrentModel& model, QWidget
|
|||
layout->addWidget( buttons );
|
||||
QWidget::setAttribute( Qt::WA_DeleteOnClose, true );
|
||||
|
||||
QList<int> initKeys;
|
||||
initKeys << Prefs :: SHOW_TRACKER_SCRAPES;
|
||||
foreach( int key, initKeys )
|
||||
refreshPref( key );
|
||||
|
||||
connect( &myTimer, SIGNAL(timeout()), this, SLOT(onTimer()));
|
||||
connect( &myPrefs, SIGNAL(changed(int)), this, SLOT(refreshPref(int)) );
|
||||
|
||||
onTimer( );
|
||||
myTimer.setSingleShot( false );
|
||||
|
@ -190,7 +201,6 @@ Details :: setIds( const QSet<int>& ids )
|
|||
}
|
||||
|
||||
myFileTreeView->clear( );
|
||||
|
||||
myIds = ids;
|
||||
|
||||
// listen to the new torrents
|
||||
|
@ -206,6 +216,24 @@ Details :: setIds( const QSet<int>& ids )
|
|||
onTimer( );
|
||||
}
|
||||
|
||||
void
|
||||
Details :: refreshPref( int key )
|
||||
{
|
||||
QString str;
|
||||
|
||||
switch( key )
|
||||
{
|
||||
case Prefs::SHOW_TRACKER_SCRAPES:
|
||||
myTrackerDelegate->setShowMore( myPrefs.getBool( key ) );
|
||||
myTrackerView->update( );
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/***
|
||||
****
|
||||
***/
|
||||
|
@ -219,6 +247,12 @@ Details :: timeToStringRounded( int seconds )
|
|||
|
||||
void
|
||||
Details :: onTimer( )
|
||||
{
|
||||
getNewData( );
|
||||
}
|
||||
|
||||
void
|
||||
Details :: getNewData( )
|
||||
{
|
||||
if( !myIds.empty( ) )
|
||||
{
|
||||
|
@ -680,190 +714,11 @@ Details :: refresh( )
|
|||
myIdleSpin->blockSignals( false );
|
||||
}
|
||||
|
||||
// tracker tab
|
||||
//
|
||||
QMap<QString,QTreeWidgetItem*> trackerTiers;
|
||||
QMap<QString,QTreeWidgetItem*> trackerItems;
|
||||
const time_t now( time( 0 ) );
|
||||
const bool showScrape = myPrefs.getBool( Prefs::SHOW_TRACKER_SCRAPES );
|
||||
foreach( const Torrent * t, torrents )
|
||||
{
|
||||
const QString idStr( QString::number( t->id( ) ) );
|
||||
const TrackerStatsList trackerStats = t->trackerStats( );
|
||||
///
|
||||
/// Tracker tab
|
||||
///
|
||||
|
||||
foreach( const TrackerStat& trackerStat, trackerStats )
|
||||
{
|
||||
QFont font;
|
||||
QString str;
|
||||
const QString tierKey( QString::number(trackerStat.tier) );
|
||||
QTreeWidgetItem * tier = (QTreeWidgetItem*) myTrackerTiers.value( tierKey, 0 );
|
||||
|
||||
if( tier == 0 ) // check if has tier been created this pass
|
||||
tier = (QTreeWidgetItem*) trackerTiers.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( tier );
|
||||
tier->addChild( item );
|
||||
if( tier->childCount() == 1 )
|
||||
tier->setExpanded( true );
|
||||
}
|
||||
str = trackerStat.host;
|
||||
|
||||
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 ) );
|
||||
str += "\n";
|
||||
if( trackerStat.lastAnnounceSucceeded )
|
||||
{
|
||||
str += tr( "Got a list of %1 peers %2 ago" )
|
||||
.arg( trackerStat.lastAnnouncePeerCount )
|
||||
.arg( tstr );
|
||||
}
|
||||
else if( trackerStat.lastAnnounceTimedOut )
|
||||
{
|
||||
str += tr( "Peer list request timed out %1 ago; will retry" )
|
||||
.arg( tstr );
|
||||
}
|
||||
else
|
||||
{
|
||||
str += tr( "Got an error %1 ago" )
|
||||
.arg( tstr );
|
||||
}
|
||||
}
|
||||
switch( trackerStat.announceState )
|
||||
{
|
||||
case TR_TRACKER_INACTIVE:
|
||||
if( trackerStat.hasAnnounced )
|
||||
{
|
||||
str += "\n";
|
||||
str += tr( "No updates scheduled" );
|
||||
}
|
||||
break;
|
||||
case TR_TRACKER_WAITING:
|
||||
{
|
||||
const QString tstr( timeToStringRounded( trackerStat.nextAnnounceTime - now ) );
|
||||
str += "\n";
|
||||
str += tr( "Asking for more peers in %1" )
|
||||
.arg( tstr );
|
||||
}
|
||||
break;
|
||||
case TR_TRACKER_QUEUED:
|
||||
str += "\n";
|
||||
str += tr( "Queued to ask for more peers" );
|
||||
break;
|
||||
case TR_TRACKER_ACTIVE:
|
||||
{
|
||||
const QString tstr( timeToStringRounded( now - trackerStat.lastAnnounceStartTime ) );
|
||||
str += "\n";
|
||||
str += tr( "Asking for more peers now... %1" )
|
||||
.arg( tstr );
|
||||
}
|
||||
break;
|
||||
}
|
||||
if( showScrape )
|
||||
{
|
||||
if( trackerStat.hasScraped )
|
||||
{
|
||||
const QString tstr( timeToStringRounded( now - trackerStat.lastScrapeTime ) );
|
||||
str += "\n";
|
||||
if( trackerStat.lastScrapeSucceeded )
|
||||
{
|
||||
str += tr( "Tracker had %1 seeders and %2 leechers %3 ago" )
|
||||
.arg( trackerStat.seederCount )
|
||||
.arg( trackerStat.leecherCount )
|
||||
.arg( tstr );
|
||||
}
|
||||
else
|
||||
{
|
||||
str += tr( "Got a scrape error %1 ago" )
|
||||
.arg( tstr );
|
||||
}
|
||||
}
|
||||
switch( trackerStat.scrapeState )
|
||||
{
|
||||
case TR_TRACKER_INACTIVE:
|
||||
break;
|
||||
case TR_TRACKER_WAITING:
|
||||
{
|
||||
const QString tstr( timeToStringRounded( trackerStat.nextScrapeTime - now ) );
|
||||
str += "\n";
|
||||
str += tr( "Asking for peer counts in %1" )
|
||||
.arg( tstr );
|
||||
}
|
||||
break;
|
||||
case TR_TRACKER_QUEUED:
|
||||
str += "\n";
|
||||
str += tr( "Queued to ask for peer counts" );
|
||||
break;
|
||||
case TR_TRACKER_ACTIVE:
|
||||
{
|
||||
const QString tstr( timeToStringRounded( now - trackerStat.lastScrapeStartTime ) );
|
||||
str += "\n";
|
||||
str += tr( "Asking for peer counts now... %1" )
|
||||
.arg( tstr );
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
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() );
|
||||
|
||||
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 );
|
||||
}
|
||||
}
|
||||
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-- );
|
||||
}
|
||||
myTrackerTiers = trackerTiers;
|
||||
myTrackerItems = trackerItems;
|
||||
myTrackerModel->refresh( myModel, myIds );
|
||||
|
||||
///
|
||||
/// Peers tab
|
||||
|
@ -1014,26 +869,31 @@ void
|
|||
Details :: onHonorsSessionLimitsToggled( bool val )
|
||||
{
|
||||
mySession.torrentSet( myIds, "honorsSessionLimits", val );
|
||||
getNewData( );
|
||||
}
|
||||
void
|
||||
Details :: onDownloadLimitedToggled( bool val )
|
||||
{
|
||||
mySession.torrentSet( myIds, "downloadLimited", val );
|
||||
getNewData( );
|
||||
}
|
||||
void
|
||||
Details :: onDownloadLimitChanged( int val )
|
||||
{
|
||||
mySession.torrentSet( myIds, "downloadLimit", val );
|
||||
getNewData( );
|
||||
}
|
||||
void
|
||||
Details :: onUploadLimitedToggled( bool val )
|
||||
{
|
||||
mySession.torrentSet( myIds, "uploadLimited", val );
|
||||
getNewData( );
|
||||
}
|
||||
void
|
||||
Details :: onUploadLimitChanged( int val )
|
||||
{
|
||||
mySession.torrentSet( myIds, "uploadLimit", val );
|
||||
getNewData( );
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1041,12 +901,14 @@ Details :: onIdleModeChanged( int index )
|
|||
{
|
||||
const int val = myIdleCombo->itemData( index ).toInt( );
|
||||
mySession.torrentSet( myIds, "seedIdleMode", val );
|
||||
getNewData( );
|
||||
}
|
||||
|
||||
void
|
||||
Details :: onIdleLimitChanged( int val )
|
||||
{
|
||||
mySession.torrentSet( myIds, "seedIdleLimit", val );
|
||||
getNewData( );
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1060,12 +922,14 @@ void
|
|||
Details :: onRatioLimitChanged( double val )
|
||||
{
|
||||
mySession.torrentSet( myIds, "seedRatioLimit", val );
|
||||
getNewData( );
|
||||
}
|
||||
|
||||
void
|
||||
Details :: onMaxPeersChanged( int val )
|
||||
{
|
||||
mySession.torrentSet( myIds, "peer-limit", val );
|
||||
getNewData( );
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1075,120 +939,114 @@ Details :: onBandwidthPriorityChanged( int index )
|
|||
{
|
||||
const int priority = myBandwidthPriorityCombo->itemData(index).toInt( );
|
||||
mySession.torrentSet( myIds, "bandwidthPriority", priority );
|
||||
getNewData( );
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Details :: onTrackerSelectionChanged( )
|
||||
{
|
||||
const QList<QTreeWidgetItem*> items = myTrackerTree->selectedItems();
|
||||
if( items.count() == 1 )
|
||||
myEditTrackerButton->setEnabled( items.first()->data( 0, TRACKERID ).toInt() >= 0 );
|
||||
const int selectionCount = myTrackerView->selectionModel()->selectedRows().size();
|
||||
myEditTrackerButton->setEnabled( selectionCount == 1 );
|
||||
myRemoveTrackerButton->setEnabled( selectionCount > 0 );
|
||||
}
|
||||
|
||||
void
|
||||
Details :: onAddTrackerClicked( )
|
||||
{
|
||||
bool ok = false;
|
||||
const QString url = QInputDialog::getText( this,
|
||||
tr( "Add URL " ),
|
||||
tr( "Add tracker announce URL:" ),
|
||||
QLineEdit::Normal, QString(), &ok );
|
||||
if( !ok )
|
||||
{
|
||||
// user pressed "cancel" -- noop
|
||||
}
|
||||
else if( !QUrl(url).isValid( ) )
|
||||
{
|
||||
QMessageBox::warning( this, tr( "Error" ), tr( "Invalid URL \"%1\"" ).arg( url ) );
|
||||
}
|
||||
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 ) )
|
||||
QSet<int> ids;
|
||||
|
||||
foreach( int id, myIds )
|
||||
if( myTrackerModel->find( id, url ) == -1 )
|
||||
ids.insert( id );
|
||||
|
||||
if( ids.empty( ) ) // all the torrents already have this tracker
|
||||
{
|
||||
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 );
|
||||
QMessageBox::warning( this, tr( "Error" ), tr( "Tracker already exists." ) );
|
||||
}
|
||||
else
|
||||
QMessageBox::warning( this, "Error", "Tracker already exists." );
|
||||
{
|
||||
QStringList urls;
|
||||
urls << url;
|
||||
mySession.torrentSet( ids, "trackerAdd", urls );
|
||||
getNewData( );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Details :: onEditTrackerPushed( )
|
||||
Details :: onEditTrackerClicked( )
|
||||
{
|
||||
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() )
|
||||
QItemSelectionModel * selectionModel = myTrackerView->selectionModel( );
|
||||
QModelIndexList selectedRows = selectionModel->selectedRows( );
|
||||
assert( selectedRows.size( ) == 1 );
|
||||
QModelIndex i = selectionModel->currentIndex( );
|
||||
const TrackerInfo trackerInfo = myTrackerModel->data( i, TrackerModel::TrackerRole ).value<TrackerInfo>();
|
||||
|
||||
bool ok = false;
|
||||
const QString newval = QInputDialog::getText( this,
|
||||
tr( "Edit URL " ),
|
||||
tr( "Edit tracker announce URL:" ),
|
||||
QLineEdit::Normal,
|
||||
trackerInfo.st.announce, &ok );
|
||||
|
||||
if( !ok )
|
||||
{
|
||||
const int torId = item->data( 0, TORRENTID ).toInt();
|
||||
if( !findTrackerByURL( urlString, torId ) )
|
||||
{
|
||||
QByteArray url = urlString.toUtf8();
|
||||
QSet<int> ids;
|
||||
tr_benc top;
|
||||
// user pressed "cancel" -- noop
|
||||
}
|
||||
else if( !QUrl(newval).isValid( ) )
|
||||
{
|
||||
QMessageBox::warning( this, tr( "Error" ), tr( "Invalid URL \"%1\"" ).arg( newval ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
QSet<int> ids;
|
||||
ids << trackerInfo.torrentId;
|
||||
|
||||
ids << torId;
|
||||
tr_bencInitDict( &top, 2 );
|
||||
tr_bencDictAddStr( &top, "announce", item->data( 0, TRACKERURL ).toByteArray() );
|
||||
tr_bencDictAddStr( &top, "announce-new", url );
|
||||
QStringList urls;
|
||||
urls << trackerInfo.st.announce;
|
||||
urls << newval;
|
||||
|
||||
mySession.torrentSet( ids, "trackerEdit", &top );
|
||||
}
|
||||
else
|
||||
QMessageBox::warning( this, "Error", "Tracker already exists." );
|
||||
mySession.torrentSet( ids, "trackerReplace", urls );
|
||||
getNewData( );
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Details :: removeTracker( const QTreeWidgetItem * item )
|
||||
Details :: onRemoveTrackerClicked( )
|
||||
{
|
||||
QByteArray url = item->data( 0, TRACKERURL ).toByteArray();
|
||||
const int torId = item->data( 0, TORRENTID ).toInt();
|
||||
QSet<int> ids;
|
||||
tr_benc top;
|
||||
// make a map of torrentIds to announce URLs to remove
|
||||
QItemSelectionModel * selectionModel = myTrackerView->selectionModel( );
|
||||
QModelIndexList selectedRows = selectionModel->selectedRows( );
|
||||
QMap<int,QStringList> torrentId_to_urls;
|
||||
foreach( QModelIndex i, selectedRows )
|
||||
{
|
||||
const TrackerInfo inf = myTrackerModel->data( i, TrackerModel::TrackerRole ).value<TrackerInfo>();
|
||||
torrentId_to_urls[ inf.torrentId ].append( inf.st.announce );
|
||||
}
|
||||
|
||||
ids << torId;
|
||||
tr_bencInitDict( &top, 1 );
|
||||
tr_bencDictAddStr( &top, "announce", url );
|
||||
|
||||
mySession.torrentSet( ids, "trackerRemove", &top );
|
||||
}
|
||||
|
||||
void
|
||||
Details :: onRemoveTrackerPushed( )
|
||||
{
|
||||
const QList<QTreeWidgetItem*> items = myTrackerTree->selectedItems();
|
||||
QSet<int> removedTiers;
|
||||
foreach( const QTreeWidgetItem * item, items ) {
|
||||
const bool isTier = item->data( 0, TRACKERID ).toInt() == -1;
|
||||
const int curTier = item->data( 0, TRACKERTIER ).toInt();
|
||||
if( isTier )
|
||||
{
|
||||
removedTiers << curTier;
|
||||
for( int i = 0; i < item->childCount(); ++i )
|
||||
removeTracker( item->child( i ) );
|
||||
}
|
||||
else if( !removedTiers.contains( curTier ) ) // skip trackers removed by clearing a tier
|
||||
removeTracker( item );
|
||||
// batch all of a tracker's torrents into one command
|
||||
foreach( int id, torrentId_to_urls.keys( ) )
|
||||
{
|
||||
QSet<int> ids;
|
||||
ids << id;
|
||||
mySession.torrentSet( ids, "trackerRemove", torrentId_to_urls.value( id ) );
|
||||
getNewData( );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1306,25 +1164,24 @@ Details :: createTrackerTab( )
|
|||
|
||||
v2->setSpacing( HIG::PAD );
|
||||
|
||||
QStringList headers;
|
||||
headers << tr("Trackers");
|
||||
myTrackerTree = new QTreeWidget;
|
||||
myTrackerTree->setHeaderLabels( headers );
|
||||
myTrackerTree->setSelectionMode( QTreeWidget::ExtendedSelection );
|
||||
myTrackerTree->setRootIsDecorated( false );
|
||||
myTrackerTree->setIndentation( 2 );
|
||||
myTrackerTree->setItemsExpandable( false );
|
||||
myTrackerTree->setTextElideMode( Qt::ElideRight );
|
||||
myTrackerTree->setAlternatingRowColors( true );
|
||||
connect( myTrackerTree, SIGNAL(itemSelectionChanged()), this, SLOT(onTrackerSelectionChanged()));
|
||||
h->addWidget( myTrackerTree, 1 );
|
||||
myTrackerView = new QTreeView;
|
||||
myTrackerView->setModel( myTrackerModel = new TrackerModel );
|
||||
myTrackerView->setHeaderHidden( true );
|
||||
myTrackerView->setSelectionMode( QTreeWidget::ExtendedSelection );
|
||||
myTrackerView->setRootIsDecorated( false );
|
||||
myTrackerView->setIndentation( 2 );
|
||||
myTrackerView->setItemsExpandable( false );
|
||||
myTrackerView->setAlternatingRowColors( true );
|
||||
myTrackerView->setItemDelegate( myTrackerDelegate = new TrackerDelegate( ) );
|
||||
connect( myTrackerView->selectionModel(), SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), this, SLOT(onTrackerSelectionChanged()));
|
||||
h->addWidget( myTrackerView, 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()));
|
||||
connect( p, SIGNAL(clicked(bool)), this, SLOT(onAddTrackerClicked()));
|
||||
|
||||
p = new QPushButton();
|
||||
p->setIcon( getStockIcon( "document-properties", QStyle::SP_DesktopIcon ) );
|
||||
|
@ -1333,7 +1190,7 @@ Details :: createTrackerTab( )
|
|||
p->setEnabled( false );
|
||||
myEditTrackerButton = p;
|
||||
v2->addWidget( p, 1 );
|
||||
connect( p, SIGNAL(clicked(bool)), this, SLOT(onEditTrackerPushed()));
|
||||
connect( p, SIGNAL(clicked(bool)), this, SLOT(onEditTrackerClicked()));
|
||||
|
||||
p = new QPushButton();
|
||||
p->setIcon( getStockIcon( "list-remove", QStyle::SP_TrashIcon ) );
|
||||
|
@ -1341,7 +1198,7 @@ Details :: createTrackerTab( )
|
|||
p->setEnabled( false );
|
||||
myRemoveTrackerButton = p;
|
||||
v2->addWidget( p, 1 );
|
||||
connect( p, SIGNAL(clicked(bool)), this, SLOT(onRemoveTrackerPushed()));
|
||||
connect( p, SIGNAL(clicked(bool)), this, SLOT(onRemoveTrackerClicked()));
|
||||
|
||||
v2->addStretch( 1 );
|
||||
|
||||
|
@ -1428,6 +1285,7 @@ Details :: onFilePriorityChanged( const QSet<int>& indices, int priority )
|
|||
default: key = "priority-normal"; break;
|
||||
}
|
||||
mySession.torrentSet( myIds, key, indices.toList( ) );
|
||||
getNewData( );
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1435,4 +1293,5 @@ Details :: onFileWantedChanged( const QSet<int>& indices, bool wanted )
|
|||
{
|
||||
QString key( wanted ? "files-wanted" : "files-unwanted" );
|
||||
mySession.torrentSet( myIds, key, indices.toList( ) );
|
||||
getNewData( );
|
||||
}
|
||||
|
|
29
qt/details.h
29
qt/details.h
|
@ -36,19 +36,15 @@ class QTreeWidgetItem;
|
|||
class Session;
|
||||
class Torrent;
|
||||
class TorrentModel;
|
||||
class TrackerDelegate;
|
||||
class TrackerModel;
|
||||
|
||||
class Details: public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
enum
|
||||
{
|
||||
TRACKERID = Qt::UserRole,
|
||||
TRACKERURL,
|
||||
TRACKERTIER,
|
||||
TORRENTID
|
||||
};
|
||||
void getNewData( );
|
||||
|
||||
private slots:
|
||||
void onTorrentChanged( );
|
||||
|
@ -71,8 +67,6 @@ class Details: public QDialog
|
|||
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;
|
||||
|
@ -126,16 +120,21 @@ class Details: public QDialog
|
|||
QLabel * myAnnounceResponseLabel;
|
||||
QLabel * myAnnounceManualLabel;
|
||||
|
||||
QTreeWidget * myTrackerTree;
|
||||
TrackerModel * myTrackerModel;
|
||||
TrackerDelegate * myTrackerDelegate;
|
||||
QTreeView * myTrackerView;
|
||||
//QMap<QString,QTreeWidgetItem*> myTrackerTiers;
|
||||
//QMap<QString,QTreeWidgetItem*> myTrackerItems;
|
||||
|
||||
QTreeWidget * myPeerTree;
|
||||
QMap<QString,QTreeWidgetItem*> myTrackerTiers;
|
||||
QMap<QString,QTreeWidgetItem*> myTrackerItems;
|
||||
QMap<QString,QTreeWidgetItem*> myPeers;
|
||||
|
||||
QWidgetList myWidgets;
|
||||
|
||||
FileTreeView * myFileTreeView;
|
||||
|
||||
private slots:
|
||||
void refreshPref( int key );
|
||||
void onBandwidthPriorityChanged( int );
|
||||
void onFilePriorityChanged( const QSet<int>& fileIndices, int );
|
||||
void onFileWantedChanged( const QSet<int>& fileIndices, bool );
|
||||
|
@ -150,9 +149,9 @@ class Details: public QDialog
|
|||
void onIdleLimitChanged( int );
|
||||
void onShowTrackerScrapesToggled( bool );
|
||||
void onTrackerSelectionChanged( );
|
||||
void onAddTrackerPushed( );
|
||||
void onEditTrackerPushed( );
|
||||
void onRemoveTrackerPushed( );
|
||||
void onAddTrackerClicked( );
|
||||
void onEditTrackerClicked( );
|
||||
void onRemoveTrackerClicked( );
|
||||
void onMaxPeersChanged( int );
|
||||
void refresh( );
|
||||
};
|
||||
|
|
|
@ -97,7 +97,9 @@ Favicons :: add( const QUrl& url )
|
|||
if( !myPixmaps.contains( host ) )
|
||||
{
|
||||
// add a placholder s.t. we only ping the server once per session
|
||||
myPixmaps.insert( host, QPixmap( ) );
|
||||
QPixmap tmp( 16, 16 );
|
||||
tmp.fill( Qt::transparent );
|
||||
myPixmaps.insert( host, tmp );
|
||||
|
||||
// try to download the favicon
|
||||
const QString path = "http://" + host + "/favicon.";
|
||||
|
|
|
@ -26,6 +26,10 @@ class Favicons: public QObject
|
|||
{
|
||||
Q_OBJECT;
|
||||
|
||||
public:
|
||||
|
||||
static QString getHost( const QUrl& url );
|
||||
|
||||
public:
|
||||
|
||||
Favicons();
|
||||
|
@ -46,8 +50,6 @@ class Favicons: public QObject
|
|||
QNetworkAccessManager * myNAM;
|
||||
QMap<QString,QPixmap> myPixmaps;
|
||||
|
||||
QString getHost( const QUrl& url );
|
||||
|
||||
QString getCacheDir( );
|
||||
void ensureCacheDirHasBeenScanned( );
|
||||
|
||||
|
|
|
@ -36,7 +36,8 @@ SOURCES += about.cc app.cc dbus-adaptor.cc details.cc favicon.cc file-tree.cc \
|
|||
relocate.cc session.cc session-dialog.cc squeezelabel.cc \
|
||||
stats-dialog.cc torrent.cc torrent-delegate.cc \
|
||||
torrent-delegate-min.cc torrent-filter.cc torrent-model.cc \
|
||||
triconpushbutton.cc utils.cc watchdir.cc
|
||||
tracker-delegate.cc tracker-model.cc triconpushbutton.cc \
|
||||
utils.cc watchdir.cc
|
||||
HEADERS += $$replace(SOURCES, .cc, .h)
|
||||
HEADERS += speed.h types.h
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include <QNetworkReply>
|
||||
#include <QNetworkRequest>
|
||||
#include <QSet>
|
||||
#include <QStringList>
|
||||
#include <QStyle>
|
||||
#include <QTextStream>
|
||||
|
||||
|
@ -405,6 +406,21 @@ Session :: torrentSet( const QSet<int>& ids, const QString& key, bool value )
|
|||
tr_bencFree( &top );
|
||||
}
|
||||
|
||||
void
|
||||
Session :: torrentSet( const QSet<int>& ids, const QString& key, const QStringList& 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 * list( tr_bencDictAddList( args, key.toUtf8().constData(), value.size( ) ) );
|
||||
foreach( const QString str, value )
|
||||
tr_bencListAddStr( list, str.toUtf8().constData() );
|
||||
exec( &top );
|
||||
tr_bencFree( &top );
|
||||
}
|
||||
|
||||
void
|
||||
Session :: torrentSet( const QSet<int>& ids, const QString& key, const QList<int>& value )
|
||||
{
|
||||
|
@ -420,20 +436,6 @@ 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 )
|
||||
{
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
#include <QString>
|
||||
#include <QUrl>
|
||||
|
||||
class QStringList;
|
||||
|
||||
#include <libtransmission/transmission.h>
|
||||
|
||||
extern "C"
|
||||
|
@ -98,7 +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 torrentSet( const QSet<int>& ids, const QString& key, const QStringList& val );
|
||||
void torrentSetLocation( const QSet<int>& ids, const QString& path, bool doMove );
|
||||
|
||||
|
||||
|
|
|
@ -566,8 +566,10 @@ Torrent :: update( tr_benc * d )
|
|||
int64_t i;
|
||||
const char * str;
|
||||
TrackerStat trackerStat;
|
||||
if( tr_bencDictFindStr( child, "announce", &str ) )
|
||||
if( tr_bencDictFindStr( child, "announce", &str ) ) {
|
||||
trackerStat.announce = QString::fromUtf8( str );
|
||||
dynamic_cast<MyApp*>(QApplication::instance())->favicons.add( QUrl( trackerStat.announce ) );
|
||||
}
|
||||
if( tr_bencDictFindInt( child, "announceState", &i ) )
|
||||
trackerStat.announceState = i;
|
||||
if( tr_bencDictFindInt( child, "downloadCount", &i ) )
|
||||
|
@ -705,3 +707,11 @@ Torrent :: getError( ) const
|
|||
|
||||
return s;
|
||||
}
|
||||
|
||||
QPixmap
|
||||
TrackerStat :: getFavicon( ) const
|
||||
{
|
||||
MyApp * myApp = dynamic_cast<MyApp*>(QApplication::instance());
|
||||
return myApp->favicons.find( QUrl( announce ) );
|
||||
}
|
||||
|
||||
|
|
|
@ -34,6 +34,7 @@ extern "C"
|
|||
}
|
||||
|
||||
class Prefs;
|
||||
class QPixmap;
|
||||
class QStyle;
|
||||
|
||||
struct Peer
|
||||
|
@ -86,6 +87,7 @@ struct TrackerStat
|
|||
int scrapeState;
|
||||
int seederCount;
|
||||
int tier;
|
||||
QPixmap getFavicon( ) const;
|
||||
};
|
||||
|
||||
typedef QList<TrackerStat> TrackerStatsList;
|
||||
|
|
|
@ -0,0 +1,304 @@
|
|||
/*
|
||||
* This file Copyright (C) 2009-2010 Mnemosyne LLC
|
||||
*
|
||||
* This file is licensed by the GPL version 2. Works owned by the
|
||||
* Transmission project are granted a special exemption to clause 2(b)
|
||||
* so that the bulk of its code can remain under the MIT license.
|
||||
* This exemption does not extend to derived works not owned by
|
||||
* the Transmission project.
|
||||
*
|
||||
* $Id: torrent-delegate.cc 11051 2010-07-24 23:51:02Z charles $
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <QApplication>
|
||||
#include <QBrush>
|
||||
#include <QFont>
|
||||
#include <QFontMetrics>
|
||||
#include <QIcon>
|
||||
#include <QModelIndex>
|
||||
#include <QPainter>
|
||||
#include <QPixmap>
|
||||
#include <QPixmapCache>
|
||||
#include <QStyleOptionProgressBarV2>
|
||||
#include <QTextDocument>
|
||||
#include <QUrl>
|
||||
|
||||
#include "favicon.h"
|
||||
#include "formatter.h"
|
||||
#include "torrent.h"
|
||||
#include "tracker-delegate.h"
|
||||
#include "tracker-model.h"
|
||||
|
||||
/***
|
||||
****
|
||||
***/
|
||||
|
||||
namespace
|
||||
{
|
||||
const int mySpacing = 6;
|
||||
const QSize myMargin( 10, 6 );
|
||||
}
|
||||
|
||||
QSize
|
||||
TrackerDelegate :: margin( const QStyle& style ) const
|
||||
{
|
||||
Q_UNUSED( style );
|
||||
|
||||
return myMargin;
|
||||
}
|
||||
|
||||
/***
|
||||
****
|
||||
***/
|
||||
|
||||
QSize
|
||||
TrackerDelegate :: sizeHint( const QStyleOptionViewItem& option, const TrackerInfo& info ) const
|
||||
{
|
||||
Q_UNUSED( option );
|
||||
|
||||
QPixmap favicon = info.st.getFavicon( );
|
||||
|
||||
const QString text = TrackerDelegate :: getText( info );
|
||||
QTextDocument textDoc;
|
||||
textDoc.setHtml( text );
|
||||
const QSize textSize = textDoc.size().toSize();
|
||||
|
||||
return QSize( myMargin.width() + favicon.width() + mySpacing + textSize.width() + myMargin.width(),
|
||||
myMargin.height() + qMax<int>( favicon.height(), textSize.height() ) + myMargin.height() );
|
||||
}
|
||||
|
||||
QSize
|
||||
TrackerDelegate :: sizeHint( const QStyleOptionViewItem & option,
|
||||
const QModelIndex & index ) const
|
||||
{
|
||||
const TrackerInfo trackerInfo = index.model()->data( index, TrackerModel::TrackerRole ).value<TrackerInfo>();
|
||||
return sizeHint( option, trackerInfo );
|
||||
}
|
||||
|
||||
void
|
||||
TrackerDelegate :: paint( QPainter * painter,
|
||||
const QStyleOptionViewItem & option,
|
||||
const QModelIndex & index) const
|
||||
{
|
||||
const TrackerInfo trackerInfo = index.model()->data( index, TrackerModel::TrackerRole ).value<TrackerInfo>();
|
||||
painter->save( );
|
||||
painter->setClipRect( option.rect );
|
||||
drawBackground( painter, option, index );
|
||||
drawTracker( painter, option, trackerInfo );
|
||||
drawFocus(painter, option, option.rect );
|
||||
painter->restore( );
|
||||
}
|
||||
|
||||
void
|
||||
TrackerDelegate :: drawTracker( QPainter * painter,
|
||||
const QStyleOptionViewItem & option,
|
||||
const TrackerInfo & inf ) const
|
||||
{
|
||||
painter->save( );
|
||||
|
||||
QPixmap icon = inf.st.getFavicon( );
|
||||
QRect iconArea( option.rect.x() + myMargin.width(),
|
||||
option.rect.y() + myMargin.height(),
|
||||
icon.width(),
|
||||
icon.height() );
|
||||
painter->drawPixmap( iconArea.x(), iconArea.y()+4, icon );
|
||||
|
||||
const int textWidth = option.rect.width() - myMargin.width()*2 - mySpacing - icon.width();
|
||||
const int textX = myMargin.width() + icon.width() + mySpacing;
|
||||
const QString text = getText( inf );
|
||||
QTextDocument textDoc;
|
||||
textDoc.setHtml( text );
|
||||
const QRect textRect( textX, iconArea.y(), textWidth, option.rect.height() - myMargin.height()*2 );
|
||||
painter->translate( textRect.topLeft( ) );
|
||||
textDoc.drawContents( painter, textRect.translated( -textRect.topLeft( ) ) );
|
||||
|
||||
painter->restore( );
|
||||
}
|
||||
|
||||
void
|
||||
TrackerDelegate :: setShowMore( bool b )
|
||||
{
|
||||
myShowMore = b;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
QString timeToStringRounded( int seconds )
|
||||
{
|
||||
if( seconds > 60 ) seconds -= ( seconds % 60 );
|
||||
return Formatter::timeToString ( seconds );
|
||||
}
|
||||
}
|
||||
|
||||
QString
|
||||
TrackerDelegate :: getText( const TrackerInfo& inf ) const
|
||||
{
|
||||
QString key;
|
||||
QString str;
|
||||
const time_t now( time( 0 ) );
|
||||
const QString err_markup_begin = "<span style=\"color:red\">";
|
||||
const QString err_markup_end = "</span>";
|
||||
const QString timeout_markup_begin = "<span style=\"color:#224466\">";
|
||||
const QString timeout_markup_end = "</span>";
|
||||
const QString success_markup_begin = "<span style=\"color:#008B00\">";
|
||||
const QString success_markup_end = "</span>";
|
||||
|
||||
// hostname
|
||||
const QString host = Favicons::getHost( QUrl( inf.st.announce ) );
|
||||
str += inf.st.isBackup ? "<i>" : "<b>";
|
||||
str += host;
|
||||
if( !key.isEmpty( ) ) str += " - " + key;
|
||||
str += inf.st.isBackup ? "</i>" : "</b>";
|
||||
|
||||
// announce & scrape info
|
||||
if( !inf.st.isBackup )
|
||||
{
|
||||
if( inf.st.hasAnnounced )
|
||||
{
|
||||
const QString tstr( timeToStringRounded( now - inf.st.lastAnnounceTime ) );
|
||||
str += "<br/>\n";
|
||||
if( inf.st.lastAnnounceSucceeded )
|
||||
{
|
||||
str += tr( "Got a list of %1%2 peers%3 %4 ago" )
|
||||
.arg( success_markup_begin )
|
||||
.arg( inf.st.lastAnnouncePeerCount )
|
||||
.arg( success_markup_end )
|
||||
.arg( tstr );
|
||||
}
|
||||
else if( inf.st.lastAnnounceTimedOut )
|
||||
{
|
||||
str += tr( "Peer list request timed out %1%2%3 ago; will retry" )
|
||||
.arg( timeout_markup_begin )
|
||||
.arg( tstr )
|
||||
.arg( timeout_markup_end );
|
||||
}
|
||||
else
|
||||
{
|
||||
str += tr( "Got an error %1'%2'%3 %4 ago" )
|
||||
.arg( err_markup_begin )
|
||||
.arg( tstr )
|
||||
.arg( err_markup_end )
|
||||
.arg( tstr );
|
||||
}
|
||||
}
|
||||
|
||||
switch( inf.st.announceState )
|
||||
{
|
||||
case TR_TRACKER_INACTIVE:
|
||||
if( inf.st.hasAnnounced ) {
|
||||
str += "<br/>\n";
|
||||
str += tr( "No updates scheduled" );
|
||||
}
|
||||
break;
|
||||
|
||||
case TR_TRACKER_WAITING: {
|
||||
const QString tstr( timeToStringRounded( inf.st.nextAnnounceTime - now ) );
|
||||
str += "<br/>\n";
|
||||
str += tr( "Asking for more peers in %1" ).arg( tstr );
|
||||
break;
|
||||
}
|
||||
|
||||
case TR_TRACKER_QUEUED:
|
||||
str += "<br/>\n";
|
||||
str += tr( "Queued to ask for more peers" );
|
||||
break;
|
||||
|
||||
case TR_TRACKER_ACTIVE: {
|
||||
const QString tstr( timeToStringRounded( now - inf.st.lastAnnounceStartTime ) );
|
||||
str += "<br/>\n";
|
||||
str += tr( "Asking for more peers now... <small>%1</small>" ).arg( tstr );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( myShowMore )
|
||||
{
|
||||
if( inf.st.hasScraped )
|
||||
{
|
||||
str += "<br/>\n";
|
||||
const QString tstr( timeToStringRounded( now - inf.st.lastScrapeTime ) );
|
||||
if( inf.st.lastScrapeSucceeded )
|
||||
{
|
||||
str += tr( "Tracker had %1%2 seeders%3 and %4%5 leechers%6 %7 ago" )
|
||||
.arg( success_markup_begin )
|
||||
.arg( inf.st.seederCount )
|
||||
.arg( success_markup_end )
|
||||
.arg( success_markup_begin )
|
||||
.arg( inf.st.leecherCount )
|
||||
.arg( success_markup_end )
|
||||
.arg( tstr );
|
||||
}
|
||||
else
|
||||
{
|
||||
str += tr( "Got a scrape error %1'%2'%3 %4 ago" )
|
||||
.arg( err_markup_begin )
|
||||
.arg( inf.st.lastScrapeResult )
|
||||
.arg( err_markup_end )
|
||||
.arg( tstr );
|
||||
}
|
||||
}
|
||||
|
||||
switch( inf.st.scrapeState )
|
||||
{
|
||||
case TR_TRACKER_INACTIVE:
|
||||
break;
|
||||
|
||||
case TR_TRACKER_WAITING: {
|
||||
str += "<br/>\n";
|
||||
const QString tstr( timeToStringRounded( inf.st.nextScrapeTime - now ) );
|
||||
str += tr( "Asking for peer counts in %1" ).arg( tstr );
|
||||
break;
|
||||
}
|
||||
|
||||
case TR_TRACKER_QUEUED: {
|
||||
str += "<br/>\n";
|
||||
str += tr( "Queued to ask for peer counts" );
|
||||
break;
|
||||
}
|
||||
|
||||
case TR_TRACKER_ACTIVE: {
|
||||
str += "<br/>\n";
|
||||
const QString tstr( timeToStringRounded( now - inf.st.lastScrapeStartTime ) );
|
||||
str += tr( "Asking for peer counts now... <small>%1</small>" ).arg( tstr );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
||||
if( inf.isBackup )
|
||||
str += "<i>";
|
||||
QString announce;
|
||||
int announceState;
|
||||
int downloadCount;
|
||||
bool hasAnnounced; bool hasScraped;
|
||||
QString host;
|
||||
int id;
|
||||
bool isBackup;
|
||||
int lastAnnouncePeerCount;
|
||||
int lastAnnounceResult;
|
||||
int lastAnnounceStartTime;
|
||||
bool lastAnnounceSucceeded;
|
||||
int lastAnnounceTime;
|
||||
bool lastAnnounceTimedOut;
|
||||
QString lastScrapeResult;
|
||||
int lastScrapeStartTime;
|
||||
bool lastScrapeSucceeded;
|
||||
int lastScrapeTime;
|
||||
bool lastScrapeTimedOut;
|
||||
int leecherCount;
|
||||
int nextAnnounceTime;
|
||||
int nextScrapeTime;
|
||||
int scrapeState;
|
||||
int seederCount;
|
||||
int tier;
|
||||
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* This file Copyright (C) 2009-2010 Mnemosyne LLC
|
||||
*
|
||||
* This file is licensed by the GPL version 2. Works owned by the
|
||||
* Transmission project are granted a special exemption to clause 2(b)
|
||||
* so that the bulk of its code can remain under the MIT license.
|
||||
* This exemption does not extend to derived works not owned by
|
||||
* the Transmission project.
|
||||
*
|
||||
* $Id: torrent-delegate.h 9868 2010-01-04 21:00:47Z charles $
|
||||
*/
|
||||
|
||||
#ifndef QTR_TORRENT_DELEGATE_H
|
||||
#define QTR_TORRENT_DELEGATE_H
|
||||
|
||||
#include <QItemDelegate>
|
||||
#include <QSize>
|
||||
|
||||
class QPainter;
|
||||
class QStyleOptionViewItem;
|
||||
class QStyle;
|
||||
class Session;
|
||||
class TrackerInfo;
|
||||
|
||||
class TrackerDelegate: public QItemDelegate
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
TrackerDelegate( QObject * parent=0 ): QItemDelegate(parent), myShowMore(false) { }
|
||||
virtual ~TrackerDelegate( ) { }
|
||||
|
||||
public:
|
||||
QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const;
|
||||
void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const;
|
||||
|
||||
public:
|
||||
void setShowMore( bool b );
|
||||
|
||||
protected:
|
||||
QString getText( const TrackerInfo& ) const;
|
||||
QSize margin( const QStyle& style ) const;
|
||||
virtual QSize sizeHint( const QStyleOptionViewItem&, const TrackerInfo& ) const;
|
||||
void drawTracker( QPainter*, const QStyleOptionViewItem&, const TrackerInfo& ) const;
|
||||
|
||||
private:
|
||||
bool myShowMore;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,154 @@
|
|||
/*
|
||||
* This file Copyright (C) 2010 Mnemosyne LLC
|
||||
*
|
||||
* This file is licensed by the GPL version 2. Works owned by the
|
||||
* Transmission project are granted a special exemption to clause 2(b)
|
||||
* so that the bulk of its code can remain under the MIT license.
|
||||
* This exemption does not extend to derived works not owned by
|
||||
* the Transmission project.
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#include <algorithm> // std::sort()
|
||||
|
||||
#include <QUrl>
|
||||
|
||||
#include "app.h" // MyApp
|
||||
#include "tracker-model.h"
|
||||
|
||||
int
|
||||
TrackerModel :: rowCount( const QModelIndex& parent ) const
|
||||
{
|
||||
Q_UNUSED( parent );
|
||||
|
||||
return parent.isValid() ? 0 : myRows.size();
|
||||
}
|
||||
|
||||
QVariant
|
||||
TrackerModel :: data( const QModelIndex& index, int role ) const
|
||||
{
|
||||
QVariant var;
|
||||
|
||||
const int row = index.row( );
|
||||
if( ( 0<=row ) && ( row<myRows.size( ) ) )
|
||||
{
|
||||
const TrackerInfo& trackerInfo = myRows.at( row );
|
||||
|
||||
switch( role )
|
||||
{
|
||||
case Qt::DisplayRole:
|
||||
var = QString( trackerInfo.st.announce );
|
||||
break;
|
||||
|
||||
case Qt::DecorationRole:
|
||||
var = trackerInfo.st.getFavicon( );
|
||||
break;
|
||||
|
||||
case TrackerRole:
|
||||
var = qVariantFromValue( trackerInfo );
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return var;
|
||||
}
|
||||
|
||||
/***
|
||||
****
|
||||
***/
|
||||
|
||||
struct CompareTrackers {
|
||||
bool operator()( const TrackerInfo& a, const TrackerInfo& b ) const {
|
||||
if( a.torrentId != b.torrentId ) return a.torrentId < b.torrentId;
|
||||
if( a.st.tier != b.st.tier ) return a.st.tier < b.st.tier;
|
||||
return a.st.announce < b.st.announce;
|
||||
}
|
||||
};
|
||||
|
||||
void
|
||||
TrackerModel :: refresh( const TorrentModel& torrentModel, const QSet<int>& ids )
|
||||
{
|
||||
// build a list of the TrackerInfos
|
||||
QVector<TrackerInfo> trackers;
|
||||
foreach( int id, ids ) {
|
||||
const Torrent * tor = torrentModel.getTorrentFromId( id );
|
||||
if( tor != 0 ) {
|
||||
const TrackerStatsList trackerList = tor->trackerStats( );
|
||||
foreach( const TrackerStat& st, trackerList ) {
|
||||
TrackerInfo trackerInfo;
|
||||
trackerInfo.st = st;
|
||||
trackerInfo.torrentId = id;
|
||||
trackers.append( trackerInfo );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// sort 'em
|
||||
CompareTrackers comp;
|
||||
std::sort( trackers.begin(), trackers.end(), comp );
|
||||
|
||||
// merge 'em with the existing list
|
||||
int old_index = 0;
|
||||
int new_index = 0;
|
||||
|
||||
while( ( old_index < myRows.size() ) || ( new_index < trackers.size() ) )
|
||||
{
|
||||
if( old_index == myRows.size() )
|
||||
{
|
||||
// add this new row
|
||||
beginInsertRows( QModelIndex( ), old_index, old_index );
|
||||
myRows.insert( old_index, trackers.at( new_index ) );
|
||||
endInsertRows( );
|
||||
++old_index;
|
||||
++new_index;
|
||||
}
|
||||
else if( new_index == trackers.size() )
|
||||
{
|
||||
// remove this old row
|
||||
beginRemoveRows( QModelIndex( ), old_index, old_index );
|
||||
myRows.remove( old_index );
|
||||
endRemoveRows( );
|
||||
}
|
||||
else if( comp( myRows.at(old_index), trackers.at(new_index) ) )
|
||||
{
|
||||
// remove this old row
|
||||
beginRemoveRows( QModelIndex( ), old_index, old_index );
|
||||
myRows.remove( old_index );
|
||||
endRemoveRows( );
|
||||
}
|
||||
else if( comp( trackers.at(new_index), myRows.at(old_index) ) )
|
||||
{
|
||||
// add this new row
|
||||
beginInsertRows( QModelIndex( ), old_index, old_index );
|
||||
myRows.insert( old_index, trackers.at( new_index ) );
|
||||
endInsertRows( );
|
||||
++old_index;
|
||||
++new_index;
|
||||
}
|
||||
else // update existing row
|
||||
{
|
||||
myRows[old_index].st = trackers.at(new_index).st;
|
||||
QModelIndex topLeft;
|
||||
QModelIndex bottomRight;
|
||||
dataChanged( index(old_index,0), index(old_index,0) );
|
||||
++old_index;
|
||||
++new_index;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
TrackerModel :: find( int torrentId, const QString& url ) const
|
||||
{
|
||||
for( int i=0, n=myRows.size(); i<n; ++i ) {
|
||||
const TrackerInfo& inf = myRows.at(i);
|
||||
if( ( inf.torrentId == torrentId ) && ( url == inf.st.announce ) )
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* This file Copyright (C) 2010 Mnemosyne LLC
|
||||
*
|
||||
* This file is licensed by the GPL version 2. Works owned by the
|
||||
* Transmission project are granted a special exemption to clause 2(b)
|
||||
* so that the bulk of its code can remain under the MIT license.
|
||||
* This exemption does not extend to derived works not owned by
|
||||
* the Transmission project.
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#ifndef QTR_TRACKER_MODEL_H
|
||||
#define QTR_TRACKER_MODEL_H
|
||||
|
||||
#include <QAbstractListModel>
|
||||
#include <QSet>
|
||||
#include <QVector>
|
||||
|
||||
#include "torrent.h"
|
||||
#include "torrent-model.h"
|
||||
|
||||
struct TrackerInfo
|
||||
{
|
||||
TrackerStat st;
|
||||
int torrentId;
|
||||
};
|
||||
Q_DECLARE_METATYPE(TrackerInfo)
|
||||
|
||||
class TrackerModel: public QAbstractListModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
typedef QVector<TrackerInfo> rows_t;
|
||||
rows_t myRows;
|
||||
|
||||
public:
|
||||
void refresh( const TorrentModel&, const QSet<int>& ids );
|
||||
int find( int torrentId, const QString& url ) const;
|
||||
|
||||
public:
|
||||
virtual int rowCount( const QModelIndex& parent = QModelIndex() ) const;
|
||||
virtual QVariant data( const QModelIndex& index, int role = Qt::DisplayRole ) const;
|
||||
enum Role { TrackerRole = Qt::UserRole };
|
||||
|
||||
public:
|
||||
TrackerModel( ) { }
|
||||
virtual ~TrackerModel( ) { }
|
||||
};
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -1853,73 +1853,73 @@ second %s is the version number
|
|||
<context>
|
||||
<name>Details</name>
|
||||
<message>
|
||||
<location filename="details.cc" line="145"/>
|
||||
<location filename="details.cc" line="151"/>
|
||||
<source>Torrent Properties</source>
|
||||
<translation>Свойства торрента</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="149"/>
|
||||
<location filename="details.cc" line="155"/>
|
||||
<source>Information</source>
|
||||
<translation>Сведения</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="151"/>
|
||||
<location filename="details.cc" line="157"/>
|
||||
<source>Peers</source>
|
||||
<translation>Узлы</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="153"/>
|
||||
<location filename="details.cc" line="159"/>
|
||||
<source>Tracker</source>
|
||||
<translation>Трекер</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="155"/>
|
||||
<location filename="details.cc" line="161"/>
|
||||
<source>Files</source>
|
||||
<translation>Файлы</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="157"/>
|
||||
<location filename="details.cc" line="163"/>
|
||||
<source>Options</source>
|
||||
<translation>Параметры</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="256"/>
|
||||
<location filename="details.cc" line="287"/>
|
||||
<source>None</source>
|
||||
<translation>Н/Д</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="257"/>
|
||||
<location filename="details.cc" line="288"/>
|
||||
<source>Mixed</source>
|
||||
<translation>Смешанный</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="258"/>
|
||||
<location filename="details.cc" line="441"/>
|
||||
<location filename="details.cc" line="289"/>
|
||||
<location filename="details.cc" line="472"/>
|
||||
<source>Unknown</source>
|
||||
<translation>Неизвестно</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="290"/>
|
||||
<location filename="details.cc" line="321"/>
|
||||
<source>Finished</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="292"/>
|
||||
<location filename="details.cc" line="323"/>
|
||||
<source>Paused</source>
|
||||
<translation type="unfinished">Приостановлен</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="328"/>
|
||||
<location filename="details.cc" line="359"/>
|
||||
<source>%1 (%2%)</source>
|
||||
<translation></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="332"/>
|
||||
<location filename="details.cc" line="363"/>
|
||||
<source>%1 (%2%); %3 Unverified</source>
|
||||
<translation>%1 (%2%); %3 непроверен</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="363"/>
|
||||
<location filename="details.cc" line="394"/>
|
||||
<source>%1 (+%2 corrupt)</source>
|
||||
<translation>%1 (+%2 испорчен)</translation>
|
||||
</message>
|
||||
|
@ -1928,17 +1928,17 @@ second %s is the version number
|
|||
<translation type="obsolete">Остановлен</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="461"/>
|
||||
<location filename="details.cc" line="492"/>
|
||||
<source>Active now</source>
|
||||
<translation>Активизирован</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="463"/>
|
||||
<location filename="details.cc" line="494"/>
|
||||
<source>%1 ago</source>
|
||||
<translation>%1 назад</translation>
|
||||
</message>
|
||||
<message numerus="yes">
|
||||
<location filename="details.cc" line="504"/>
|
||||
<location filename="details.cc" line="535"/>
|
||||
<source>%1 (%Ln pieces @ %2)</source>
|
||||
<translation>
|
||||
<numerusform>%1 (%Ln часть @ %2)</numerusform>
|
||||
|
@ -1947,7 +1947,7 @@ second %s is the version number
|
|||
</translation>
|
||||
</message>
|
||||
<message numerus="yes">
|
||||
<location filename="details.cc" line="508"/>
|
||||
<location filename="details.cc" line="539"/>
|
||||
<source>%1 (%Ln pieces)</source>
|
||||
<translation>
|
||||
<numerusform>%1 (%Ln часть)</numerusform>
|
||||
|
@ -1956,27 +1956,27 @@ second %s is the version number
|
|||
</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="532"/>
|
||||
<location filename="details.cc" line="563"/>
|
||||
<source>Private to this tracker -- DHT and PEX disabled</source>
|
||||
<translation>Приватно для этого трекера -- DHT и PEX выключены</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="533"/>
|
||||
<location filename="details.cc" line="564"/>
|
||||
<source>Public torrent</source>
|
||||
<translation>Публичный торрент</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="572"/>
|
||||
<location filename="details.cc" line="603"/>
|
||||
<source>Created by %1</source>
|
||||
<translation>Создано при помощи %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="574"/>
|
||||
<location filename="details.cc" line="605"/>
|
||||
<source>Created on %1</source>
|
||||
<translation>Создано %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="576"/>
|
||||
<location filename="details.cc" line="607"/>
|
||||
<source>Created by %1 on %2</source>
|
||||
<translation>Создано %2 при помощи %1</translation>
|
||||
</message>
|
||||
|
@ -1985,248 +1985,304 @@ second %s is the version number
|
|||
<translation type="obsolete">Сейчас</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="735"/>
|
||||
<location filename="details.cc" line="778"/>
|
||||
<source>Got a list of %1 peers %2 ago</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="741"/>
|
||||
<location filename="details.cc" line="784"/>
|
||||
<source>Peer list request timed out %1 ago; will retry</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="746"/>
|
||||
<location filename="details.cc" line="789"/>
|
||||
<source>Got an error %1 ago</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="756"/>
|
||||
<location filename="details.cc" line="799"/>
|
||||
<source>No updates scheduled</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="763"/>
|
||||
<location filename="details.cc" line="806"/>
|
||||
<source>Asking for more peers in %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="769"/>
|
||||
<location filename="details.cc" line="812"/>
|
||||
<source>Queued to ask for more peers</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="775"/>
|
||||
<location filename="details.cc" line="818"/>
|
||||
<source>Asking for more peers now... %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="788"/>
|
||||
<location filename="details.cc" line="831"/>
|
||||
<source>Tracker had %1 seeders and %2 leechers %3 ago</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="795"/>
|
||||
<location filename="details.cc" line="838"/>
|
||||
<source>Got a scrape error %1 ago</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="807"/>
|
||||
<location filename="details.cc" line="850"/>
|
||||
<source>Asking for peer counts in %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="813"/>
|
||||
<location filename="details.cc" line="856"/>
|
||||
<source>Queued to ask for peer counts</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="819"/>
|
||||
<location filename="details.cc" line="862"/>
|
||||
<source>Asking for peer counts now... %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="883"/>
|
||||
<location filename="details.cc" line="904"/>
|
||||
<location filename="details.cc" line="926"/>
|
||||
<location filename="details.cc" line="947"/>
|
||||
<source>Encrypted connection</source>
|
||||
<translation>Зашифрованное соединение</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="897"/>
|
||||
<location filename="details.cc" line="940"/>
|
||||
<source>Optimistic unchoke</source>
|
||||
<translation>Благоприятная передача</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="898"/>
|
||||
<location filename="details.cc" line="941"/>
|
||||
<source>Downloading from this peer</source>
|
||||
<translation>Загрузка с этого узла</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="899"/>
|
||||
<location filename="details.cc" line="942"/>
|
||||
<source>We would download from this peer if they would let us</source>
|
||||
<translation>Возможен приём данных от этого узла, если он позволит</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="900"/>
|
||||
<location filename="details.cc" line="943"/>
|
||||
<source>Uploading to peer</source>
|
||||
<translation>Передача узлу</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="901"/>
|
||||
<location filename="details.cc" line="944"/>
|
||||
<source>We would upload to this peer if they asked</source>
|
||||
<translation>Возможна раздача данных этому узлу, если он будет заинтересован</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="902"/>
|
||||
<location filename="details.cc" line="945"/>
|
||||
<source>Peer has unchoked us, but we're not interested</source>
|
||||
<translation>Узел согласен передавать данные, но мы не заинтересованы</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="903"/>
|
||||
<location filename="details.cc" line="946"/>
|
||||
<source>We unchoked this peer, but they're not interested</source>
|
||||
<translation>Передача узлу была разрешена, но он не заинтересован</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="905"/>
|
||||
<location filename="details.cc" line="948"/>
|
||||
<source>Peer was discovered through DHT</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="906"/>
|
||||
<location filename="details.cc" line="949"/>
|
||||
<source>Peer was discovered through Peer Exchange (PEX)</source>
|
||||
<translation>Узел был обнаружен с помощью обмена узлами (PEX)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="907"/>
|
||||
<location filename="details.cc" line="950"/>
|
||||
<source>Peer is an incoming connection</source>
|
||||
<translation>Узел работает в режиме приёма</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="963"/>
|
||||
<location filename="details.cc" line="1006"/>
|
||||
<source>Activity</source>
|
||||
<translation>Активность</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="964"/>
|
||||
<location filename="details.cc" line="1007"/>
|
||||
<source>Torrent size:</source>
|
||||
<translation>Размер торрента:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="965"/>
|
||||
<location filename="details.cc" line="1008"/>
|
||||
<source>Have:</source>
|
||||
<translation>В наличии:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="966"/>
|
||||
<location filename="details.cc" line="1009"/>
|
||||
<source>Availability:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="967"/>
|
||||
<location filename="details.cc" line="1010"/>
|
||||
<source>Downloaded:</source>
|
||||
<translation>Загружено:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="968"/>
|
||||
<location filename="details.cc" line="1011"/>
|
||||
<source>Uploaded:</source>
|
||||
<translation>Роздано:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="969"/>
|
||||
<location filename="details.cc" line="1012"/>
|
||||
<source>Ratio:</source>
|
||||
<translation>Рейтинг:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="970"/>
|
||||
<location filename="details.cc" line="1013"/>
|
||||
<source>State:</source>
|
||||
<translation>Состояние:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="971"/>
|
||||
<location filename="details.cc" line="1014"/>
|
||||
<source>Running time:</source>
|
||||
<translation>Длительность:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="972"/>
|
||||
<location filename="details.cc" line="1015"/>
|
||||
<source>Remaining time:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="973"/>
|
||||
<location filename="details.cc" line="1016"/>
|
||||
<source>Last activity:</source>
|
||||
<translation>Последняя активность:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="974"/>
|
||||
<location filename="details.cc" line="1017"/>
|
||||
<source>Error:</source>
|
||||
<translation>Ошибка:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="978"/>
|
||||
<location filename="details.cc" line="1021"/>
|
||||
<source>Details</source>
|
||||
<translation>Подробности</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="979"/>
|
||||
<location filename="details.cc" line="1022"/>
|
||||
<source>Location:</source>
|
||||
<translation>Местонахождение:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="980"/>
|
||||
<location filename="details.cc" line="1023"/>
|
||||
<source>Hash:</source>
|
||||
<translation>Хеш:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="981"/>
|
||||
<location filename="details.cc" line="1024"/>
|
||||
<source>Privacy:</source>
|
||||
<translation>Конфиденциальность:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="982"/>
|
||||
<location filename="details.cc" line="1025"/>
|
||||
<source>Origin:</source>
|
||||
<translation>Происхождение:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="984"/>
|
||||
<location filename="details.cc" line="1027"/>
|
||||
<source>Comment:</source>
|
||||
<translation>Комментарий:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="1100"/>
|
||||
<location filename="details.cc" line="1125"/>
|
||||
<source>Add tracker announce URL </source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="1124"/>
|
||||
<location filename="details.cc" line="1137"/>
|
||||
<location filename="details.cc" line="1165"/>
|
||||
<source>Error</source>
|
||||
<translation type="unfinished">ошибок</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="1137"/>
|
||||
<source>Tracker already exists.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="1158"/>
|
||||
<source>Edit tracker announce URL </source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="1196"/>
|
||||
<source>Speed</source>
|
||||
<translation>Скорость</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="1198"/>
|
||||
<source>Honor global &limits</source>
|
||||
<translation>Использовать &глобальные ограничения</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="1203"/>
|
||||
<source>Limit &download speed (%1):</source>
|
||||
<location filename="details.cc" line="1165"/>
|
||||
<source>Invalid URL "%1"</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="1213"/>
|
||||
<source>Speed</source>
|
||||
<translation>Скорость</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="1215"/>
|
||||
<source>Honor global &limits</source>
|
||||
<translation>Использовать &глобальные ограничения</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="1220"/>
|
||||
<source>Limit &download speed (%1):</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="1230"/>
|
||||
<source>Limit &upload speed (%1):</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="1297"/>
|
||||
<source>Trackers</source>
|
||||
<translation type="unfinished">Трекеры</translation>
|
||||
<location filename="details.cc" line="1249"/>
|
||||
<source>Seeding Limits</source>
|
||||
<translation type="unfinished">Раздача</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="1340"/>
|
||||
<location filename="details.cc" line="1254"/>
|
||||
<location filename="details.cc" line="1268"/>
|
||||
<source>Use Global Settings</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="1255"/>
|
||||
<source>Seed regardless of ratio</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="1256"/>
|
||||
<source>Stop seeding at ratio:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="1263"/>
|
||||
<source>&Ratio:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="1269"/>
|
||||
<source>Seed regardless of activity</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="1270"/>
|
||||
<source>Stop seeding if idle for N minutes:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="1277"/>
|
||||
<source>&Idle:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Trackers</source>
|
||||
<translation type="obsolete">Трекеры</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="1359"/>
|
||||
<source>Show &more details</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
|
@ -2239,52 +2295,48 @@ second %s is the version number
|
|||
<translation type="obsolete">Ограничить скорость &раздачи (КБ/с):</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="1224"/>
|
||||
<location filename="details.cc" line="1241"/>
|
||||
<source>High</source>
|
||||
<translation>Высокий</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="1225"/>
|
||||
<location filename="details.cc" line="1242"/>
|
||||
<source>Normal</source>
|
||||
<translation>Обычный</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="1226"/>
|
||||
<location filename="details.cc" line="1243"/>
|
||||
<source>Low</source>
|
||||
<translation>Низкий</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="1228"/>
|
||||
<location filename="details.cc" line="1245"/>
|
||||
<source>Torrent &priority:</source>
|
||||
<translation>&Приоритет торрента:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="1232"/>
|
||||
<source>Seed-Until Ratio</source>
|
||||
<translation>Рейтинг для завершения раздачи</translation>
|
||||
<translation type="obsolete">Рейтинг для завершения раздачи</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="1234"/>
|
||||
<source>Use &global settings</source>
|
||||
<translation>Использовать &глобальные настройки</translation>
|
||||
<translation type="obsolete">Использовать &глобальные настройки</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="1240"/>
|
||||
<source>Seed &regardless of ratio</source>
|
||||
<translation>Раздавать &несмотря на рейтинг</translation>
|
||||
<translation type="obsolete">Раздавать &несмотря на рейтинг</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="1248"/>
|
||||
<source>&Seed torrent until its ratio reaches:</source>
|
||||
<translation>&Раздавать до достижения рейтинга:</translation>
|
||||
<translation type="obsolete">&Раздавать до достижения рейтинга:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="1261"/>
|
||||
<location filename="details.cc" line="1281"/>
|
||||
<source>Peer Connections</source>
|
||||
<translation>Соединения с узлами</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="1267"/>
|
||||
<location filename="details.cc" line="1287"/>
|
||||
<source>&Maximum peers:</source>
|
||||
<translation>&Максимальное количество узлов:</translation>
|
||||
</message>
|
||||
|
@ -2325,32 +2377,32 @@ second %s is the version number
|
|||
<translation type="obsolete">Запрос дополнительных узлов можно будет сделать через:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="1362"/>
|
||||
<location filename="details.cc" line="1381"/>
|
||||
<source>Up</source>
|
||||
<translation>Раздача</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="1362"/>
|
||||
<location filename="details.cc" line="1381"/>
|
||||
<source>Down</source>
|
||||
<translation>Приём</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="1362"/>
|
||||
<location filename="details.cc" line="1381"/>
|
||||
<source>%</source>
|
||||
<translation>%</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="1362"/>
|
||||
<location filename="details.cc" line="1381"/>
|
||||
<source>Status</source>
|
||||
<translation>Состояние</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="1362"/>
|
||||
<location filename="details.cc" line="1381"/>
|
||||
<source>Address</source>
|
||||
<translation>Адрес</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="details.cc" line="1362"/>
|
||||
<location filename="details.cc" line="1381"/>
|
||||
<source>Client</source>
|
||||
<translation>Клиент</translation>
|
||||
</message>
|
||||
|
@ -2786,8 +2838,9 @@ second %s is the version number
|
|||
</message>
|
||||
<message>
|
||||
<location filename="mainwin.ui" line="336"/>
|
||||
<source>Alt+M</source>
|
||||
<translation></translation>
|
||||
<source>Alt+C</source>
|
||||
<oldsource>Alt+M</oldsource>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="mainwin.ui" line="344"/>
|
||||
|
@ -3436,7 +3489,7 @@ To add another primary URL, add it after a blank line.</source>
|
|||
</message>
|
||||
<message>
|
||||
<location filename="prefs-dialog.cc" line="373"/>
|
||||
<location filename="prefs-dialog.cc" line="730"/>
|
||||
<location filename="prefs-dialog.cc" line="735"/>
|
||||
<source>Status unknown</source>
|
||||
<translation>Статус неизвестен</translation>
|
||||
</message>
|
||||
|
@ -3516,7 +3569,7 @@ To add another primary URL, add it after a blank line.</source>
|
|||
</message>
|
||||
<message>
|
||||
<location filename="prefs-dialog.cc" line="475"/>
|
||||
<location filename="prefs-dialog.cc" line="625"/>
|
||||
<location filename="prefs-dialog.cc" line="630"/>
|
||||
<source>Privacy</source>
|
||||
<translation>Конфиденциальность</translation>
|
||||
</message>
|
||||
|
@ -3610,6 +3663,16 @@ To add another primary URL, add it after a blank line.</source>
|
|||
<source>Call scrip&t when torrent is completed</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="prefs-dialog.cc" line="600"/>
|
||||
<source>Stop seeding at &ratio:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="prefs-dialog.cc" line="605"/>
|
||||
<source>Stop seeding if idle for &N minutes:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Adding Torrents</source>
|
||||
<translation type="obsolete">Добавление торрентов</translation>
|
||||
|
@ -3639,51 +3702,51 @@ To add another primary URL, add it after a blank line.</source>
|
|||
</message>
|
||||
<message>
|
||||
<location filename="prefs-dialog.cc" line="598"/>
|
||||
<source>Seeding</source>
|
||||
<source>Seeding Limits</source>
|
||||
<oldsource>Seeding</oldsource>
|
||||
<translation type="unfinished">Раздача</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="prefs-dialog.cc" line="600"/>
|
||||
<source>&Seed torrent until its ratio reaches:</source>
|
||||
<translation>&Раздавать до достижения рейтинга:</translation>
|
||||
<translation type="obsolete">&Раздавать до достижения рейтинга:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="prefs-dialog.cc" line="620"/>
|
||||
<location filename="prefs-dialog.cc" line="625"/>
|
||||
<source>Transmission Preferences</source>
|
||||
<translation>Параметры Transmission</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="prefs-dialog.cc" line="623"/>
|
||||
<location filename="prefs-dialog.cc" line="628"/>
|
||||
<source>Torrents</source>
|
||||
<translation>Торренты</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="prefs-dialog.cc" line="624"/>
|
||||
<location filename="prefs-dialog.cc" line="629"/>
|
||||
<source>Speed</source>
|
||||
<translation>Скорость</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="prefs-dialog.cc" line="626"/>
|
||||
<location filename="prefs-dialog.cc" line="631"/>
|
||||
<source>Network</source>
|
||||
<translation>Сеть</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="prefs-dialog.cc" line="627"/>
|
||||
<location filename="prefs-dialog.cc" line="632"/>
|
||||
<source>Web</source>
|
||||
<translation>Веб-интерфейс</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="prefs-dialog.cc" line="656"/>
|
||||
<location filename="prefs-dialog.cc" line="661"/>
|
||||
<source>Not supported by remote sessions</source>
|
||||
<translation>Не поддерживается удаленными сеансами</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="prefs-dialog.cc" line="682"/>
|
||||
<location filename="prefs-dialog.cc" line="687"/>
|
||||
<source>Enable &blocklist</source>
|
||||
<translation>&Включить черный список</translation>
|
||||
</message>
|
||||
<message numerus="yes">
|
||||
<location filename="prefs-dialog.cc" line="684"/>
|
||||
<location filename="prefs-dialog.cc" line="689"/>
|
||||
<source>Enable &blocklist (%Ln rules)</source>
|
||||
<translation>
|
||||
<numerusform>Включить &чёрный список (%Ln правило)</numerusform>
|
||||
|
@ -3745,7 +3808,7 @@ To add another primary URL, add it after a blank line.</source>
|
|||
<context>
|
||||
<name>Session</name>
|
||||
<message>
|
||||
<location filename="session.cc" line="765"/>
|
||||
<location filename="session.cc" line="769"/>
|
||||
<source>Add Torrent</source>
|
||||
<translation>Добавить торрент</translation>
|
||||
</message>
|
||||
|
@ -3853,47 +3916,47 @@ To add another primary URL, add it after a blank line.</source>
|
|||
<context>
|
||||
<name>Torrent</name>
|
||||
<message>
|
||||
<location filename="torrent.cc" line="677"/>
|
||||
<location filename="torrent.cc" line="683"/>
|
||||
<source>Waiting to verify local data</source>
|
||||
<translation>Ожидается проверка локальных данных</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="torrent.cc" line="678"/>
|
||||
<location filename="torrent.cc" line="684"/>
|
||||
<source>Verifying local data</source>
|
||||
<translation>Проверка локальных данных</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="torrent.cc" line="679"/>
|
||||
<location filename="torrent.cc" line="685"/>
|
||||
<source>Downloading</source>
|
||||
<translation>Приём</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="torrent.cc" line="680"/>
|
||||
<location filename="torrent.cc" line="686"/>
|
||||
<source>Seeding</source>
|
||||
<translation>Раздача</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="torrent.cc" line="681"/>
|
||||
<location filename="torrent.cc" line="687"/>
|
||||
<source>Paused</source>
|
||||
<translation>Приостановлен</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="torrent.cc" line="681"/>
|
||||
<location filename="torrent.cc" line="687"/>
|
||||
<source>Finished</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="torrent.cc" line="694"/>
|
||||
<location filename="torrent.cc" line="700"/>
|
||||
<source>Tracker gave a warning: %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="torrent.cc" line="695"/>
|
||||
<location filename="torrent.cc" line="701"/>
|
||||
<source>Tracker gave an error: %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="torrent.cc" line="696"/>
|
||||
<location filename="torrent.cc" line="702"/>
|
||||
<source>Error: %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
|
@ -3932,7 +3995,7 @@ To add another primary URL, add it after a blank line.</source>
|
|||
</message>
|
||||
<message>
|
||||
<location filename="torrent-delegate.cc" line="150"/>
|
||||
<location filename="torrent-delegate.cc" line="250"/>
|
||||
<location filename="torrent-delegate.cc" line="252"/>
|
||||
<source> - </source>
|
||||
<translation> - </translation>
|
||||
</message>
|
||||
|
@ -3947,22 +4010,27 @@ To add another primary URL, add it after a blank line.</source>
|
|||
<translation>Оставшееся время неизвестно</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="torrent-delegate.cc" line="174"/>
|
||||
<source>Down: %1, Up: %2</source>
|
||||
<translation>Приём: %1, раздача: %2</translation>
|
||||
<location filename="torrent-delegate.cc" line="176"/>
|
||||
<source>%1 %2, %3 %4</source>
|
||||
<translation type="unfinished">%1, %3 %4 {1 %2,?}</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Down: %1, Up: %2</source>
|
||||
<translation type="obsolete">Приём: %1, раздача: %2</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="torrent-delegate.cc" line="176"/>
|
||||
<source>Down: %1</source>
|
||||
<translation>Приём: %1</translation>
|
||||
<translation type="obsolete">Приём: %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="torrent-delegate.cc" line="178"/>
|
||||
<source>Up: %1</source>
|
||||
<translation>Раздача: %1</translation>
|
||||
<location filename="torrent-delegate.cc" line="180"/>
|
||||
<source>%1 %2</source>
|
||||
<oldsource>Up: %1</oldsource>
|
||||
<translation type="unfinished">Раздача: %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="torrent-delegate.cc" line="180"/>
|
||||
<location filename="torrent-delegate.cc" line="182"/>
|
||||
<source>Idle</source>
|
||||
<translation>Нет активности</translation>
|
||||
</message>
|
||||
|
@ -3975,17 +4043,17 @@ To add another primary URL, add it after a blank line.</source>
|
|||
<translation type="obsolete">Ожидается проверка локальных данных</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="torrent-delegate.cc" line="193"/>
|
||||
<location filename="torrent-delegate.cc" line="195"/>
|
||||
<source>Verifying local data (%1% tested)</source>
|
||||
<translation>Проверка локальных данных (%1% проверено)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="torrent-delegate.cc" line="199"/>
|
||||
<location filename="torrent-delegate.cc" line="201"/>
|
||||
<source>Ratio: %1, </source>
|
||||
<translation>Рейтинг: %1</translation>
|
||||
</message>
|
||||
<message numerus="yes">
|
||||
<location filename="torrent-delegate.cc" line="230"/>
|
||||
<location filename="torrent-delegate.cc" line="232"/>
|
||||
<source>Downloading from %1 of %n connected peer(s)</source>
|
||||
<translation>
|
||||
<numerusform>Приём от %1 из %n подключённого узла</numerusform>
|
||||
|
@ -3994,7 +4062,7 @@ To add another primary URL, add it after a blank line.</source>
|
|||
</translation>
|
||||
</message>
|
||||
<message numerus="yes">
|
||||
<location filename="torrent-delegate.cc" line="233"/>
|
||||
<location filename="torrent-delegate.cc" line="235"/>
|
||||
<source>Downloading metadata from %n peer(s) (%1% done)</source>
|
||||
<translation type="unfinished">
|
||||
<numerusform></numerusform>
|
||||
|
@ -4003,7 +4071,7 @@ To add another primary URL, add it after a blank line.</source>
|
|||
</translation>
|
||||
</message>
|
||||
<message numerus="yes">
|
||||
<location filename="torrent-delegate.cc" line="238"/>
|
||||
<location filename="torrent-delegate.cc" line="240"/>
|
||||
<source>Seeding to %1 of %n connected peer(s)</source>
|
||||
<translation>
|
||||
<numerusform>Раздача к %1 из %n подключённого узла</numerusform>
|
||||
|
@ -4238,6 +4306,69 @@ To add another primary URL, add it after a blank line.</source>
|
|||
<translation>Последний ответ от сервера %1 назад</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>TrackerDelegate</name>
|
||||
<message>
|
||||
<location filename="tracker-delegate.cc" line="161"/>
|
||||
<source>Got a list of %1%2 peers%3 %4 ago</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="tracker-delegate.cc" line="169"/>
|
||||
<source>Peer list request timed out %1%2%3 ago; will retry</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="tracker-delegate.cc" line="176"/>
|
||||
<source>Got an error %1'%2'%3 %4 ago</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="tracker-delegate.cc" line="189"/>
|
||||
<source>No updates scheduled</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="tracker-delegate.cc" line="196"/>
|
||||
<source>Asking for more peers in %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="tracker-delegate.cc" line="202"/>
|
||||
<source>Queued to ask for more peers</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="tracker-delegate.cc" line="208"/>
|
||||
<source>Asking for more peers now... <small>%1</small></source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="tracker-delegate.cc" line="221"/>
|
||||
<source>Tracker had %1%2 seeders%3 and %4%5 leechers%6 %7 ago</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="tracker-delegate.cc" line="232"/>
|
||||
<source>Got a scrape error %1'%2'%3 %4 ago</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="tracker-delegate.cc" line="248"/>
|
||||
<source>Asking for peer counts in %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="tracker-delegate.cc" line="254"/>
|
||||
<source>Queued to ask for peer counts</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="tracker-delegate.cc" line="261"/>
|
||||
<source>Asking for peer counts now... <small>%1</small></source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>Utils</name>
|
||||
<message>
|
||||
|
|
Loading…
Reference in New Issue