Ticket #671: queue.3.patch

File queue.3.patch, 128.5 KB (added by charles, 11 years ago)

exactly the same content, but resynced to r11380.

  • libtransmission/torrent.h

     
    1818#define TR_TORRENT_H 1
    1919
    2020#include "completion.h" /* tr_completion */
    21 #include "session.h" /* tr_sessionLock(), tr_sessionUnlock() */
     21#include "session.h" /* tr_sessionLock(), tr_sessionUnlock(), tr_sessionProcessQueue */
    2222#include "utils.h" /* TR_GNUC_PRINTF */
    2323
    2424struct tr_bandwidth;
     
    114114
    115115void             tr_torrentSetLocalError( tr_torrent * tor, const char * fmt, ... ) TR_GNUC_PRINTF( 2, 3 );
    116116
     117/* helper functions needed for the queue */
     118void             tr_torrentStartQueued( tr_torrent * tor );
     119void             tr_torrentStopQueued( tr_torrent * tor );
    117120
    118 
    119121typedef enum
    120122{
    121123    TR_VERIFY_NONE,
     
    232234    tr_torrent_idle_limit_hit_func  * idle_limit_hit_func;
    233235    void                            * idle_limit_hit_func_user_data;
    234236
     237    tr_torrent_queue_func * queue_hit_func;
     238    void                  * queue_hit_func_user_data;
     239
    235240    tr_bool                    isRunning;
    236241    tr_bool                    isStopping;
    237242    tr_bool                    isDeleting;
     
    250255    tr_torrent *               next;
    251256
    252257    int                        uniqueId;
     258    int                        queueRank;
    253259
     260    tr_bool                    isQueued;
     261    time_t                     timeQueue;
     262    uint64_t                   downloadedQueue;
     263    double                     downloadedQueueKBps;
     264    uint64_t                   uploadedQueue;
     265    double                     uploadedQueueKBps;
     266
    254267    struct tr_bandwidth      * bandwidth;
    255268
    256269    struct tr_torrent_peers  * torrentPeers;
     
    359372    return tr_bitfieldHasFast( &tor->checkedPieces, i );
    360373}
    361374
     375static inline void tr_torrentMoveQueueRankUp( tr_torrent * tor )
     376{
     377    if( tor->queueRank > 0 ) tr_torrentSetQueueRank( tor, tor->queueRank - 1 );
     378}
     379
     380static inline void tr_torrentMoveQueueRankDown( tr_torrent * tor )
     381{
     382    if( tor->queueRank >= 0 ) tr_torrentSetQueueRank( tor, tor->queueRank + 1 );
     383}
     384
     385static inline void tr_torrentMoveQueueRankTop( tr_torrent * tor )
     386{
     387    if( tor->queueRank > 0 ) tr_torrentSetQueueRank( tor, 0 );
     388}
     389
     390static inline void tr_torrentMoveQueueRankBottom( tr_torrent * tor )
     391{
     392    if( tor->queueRank >= 0 ) tr_torrentSetQueueRank( tor, -1 );
     393}
     394
    362395/***
    363396****
    364397***/
     
    382415{
    383416    assert( tr_isTorrent( tor ) );
    384417
     418    tor->anyDate = tr_time();
    385419    tor->isDirty = TRUE;
    386420}
    387421
  • libtransmission/rpcimpl.c

     
    202202    for( i = 0; i < torrentCount; ++i )
    203203    {
    204204        tr_torrent * tor = torrents[i];
     205
    205206        if( !tor->isRunning )
    206207        {
    207208            tr_torrentStart( tor );
     
    213214}
    214215
    215216static const char*
     217torrentForceStart( tr_session               * session,
     218                   tr_benc                  * args_in,
     219                   tr_benc                  * args_out UNUSED,
     220                   struct tr_rpc_idle_data  * idle_data UNUSED )
     221{
     222    int           i, torrentCount;
     223    tr_torrent ** torrents = getTorrents( session, args_in, &torrentCount );
     224
     225    assert( idle_data == NULL );
     226
     227    for( i = 0; i < torrentCount; ++i )
     228    {
     229        tr_torrent * tor = torrents[i];
     230
     231        tr_torrentSetQueued( tor, FALSE );
     232        if( !tor->isRunning )
     233        {
     234            tr_torrentStartQueued( tor );
     235            notify( session, TR_RPC_TORRENT_STARTED, tor );
     236            tr_sessionProcessQueue( tor->session );
     237        }
     238    }
     239    tr_free( torrents );
     240    return NULL;
     241}
     242
     243static const char*
    216244torrentStop( tr_session               * session,
    217245             tr_benc                  * args_in,
    218246             tr_benc                  * args_out UNUSED,
     
    227255    {
    228256        tr_torrent * tor = torrents[i];
    229257
    230         if( tor->isRunning )
    231         {
    232             tor->isStopping = TRUE;
    233             notify( session, TR_RPC_TORRENT_STOPPED, tor );
    234         }
     258        tr_torrentStop( tor );
     259        notify( session, TR_RPC_TORRENT_STOPPED, tor );
    235260    }
    236261    tr_free( torrents );
    237262    return NULL;
     
    574599        for( i = 0; i < inf->fileCount; ++i )
    575600            tr_bencListAddInt( p, inf->files[i].priority );
    576601    }
     602    else if( tr_streq( key, keylen, "queued" ) )
     603        tr_bencDictAddBool( d, key, tr_torrentIsQueued( tor ) );
     604    else if( tr_streq( key, keylen, "queueRank" ) )
     605        tr_bencDictAddInt( d, key, tr_torrentGetQueueRank( tor ) );
    577606    else if( tr_streq( key, keylen, "rateDownload" ) )
    578607        tr_bencDictAddInt( d, key, toSpeedBytes( st->pieceDownloadSpeed_KBps ) );
    579608    else if( tr_streq( key, keylen, "rateUpload" ) )
     
    951980    return errmsg;
    952981}
    953982
     983static void
     984torrentMoveQueueRank( tr_torrent * tor , const tr_queue_direction dir )
     985{
     986    switch(dir)
     987    {
     988        case TR_QUEUE_UP        : tr_torrentMoveQueueRankUp( tor ); break;
     989        case TR_QUEUE_DOWN      : tr_torrentMoveQueueRankDown( tor ); break;
     990        case TR_QUEUE_TOP       : tr_torrentMoveQueueRankTop( tor ); break;
     991        case TR_QUEUE_BOTTOM    : tr_torrentMoveQueueRankBottom( tor ); break;
     992        default                 : break;
     993    }
     994}
     995
    954996static const char*
    955997torrentSet( tr_session               * session,
    956998            tr_benc                  * args_in,
     
    10111053            errmsg = removeTrackers( tor, trackers );
    10121054        if( !errmsg && tr_bencDictFindList( args_in, "trackerReplace", &trackers ) )
    10131055            errmsg = replaceTrackers( tor, trackers );
     1056        if( tr_bencDictFindInt( args_in, "moveQueueRank", &tmp ) )
     1057            torrentMoveQueueRank( tor, (tr_queue_direction)tmp );
     1058        if( tr_bencDictFindBool( args_in, "queued", &boolVal) )
     1059            tr_torrentSetQueued( tor, boolVal );
     1060        if( tr_bencDictFindInt( args_in, "queueRank", &tmp ) )
     1061            tr_torrentSetQueueRank( tor, tmp );
    10141062        notify( session, TR_RPC_TORRENT_CHANGED, tor );
    10151063    }
    10161064
     
    15021550        else
    15031551            tr_sessionSetEncryption( session, TR_ENCRYPTION_PREFERRED );
    15041552    }
     1553    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_QUEUE_ENABLED_DOWNLOAD, &boolVal ) )
     1554        tr_sessionSetQueueEnabledDownload( session, boolVal );
     1555    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_QUEUE_ENABLED_SEED, &boolVal ) )
     1556        tr_sessionSetQueueEnabledSeed( session, boolVal );
     1557    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_QUEUE_MAX_DOWNLOAD_ACTIVE, &i ) )
     1558        tr_sessionSetQueueMaxDownloadActive( session, i );
     1559    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_QUEUE_MAX_SEED_ACTIVE, &i ) )
     1560        tr_sessionSetQueueMaxSeedActive( session, i );
     1561    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_QUEUE_NEW_TORRENTS_TOP, &boolVal ) )
     1562        tr_sessionSetQueueNewTorrentsTop( session, boolVal );
     1563    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_QUEUE_SKIP_SLOW_TORRENTS, &boolVal ) )
     1564        tr_sessionSetQueueSkipSlowTorrentsEnabled( session, boolVal );
     1565    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_QUEUE_SLOW_CUTOFF_KBps, &i ) )
     1566        tr_sessionSetQueueSlowCutoff( session, i );
     1567    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_QUEUE_SPEED_LIMIT, &boolVal ) )
     1568        tr_sessionSetQueueSpeedLimitEnabled( session, boolVal );
    15051569
    15061570    notify( session, TR_RPC_SESSION_CHANGED, NULL );
    15071571
     
    16101674        case TR_ENCRYPTION_REQUIRED: str = "required"; break;
    16111675        default: str = "preferred"; break;
    16121676    }
    1613     tr_bencDictAddStr( d, TR_PREFS_KEY_ENCRYPTION, str );
     1677    tr_bencDictAddStr ( d, TR_PREFS_KEY_ENCRYPTION, str );
     1678    tr_bencDictAddBool( d, TR_PREFS_KEY_QUEUE_ENABLED_DOWNLOAD, tr_sessionIsQueueEnabledDownload( s ) );
     1679    tr_bencDictAddBool( d, TR_PREFS_KEY_QUEUE_ENABLED_SEED, tr_sessionIsQueueEnabledSeed( s ) );
     1680    tr_bencDictAddInt ( d, TR_PREFS_KEY_QUEUE_MAX_DOWNLOAD_ACTIVE, tr_sessionGetQueueMaxDownloadActive( s ) );
     1681    tr_bencDictAddInt ( d, TR_PREFS_KEY_QUEUE_MAX_SEED_ACTIVE, tr_sessionGetQueueMaxSeedActive( s ) );
     1682    tr_bencDictAddBool( d, TR_PREFS_KEY_QUEUE_NEW_TORRENTS_TOP, tr_sessionGetQueueNewTorrentsTop( s ) );
     1683    tr_bencDictAddBool( d, TR_PREFS_KEY_QUEUE_SKIP_SLOW_TORRENTS, tr_sessionIsQueueSkipSlowTorrentsEnabled( s ) );
     1684    tr_bencDictAddInt ( d, TR_PREFS_KEY_QUEUE_SLOW_CUTOFF_KBps, tr_sessionGetQueueSlowCutoff( s ) );
     1685    tr_bencDictAddBool( d, TR_PREFS_KEY_QUEUE_SPEED_LIMIT, tr_sessionIsQueueSpeedLimitEnabled( s ) );
    16141686
    16151687    return NULL;
    16161688}
     
    16401712    { "torrent-set",           TRUE,  torrentSet          },
    16411713    { "torrent-set-location",  TRUE,  torrentSetLocation  },
    16421714    { "torrent-start",         TRUE,  torrentStart        },
     1715    { "torrent-force-start",   TRUE,  torrentForceStart   },
    16431716    { "torrent-stop",          TRUE,  torrentStop         },
    16441717    { "torrent-verify",        TRUE,  torrentVerify       },
    16451718    { "torrent-reannounce",    TRUE,  torrentReannounce   }
  • libtransmission/transmission.h

     
    181181#define TR_PREFS_KEY_PEX_ENABLED                   "pex-enabled"
    182182#define TR_PREFS_KEY_PORT_FORWARDING               "port-forwarding-enabled"
    183183#define TR_PREFS_KEY_PREALLOCATION                 "preallocation"
     184#define TR_PREFS_KEY_QUEUE_ENABLED_DOWNLOAD        "queue-enabled-download"
     185#define TR_PREFS_KEY_QUEUE_ENABLED_SEED            "queue-enabled-seed"
     186#define TR_PREFS_KEY_QUEUE_MAX_DOWNLOAD_ACTIVE     "queue-max-download-active"
     187#define TR_PREFS_KEY_QUEUE_MAX_SEED_ACTIVE         "queue-max-seed-active"
     188#define TR_PREFS_KEY_QUEUE_NEW_TORRENTS_TOP        "queue-new-torrents-top"
     189#define TR_PREFS_KEY_QUEUE_SKIP_SLOW_TORRENTS      "queue-skip-slow-enabled"
     190#define TR_PREFS_KEY_QUEUE_SLOW_COUNT              "queue-slow-count"
     191#define TR_PREFS_KEY_QUEUE_SLOW_CUTOFF_KBps        "queue-slow-cutoff"
     192#define TR_PREFS_KEY_QUEUE_SPEED_LIMIT             "queue-speed-limit-enabled"
    184193#define TR_PREFS_KEY_RATIO                         "ratio-limit"
    185194#define TR_PREFS_KEY_RATIO_ENABLED                 "ratio-limit-enabled"
    186195#define TR_PREFS_KEY_RENAME_PARTIAL_FILES          "rename-partial-files"
     
    693702void       tr_sessionSetDeleteSource  ( tr_session *, tr_bool deleteSource );
    694703tr_bool    tr_sessionGetDeleteSource  ( const tr_session * );
    695704
     705/***
     706****
     707***/
     708
     709tr_bool     tr_sessionIsQueueEnabledDownload( const tr_session * session );
     710void        tr_sessionSetQueueEnabledDownload( tr_session * session, tr_bool enabled );
     711
     712tr_bool     tr_sessionIsQueueEnabledSeed( const tr_session * session );
     713void        tr_sessionSetQueueEnabledSeed( tr_session * session, tr_bool enabled );
     714
     715int         tr_sessionGetQueueMaxDownloadActive( const tr_session * session );
     716void        tr_sessionSetQueueMaxDownloadActive( tr_session * session, int maxActive );
     717
     718int         tr_sessionGetQueueMaxSeedActive( const tr_session * session );
     719void        tr_sessionSetQueueMaxSeedActive( tr_session * session, int maxActive );
     720
     721tr_bool     tr_sessionGetQueueNewTorrentsTop( const tr_session * session );
     722void        tr_sessionSetQueueNewTorrentsTop( tr_session * session, tr_bool enabled );
     723
     724tr_bool     tr_sessionIsQueueSkipSlowTorrentsEnabled( const tr_session * session );
     725void        tr_sessionSetQueueSkipSlowTorrentsEnabled( tr_session * session, tr_bool enabled );
     726
     727int         tr_sessionGetQueueSlowCount( const tr_session * session );
     728void        tr_sessionSetQueueSlowCount( tr_session * session, int count );
     729
     730int         tr_sessionGetQueueSlowCutoff( const tr_session * session );
     731void        tr_sessionSetQueueSlowCutoff( tr_session * session, int cutoff );
     732
     733tr_bool     tr_sessionIsQueueSpeedLimitEnabled( const tr_session * session );
     734void        tr_sessionSetQueueSpeedLimitEnabled( tr_session * session, tr_bool enabled );
     735
    696736/**
    697737 *  Load all the torrents in tr_getTorrentDir().
    698738 *  This can be used at startup to kickstart all the torrents
     
    10151055           this torrent, then calls tr_torrentFree(). */
    10161056void tr_torrentRemove( tr_torrent * torrent );
    10171057
    1018 /** @brief Start a torrent */
     1058/** @brief Start a torrent or add it to the queue */
    10191059void tr_torrentStart( tr_torrent * torrent );
    10201060
    1021 /** @brief Stop (pause) a torrent */
     1061/** @brief Stop (pause) a torrent and remove from queue */
    10221062void tr_torrentStop( tr_torrent * torrent );
    10231063
    10241064enum
     
    12521292                           const tr_tracker_info  * trackers,
    12531293                           int                      trackerCount );
    12541294
     1295/**
     1296 * @brief returns if an active queue could apply to this torrent
     1297 */
     1298tr_bool tr_torrentCouldQueue( const tr_torrent * tor );
    12551299
    12561300/**
     1301 * @brief returns if a torrent is queued
     1302 */
     1303tr_bool tr_torrentIsQueued( const tr_torrent * tor );
     1304
     1305/**
     1306 * @brief set if a torrent is queued
     1307 */
     1308void tr_torrentSetQueued( tr_torrent * tor, tr_bool queued );
     1309
     1310/**
     1311 * @brief returns a torrent's queueRank
     1312 */
     1313int tr_torrentGetQueueRank( const tr_torrent * tor );
     1314
     1315/**
     1316 * @brief sets a torrent's queueRank.
     1317 *
     1318 * The queue starts downloading torrents starting from queueRank 1.
     1319 */
     1320void tr_torrentSetQueueRank( tr_torrent * tor, int rank );
     1321
     1322/**
     1323 * @brief comparator for sorting torrents by queue rank
     1324 *
     1325 * Compares first by queue and then by name and hash for torrents that have
     1326 * equal queue rankings.
     1327 */
     1328int tr_sessionCompareTorrentByQueueRank( const void * va, const void * vb );
     1329
     1330typedef enum
     1331{
     1332    TR_QUEUE_UP         = 0,
     1333    TR_QUEUE_DOWN       = 1,
     1334    TR_QUEUE_TOP        = 2,
     1335    TR_QUEUE_BOTTOM     = 3
     1336}
     1337tr_queue_direction;
     1338
     1339/**
    12571340***
    12581341**/
    12591342
     
    12801363typedef void ( tr_torrent_idle_limit_hit_func )( tr_torrent   * torrent,
    12811364                                                 void         * user_data );
    12821365
     1366typedef void ( tr_torrent_queue_func )( tr_torrent   * torrent,
     1367                                        void         * user_data );
    12831368
    12841369/**
    12851370 * Register to be notified whenever a torrent's "completeness"
     
    13441429
    13451430void tr_torrentClearIdleLimitHitCallback( tr_torrent * torrent );
    13461431
     1432/**
     1433 * Register to be notified whenever a torrent has been started or
     1434 * or stopped by the queue.
     1435 *
     1436 * Has the same restrictions as tr_torrentSetCompletenessCallback
     1437 */
     1438void tr_torrentSetQueueCallback(
     1439     tr_torrent           * tor,
     1440     tr_torrent_queue_func  func,
     1441     void                 * user_data );
    13471442
     1443void tr_torrentClearQueueCallback( tr_torrent * torrent );
    13481444/**
    13491445 * MANUAL ANNOUNCE
    13501446 *
  • libtransmission/resume.c

     
    6464#define KEY_PROGRESS_BITFIELD  "bitfield"
    6565#define KEY_PROGRESS_HAVE      "have"
    6666
     67#define KEY_QUEUE_RANK         "queue-rank"
     68#define KEY_QUEUED             "queued"
     69
    6770enum
    6871{
    6972    MAX_REMEMBERED_PEERS = 200
     
    561564    tr_bencDictAddInt( &top, KEY_MAX_PEERS, tor->maxConnectedPeers );
    562565    tr_bencDictAddInt( &top, KEY_BANDWIDTH_PRIORITY, tr_torrentGetPriority( tor ) );
    563566    tr_bencDictAddBool( &top, KEY_PAUSED, !tor->isRunning );
     567    tr_bencDictAddInt( &top, KEY_QUEUE_RANK, tr_torrentGetQueueRank( tor ) );
     568    tr_bencDictAddInt( &top, KEY_QUEUED, tr_torrentIsQueued( tor ) );
    564569    savePeers( &top, tor );
    565570    if( tr_torrentHasMetadata( tor ) )
    566571    {
     
    688693        fieldsLoaded |= TR_FR_BANDWIDTH_PRIORITY;
    689694    }
    690695
     696    if( ( fieldsToLoad & TR_FR_QUEUERANK )
     697      && tr_bencDictFindInt( &top, KEY_QUEUE_RANK, &i ) )
     698    {
     699        tor->queueRank = i;
     700        fieldsLoaded |= TR_FR_QUEUERANK;
     701    }
     702
     703    if( ( fieldsToLoad & TR_FR_QUEUED )
     704      && tr_bencDictFindBool( &top, KEY_QUEUED, &boolVal ) )
     705    {
     706        tor->isQueued = boolVal;
     707        fieldsLoaded |= TR_FR_QUEUED;
     708    }
     709
    691710    if( fieldsToLoad & TR_FR_PEERS )
    692711        fieldsLoaded |= loadPeers( &top, tor );
    693712
  • libtransmission/resume.h

     
    3636    TR_FR_DONE_DATE           = ( 1 << 14 ),
    3737    TR_FR_ACTIVITY_DATE       = ( 1 << 15 ),
    3838    TR_FR_RATIOLIMIT          = ( 1 << 16 ),
    39     TR_FR_IDLELIMIT           = ( 1 << 17 )
     39    TR_FR_IDLELIMIT           = ( 1 << 17 ),
     40    TR_FR_QUEUERANK           = ( 1 << 18 ),
     41    TR_FR_QUEUED              = ( 1 << 19 )
    4042};
    4143
    4244/**
  • libtransmission/session.c

     
    1212
    1313#include <assert.h>
    1414#include <errno.h> /* ENOENT */
     15#include <math.h> /* ceil */
    1516#include <stdlib.h>
    1617#include <string.h> /* memcpy */
    1718
     
    5556{
    5657    SAVE_INTERVAL_SECS = 120,
    5758
     59    QUEUE_INTERVAL_SECS = 60,
     60
     61    QUEUE_MIN_INTERVAL_SECS = 1,
     62
     63    QUEUE_MIN_DT = 5,
     64
     65    QUEUE_MAX_DT = 600, /* 10 minutes */
     66
    5867    DEFAULT_CACHE_SIZE_MB = 2
    5968};
    6069
     
    246255{
    247256    assert( tr_bencIsDict( d ) );
    248257
    249     tr_bencDictReserve( d, 60 );
     258    tr_bencDictReserve( d, 69 );
    250259    tr_bencDictAddBool( d, TR_PREFS_KEY_BLOCKLIST_ENABLED,        FALSE );
    251260    tr_bencDictAddStr ( d, TR_PREFS_KEY_BLOCKLIST_URL,            "http://www.example.com/blocklist" );
    252261    tr_bencDictAddInt ( d, TR_PREFS_KEY_MAX_CACHE_SIZE_MB,        DEFAULT_CACHE_SIZE_MB );
     
    273282    tr_bencDictAddBool( d, TR_PREFS_KEY_PEX_ENABLED,              TRUE );
    274283    tr_bencDictAddBool( d, TR_PREFS_KEY_PORT_FORWARDING,          TRUE );
    275284    tr_bencDictAddInt ( d, TR_PREFS_KEY_PREALLOCATION,            TR_PREALLOCATE_SPARSE );
     285    tr_bencDictAddBool( d, TR_PREFS_KEY_QUEUE_ENABLED_DOWNLOAD,   FALSE );
     286    tr_bencDictAddBool( d, TR_PREFS_KEY_QUEUE_ENABLED_SEED,       FALSE );
     287    tr_bencDictAddInt ( d, TR_PREFS_KEY_QUEUE_MAX_DOWNLOAD_ACTIVE,2 );
     288    tr_bencDictAddInt ( d, TR_PREFS_KEY_QUEUE_MAX_SEED_ACTIVE,    2 );
     289    tr_bencDictAddBool( d, TR_PREFS_KEY_QUEUE_NEW_TORRENTS_TOP,   FALSE );
     290    tr_bencDictAddBool( d, TR_PREFS_KEY_QUEUE_SKIP_SLOW_TORRENTS, FALSE );
     291    tr_bencDictAddInt ( d, TR_PREFS_KEY_QUEUE_SLOW_COUNT,         5 );
     292    tr_bencDictAddInt ( d, TR_PREFS_KEY_QUEUE_SLOW_CUTOFF_KBps,   5 );
     293    tr_bencDictAddBool( d, TR_PREFS_KEY_QUEUE_SPEED_LIMIT,        FALSE );
    276294    tr_bencDictAddReal( d, TR_PREFS_KEY_RATIO,                    2.0 );
    277295    tr_bencDictAddBool( d, TR_PREFS_KEY_RATIO_ENABLED,            FALSE );
    278296    tr_bencDictAddBool( d, TR_PREFS_KEY_RENAME_PARTIAL_FILES,     TRUE );
     
    308326{
    309327    assert( tr_bencIsDict( d ) );
    310328
    311     tr_bencDictReserve( d, 60 );
     329    tr_bencDictReserve( d, 69 );
    312330    tr_bencDictAddBool( d, TR_PREFS_KEY_BLOCKLIST_ENABLED,        tr_blocklistIsEnabled( s ) );
    313331    tr_bencDictAddStr ( d, TR_PREFS_KEY_BLOCKLIST_URL,            tr_blocklistGetURL( s ) );
    314332    tr_bencDictAddInt ( d, TR_PREFS_KEY_MAX_CACHE_SIZE_MB,        tr_sessionGetCacheLimit_MB( s ) );
     
    337355    tr_bencDictAddBool( d, TR_PREFS_KEY_PEX_ENABLED,              s->isPexEnabled );
    338356    tr_bencDictAddBool( d, TR_PREFS_KEY_PORT_FORWARDING,          tr_sessionIsPortForwardingEnabled( s ) );
    339357    tr_bencDictAddInt ( d, TR_PREFS_KEY_PREALLOCATION,            s->preallocationMode );
     358    tr_bencDictAddBool( d, TR_PREFS_KEY_QUEUE_ENABLED_DOWNLOAD,   tr_sessionIsQueueEnabledDownload( s ) );
     359    tr_bencDictAddBool( d, TR_PREFS_KEY_QUEUE_ENABLED_SEED,       tr_sessionIsQueueEnabledSeed( s ) );
     360    tr_bencDictAddInt ( d, TR_PREFS_KEY_QUEUE_MAX_DOWNLOAD_ACTIVE,tr_sessionGetQueueMaxDownloadActive( s ) );
     361    tr_bencDictAddInt ( d, TR_PREFS_KEY_QUEUE_MAX_SEED_ACTIVE,    tr_sessionGetQueueMaxSeedActive( s ) );
     362    tr_bencDictAddBool( d, TR_PREFS_KEY_QUEUE_NEW_TORRENTS_TOP,   tr_sessionGetQueueNewTorrentsTop( s ) );
     363    tr_bencDictAddBool( d, TR_PREFS_KEY_QUEUE_SKIP_SLOW_TORRENTS, tr_sessionIsQueueSkipSlowTorrentsEnabled( s ) );
     364    tr_bencDictAddInt ( d, TR_PREFS_KEY_QUEUE_SLOW_COUNT,         tr_sessionGetQueueSlowCount( s ) );
     365    tr_bencDictAddInt ( d, TR_PREFS_KEY_QUEUE_SLOW_CUTOFF_KBps,   tr_sessionGetQueueSlowCutoff( s ) );
     366    tr_bencDictAddBool( d, TR_PREFS_KEY_QUEUE_SPEED_LIMIT,        tr_sessionIsQueueSpeedLimitEnabled( s ) );
    340367    tr_bencDictAddReal( d, TR_PREFS_KEY_RATIO,                    s->desiredRatio );
    341368    tr_bencDictAddBool( d, TR_PREFS_KEY_RATIO_ENABLED,            s->isRatioLimited );
    342369    tr_bencDictAddBool( d, TR_PREFS_KEY_RENAME_PARTIAL_FILES,     tr_sessionIsIncompleteFileNamingEnabled( s ) );
     
    478505****
    479506***/
    480507
     508static int
     509compareTorrentByQueueRank( const void * va, const void * vb )
     510{
     511    const tr_torrent * a = *(const tr_torrent**)va;
     512    const tr_torrent * b = *(const tr_torrent**)vb;
     513    return a->queueRank - b->queueRank;
     514}
     515
     516int
     517tr_sessionCompareTorrentByQueueRank( const void * va, const void * vb )
     518{
     519    return compareTorrentByQueueRank( &va, &vb );
     520}
     521
     522static tr_ptrArray *
     523initQueue( tr_session * session )
     524{
     525    tr_ptrArray * queueList;
     526
     527    assert( tr_isSession( session ) );
     528
     529    queueList = tr_new( tr_ptrArray, 1 );
     530    *queueList = TR_PTR_ARRAY_INIT;
     531    return queueList;
     532}
     533
     534static void
     535destroyQueue( tr_session * session )
     536{
     537    assert( tr_isSession( session ) );
     538
     539    tr_ptrArrayDestruct( session->queueList, NULL );
     540}
     541
     542void
     543tr_sessionSortQueue( tr_session * session )
     544{
     545    int count;
     546    tr_torrent ** torrents;
     547
     548    assert( tr_isSession( session ) );
     549
     550    torrents = (tr_torrent**)tr_ptrArrayPeek( session->queueList, &count );
     551    qsort( torrents, count, sizeof( tr_torrent* ), compareTorrentByQueueRank );
     552}
     553
     554void
     555tr_sessionCompactQueue( tr_session * session )
     556{
     557    int i, count;
     558    tr_torrent ** torrents;
     559    tr_bool sort = FALSE;
     560
     561    assert( tr_isSession( session ) );
     562
     563    torrents = (tr_torrent**)tr_ptrArrayPeek( session->queueList, &count );
     564    for( i = 0; i < count; ++i )
     565    {
     566        tr_torrent * tor = torrents[i];
     567        if( tor->queueRank != i )
     568        {
     569            sort = TRUE;
     570            tor->queueRank = i;
     571            tr_torrentSetDirty( tor );
     572        }
     573    }
     574    if( sort )
     575    {
     576        tr_sessionSortQueue( session );
     577        tr_sessionProcessQueue( session );
     578    }
     579}
     580
     581/**
     582 * returns true if the torrent should be skipped
     583 */
     584static tr_bool
     585testSkipTorrent( tr_torrent *       tor,
     586                 const int          slowTime,
     587                 const unsigned     slowCutoff,
     588                 const time_t       now,
     589                 const double       dt,
     590                 const tr_direction dir )
     591{
     592    tr_bool skip;
     593
     594    assert( tr_isTorrent( tor ) );
     595    assert( tr_isDirection( dir ) );
     596
     597    if( dt > QUEUE_MIN_DT )
     598    {
     599        const uint64_t downloaded = tor->downloadedPrev + tor->downloadedCur;
     600        const uint64_t uploaded = tor->uploadedPrev + tor->uploadedCur;
     601
     602        tor->downloadedQueueKBps = ceil( toSpeedKBps( ( downloaded - tor->downloadedQueue ) / dt ) );
     603        tor->uploadedQueueKBps = ceil( toSpeedKBps( ( uploaded - tor->uploadedQueue ) / dt ) );
     604        tor->downloadedQueue = downloaded;
     605        tor->uploadedQueue = uploaded;
     606    }
     607
     608    if( dir == TR_DOWN )
     609        skip = tor->downloadedQueueKBps <= slowCutoff;
     610    else
     611        skip = tor->uploadedQueueKBps <= slowCutoff;
     612
     613    if( skip && now < tor->timeQueue + slowTime )
     614        skip = FALSE;
     615    else if( !skip )
     616        tor->timeQueue = now;
     617
     618    return skip;
     619}
     620
     621static void
     622fireQueueCallback( tr_torrent * tor )
     623{
     624    assert( tr_isTorrent( tor ) );
     625
     626    if( tor->queue_hit_func )
     627        tor->queue_hit_func( tor, tor->completeness_func_user_data );
     628}
     629
     630static void
     631resetTimeQueue( tr_torrent * tor )
     632{
     633    assert( tr_isTorrent( tor ) );
     634
     635    tor->timeQueue = tr_time();
     636}
     637
     638static void
     639processQueue( tr_session * session, const tr_bool download, const tr_bool seed )
     640{
     641    tr_bool skip, speedLimit = FALSE;
     642    tr_torrent ** torrents;
     643    int count, slowTime, i, maxDown, maxSeed;
     644    unsigned slowCutoff;
     645    static uint64_t last_msec = 0;
     646    const uint64_t now_msec = tr_time_msec();
     647    const time_t now = tr_time();
     648    double dt;
     649
     650    assert( tr_isSession( session ) );
     651
     652    maxDown     = tr_sessionGetQueueMaxDownloadActive( session );
     653    maxSeed     = tr_sessionGetQueueMaxSeedActive( session );
     654    skip        = tr_sessionIsQueueSkipSlowTorrentsEnabled( session );
     655    slowTime    = tr_sessionGetQueueSlowCount( session ) * 60;
     656    slowCutoff  = tr_sessionGetQueueSlowCutoff( session );
     657    torrents    = (tr_torrent**)tr_ptrArrayPeek( session->queueList, &count );
     658    dt          = ( now_msec - last_msec ) / 1000.0;
     659    if( dt > QUEUE_MIN_DT )
     660        last_msec = now_msec;
     661    /* reset counters if it's been too long since queue was processed */
     662    if( dt > QUEUE_MAX_DT && skip )
     663        tr_ptrArrayForeach( session->queueList, (PtrArrayForeachFunc)resetTimeQueue );
     664
     665    if( tr_sessionIsQueueSpeedLimitEnabled( session ) )
     666    {
     667        int speed;
     668        /* allow for a little deviation in speeds from the limit */
     669        const double dev = 0.9;
     670        /* throw out the first data point */
     671        static uint64_t uploadedBytes = INT64_MAX;
     672        static uint64_t downloadedBytes = INT64_MAX;
     673        tr_session_stats stats;
     674
     675        tr_sessionGetStats( session, &stats );
     676
     677        if( tr_sessionGetActiveSpeedLimit_Bps( session, TR_UP, &speed )
     678            && ( speed * dev ) < ( ( stats.uploadedBytes - uploadedBytes ) / dt ) )
     679            speedLimit = TRUE;
     680        else if( tr_sessionGetActiveSpeedLimit_Bps( session, TR_DOWN, &speed )
     681            && ( speed * dev ) < ( ( stats.downloadedBytes - downloadedBytes ) / dt ) )
     682            speedLimit = TRUE;
     683
     684        if( dt > QUEUE_MIN_DT )
     685        {
     686            uploadedBytes = stats.uploadedBytes;
     687            downloadedBytes = stats.downloadedBytes;
     688        }
     689    }
     690
     691    /* count slots used by unqueued transfers */
     692    for( i = 0; i < count; ++i )
     693    {
     694        tr_torrent * tor = torrents[i];
     695        if( !tr_torrentIsQueued( tor ) )
     696        {
     697            const tr_torrent_activity activity = tr_torrentGetActivity( tor );
     698
     699            if( download && activity == TR_STATUS_DOWNLOAD )
     700            {
     701                if( skip && testSkipTorrent( tor, slowTime, slowCutoff, now, dt, TR_DOWN ) )
     702                    continue;
     703                else
     704                    --maxDown;
     705            }
     706            else if( seed && activity == TR_STATUS_SEED )
     707            {
     708                if( skip && testSkipTorrent( tor, slowTime, slowCutoff, now, dt, TR_UP ) )
     709                    continue;
     710                else
     711                    --maxSeed;
     712            }
     713        }
     714    }
     715
     716    for( i = 0; i < count; ++i )
     717    {
     718        tr_torrent * tor = torrents[i];
     719
     720        if( !tr_torrentIsQueued( tor )
     721            || tor->error == TR_STAT_LOCAL_ERROR
     722            || tor->verifyState != TR_VERIFY_NONE )
     723            continue;
     724
     725        if( download && !tr_torrentIsSeed( tor ) )
     726        {
     727            if( maxDown > 0 )
     728            {
     729                if( tor->isRunning
     730                    && skip
     731                    && testSkipTorrent( tor, slowTime, slowCutoff, now, dt, TR_DOWN ) )
     732                    continue;
     733
     734                if( !speedLimit && !tor->isRunning )
     735                {
     736                    tr_torrentStartQueued( tor );
     737                    fireQueueCallback( tor );
     738                }
     739
     740                --maxDown;
     741            }
     742            else if( tor->isRunning )
     743            {
     744                tr_torrentStopQueued( tor );
     745                fireQueueCallback( tor );
     746            }
     747        }
     748        else if( seed && tr_torrentIsSeed( tor ) )
     749        {
     750            if( maxSeed > 0 )
     751            {
     752                if( tor->isRunning
     753                    && skip
     754                    && testSkipTorrent( tor, slowTime, slowCutoff, now, dt, TR_UP ) )
     755                    continue;
     756
     757                if( !speedLimit && !tor->isRunning )
     758                {
     759                    tr_torrentStartQueued( tor );
     760                    fireQueueCallback( tor );
     761                }
     762
     763                --maxSeed;
     764            }
     765            else if( tor->isRunning )
     766            {
     767                tr_torrentStopQueued( tor );
     768                fireQueueCallback( tor );
     769            }
     770        }
     771    }
     772}
     773
     774void
     775tr_sessionProcessQueue( tr_session * session )
     776{
     777    assert( tr_isSession( session ) );
     778
     779    tr_timerAdd( session->queueTimer, QUEUE_MIN_INTERVAL_SECS, 0 );
     780}
     781
     782/***
     783****
     784***/
     785
     786static void
     787onQueueTimer( int foo UNUSED, short bar UNUSED, void * vsession )
     788{
     789    tr_session * session = vsession;
     790    tr_bool download, seed;
     791
     792    assert( tr_isSession( session ) );
     793
     794    download = tr_sessionIsQueueEnabledDownload( session );
     795    seed     = tr_sessionIsQueueEnabledSeed( session );
     796    if( download || seed )
     797        processQueue( session, download, seed );
     798
     799    tr_timerAdd( session->queueTimer, QUEUE_INTERVAL_SECS, 0 );
     800}
     801
     802/***
     803****
     804***/
     805
    481806static void tr_sessionInitImpl( void * );
    482807
    483808struct init_data
     
    509834    session->tag = tr_strdup( tag );
    510835    session->magicNumber = SESSION_MAGIC_NUMBER;
    511836    session->buffer = tr_valloc( SESSION_BUFFER_SIZE );
     837    session->queueList = initQueue( session );
    512838    tr_bencInitList( &session->removedTorrents, 0 );
    513839
    514840    /* nice to start logging at the very beginning */
     
    611937
    612938    assert( tr_isSession( session ) );
    613939
     940    session->queueTimer = tr_new0( struct event, 1 );
     941    evtimer_set( session->queueTimer, onQueueTimer, session );
     942    tr_timerAdd( session->queueTimer, QUEUE_INTERVAL_SECS, 0 );
     943
    614944    session->saveTimer = tr_new0( struct event, 1 );
    615945    evtimer_set( session->saveTimer, onSaveTimer, session );
    616946    tr_timerAdd( session->saveTimer, SAVE_INTERVAL_SECS, 0 );
     
    7061036    if( tr_bencDictFindBool( settings, TR_PREFS_KEY_RENAME_PARTIAL_FILES, &boolVal ) )
    7071037        tr_sessionSetIncompleteFileNamingEnabled( session, boolVal );
    7081038
     1039    /* torrent queueing */
     1040    if( tr_bencDictFindBool( settings, TR_PREFS_KEY_QUEUE_ENABLED_DOWNLOAD, &boolVal ) )
     1041        session->queueEnabledDownload = boolVal;
     1042    if( tr_bencDictFindBool( settings, TR_PREFS_KEY_QUEUE_ENABLED_SEED, &boolVal ) )
     1043        session->queueEnabledSeed = boolVal;
     1044    if( tr_bencDictFindInt( settings, TR_PREFS_KEY_QUEUE_MAX_DOWNLOAD_ACTIVE, &i ) )
     1045        session->queueMaxDownloadActive = i;
     1046    if( tr_bencDictFindInt( settings, TR_PREFS_KEY_QUEUE_MAX_SEED_ACTIVE, &i ) )
     1047        session->queueMaxSeedActive = i;
     1048    if( tr_bencDictFindBool( settings, TR_PREFS_KEY_QUEUE_NEW_TORRENTS_TOP, &boolVal ) )
     1049        tr_sessionSetQueueNewTorrentsTop( session, boolVal );
     1050    if( tr_bencDictFindBool( settings, TR_PREFS_KEY_QUEUE_SKIP_SLOW_TORRENTS, &boolVal ) )
     1051        session->queueSkipSlowTorrents = boolVal;
     1052    if( tr_bencDictFindInt ( settings, TR_PREFS_KEY_QUEUE_SLOW_COUNT, &i ) )
     1053        session->queueSlowCount = i;
     1054        tr_sessionSetQueueSlowCount( session, i );
     1055    if( tr_bencDictFindInt ( settings, TR_PREFS_KEY_QUEUE_SLOW_CUTOFF_KBps, &i ) )
     1056        session->queueSlowCutoff = i;
     1057    if( tr_bencDictFindBool( settings, TR_PREFS_KEY_QUEUE_SPEED_LIMIT, &boolVal ) )
     1058        tr_sessionSetQueueSpeedLimitEnabled( session, boolVal );
     1059
    7091060    /* rpc server */
    7101061    if( session->rpcServer != NULL ) /* close the old one */
    7111062        tr_rpcClose( &session->rpcServer );
     
    16441995    if( session->isDHTEnabled )
    16451996        tr_dhtUninit( session );
    16461997
     1998    evtimer_del( session->queueTimer );
     1999    tr_free( session->queueTimer );
     2000    session->queueTimer = NULL;
     2001
    16472002    evtimer_del( session->saveTimer );
    16482003    tr_free( session->saveTimer );
    16492004    session->saveTimer = NULL;
     
    16562011    tr_sharedClose( session );
    16572012    tr_rpcClose( &session->rpcServer );
    16582013
     2014    destroyQueue( session );
     2015
    16592016    /* close the torrents.  get the most active ones first so that
    16602017     * if we can't get them all closed in a reasonable amount of time,
    16612018     * at least we get the most important ones first. */
     
    18132170    if( n )
    18142171        tr_inf( _( "Loaded %d torrents" ), n );
    18152172
     2173    tr_sessionCompactQueue( session );
     2174    tr_sessionProcessQueue( session );
     2175
    18162176    if( setmeCount )
    18172177        *setmeCount = n;
    18182178    return torrents;
     
    24692829        session->torrentDoneScript = tr_strdup( scriptFilename );
    24702830    }
    24712831}
     2832
     2833/***
     2834****
     2835***/
     2836
     2837tr_bool
     2838tr_sessionIsQueueEnabledDownload( const tr_session* session )
     2839{
     2840    assert( tr_isSession( session ) );
     2841
     2842    return session->queueEnabledDownload;
     2843}
     2844
     2845void
     2846tr_sessionSetQueueEnabledDownload( tr_session * session, tr_bool enabled )
     2847{
     2848    assert( tr_isSession( session ) );
     2849
     2850    if( session->queueEnabledDownload != enabled )
     2851    {
     2852        session->queueEnabledDownload = enabled;
     2853        tr_sessionProcessQueue( session );
     2854    }
     2855}
     2856
     2857tr_bool
     2858tr_sessionIsQueueEnabledSeed( const tr_session* session )
     2859{
     2860    assert( tr_isSession( session ) );
     2861
     2862    return session->queueEnabledSeed;
     2863}
     2864
     2865void
     2866tr_sessionSetQueueEnabledSeed( tr_session * session, tr_bool enabled )
     2867{
     2868    assert( tr_isSession( session ) );
     2869
     2870    if( session->queueEnabledSeed != enabled )
     2871    {
     2872        session->queueEnabledSeed = enabled;
     2873        tr_sessionProcessQueue( session );
     2874    }
     2875}
     2876
     2877int
     2878tr_sessionGetQueueMaxDownloadActive( const tr_session * session )
     2879{
     2880    assert( tr_isSession( session ) );
     2881
     2882    return session->queueMaxDownloadActive;
     2883}
     2884
     2885void
     2886tr_sessionSetQueueMaxDownloadActive( tr_session * session, int maxActive )
     2887{
     2888    assert( tr_isSession( session ) );
     2889
     2890    if( session->queueMaxDownloadActive != maxActive )
     2891    {
     2892        session->queueMaxDownloadActive = maxActive;
     2893        tr_sessionProcessQueue( session );
     2894    }
     2895}
     2896
     2897int
     2898tr_sessionGetQueueMaxSeedActive( const tr_session * session )
     2899{
     2900    assert( tr_isSession( session ) );
     2901
     2902    return session->queueMaxSeedActive;
     2903}
     2904
     2905void
     2906tr_sessionSetQueueMaxSeedActive( tr_session * session , int maxActive )
     2907{
     2908    assert( tr_isSession( session ) );
     2909
     2910    if( session->queueMaxSeedActive != maxActive )
     2911    {
     2912        session->queueMaxSeedActive = maxActive;
     2913        tr_sessionProcessQueue( session );
     2914    }
     2915}
     2916
     2917tr_bool
     2918tr_sessionGetQueueNewTorrentsTop( const tr_session * session )
     2919{
     2920    assert( tr_isSession( session ) );
     2921
     2922    return session->queueNewTorrentsTop;
     2923}
     2924
     2925void
     2926tr_sessionSetQueueNewTorrentsTop( tr_session * session, tr_bool enabled )
     2927{
     2928    assert( tr_isSession( session ) );
     2929
     2930    session->queueNewTorrentsTop = enabled;
     2931}
     2932
     2933tr_bool
     2934tr_sessionIsQueueSkipSlowTorrentsEnabled( const tr_session * session )
     2935{
     2936    assert( tr_isSession( session ) );
     2937
     2938    return session->queueSkipSlowTorrents;
     2939}
     2940
     2941void
     2942tr_sessionSetQueueSkipSlowTorrentsEnabled( tr_session * session, tr_bool enabled )
     2943{
     2944    assert( tr_isSession( session ) );
     2945
     2946    if( session->queueSkipSlowTorrents != enabled )
     2947    {
     2948        session->queueSkipSlowTorrents = enabled;
     2949        /* reset time counters */
     2950        if( enabled )
     2951            tr_ptrArrayForeach( session->queueList, (PtrArrayForeachFunc)resetTimeQueue );
     2952        tr_sessionProcessQueue( session );
     2953    }
     2954}
     2955
     2956int
     2957tr_sessionGetQueueSlowCount( const tr_session * session )
     2958{
     2959    assert( tr_isSession( session ) );
     2960
     2961    return session->queueSlowCount;
     2962}
     2963
     2964void
     2965tr_sessionSetQueueSlowCount( tr_session * session, int count )
     2966{
     2967    assert( tr_isSession( session ) );
     2968
     2969    if( session->queueSlowCount != count )
     2970    {
     2971        session->queueSlowCount = count;
     2972        tr_sessionProcessQueue( session );
     2973    }
     2974}
     2975
     2976int
     2977tr_sessionGetQueueSlowCutoff( const tr_session * session )
     2978{
     2979    assert( tr_isSession( session ) );
     2980
     2981    return session->queueSlowCutoff;
     2982}
     2983
     2984void
     2985tr_sessionSetQueueSlowCutoff( tr_session * session, int cutoff )
     2986{
     2987    assert( tr_isSession( session ) );
     2988
     2989    if( session->queueSlowCutoff != cutoff )
     2990    {
     2991        session->queueSlowCutoff = cutoff;
     2992        tr_sessionProcessQueue( session );
     2993    }
     2994}
     2995
     2996tr_bool
     2997tr_sessionIsQueueSpeedLimitEnabled( const tr_session * session )
     2998{
     2999    assert( tr_isSession( session ) );
     3000
     3001    return session->queueSpeedLimit;
     3002}
     3003
     3004void
     3005tr_sessionSetQueueSpeedLimitEnabled( tr_session * session, tr_bool enabled )
     3006{
     3007    assert( tr_isSession( session ) );
     3008
     3009    session->queueSpeedLimit = enabled;
     3010}
  • libtransmission/session.h

     
    4343struct tr_bindsockets;
    4444struct tr_cache;
    4545struct tr_fdInfo;
     46struct tr_ptrArray;
    4647
    4748struct tr_turtle_info
    4849{
     
    138139    int                          peerSocketTOS;
    139140    char *                       peer_congestion_algorithm;
    140141
     142    tr_bool                      queueEnabledDownload;
     143    tr_bool                      queueEnabledSeed;
     144    int                          queueMaxDownloadActive;
     145    int                          queueMaxSeedActive;
     146    tr_bool                      queueNewTorrentsTop;
     147    tr_bool                      queueSkipSlowTorrents;
     148    int                          queueSlowCount;
     149    int                          queueSlowCutoff;
     150    tr_bool                      queueSpeedLimit;
     151    struct tr_ptrArray *         queueList;
     152
    141153    int                          torrentCount;
    142154    tr_torrent *                 torrentList;
    143155
     
    174186
    175187    struct event               * nowTimer;
    176188    struct event               * saveTimer;
     189    struct event               * queueTimer;
    177190
    178191    /* monitors the "global pool" speeds */
    179192    struct tr_bandwidth        * bandwidth;
     
    222235
    223236struct tr_bindsockets * tr_sessionGetBindSockets( tr_session * );
    224237
    225 int tr_sessionCountTorrents( const tr_session * session ); 
     238int tr_sessionCountTorrents( const tr_session * session );
    226239
    227240enum
    228241{
     
    287300                                            tr_direction        dir,
    288301                                            int               * setme );
    289302
     303/**
     304 * Run processQueue
     305 */
     306void tr_sessionProcessQueue( tr_session * session );
    290307
     308/**
     309 * Ensure the queue is sorted
     310 */
     311void tr_sessionSortQueue( tr_session * session );
     312
     313/**
     314 * Ensure the queue is consistent
     315 */
     316void tr_sessionCompactQueue( tr_session * session );
    291317#endif
  • libtransmission/torrent.c

     
    1616#include <dirent.h>
    1717
    1818#include <assert.h>
     19#include <float.h> /* DBL_MAX */
    1920#include <limits.h> /* INT_MAX */
    2021#include <math.h>
    2122#include <stdarg.h>
     
    3233#include "cache.h"
    3334#include "completion.h"
    3435#include "crypto.h" /* for tr_sha1 */
     36#include "history.h"
    3537#include "resume.h"
    3638#include "fdlimit.h" /* tr_fdTorrentClose */
    3739#include "magnet.h"
     
    427429        /* maybe notify the client */
    428430        if( tor->ratio_limit_hit_func != NULL )
    429431            tor->ratio_limit_hit_func( tor, tor->ratio_limit_hit_func_user_data );
     432
     433        tr_torrentSetQueued( tor, FALSE );
    430434    }
    431435    /* if we're seeding and reach our inactiviy limit, stop the torrent */
    432436    else if( tr_torrentIsSeedIdleLimitDone( tor ) )
     
    439443        /* maybe notify the client */
    440444        if( tor->idle_limit_hit_func != NULL )
    441445            tor->idle_limit_hit_func( tor, tor->idle_limit_hit_func_user_data );
     446
     447        tr_torrentSetQueued( tor, FALSE );
    442448    }
     449
    443450}
    444451
    445452/***
    446453****
    447454***/
    448455
     456static void
     457torrentSetQueueRank( tr_torrent * tor, int rank )
     458{
     459    tr_session * session;
     460    tr_torrent ** torrents;
     461    int count, i, offset;
     462
     463    assert( tr_isTorrent( tor ) );
     464
     465    session = tor->session;
     466    torrents = (tr_torrent**)tr_ptrArrayPeek( session->queueList, &count );
     467    if( rank >= count )
     468        rank = count - 1;
     469
     470    offset = rank - tor->queueRank;
     471    if( !offset )
     472        return;
     473    else if( offset > 0 ) /* rank > tor->queueRank */
     474    {
     475        tr_torrent * t;
     476        memmove( torrents + tor->queueRank,
     477                 torrents + tor->queueRank + 1,
     478                 sizeof( tr_torrent* ) * offset );
     479        for( i = tor->queueRank; i < rank; ++i )
     480        {
     481            t = torrents[i];
     482            --t->queueRank;
     483            tr_torrentSetDirty( t );
     484        }
     485    }
     486    else /* rank < tor->queueRank */
     487    {
     488        tr_torrent * t;
     489        memmove( torrents + rank + 1,
     490                 torrents + rank,
     491                 sizeof( tr_torrent* ) * -offset );
     492        for( i = tor->queueRank; i > rank; --i )
     493        {
     494            t = torrents[i];
     495            ++t->queueRank;
     496            tr_torrentSetDirty( t );
     497        }
     498    }
     499
     500    torrents[rank] = tor;
     501    tor->queueRank = rank;
     502    tr_torrentSetDirty( tor );
     503    tr_sessionProcessQueue( session );
     504}
     505
     506static void
     507torrentRemoveFromQueue( tr_torrent * tor )
     508{
     509    assert( tr_isTorrent( tor ) );
     510
     511    tr_torrentMoveQueueRankBottom( tor );
     512    tr_ptrArrayPop( tor->session->queueList );
     513    tr_torrentSetQueued( tor, FALSE );
     514}
     515
     516int
     517tr_torrentGetQueueRank( const tr_torrent * tor )
     518{
     519    assert( tr_isTorrent( tor ) );
     520
     521    return tor->queueRank;
     522}
     523
    449524void
     525tr_torrentSetQueueRank( tr_torrent * tor, int rank )
     526{
     527    assert( tr_isTorrent( tor ) );
     528
     529    torrentSetQueueRank( tor, rank < 0 ? INT_MAX : rank );
     530}
     531
     532tr_bool
     533tr_torrentCouldQueue( const tr_torrent * tor )
     534{
     535    tr_bool ret = FALSE;
     536
     537    assert( tr_isTorrent( tor ) );
     538    assert( tr_isSession( tor->session ) );
     539
     540    if( tr_torrentIsSeed( tor ) )
     541    {
     542        if( tr_sessionIsQueueEnabledSeed( tor->session ) )
     543            ret = TRUE;
     544    }
     545    else
     546    {
     547        if( tr_sessionIsQueueEnabledDownload( tor->session ) )
     548            ret = TRUE;
     549    }
     550
     551    return ret;
     552}
     553
     554tr_bool
     555tr_torrentIsQueued( const tr_torrent * tor )
     556{
     557    assert( tr_isTorrent( tor ) );
     558
     559    return tor->isQueued;
     560}
     561
     562void
     563tr_torrentSetQueued( tr_torrent * tor, tr_bool queued )
     564{
     565    assert( tr_isTorrent( tor ) );
     566
     567    if( tor->isQueued != queued )
     568    {
     569        tor->isQueued = queued;
     570        tr_sessionProcessQueue( tor->session );
     571        tr_torrentSetDirty( tor );
     572    }
     573}
     574
     575/***
     576****
     577***/
     578
     579void
    450580tr_torrentSetLocalError( tr_torrent * tor, const char * fmt, ... )
    451581{
    452582    va_list ap;
     
    845975
    846976    tor->tiers = tr_announcerAddTorrent( tor->session->announcer, tor, onTrackerResponse, NULL );
    847977
     978    if( !( loaded & TR_FR_QUEUERANK ) )
     979    {
     980        tor->queueRank = tr_ptrArraySize( session->queueList );
     981        tr_ptrArrayInsertSorted( session->queueList, tor, tr_sessionCompareTorrentByQueueRank );
     982        if( tr_sessionGetQueueNewTorrentsTop( session ) )
     983            tr_torrentMoveQueueRankTop( tor );
     984    }
     985    else
     986    {
     987        tr_ptrArrayInsertSorted( session->queueList, tor, tr_sessionCompareTorrentByQueueRank );
     988    }
     989
    848990    if( doStart )
    849         torrentStart( tor );
     991    {
     992        if( loaded & TR_FR_ADDED_DATE ) /* a resumed torrent should start right away */
     993            torrentStart( tor );
     994        else                            /* new torrents should respect any enabled queues */
     995            tr_torrentStart( tor );
     996    }
    850997
    851998    tr_sessionUnlock( session );
    852999}
     
    15411688        tr_free( tor->peer_id );
    15421689        tor->peer_id = tr_peerIdNew( );
    15431690
     1691        /* refresh time used in the queue cutoff calculation */
     1692        tor->timeQueue = tr_time();
     1693        /* throw out the first data point */
     1694        tor->downloadedQueue = INT64_MAX;
     1695        tor->downloadedQueueKBps = DBL_MAX;
     1696        tor->uploadedQueue = INT64_MAX;
     1697        tor->uploadedQueueKBps = DBL_MAX;
     1698
    15441699        tor->isRunning = 1;
    15451700        tr_torrentSetDirty( tor );
    15461701        tor->preVerifyTotal = tr_cpHaveTotal( &tor->completion );
     
    15541709tr_torrentStart( tr_torrent * tor )
    15551710{
    15561711    if( tr_isTorrent( tor ) )
     1712    {
     1713        const tr_bool isSeed = tr_torrentIsSeed( tor );
     1714        tr_torrentSetQueued( tor, TRUE );
     1715        if( !( tr_sessionIsQueueEnabledDownload( tor->session ) && !isSeed )
     1716         && !( tr_sessionIsQueueEnabledSeed( tor->session ) && isSeed ) )
     1717            torrentStart( tor );
     1718    }
     1719}
     1720
     1721void
     1722tr_torrentStartQueued( tr_torrent * tor )
     1723{
     1724    if( tr_isTorrent( tor ) )
    15571725        torrentStart( tor );
    15581726}
    15591727
     
    15731741    {
    15741742        tor->startAfterVerify = FALSE;
    15751743
    1576         tr_torrentStart( tor );
     1744        if( tor->isQueued )
     1745            tr_torrentStart( tor );
     1746        else
     1747            torrentStart( tor );
    15771748    }
    15781749}
    15791750
     
    16001771    if( tor->startAfterVerify || tor->isRunning ) {
    16011772        /* don't clobber isStopping */
    16021773        const tr_bool startAfter = tor->isStopping ? FALSE : TRUE;
    1603         tr_torrentStop( tor );
     1774        tr_torrentStopQueued( tor );
    16041775        tor->startAfterVerify = startAfter;
    16051776    }
    16061777
     
    16161787tr_torrentVerify( tr_torrent * tor )
    16171788{
    16181789    if( tr_isTorrent( tor ) )
     1790    {
    16191791        tr_runInEventThread( tor->session, verifyTorrent, tor );
     1792        tr_sessionProcessQueue( tor->session );
     1793    }
    16201794}
    16211795
    16221796void
     
    16571831
    16581832    if( tr_isTorrent( tor ) )
    16591833    {
     1834        tr_torrentSetQueued( tor, FALSE );
     1835
     1836        if( tor->isRunning )
     1837            tr_torrentStopQueued( tor );
     1838    }
     1839}
     1840
     1841void
     1842tr_torrentStopQueued( tr_torrent * tor )
     1843{
     1844    assert( tr_isTorrent( tor ) );
     1845
     1846    if( tr_isTorrent( tor ) )
     1847    {
    16601848        tr_sessionLock( tor->session );
    16611849
    16621850        tor->isRunning = 0;
     
    17151903{
    17161904    assert( tr_isTorrent( tor ) );
    17171905
     1906    torrentRemoveFromQueue( tor );
    17181907    tor->isDeleting = 1;
    17191908    tr_torrentFree( tor );
    17201909}
     
    18101999    tr_torrentSetIdleLimitHitCallback( torrent, NULL, NULL );
    18112000}
    18122001
     2002void
     2003tr_torrentSetQueueCallback( tr_torrent           * tor,
     2004                            tr_torrent_queue_func  func,
     2005                            void                 * user_data )
     2006{
     2007    assert( tr_isTorrent( tor ) );
     2008
     2009    tor->queue_hit_func = func;
     2010    tor->queue_hit_func_user_data = user_data;
     2011}
     2012
     2013void
     2014tr_torrentClearQueueCallback( tr_torrent * torrent )
     2015{
     2016    tr_torrentSetQueueCallback( torrent, NULL, NULL );
     2017}
     2018
    18132019static void
    18142020tr_setenv( const char * name, const char * value, tr_bool override )
    18152021{
     
    18772083            if( recentChange )
    18782084            {
    18792085                tr_announcerTorrentCompleted( tor );
    1880                 tor->doneDate = tor->anyDate = tr_time( );
     2086                tor->doneDate = tr_time( );
    18812087            }
    18822088
    18832089            if( wasLeeching && wasRunning )
     
    18982104
    18992105        fireCompletenessChange( tor, wasRunning, completeness );
    19002106
     2107        tr_sessionProcessQueue( tor->session );
     2108
    19012109        tr_torrentSetDirty( tor );
    19022110    }
    19032111
  • qt/torrent.h

     
    146146            DATE_CREATED,
    147147            PEERS_CONNECTED,
    148148            ETA,
     149            QUEUED,
     150            QUEUE_RANK,
    149151            RATIO,
    150152            DOWNLOADED_EVER,
    151153            UPLOADED_EVER,
     
    272274        int compareETA( const Torrent& ) const;
    273275        bool hasETA( ) const { return getETA( ) >= 0; }
    274276        int getETA( ) const { return getInt( ETA ); }
     277        int queueRank( ) const { return getInt( QUEUE_RANK ); }
    275278        QDateTime lastActivity( ) const { return getDateTime( DATE_ACTIVITY ); }
    276279        QDateTime lastStarted( ) const { return getDateTime( DATE_STARTED ); }
    277280        QDateTime dateAdded( ) const { return getDateTime( DATE_ADDED ); }
     
    306309    public:
    307310        QString activityString( ) const;
    308311        tr_torrent_activity getActivity( ) const { return (tr_torrent_activity) getInt( ACTIVITY ); }
     312        bool isQueued( ) const { return getBool( QUEUED ); }
    309313        bool isFinished( ) const { return getBool( IS_FINISHED ); }
    310314        bool isPaused( ) const { return getActivity( ) == TR_STATUS_STOPPED; }
    311315        bool isWaitingToVerify( ) const { return getActivity( ) == TR_STATUS_CHECK_WAIT; }
  • qt/mainwin.h

     
    8989        QSet<int> getSelectedTorrents( ) const;
    9090        void updateNetworkIcon( );
    9191        QWidgetList myHidden;
     92        void moveQueue( int dir );
    9293
    9394    public slots:
    9495        void openURL( QString );
     
    135136        void onSortByETAToggled      ( bool );
    136137        void onSortByNameToggled     ( bool );
    137138        void onSortByProgressToggled ( bool );
     139        void onSortByQueueToggled    ( bool );
    138140        void onSortByRatioToggled    ( bool );
    139141        void onSortBySizeToggled     ( bool );
    140142        void onSortByStateToggled    ( bool );
     143        void moveQueueUp             ( );
     144        void moveQueueDown           ( );
     145        void moveQueueTop            ( );
     146        void moveQueueBottom         ( );
    141147
    142148    private:
    143149        QWidget * myFilterBar;
     
    158164    public slots:
    159165        void startAll( );
    160166        void startSelected( );
     167        void forceStartSelected( );
    161168        void pauseAll( );
    162169        void pauseSelected( );
    163170        void removeSelected( );
  • qt/torrent-delegate.cc

     
    229229
    230230        case TR_STATUS_DOWNLOAD:
    231231            if( tor.hasMetadata( ) )
    232                 str = tr( "Downloading from %1 of %n connected peer(s)", 0, tor.connectedPeersAndWebseeds( ) )
     232                str = tr( "%1 from %2 of %n connected peer(s)", 0, tor.connectedPeersAndWebseeds( ) )
     233                        .arg( tor.activityString( ) )
    233234                        .arg( tor.peersWeAreDownloadingFrom( ) );
    234235            else
    235                 str = tr( "Downloading metadata from %n peer(s) (%1% done)", 0, tor.peersWeAreDownloadingFrom( ) )
     236                str = tr( "%1 metadata from %n peer(s) (%2% done)", 0, tor.peersWeAreDownloadingFrom( ) )
     237                        .arg( tor.activityString( ) )
    236238                        .arg( Formatter::percentToString( 100.0 * tor.metadataPercentDone( ) ) );
    237239            break;
    238240
    239241        case TR_STATUS_SEED:
    240             str = tr( "Seeding to %1 of %n connected peer(s)", 0, tor.connectedPeers( ) )
     242            str = tr( "%1 to %2 of %n connected peer(s)", 0, tor.connectedPeers( ) )
     243                  .arg( tor.activityString( ) )
    241244                  .arg( tor.peersWeAreUploadingTo( ) );
    242245            break;
    243246
  • qt/prefs-dialog.h

     
    8080        void setPref( int key, const QVariant& v );
    8181        bool isAllowed( int key ) const;
    8282        QWidget * createTorrentsTab( );
     83        QWidget * createQueueTab( );
    8384        QWidget * createSpeedTab( );
    8485        QWidget * createPrivacyTab( );
    8586        QWidget * createNetworkTab( );
  • qt/filters.h

     
    4242        SortMode( const QString& name ): myMode(modeFromName(name)) { }
    4343        static const QString names[];
    4444        enum { SORT_BY_ACTIVITY, SORT_BY_AGE, SORT_BY_ETA, SORT_BY_NAME,
    45                SORT_BY_PROGRESS, SORT_BY_RATIO, SORT_BY_SIZE,
     45               SORT_BY_PROGRESS, SORT_BY_QUEUE, SORT_BY_RATIO, SORT_BY_SIZE,
    4646               SORT_BY_STATE, SORT_BY_ID, NUM_MODES };
    4747        static int modeFromName( const QString& name );
    4848        static const QString& nameFromMode( int mode );
  • qt/prefs-dialog.cc

     
    224224****
    225225***/
    226226
     227QWidget *
     228PrefsDialog :: createQueueTab( )
     229{
     230    QWidget *l, *r;
     231    QString s;
     232    HIG * hig = new HIG( this );
     233    const QString speed_K_str = Formatter::unitStr( Formatter::SPEED, Formatter::KB );
     234
     235    hig->addSectionTitle( tr( "Limits" ) );
     236
     237        l = checkBoxNew( tr( "Maximum &downloads active:" ), Prefs::QUEUE_ENABLED_DOWNLOAD );
     238        r = spinBoxNew( Prefs::QUEUE_MAX_DOWNLOAD_ACTIVE, 0, INT_MAX, 1 );
     239        hig->addRow( l, r );
     240        enableBuddyWhenChecked( qobject_cast<QCheckBox*>(l), r );
     241
     242        l = checkBoxNew( tr( "Maximum &seeds active:" ), Prefs::QUEUE_ENABLED_SEED );
     243        r = spinBoxNew( Prefs::QUEUE_MAX_SEED_ACTIVE, 0, INT_MAX, 1 );
     244        hig->addRow( l, r );
     245        enableBuddyWhenChecked( qobject_cast<QCheckBox*>(l), r );
     246
     247    hig->addSectionTitle( tr( "Skip" ) );
     248
     249        l = checkBoxNew( tr( "Skip torrents if slow for &N minutes:" ), Prefs::QUEUE_SKIP_SLOW_TORRENTS );
     250        r = spinBoxNew( Prefs::QUEUE_SLOW_COUNT, 0, INT_MAX, 1 );
     251        hig->addRow( l, r );
     252        enableBuddyWhenChecked( qobject_cast<QCheckBox*>(l), r );
     253
     254        s = tr( "Slow torrent cutoff sp&eed (%1):" ).arg( speed_K_str );
     255        r = spinBoxNew( Prefs::QUEUE_SLOW_CUTOFF, 0, INT_MAX, 1 );
     256        hig->addRow( s, r );
     257        enableBuddyWhenChecked( qobject_cast<QCheckBox*>(l), r );
     258
     259    hig->addSectionTitle( tr( "Options" ) );
     260
     261        l = checkBoxNew( tr( "Don't s&tart new torrents if a speed limit is reached" ), Prefs::QUEUE_SPEED_LIMIT );
     262        hig->addWideControl( l );
     263
     264        l = checkBoxNew( tr( "&Add new torrents to the top of the queue" ), Prefs::QUEUE_NEW_TORRENTS_TOP );
     265        hig->addWideControl( l );
     266
     267    hig->finish( );
     268    return hig;
     269}
     270
     271/***
     272****
     273***/
     274
    227275void
    228276PrefsDialog :: altSpeedDaysEdited( int i )
    229277{
     
    621669    t->addTab( createSpeedTab( ),        tr( "Speed" ) );
    622670    t->addTab( createPrivacyTab( ),      tr( "Privacy" ) );
    623671    t->addTab( createNetworkTab( ),      tr( "Network" ) );
     672    t->addTab( createQueueTab( ),        tr( "Queue" ) );
    624673    t->addTab( createDesktopTab( ),      tr( "Desktop" ) );
    625674    t->addTab( createWebTab( session ),  tr( "Web" ) );
    626675    myLayout->addWidget( t );
  • qt/prefs.cc

     
    9393    { PEER_PORT_RANDOM_ON_START, TR_PREFS_KEY_PEER_PORT_RANDOM_ON_START, QVariant::Bool },
    9494    { PEER_PORT_RANDOM_LOW, TR_PREFS_KEY_PEER_PORT_RANDOM_LOW, QVariant::Int },
    9595    { PEER_PORT_RANDOM_HIGH, TR_PREFS_KEY_PEER_PORT_RANDOM_HIGH, QVariant::Int },
     96    { QUEUE_ENABLED_DOWNLOAD, TR_PREFS_KEY_QUEUE_ENABLED_DOWNLOAD, QVariant::Bool },
     97    { QUEUE_ENABLED_SEED, TR_PREFS_KEY_QUEUE_ENABLED_SEED, QVariant::Bool },
     98    { QUEUE_MAX_DOWNLOAD_ACTIVE, TR_PREFS_KEY_QUEUE_MAX_DOWNLOAD_ACTIVE, QVariant::Int },
     99    { QUEUE_MAX_SEED_ACTIVE, TR_PREFS_KEY_QUEUE_MAX_SEED_ACTIVE, QVariant::Int },
     100    { QUEUE_NEW_TORRENTS_TOP, TR_PREFS_KEY_QUEUE_NEW_TORRENTS_TOP, QVariant::Bool },
     101    { QUEUE_SKIP_SLOW_TORRENTS, TR_PREFS_KEY_QUEUE_SKIP_SLOW_TORRENTS, QVariant::Bool },
     102    { QUEUE_SLOW_COUNT, TR_PREFS_KEY_QUEUE_SLOW_COUNT, QVariant::Int },
     103    { QUEUE_SLOW_CUTOFF, TR_PREFS_KEY_QUEUE_SLOW_CUTOFF_KBps, QVariant::Int },
     104    { QUEUE_SPEED_LIMIT, TR_PREFS_KEY_QUEUE_SPEED_LIMIT, QVariant::Bool },
    96105    { SCRIPT_TORRENT_DONE_ENABLED, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_ENABLED, QVariant::Bool },
    97106    { SCRIPT_TORRENT_DONE_FILENAME, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_FILENAME, QVariant::String },
    98107    { SOCKET_TOS, TR_PREFS_KEY_PEER_SOCKET_TOS, QVariant::Int },
  • qt/prefs.h

     
    9898            PEER_PORT_RANDOM_ON_START,
    9999            PEER_PORT_RANDOM_LOW,
    100100            PEER_PORT_RANDOM_HIGH,
     101            QUEUE_ENABLED_DOWNLOAD,
     102            QUEUE_ENABLED_SEED,
     103            QUEUE_MAX_DOWNLOAD_ACTIVE,
     104            QUEUE_MAX_SEED_ACTIVE,
     105            QUEUE_NEW_TORRENTS_TOP,
     106            QUEUE_SKIP_SLOW_TORRENTS,
     107            QUEUE_SLOW_COUNT,
     108            QUEUE_SLOW_CUTOFF,
     109            QUEUE_SPEED_LIMIT,
    101110            SCRIPT_TORRENT_DONE_ENABLED,
    102111            SCRIPT_TORRENT_DONE_FILENAME,
    103112            SOCKET_TOS,
  • qt/details.cc

     
    324324        string = none;
    325325    else {
    326326        bool isMixed = false;
    327         bool allPaused = true;
    328         bool allFinished = true;
    329         const tr_torrent_activity activity = torrents[0]->getActivity( );
     327        const QString activity = torrents[0]->activityString( );
    330328        foreach( const Torrent * t, torrents ) {
    331             if( activity != t->getActivity( ) )
     329            if( t->activityString( ) != activity )
     330            {
    332331                isMixed = true;
    333             if( activity != TR_STATUS_STOPPED )
    334                 allPaused = allFinished = false;
    335             if( !t->isFinished( ) )
    336                 allFinished = false;
     332                break;
     333            }
    337334        }
    338335        if( isMixed )
    339336            string = mixed;
    340         else if( allFinished )
    341             string = tr( "Finished" );
    342         else if( allPaused )
    343             string = tr( "Paused" );
    344337        else
    345             string = torrents[0]->activityString( );
     338            string = activity;
    346339    }
    347340    myStateLabel->setText( string );
    348341    const QString stateString = string;
  • qt/mainwin.ui

     
    6969    <addaction name="action_OpenFolder"/>
    7070    <addaction name="separator"/>
    7171    <addaction name="action_Start"/>
     72    <addaction name="action_ForceStart"/>
    7273    <addaction name="action_Announce"/>
    7374    <addaction name="action_Pause"/>
    7475    <addaction name="action_CopyMagnetToClipboard"/>
     
    115116    <addaction name="action_SortByAge"/>
    116117    <addaction name="action_SortByName"/>
    117118    <addaction name="action_SortByProgress"/>
     119    <addaction name="action_SortByQueue"/>
    118120    <addaction name="action_SortByRatio"/>
    119121    <addaction name="action_SortBySize"/>
    120122    <addaction name="action_SortByState"/>
     
    174176   <addaction name="action_Pause"/>
    175177   <addaction name="action_Remove"/>
    176178   <addaction name="separator"/>
     179   <addaction name="action_QueueUp"/>
     180   <addaction name="action_QueueDown"/>
     181   <addaction name="action_QueueTop"/>
     182   <addaction name="action_QueueBottom"/>
     183   <addaction name="separator"/>
    177184   <addaction name="action_Properties"/>
    178185  </widget>
    179186  <action name="action_AddFile">
     
    237244    <enum>QAction::LowPriority</enum>
    238245   </property>
    239246  </action>
     247  <action name="action_ForceStart">
     248   <property name="checkable">
     249    <bool>true</bool>
     250   </property>
     251   <property name="text">
     252    <string>&amp;Force Start</string>
     253   </property>
     254   <property name="toolTip">
     255    <string>Force start torrent</string>
     256   </property>
     257   <!--
     258   <property name="shortcut">
     259    <string>Ctrl+S</string>
     260   </property>
     261   -->
     262   <property name="priority">
     263    <enum>QAction::LowPriority</enum>
     264   </property>
     265  </action>
    240266  <action name="action_Announce">
    241267   <property name="text">
    242268    <string>Ask Tracker for &amp;More Peers</string>
     
    412438    <string>Sort by &amp;Progress</string>
    413439   </property>
    414440  </action>
     441  <action name="action_SortByQueue">
     442   <property name="checkable">
     443    <bool>true</bool>
     444   </property>
     445   <property name="text">
     446    <string>Sort by &amp;Queue</string>
     447   </property>
     448  </action>
    415449  <action name="action_SortByRatio">
    416450   <property name="checkable">
    417451    <bool>true</bool>
     
    581615    <string>&amp;Donate</string>
    582616   </property>
    583617  </action>
     618  <action name="action_QueueUp">
     619   <property name="text">
     620    <string>Queue &amp;Up</string>
     621   </property>
     622   <property name="toolTip">
     623    <string>Move up queue</string>
     624   </property>
     625   <property name="priority">
     626    <enum>QAction::LowPriority</enum>
     627   </property>
     628  </action>
     629  <action name="action_QueueDown">
     630   <property name="text">
     631    <string>Queue &amp;Down</string>
     632   </property>
     633   <property name="toolTip">
     634    <string>Move down queue</string>
     635   </property>
     636   <property name="priority">
     637    <enum>QAction::LowPriority</enum>
     638   </property>
     639  </action>
     640  <action name="action_QueueTop">
     641   <property name="text">
     642    <string>Queue &amp;Top</string>
     643   </property>
     644   <property name="toolTip">
     645    <string>Move to top of queue</string>
     646   </property>
     647   <property name="priority">
     648    <enum>QAction::LowPriority</enum>
     649   </property>
     650  </action>
     651  <action name="action_QueueBottom">
     652   <property name="text">
     653    <string>Queue &amp;Bottom</string>
     654   </property>
     655   <property name="toolTip">
     656    <string>Move to bottom of  queue</string>
     657   </property>
     658   <property name="priority">
     659    <enum>QAction::LowPriority</enum>
     660   </property>
     661  </action>
    584662 </widget>
    585663 <resources>
    586664  <include location="application.qrc"/>
  • qt/torrent.cc

     
    8282    { DATE_CREATED, "dateCreated", QVariant::DateTime, INFO },
    8383    { PEERS_CONNECTED, "peersConnected", QVariant::Int, STAT },
    8484    { ETA, "eta", QVariant::Int, STAT },
     85    { QUEUED, "queued", QVariant::Bool, STAT },
     86    { QUEUE_RANK, "queueRank", QVariant::Int, STAT },
    8587    { RATIO, "uploadRatio", QVariant::Double, STAT },
    8688    { DOWNLOADED_EVER, "downloadedEver", QVariant::ULongLong, STAT },
    8789    { UPLOADED_EVER, "uploadedEver", QVariant::ULongLong, STAT },
     
    685687    {
    686688        case TR_STATUS_CHECK_WAIT: str = tr( "Waiting to verify local data" ); break;
    687689        case TR_STATUS_CHECK:      str = tr( "Verifying local data" ); break;
    688         case TR_STATUS_DOWNLOAD:   str = tr( "Downloading" ); break;
    689         case TR_STATUS_SEED:       str = tr( "Seeding" ); break;
    690         case TR_STATUS_STOPPED:    str = isFinished() ? tr( "Finished" ): tr( "Paused" ); break;
     690        case TR_STATUS_DOWNLOAD: {
     691            if( myPrefs.getBool( Prefs::QUEUE_ENABLED_DOWNLOAD ) && !isQueued() )
     692                str = tr( "Forced downloading" );
     693            else
     694                str = tr( "Downloading" );
     695            break;
     696        }
     697        case TR_STATUS_SEED: {
     698            if( myPrefs.getBool( Prefs::QUEUE_ENABLED_SEED ) && !isQueued() )
     699                str = tr( "Forced seeding" );
     700            else
     701                str = tr( "Seeding" );
     702            break;
     703        }
     704        case TR_STATUS_STOPPED: {
     705            const bool leech = !isDone( ) || isMagnet( );
     706            bool queued = isQueued();
     707
     708            if( queued )
     709                queued = leech
     710                    ? myPrefs.getBool( Prefs::QUEUE_ENABLED_DOWNLOAD )
     711                    : myPrefs.getBool( Prefs::QUEUE_ENABLED_SEED );
     712
     713            str = queued
     714                ? ( leech ? tr( "Queued to download" ) : tr( "Queued to seed" ) )
     715                : ( isFinished() ? tr( "Finished" ) : tr( "Paused" ) );
     716            break;
     717        }
    691718    }
    692719
    693720    return str;
  • qt/filters.cc

     
    4040    "sort-by-eta",
    4141    "sort-by-name",
    4242    "sort-by-progress",
     43    "sort-by-queue",
    4344    "sort-by-ratio",
    4445    "sort-by-size",
    4546    "sort-by-state",
  • qt/mainwin.cc

     
    109109    ui.action_Properties->setIcon( getStockIcon( "document-properties", QStyle::SP_DesktopIcon ) );
    110110    ui.action_OpenFolder->setIcon( getStockIcon( "folder-open", QStyle::SP_DirOpenIcon ) );
    111111    ui.action_Start->setIcon( getStockIcon( "media-playback-start", QStyle::SP_MediaPlay ) );
     112    ui.action_ForceStart->setIcon( getStockIcon( "media-seek-forward", QStyle::SP_MediaSeekForward ) );
    112113    ui.action_Announce->setIcon( getStockIcon( "network-transmit-receive" ) );
    113114    ui.action_Pause->setIcon( getStockIcon( "media-playback-pause", QStyle::SP_MediaPause ) );
    114115    ui.action_Remove->setIcon( getStockIcon( "list-remove", QStyle::SP_TrashIcon ) );
     
    121122    ui.action_Preferences->setIcon( getStockIcon( "preferences-system" ) );
    122123    ui.action_Contents->setIcon( getStockIcon( "help-contents", QStyle::SP_DialogHelpButton ) );
    123124    ui.action_About->setIcon( getStockIcon( "help-about" ) );
     125    ui.action_QueueUp->setIcon( getStockIcon( "go-up", QStyle::SP_ArrowUp ) );
     126    ui.action_QueueDown->setIcon( getStockIcon( "go-down", QStyle::SP_ArrowDown ) );
     127    ui.action_QueueTop->setIcon( getStockIcon( "go-top" ) );
     128    ui.action_QueueBottom->setIcon( getStockIcon( "go-bottom" ) );
    124129
    125130    // ui signals
    126131    connect( ui.action_Toolbar, SIGNAL(toggled(bool)), this, SLOT(setToolbarVisible(bool)));
     
    132137    connect( ui.action_SortByETA,      SIGNAL(toggled(bool)), this, SLOT(onSortByETAToggled(bool)));
    133138    connect( ui.action_SortByName,     SIGNAL(toggled(bool)), this, SLOT(onSortByNameToggled(bool)));
    134139    connect( ui.action_SortByProgress, SIGNAL(toggled(bool)), this, SLOT(onSortByProgressToggled(bool)));
     140    connect( ui.action_SortByQueue,    SIGNAL(toggled(bool)), this, SLOT(onSortByQueueToggled(bool)));
    135141    connect( ui.action_SortByRatio,    SIGNAL(toggled(bool)), this, SLOT(onSortByRatioToggled(bool)));
    136142    connect( ui.action_SortBySize,     SIGNAL(toggled(bool)), this, SLOT(onSortBySizeToggled(bool)));
    137143    connect( ui.action_SortByState,    SIGNAL(toggled(bool)), this, SLOT(onSortByStateToggled(bool)));
    138144    connect( ui.action_ReverseSortOrder, SIGNAL(toggled(bool)), this, SLOT(setSortAscendingPref(bool)));
     145    connect( ui.action_QueueUp, SIGNAL(triggered()), this, SLOT(moveQueueUp()));
     146    connect( ui.action_QueueDown, SIGNAL(triggered()), this, SLOT(moveQueueDown()));
     147    connect( ui.action_QueueTop, SIGNAL(triggered()), this, SLOT(moveQueueTop()));
     148    connect( ui.action_QueueBottom, SIGNAL(triggered()), this, SLOT(moveQueueBottom()));
    139149    connect( ui.action_Start, SIGNAL(triggered()), this, SLOT(startSelected()));
     150    connect( ui.action_ForceStart, SIGNAL(triggered()), this, SLOT(forceStartSelected()));
    140151    connect( ui.action_Pause, SIGNAL(triggered()), this, SLOT(pauseSelected()));
    141152    connect( ui.action_Remove, SIGNAL(triggered()), this, SLOT(removeSelected()));
    142153    connect( ui.action_Delete, SIGNAL(triggered()), this, SLOT(deleteSelected()));
     
    170181            << ui.action_OpenFolder
    171182            << sep2
    172183            << ui.action_Start
     184            << ui.action_ForceStart
    173185            << ui.action_Announce
    174186            << ui.action_Pause
    175187            << ui.action_CopyMagnetToClipboard
     
    209221    actionGroup->addAction( ui.action_SortByETA );
    210222    actionGroup->addAction( ui.action_SortByName );
    211223    actionGroup->addAction( ui.action_SortByProgress );
     224    actionGroup->addAction( ui.action_SortByQueue );
    212225    actionGroup->addAction( ui.action_SortByRatio );
    213226    actionGroup->addAction( ui.action_SortBySize );
    214227    actionGroup->addAction( ui.action_SortByState );
     
    520533TrMainWindow :: setSortPref( int i )
    521534{
    522535    myPrefs.set( Prefs::SORT_MODE, SortMode( i ) );
     536    refreshActionSensitivity( );
    523537}
    524538void TrMainWindow :: onSortByActivityToggled ( bool b ) { if( b ) setSortPref( SortMode::SORT_BY_ACTIVITY ); }
    525539void TrMainWindow :: onSortByAgeToggled      ( bool b ) { if( b ) setSortPref( SortMode::SORT_BY_AGE );      }
    526540void TrMainWindow :: onSortByETAToggled      ( bool b ) { if( b ) setSortPref( SortMode::SORT_BY_ETA );      }
    527541void TrMainWindow :: onSortByNameToggled     ( bool b ) { if( b ) setSortPref( SortMode::SORT_BY_NAME );     }
    528542void TrMainWindow :: onSortByProgressToggled ( bool b ) { if( b ) setSortPref( SortMode::SORT_BY_PROGRESS ); }
     543void TrMainWindow :: onSortByQueueToggled    ( bool b ) { if( b ) setSortPref( SortMode::SORT_BY_QUEUE );    }
    529544void TrMainWindow :: onSortByRatioToggled    ( bool b ) { if( b ) setSortPref( SortMode::SORT_BY_RATIO );    }
    530545void TrMainWindow :: onSortBySizeToggled     ( bool b ) { if( b ) setSortPref( SortMode::SORT_BY_SIZE );     }
    531546void TrMainWindow :: onSortByStateToggled    ( bool b ) { if( b ) setSortPref( SortMode::SORT_BY_STATE );    }
     
    540555*****
    541556****/
    542557
     558namespace
     559{
     560    QList<int>
     561    reverse( const QList<int> &list )
     562    {
     563        QList<int> ret;
     564        for( int i = list.count(); i > 0; --i )
     565            ret << list[i-1];
     566        return ret;
     567    }
     568}
     569
    543570void
     571TrMainWindow :: moveQueue( int dir )
     572{
     573    QMap<int, int> map;
     574
     575    foreach( int id, getSelectedTorrents( ) )
     576        map.insert( myModel.getTorrentFromId( id )->queueRank(), id );
     577
     578    switch( dir )
     579    {
     580        case TR_QUEUE_DOWN:
     581            if( myModel.getTorrentFromId( map.values().last() )->queueRank() + 1 == myModel.rowCount() )
     582                break;
     583        case TR_QUEUE_TOP:
     584            mySession.torrentSet( reverse( map.values() ), "moveQueueRank", dir );
     585            break;
     586        case TR_QUEUE_UP:
     587            if( myModel.getTorrentFromId( map.values().first() )->queueRank() == 0 )
     588                break;
     589        case TR_QUEUE_BOTTOM:
     590            mySession.torrentSet( map.values(), "moveQueueRank", dir );
     591            break;
     592    }
     593
     594    mySession.refreshActiveTorrents( );
     595}
     596
     597void
     598TrMainWindow :: moveQueueUp( )
     599{
     600    moveQueue( TR_QUEUE_UP );
     601}
     602
     603void
     604TrMainWindow :: moveQueueDown( )
     605{
     606    moveQueue( TR_QUEUE_DOWN );
     607}
     608
     609void
     610TrMainWindow :: moveQueueTop( )
     611{
     612    moveQueue( TR_QUEUE_TOP );
     613}
     614
     615void
     616TrMainWindow :: moveQueueBottom( )
     617{
     618    moveQueue( TR_QUEUE_BOTTOM );
     619}
     620
     621/****
     622*****
     623****/
     624
     625void
    544626TrMainWindow :: onPrefsDestroyed( )
    545627{
    546628    myPrefsDialog = 0;
     
    699781    int selected( 0 );
    700782    int paused( 0 );
    701783    int selectedAndPaused( 0 );
     784    int pausedAndQueued( 0 );
     785    int selectedAndQueued( 0 );
     786    int selectedPausedAndQueued( 0 );
     787    int selectedAndForced( 0 );
    702788    int canAnnounce( 0 );
    703789    const QAbstractItemModel * model( ui.listView->model( ) );
    704790    const QItemSelectionModel * selectionModel( ui.listView->selectionModel( ) );
    705791    const int rowCount( model->rowCount( ) );
     792    const bool downloadQueue( myPrefs.getBool( Prefs::QUEUE_ENABLED_DOWNLOAD ) );
     793    const bool seedQueue( myPrefs.getBool( Prefs::QUEUE_ENABLED_SEED ) );
    706794
    707795    // count how many torrents are selected, paused, etc
    708796    for( int row=0; row<rowCount; ++row ) {
    709797        const QModelIndex modelIndex( model->index( row, 0 ) );
    710798        assert( model == modelIndex.model( ) );
    711799        const Torrent * tor( model->data( modelIndex, TorrentModel::TorrentRole ).value<const Torrent*>( ) );
    712         if( tor ) {
    713             const bool isSelected( selectionModel->isSelected( modelIndex ) );
    714             const bool isPaused( tor->isPaused( ) );
    715             if( isSelected )
    716                 ++selected;
    717             if( isPaused )
    718                 ++ paused;
    719             if( isSelected && isPaused )
    720                 ++selectedAndPaused;
    721             if( tor->canManualAnnounce( ) )
    722                 ++canAnnounce;
     800        const bool isSelected( selectionModel->isSelected( modelIndex ) );
     801        const bool isPaused( tor->isPaused( ) );
     802        const bool isQueued( tor->isQueued( ) );
     803        const bool isDone( tor->leftUntilDone( ) == 0 );
     804        const bool queueTest = ( downloadQueue && !isDone ) || ( seedQueue && isDone );
     805
     806        if( isSelected )
     807        {
     808            ++selected;
     809            if( queueTest && isQueued )
     810                ++selectedAndQueued;
     811            if( !isQueued && !isPaused )
     812                ++selectedAndForced;
    723813        }
     814        if( isPaused )
     815        {
     816            ++paused;
     817            if( queueTest && isQueued )
     818                ++pausedAndQueued;
     819        }
     820        if( isSelected && isPaused )
     821        {
     822            ++selectedAndPaused;
     823            if( queueTest && isQueued )
     824                ++selectedPausedAndQueued;
     825        }
     826        if( tor->canManualAnnounce( ) )
     827            ++canAnnounce;
    724828    }
    725829
    726830    const bool haveSelection( selected > 0 );
     
    735839    ui.action_OpenFolder->setEnabled( oneSelection && mySession.isLocal( ) );
    736840    ui.action_CopyMagnetToClipboard->setEnabled( oneSelection );
    737841
     842    const bool queueSort( ( myPrefs.get<SortMode>(Prefs::SORT_MODE).mode() == SortMode::SORT_BY_QUEUE ) && haveSelection );
     843    ui.action_QueueUp->setEnabled( queueSort );
     844    ui.action_QueueDown->setEnabled( queueSort );
     845    ui.action_QueueTop->setEnabled( queueSort );
     846    ui.action_QueueBottom->setEnabled( queueSort );
     847
    738848    ui.action_SelectAll->setEnabled( selected < rowCount );
    739     ui.action_StartAll->setEnabled( paused > 0 );
    740     ui.action_PauseAll->setEnabled( paused < rowCount );
    741     ui.action_Start->setEnabled( selectedAndPaused > 0 );
    742     ui.action_Pause->setEnabled( selectedAndPaused < selected );
     849    /*
     850       A stopped torrent is a paused torrent that is not queue managed.
     851       A queued torrent is a paused torrent that is queue managed.
     852
     853       Start should display only for torrents that are stopped.
     854       Pause should only display for non stopped torrents.
     855       StartAll should only display if there are stopped torrents.
     856       PauseAll should only display if there are non stopped torrents.
     857       ForceStart should display for every torrent.
     858    */
     859    ui.action_StartAll->setEnabled( paused > 0 && pausedAndQueued < paused );
     860    ui.action_PauseAll->setEnabled( paused < rowCount || pausedAndQueued > 0 );
     861    ui.action_Start->setEnabled( selectedAndPaused > 0 && selectedAndQueued < selected );
     862    ui.action_ForceStart->setEnabled( selected );
     863    ui.action_Pause->setEnabled( selectedAndPaused < selected || selectedPausedAndQueued > 0 );
    743864    ui.action_Announce->setEnabled( selected > 0 && ( canAnnounce == selected ) );
    744865
     866    ui.action_ForceStart->setChecked( selectedAndForced > 0 );
     867
    745868    if( myDetailsDialog )
    746869        myDetailsDialog->setIds( getSelectedTorrents( ) );
    747870}
     
    776899    mySession.startTorrents( getSelectedTorrents( ) );
    777900}
    778901void
     902TrMainWindow :: forceStartSelected( )
     903{
     904    const QSet<int> ids = getSelectedTorrents( );
     905    // FIXME: why is this returning the opposite of what it should?
     906    const bool start = ui.action_ForceStart->isChecked( );
     907
     908    if( start )
     909        mySession.forceStartTorrents( ids );
     910    else
     911        mySession.torrentSet( ids, "queued", true );
     912}
     913void
    779914TrMainWindow :: pauseSelected( )
    780915{
    781916    mySession.pauseTorrents( getSelectedTorrents( ) );
     
    9131048            ui.action_SortByETA->setChecked      ( i == SortMode::SORT_BY_ETA );
    9141049            ui.action_SortByName->setChecked     ( i == SortMode::SORT_BY_NAME );
    9151050            ui.action_SortByProgress->setChecked ( i == SortMode::SORT_BY_PROGRESS );
     1051            ui.action_SortByQueue->setChecked    ( i == SortMode::SORT_BY_QUEUE );
    9161052            ui.action_SortByRatio->setChecked    ( i == SortMode::SORT_BY_RATIO );
    9171053            ui.action_SortBySize->setChecked     ( i == SortMode::SORT_BY_SIZE );
    9181054            ui.action_SortByState->setChecked    ( i == SortMode::SORT_BY_STATE );
  • qt/torrent-filter.cc

     
    113113        case SortMode :: SORT_BY_RATIO:
    114114            less = a->compareRatio( *b );
    115115            break;
     116        case SortMode :: SORT_BY_QUEUE:
     117            less = compare( b->queueRank(), a->queueRank() );
     118            break;
    116119        case SortMode :: SORT_BY_ETA:
    117120            less = a->compareETA( *b );
    118121            break;
     
    160163            accepts = tor->isFinished( );
    161164            break;
    162165        case FilterMode::SHOW_QUEUED:
    163             accepts = tor->isWaitingToVerify( );
     166            accepts = tor->isQueued();
    164167            break;
    165168        case FilterMode::SHOW_VERIFYING:
    166             accepts = tor->isVerifying( );
     169            accepts = tor->isWaitingToVerify( ) || tor->isVerifying( );
    167170            break;
    168171        case FilterMode::SHOW_ERROR:
    169172            accepts = tor->hasError( );
  • qt/session.cc

     
    157157        case Prefs :: PEER_PORT_RANDOM_ON_START:
    158158        case Prefs :: PEX_ENABLED:
    159159        case Prefs :: PORT_FORWARDING:
     160        case Prefs :: QUEUE_ENABLED_DOWNLOAD:
     161        case Prefs :: QUEUE_ENABLED_SEED:
     162        case Prefs :: QUEUE_MAX_DOWNLOAD_ACTIVE:
     163        case Prefs :: QUEUE_MAX_SEED_ACTIVE:
     164        case Prefs :: QUEUE_NEW_TORRENTS_TOP:
     165        case Prefs :: QUEUE_SKIP_SLOW_TORRENTS:
     166        case Prefs :: QUEUE_SLOW_COUNT:
     167        case Prefs :: QUEUE_SLOW_CUTOFF:
     168        case Prefs :: QUEUE_SPEED_LIMIT:
    160169        case Prefs :: SCRIPT_TORRENT_DONE_ENABLED:
    161170        case Prefs :: SCRIPT_TORRENT_DONE_FILENAME:
    162171        case Prefs :: START:
     
    381390                tr_bencListAddInt( idList, i );
    382391        }
    383392    }
     393
     394    void
     395    addOptionalIds( tr_benc * args, const QList<int>& ids )
     396    {
     397        if( !ids.isEmpty( ) )
     398        {
     399            tr_benc * idList( tr_bencDictAddList( args, "ids", ids.size( ) ) );
     400            for( int i = 0; i < ids.count(); ++i )
     401                tr_bencListAddInt( idList, ids[i] );
     402        }
     403    }
    384404}
    385405
    386406void
     407Session :: torrentSet( const QList<int>& ids, const QString& key, int value )
     408{
     409    tr_benc top;
     410    tr_bencInitDict( &top, 2 );
     411    tr_bencDictAddStr( &top, "method", "torrent-set" );
     412    tr_benc * args = tr_bencDictAddDict( &top, "arguments", 2 );
     413    tr_bencDictAddInt( args, key.toUtf8().constData(), value );
     414    addOptionalIds( args, ids );
     415    exec( &top );
     416    tr_bencFree( &top );
     417}
     418
     419void
    387420Session :: torrentSet( const QSet<int>& ids, const QString& key, double value )
    388421{
    389422    tr_benc top;
     
    541574}
    542575
    543576void
     577Session :: forceStartTorrents( const QSet<int>& ids )
     578{
     579    sendTorrentRequest( "torrent-force-start", ids );
     580}
     581
     582void
    544583Session :: refreshActiveTorrents( )
    545584{
    546585    tr_benc top;
  • qt/session.h

     
    9292        QNetworkAccessManager * networkAccessManager( );
    9393
    9494    public:
     95        void torrentSet( const QList<int>& ids, const QString& key, int val );
    9596        void torrentSet( const QSet<int>& ids, const QString& key, bool val );
    9697        void torrentSet( const QSet<int>& ids, const QString& key, int val );
    9798        void torrentSet( const QSet<int>& ids, const QString& key, double val );
     
    104105    public slots:
    105106        void pauseTorrents( const QSet<int>& torrentIds = QSet<int>() );
    106107        void startTorrents( const QSet<int>& torrentIds = QSet<int>() );
     108        void forceStartTorrents( const QSet<int>& torrentIds = QSet<int>() );
    107109        void refreshSessionInfo( );
    108110        void refreshSessionStats( );
    109111        void refreshActiveTorrents( );
  • gtk/actions.c

     
    3535
    3636static GtkActionGroup * myGroup = NULL;
    3737
     38static struct cbdata  * myCbdata = NULL;
     39
    3840static void
    3941action_cb( GtkAction * a,
    4042           gpointer    user_data )
     
    5961    { "sort-by-state",     NULL, N_( "Sort by Stat_e" ),     NULL, NULL, 4 },
    6062    { "sort-by-age",       NULL, N_( "Sort by A_ge" ),       NULL, NULL, 5 },
    6163    { "sort-by-time-left", NULL, N_( "Sort by Time _Left" ), NULL, NULL, 6 },
    62     { "sort-by-size",      NULL, N_( "Sort by Si_ze" ),      NULL, NULL, 7 }
     64    { "sort-by-size",      NULL, N_( "Sort by Si_ze" ),      NULL, NULL, 7 },
     65    { "sort-by-queue",     NULL, N_( "Sort by _Queue" ),     NULL, NULL, 8 }
    6366};
    6467
    6568static void
     
    7679
    7780static GtkToggleActionEntry show_toggle_entries[] =
    7881{
     82    { "force-start-torrent", GTK_STOCK_MEDIA_FORWARD, N_( "_Force Start" ), "<control>F", N_( "Force start torrent" ), G_CALLBACK( action_cb ), FALSE },
    7983    { "toggle-main-window", NULL, N_( "_Show Transmission" ), NULL, NULL, G_CALLBACK( action_cb ), TRUE },
    8084    { "toggle-message-log", NULL, N_( "Message _Log" ), NULL, NULL, G_CALLBACK( action_cb ), FALSE }
    8185};
     
    121125    { "start-all-torrents", GTK_STOCK_MEDIA_PLAY, N_( "_Start All" ), NULL, N_( "Start all torrents" ), G_CALLBACK( action_cb ) },
    122126    { "relocate-torrent", NULL, N_("Set _Location..." ), NULL, NULL, G_CALLBACK( action_cb ) },
    123127    { "remove-torrent", GTK_STOCK_REMOVE, NULL, "Delete", N_( "Remove torrent" ), G_CALLBACK( action_cb ) },
     128    { "queue-up", GTK_STOCK_GO_UP, NULL, NULL, N_( "Move torrent up queue" ), G_CALLBACK( action_cb ) },
     129    { "queue-down", GTK_STOCK_GO_DOWN, NULL, NULL, N_( "Move torrent down queue " ), G_CALLBACK( action_cb ) },
     130    { "queue-top", GTK_STOCK_GOTO_TOP, NULL, NULL, N_( "Move torrent to of top queue" ), G_CALLBACK( action_cb ) },
     131    { "queue-bottom", GTK_STOCK_GOTO_BOTTOM, NULL, NULL, N_( "Move torrent to of bottom queue" ), G_CALLBACK( action_cb ) },
    124132    { "delete-torrent", GTK_STOCK_DELETE, N_( "_Delete Files and Remove" ), "<shift>Delete", NULL, G_CALLBACK( action_cb ) },
    125133    { "new-torrent", GTK_STOCK_NEW, N_( "_New..." ), NULL, N_( "Create a torrent" ), G_CALLBACK( action_cb ) },
    126134    { "quit", GTK_STOCK_QUIT, N_( "_Quit" ), NULL, NULL, G_CALLBACK( action_cb ) },
     
    210218
    211219    myUIManager = ui_manager;
    212220
     221    myCbdata = callback_user_data;
     222
    213223    register_my_icons( );
    214224
    215225    action_group = myGroup = gtk_action_group_new( "Actions" );
     
    328338    gtk_toggle_action_set_active( GTK_TOGGLE_ACTION( action ), b );
    329339}
    330340
     341void
     342action_set_toggle( const char * name,
     343                   gboolean     b )
     344{
     345    GtkAction * action = get_action( name );
     346
     347    g_signal_handlers_block_by_func( action, action_cb, myCbdata );
     348    gtk_toggle_action_set_active( GTK_TOGGLE_ACTION( action ), b );
     349    g_signal_handlers_unblock_by_func( action, action_cb, myCbdata );
     350}
     351
     352gboolean
     353action_get_toggle( const char * name )
     354{
     355    GtkAction * action = get_action( name );
     356
     357    return gtk_toggle_action_get_active( GTK_TOGGLE_ACTION( action ) );
     358}
     359
    331360GtkWidget*
    332361action_get_widget( const char * path )
    333362{
  • gtk/actions.h

     
    3434void       action_toggle( const char * name,
    3535                          gboolean     b );
    3636
     37void       action_set_toggle( const char * name,
     38                              gboolean     b );
     39
     40gboolean   action_get_toggle( const char * name );
     41
    3742void       action_set_important( const char * name, gboolean b );
    3843
    3944
  • gtk/tr-prefs.c

     
    337337}
    338338
    339339/****
     340*****  Queue Tab
     341****/
     342
     343static GtkWidget*
     344queuePage( GObject * core )
     345{
     346    int          row = 0;
     347    const char * s;
     348    GtkWidget *  t;
     349    GtkWidget *  w;
     350    GtkWidget *  w2;
     351    char buf[512];
     352
     353    t = hig_workarea_create( );
     354
     355    hig_workarea_add_section_title( t, &row, _( "Limits" ) );
     356
     357        s = _( "Maxiumum _downloads active:" );
     358        w = new_check_button( s, TR_PREFS_KEY_QUEUE_ENABLED_DOWNLOAD, core );
     359        w2 = new_spin_button( TR_PREFS_KEY_QUEUE_MAX_DOWNLOAD_ACTIVE, core, 0, 9999, 1 );
     360        gtk_widget_set_sensitive( GTK_WIDGET( w2 ), pref_flag_get( TR_PREFS_KEY_QUEUE_ENABLED_DOWNLOAD ) );
     361        g_signal_connect( w, "toggled", G_CALLBACK( target_cb ), w2 );
     362        hig_workarea_add_row_w( t, &row, w, w2, NULL );
     363
     364        s = _( "Maxiumum _seeds active:" );
     365        w = new_check_button( s, TR_PREFS_KEY_QUEUE_ENABLED_SEED, core );
     366        w2 = new_spin_button( TR_PREFS_KEY_QUEUE_MAX_SEED_ACTIVE, core, 0, 9999, 1 );
     367        gtk_widget_set_sensitive( GTK_WIDGET( w2 ), pref_flag_get( TR_PREFS_KEY_QUEUE_ENABLED_SEED ) );
     368        g_signal_connect( w, "toggled", G_CALLBACK( target_cb ), w2 );
     369        hig_workarea_add_row_w( t, &row, w, w2, NULL );
     370
     371    hig_workarea_add_section_divider( t, &row );
     372    hig_workarea_add_section_title( t, &row, _( "Skip" ) );
     373
     374        s = _( "Skip torrent if slow for _N minutes:" );
     375        w = new_check_button( s, TR_PREFS_KEY_QUEUE_SKIP_SLOW_TORRENTS, core );
     376        w2 = new_spin_button( TR_PREFS_KEY_QUEUE_SLOW_COUNT, core, 0, 9999, 1 );
     377        gtk_widget_set_sensitive( GTK_WIDGET( w2 ), pref_flag_get( TR_PREFS_KEY_QUEUE_SKIP_SLOW_TORRENTS ) );
     378        g_signal_connect( w, "toggled", G_CALLBACK( target_cb ), w2 );
     379        hig_workarea_add_row_w( t, &row, w, w2, NULL );
     380
     381        g_snprintf( buf, sizeof( buf ), _( "Slow torrent cutoff sp_eed (%s):" ), _(speed_K_str) );
     382        w2 = new_spin_button( TR_PREFS_KEY_QUEUE_SLOW_CUTOFF_KBps, core, 0, 9999, 1 );
     383        gtk_widget_set_sensitive( GTK_WIDGET( w2 ), pref_flag_get( TR_PREFS_KEY_QUEUE_SKIP_SLOW_TORRENTS ) );
     384        g_signal_connect( w, "toggled", G_CALLBACK( target_cb ), w2 );
     385        hig_workarea_add_row( t, &row, buf, w2, NULL );
     386
     387    hig_workarea_add_section_divider( t, &row );
     388    hig_workarea_add_section_title( t, &row, _( "Options" ) );
     389
     390        s = _( "_Add new torrents to the top of the queue" );
     391        w = new_check_button( s, TR_PREFS_KEY_QUEUE_NEW_TORRENTS_TOP, core );
     392        hig_workarea_add_wide_control( t, &row, w );
     393
     394        s = _( "Don't start _new torrents if a speed limit is reached" );
     395        w = new_check_button( s, TR_PREFS_KEY_QUEUE_SPEED_LIMIT, core );
     396        hig_workarea_add_wide_control( t, &row, w );
     397
     398    hig_workarea_finish( t, &row );
     399    return t;
     400}
     401
     402/****
    340403*****  Desktop Tab
    341404****/
    342405
     
    12361299                              peerPage( core ),
    12371300                              gtk_label_new ( _( "Network" ) ) );
    12381301    gtk_notebook_append_page( GTK_NOTEBOOK( n ),
     1302                              queuePage( core ),
     1303                              gtk_label_new ( _( "Queue" ) ) );
     1304    gtk_notebook_append_page( GTK_NOTEBOOK( n ),
    12391305                              desktopPage( core ),
    12401306                              gtk_label_new ( _( "Desktop" ) ) );
    12411307    gtk_notebook_append_page( GTK_NOTEBOOK( n ),
  • gtk/filter.c

     
    630630            return st->finished == TRUE;
    631631
    632632        case ACTIVITY_FILTER_QUEUED:
    633             return st->activity == TR_STATUS_CHECK_WAIT;
     633            return tr_torrentIsQueued( tor );
    634634
    635635        case ACTIVITY_FILTER_VERIFYING:
    636             return st->activity == TR_STATUS_CHECK ;
     636            return ( st->activity == TR_STATUS_CHECK_WAIT )
     637                || ( st->activity == TR_STATUS_CHECK );
    637638
    638639        case ACTIVITY_FILTER_ERROR:
    639640            return st->error != 0;
  • gtk/torrent-cell-renderer.c

     
    192192}
    193193
    194194static char*
     195getActivityString( const tr_torrent * tor,
     196                   const tr_stat    * torStat )
     197{
     198    GString * gstr = g_string_new( NULL );
     199
     200    switch( torStat->activity )
     201    {
     202        case TR_STATUS_DOWNLOAD:
     203        {
     204            if( !tr_torrentIsQueued( tor ) && tr_torrentCouldQueue( tor ) )
     205                g_string_assign( gstr, _( "Forced downloading" ) );
     206            else
     207                g_string_assign( gstr, _( "Downloading" ) );
     208            break;
     209        }
     210
     211        case TR_STATUS_SEED:
     212        {
     213            if( !tr_torrentIsQueued( tor ) && tr_torrentCouldQueue( tor ) )
     214                g_string_assign( gstr, _( "Forced seeding" ) );
     215            else
     216                g_string_assign( gstr, _( "Seeding" ) );
     217            break;
     218        }
     219
     220        default:
     221            break;
     222    }
     223
     224    return g_string_free( gstr, FALSE );
     225}
     226
     227static char*
    195228getShortStatusString( const tr_torrent  * tor,
    196229                      const tr_stat     * torStat,
    197230                      double              uploadSpeed_KBps,
     
    201234
    202235    switch( torStat->activity )
    203236    {
    204         case TR_STATUS_STOPPED:
    205             if( torStat->finished )
    206                 g_string_assign( gstr, _( "Finished" ) );
    207             else
    208                 g_string_assign( gstr, _( "Paused" ) );
    209             break;
    210 
    211237        case TR_STATUS_CHECK_WAIT:
    212238            g_string_assign( gstr, _( "Waiting to verify local data" ) );
    213239            break;
     
    233259            break;
    234260        }
    235261
     262        case TR_STATUS_STOPPED:
     263            if( tr_torrentIsQueued( tor ) && tr_torrentCouldQueue( tor ) )
     264            {
     265                if( torStat->leftUntilDone )
     266                    g_string_assign( gstr, _( "Queued to download" ) );
     267                else
     268                    g_string_assign( gstr, _( "Queued to seed" ) );
     269            }
     270            else
     271            {
     272                if( torStat->finished )
     273                    g_string_assign( gstr, _( "Finished" ) );
     274                else
     275                    g_string_assign( gstr, _( "Paused" ) );
     276            }
     277            break;
     278
    236279        default:
    237280            break;
    238281    }
     
    273316
    274317        case TR_STATUS_DOWNLOAD:
    275318        {
     319            char * pch = getActivityString( tor, torStat );
    276320            if( tr_torrentHasMetadata( tor ) )
    277321            {
    278322                g_string_append_printf( gstr,
    279                     gtr_ngettext( "Downloading from %1$'d of %2$'d connected peer",
    280                                   "Downloading from %1$'d of %2$'d connected peers",
     323                    gtr_ngettext( "%1$s from %2$'d of %3$'d connected peer",
     324                                  "%1$s from %2$'d of %3$'d connected peers",
    281325                                  torStat->peersConnected ),
     326                    pch,
    282327                    torStat->peersSendingToUs +
    283328                    torStat->webseedsSendingToUs,
    284329                    torStat->peersConnected +
     
    287332            else
    288333            {
    289334                g_string_append_printf( gstr,
    290                     gtr_ngettext( "Downloading metadata from %1$'d peer (%2$d%% done)",
    291                                   "Downloading metadata from %1$'d peers (%2$d%% done)",
     335                    gtr_ngettext( "%1$s metadata from %1$'d peer (%2$d%% done)",
     336                                  "%1$s metadata from %1$'d peers (%2$d%% done)",
    292337                                  torStat->peersConnected ),
     338                    pch,
    293339                    torStat->peersConnected + torStat->webseedsSendingToUs,
    294340                    (int)(100.0*torStat->metadataPercentComplete) );
    295341            }
     342            g_free( pch );
    296343            break;
    297344        }
    298345
    299346        case TR_STATUS_SEED:
     347        {
     348            char * pch = getActivityString( tor, torStat );
    300349            g_string_append_printf( gstr,
    301                 gtr_ngettext( "Seeding to %1$'d of %2$'d connected peer",
    302                               "Seeding to %1$'d of %2$'d connected peers",
     350                gtr_ngettext( "%1$s to %2$'d of %3$'d connected peer",
     351                              "%1$s to %2$'d of %3$'d connected peers",
    303352                              torStat->peersConnected ),
     353                pch,
    304354                torStat->peersGettingFromUs,
    305355                torStat->peersConnected );
    306                 break;
     356            g_free( pch );
     357            break;
     358        }
    307359    }
    308360
    309361    if( isActive && !isChecking )
  • gtk/ui.h

     
    2222    "      <menuitem action='open-torrent-folder'/>\n"
    2323    "      <separator/>\n"
    2424    "      <menuitem action='start-torrent'/>\n"
     25    "      <menuitem action='force-start-torrent'/>\n"
    2526    "      <menuitem action='update-tracker'/>\n"
    2627    "      <menuitem action='pause-torrent'/>\n"
    2728    "      <menuitem action='copy-magnet-link-to-clipboard'/>\n"
     
    4344    "      <menuitem action='sort-by-age'/>\n"
    4445    "      <menuitem action='sort-by-name'/>\n"
    4546    "      <menuitem action='sort-by-progress'/>\n"
     47    "      <menuitem action='sort-by-queue'/>\n"
    4648    "      <menuitem action='sort-by-ratio'/>\n"
    4749    "      <menuitem action='sort-by-size'/>\n"
    4850    "      <menuitem action='sort-by-state'/>\n"
     
    6769    "    <toolitem action='pause-torrent'/>\n"
    6870    "    <toolitem action='remove-torrent'/>\n"
    6971    "    <separator/>\n"
     72    "    <toolitem action='queue-up'/>\n"
     73    "    <toolitem action='queue-down'/>\n"
     74    "    <toolitem action='queue-top'/>\n"
     75    "    <toolitem action='queue-bottom'/>\n"
     76    "    <separator/>\n"
    7077    "    <toolitem action='show-torrent-properties'/>\n"
    7178    "  </toolbar>\n"
    7279    "\n"
     
    7986    "      <menuitem action='sort-by-age'/>\n"
    8087    "      <menuitem action='sort-by-name'/>\n"
    8188    "      <menuitem action='sort-by-progress'/>\n"
     89    "      <menuitem action='sort-by-queue'/>\n"
    8290    "      <menuitem action='sort-by-ratio'/>\n"
    8391    "      <menuitem action='sort-by-size'/>\n"
    8492    "      <menuitem action='sort-by-state'/>\n"
     
    8896    "    </menu>\n"
    8997    "    <separator/>\n"
    9098    "    <menuitem action='start-torrent'/>\n"
     99    "    <menuitem action='force-start-torrent'/>\n"
    91100    "    <menuitem action='update-tracker'/>\n"
    92101    "    <menuitem action='pause-torrent'/>\n"
    93102    "    <menuitem action='copy-magnet-link-to-clipboard'/>\n"
  • gtk/tr-core.c

     
    438438}
    439439
    440440static int
     441compareByQueue( GtkTreeModel * model,
     442                GtkTreeIter  * a,
     443                GtkTreeIter  * b,
     444                gpointer       user_data UNUSED )
     445{
     446    tr_torrent *ta, *tb;
     447
     448    gtk_tree_model_get( model, a, MC_TORRENT_RAW, &ta, -1 );
     449    gtk_tree_model_get( model, b, MC_TORRENT_RAW, &tb, -1 );
     450
     451    return -tr_sessionCompareTorrentByQueueRank( ta, tb );
     452}
     453
     454static int
    441455compareByState( GtkTreeModel * model,
    442456                GtkTreeIter *  a,
    443457                GtkTreeIter *  b,
     
    477491        sort_func = compareByProgress;
    478492    else if( !strcmp( mode, "sort-by-time-left" ) )
    479493        sort_func = compareByETA;
     494    else if( !strcmp( mode, "sort-by-queue" ) )
     495        sort_func = compareByQueue;
    480496    else if( !strcmp( mode, "sort-by-ratio" ) )
    481497        sort_func = compareByRatio;
    482498    else if( !strcmp( mode, "sort-by-state" ) )
     
    17831799    return activeCount;
    17841800}
    17851801
     1802int
     1803tr_core_get_inactive_torrent_count( TrCore * core )
     1804{
     1805    GtkTreeIter iter;
     1806    GtkTreeModel * model = tr_core_model( core );
     1807    int inactiveCount = 0;
     1808
     1809    if( gtk_tree_model_get_iter_first( model, &iter ) ) do
     1810    {
     1811        int activity;
     1812        gtk_tree_model_get( model, &iter, MC_ACTIVITY, &activity, -1 );
     1813
     1814        if( activity == TR_STATUS_STOPPED )
     1815            ++inactiveCount;
     1816    }
     1817    while( gtk_tree_model_iter_next( model, &iter ) );
     1818
     1819    return inactiveCount;
     1820}
     1821
     1822int
     1823tr_core_get_inactive_queued_torrent_count( TrCore * core )
     1824{
     1825    GtkTreeIter iter;
     1826    GtkTreeModel * model = tr_core_model( core );
     1827    int inactiveQueuedCount = 0;
     1828
     1829    if( gtk_tree_model_get_iter_first( model, &iter ) ) do
     1830    {
     1831        int activity;
     1832        tr_torrent * tor;
     1833        gtk_tree_model_get( model, &iter, MC_ACTIVITY, &activity, -1 );
     1834        gtk_tree_model_get( model, &iter, MC_TORRENT_RAW, &tor, -1 );
     1835
     1836        if( activity == TR_STATUS_STOPPED && tr_torrentIsQueued( tor ) )
     1837            ++inactiveQueuedCount;
     1838    }
     1839    while( gtk_tree_model_iter_next( model, &iter ) );
     1840
     1841    return inactiveQueuedCount;
     1842}
  • gtk/tr-core.h

     
    8383
    8484tr_session *   tr_core_session( TrCore * self );
    8585
     86int            tr_core_get_inactive_queued_torrent_count( TrCore * self );
     87
     88int            tr_core_get_inactive_torrent_count( TrCore * self );
     89
    8690int            tr_core_get_active_torrent_count( TrCore * self );
    8791
    8892int            tr_core_get_torrent_count( TrCore * self );
  • gtk/main.c

     
    263263    int    totalCount;
    264264    int    activeCount;
    265265    int    inactiveCount;
     266    int    queuedCount;
     267    int    inactiveAndQueuedCount;
     268    int    forcedCount;
    266269};
    267270
    268271static void
     
    273276{
    274277    int activity = 0;
    275278    struct counts_data * counts = user_data;
     279    tr_torrent * tor;
    276280
    277281    ++counts->totalCount;
    278282
    279283    gtk_tree_model_get( model, iter, MC_ACTIVITY, &activity, -1 );
     284    gtk_tree_model_get( model, iter, MC_TORRENT_RAW, &tor, -1 );
    280285
    281286    if( activity == TR_STATUS_STOPPED )
     287    {
    282288        ++counts->inactiveCount;
     289        if( tr_torrentIsQueued( tor ) )
     290            ++counts->inactiveAndQueuedCount;
     291    }
    283292    else
     293    {
    284294        ++counts->activeCount;
     295        if( !tr_torrentIsQueued( tor ) )
     296            ++counts->forcedCount;
     297    }
     298    if( tr_torrentIsQueued( tor ) )
     299        ++counts->queuedCount;
    285300}
    286301
    287302static void
     
    290305    counts->activeCount = 0;
    291306    counts->inactiveCount = 0;
    292307    counts->totalCount = 0;
     308    counts->queuedCount = 0;
     309    counts->inactiveAndQueuedCount = 0;
     310    counts->forcedCount = 0;
    293311
    294312    gtk_tree_selection_selected_foreach( data->sel, accumulateStatusForeach, counts );
    295313}
     
    311329    int canUpdate;
    312330    struct counts_data counts;
    313331    struct cbdata * data = gdata;
     332    const char * sort_mode = pref_string_get( PREF_KEY_SORT_MODE );
     333    gboolean sort_queue = !strcmp( sort_mode, "sort-by-queue" );
    314334
    315335    getTorrentCounts( data, &counts );
    316     action_sensitize( "pause-torrent", counts.activeCount != 0 );
    317     action_sensitize( "start-torrent", counts.inactiveCount != 0 );
     336    sort_queue = sort_queue && ( counts.totalCount != 0 );
     337    action_sensitize( "pause-torrent", counts.activeCount != 0 || counts.inactiveAndQueuedCount != 0 );
     338    action_sensitize( "start-torrent", counts.inactiveCount != 0 && counts.inactiveCount != counts.inactiveAndQueuedCount );
     339    action_sensitize( "force-start-torrent", counts.totalCount != 0 );
    318340    action_sensitize( "remove-torrent", counts.totalCount != 0 );
    319341    action_sensitize( "delete-torrent", counts.totalCount != 0 );
     342    action_sensitize( "queue-up", sort_queue );
     343    action_sensitize( "queue-down", sort_queue );
     344    action_sensitize( "queue-top", sort_queue );
     345    action_sensitize( "queue-bottom", sort_queue );
    320346    action_sensitize( "verify-torrent", counts.totalCount != 0 );
    321347    action_sensitize( "relocate-torrent", counts.totalCount != 0 );
    322348    action_sensitize( "show-torrent-properties", counts.totalCount != 0 );
    323349    action_sensitize( "open-torrent-folder", counts.totalCount == 1 );
    324350    action_sensitize( "copy-magnet-link-to-clipboard", counts.totalCount == 1 );
    325351
     352    action_set_toggle( "force-start-torrent", counts.forcedCount != 0 );
     353
    326354    canUpdate = 0;
    327355    gtk_tree_selection_selected_foreach( data->sel, accumulateCanUpdateForeach, &canUpdate );
    328356    action_sensitize( "update-tracker", canUpdate != 0 );
     
    337365
    338366    {
    339367        const int total = tr_core_get_torrent_count( data->core );
    340         const int active = tr_core_get_active_torrent_count( data->core );
    341         action_sensitize( "pause-all-torrents", active != 0 );
    342         action_sensitize( "start-all-torrents", active != total );
     368        const int inactive = tr_core_get_inactive_torrent_count( data->core );
     369        const int inactiveQueued = tr_core_get_inactive_queued_torrent_count( data->core );
     370        action_sensitize( "pause-all-torrents", inactive < total || inactiveQueued > 0 );
     371        action_sensitize( "start-all-torrents", inactive > 0 && inactiveQueued < inactive );
    343372    }
    344373
    345374    return FALSE;
     
    13481377    {
    13491378        tr_sessionSetDeleteSource( tr, pref_flag_get( key ) );
    13501379    }
     1380    else if( !strcmp( key, TR_PREFS_KEY_QUEUE_ENABLED_DOWNLOAD ) )
     1381    {
     1382        tr_sessionSetQueueEnabledDownload( tr, pref_flag_get( key ) );
     1383    }
     1384    else if( !strcmp( key, TR_PREFS_KEY_QUEUE_ENABLED_SEED ) )
     1385    {
     1386        tr_sessionSetQueueEnabledSeed( tr, pref_flag_get( key ) );
     1387    }
     1388    else if( !strcmp( key, TR_PREFS_KEY_QUEUE_MAX_DOWNLOAD_ACTIVE ) )
     1389    {
     1390        tr_sessionSetQueueMaxDownloadActive( tr, pref_int_get( key ) );
     1391    }
     1392    else if( !strcmp( key, TR_PREFS_KEY_QUEUE_MAX_SEED_ACTIVE ) )
     1393    {
     1394        tr_sessionSetQueueMaxSeedActive( tr, pref_int_get( key ) );
     1395    }
     1396    else if( !strcmp( key, TR_PREFS_KEY_QUEUE_NEW_TORRENTS_TOP ) )
     1397    {
     1398        tr_sessionSetQueueNewTorrentsTop( tr, pref_flag_get( key ) );
     1399    }
     1400    else if( !strcmp( key, TR_PREFS_KEY_QUEUE_SKIP_SLOW_TORRENTS ) )
     1401    {
     1402        tr_sessionSetQueueSkipSlowTorrentsEnabled( tr, pref_flag_get( key ) );
     1403    }
     1404    else if( !strcmp( key, TR_PREFS_KEY_QUEUE_SLOW_COUNT ) )
     1405    {
     1406        tr_sessionSetQueueSlowCount( tr, pref_int_get( key ) );
     1407    }
     1408    else if( !strcmp( key, TR_PREFS_KEY_QUEUE_SLOW_CUTOFF_KBps ) )
     1409    {
     1410        tr_sessionSetQueueSlowCutoff( tr, pref_int_get( key ) );
     1411    }
     1412    else if( !strcmp( key, TR_PREFS_KEY_QUEUE_SPEED_LIMIT ) )
     1413    {
     1414        tr_sessionSetQueueSpeedLimitEnabled( tr, pref_flag_get( key ) );
     1415    }
    13511416}
    13521417
    13531418static gboolean
     
    15031568}
    15041569
    15051570static void
     1571accumulateSelectedTorrentsRaw( GtkTreeModel *      model,
     1572                               GtkTreePath  * path UNUSED,
     1573                               GtkTreeIter *       iter,
     1574                               gpointer            gdata )
     1575{
     1576    GSList ** data = ( GSList** ) gdata;
     1577    tr_torrent * tor = NULL;
     1578
     1579    gtk_tree_model_get( model, iter, MC_TORRENT_RAW, &tor, -1 );
     1580    *data = g_slist_prepend( *data, tor );
     1581}
     1582
     1583static gboolean
     1584moveTorrentQueue( struct cbdata      * data,
     1585                  tr_queue_direction   dir )
     1586{
     1587    gboolean changed = FALSE;
     1588    GtkTreeSelection * s = tr_window_get_selection( data->wind );
     1589    GSList * l = NULL;
     1590
     1591    gtk_tree_selection_selected_foreach( s, accumulateSelectedTorrentsRaw, &l );
     1592    l = g_slist_sort( l, tr_sessionCompareTorrentByQueueRank );
     1593
     1594    switch( dir )
     1595    {
     1596        case TR_QUEUE_DOWN:
     1597            if( tr_torrentGetQueueRank( g_slist_last( l )->data ) + 1 == tr_core_get_torrent_count( data-> core ) )
     1598                break;
     1599        case TR_QUEUE_TOP:
     1600            l = g_slist_reverse( l );
     1601            changed = TRUE;
     1602            break;
     1603        case TR_QUEUE_UP:
     1604            if( tr_torrentGetQueueRank( l->data ) == 0 )
     1605                break;
     1606        case TR_QUEUE_BOTTOM:
     1607            changed = TRUE;
     1608            break;
     1609    }
     1610
     1611    if( changed )
     1612    {
     1613        tr_benc top, *args, *ids;
     1614        GSList * le = l;
     1615
     1616        tr_bencInitDict( &top, 2 );
     1617        tr_bencDictAddStr( &top, "method", "torrent-set" );
     1618        args = tr_bencDictAddDict( &top, "arguments", 1 );
     1619        tr_bencDictAddInt( args, "moveQueueRank", (int)dir );
     1620        ids = tr_bencDictAddList( args, "ids", g_slist_length( le ) );
     1621        do
     1622        {
     1623            const tr_torrent * tor = ( tr_torrent* )le->data;
     1624            tr_bencListAddInt( ids, tr_torrentId( tor ) );
     1625        }
     1626        while(( le = g_slist_next( le ) ));
     1627        tr_core_exec( data->core, &top );
     1628
     1629        tr_bencFree( &top );
     1630    }
     1631
     1632    g_slist_free( l );
     1633    return changed;
     1634}
     1635
     1636static void
    15061637startAllTorrents( struct cbdata * data )
    15071638{
    15081639    tr_session * session = tr_core_session( data->core );
     
    16261757    {
    16271758        changed |= rpcOnSelectedTorrents( data, "torrent-start" );
    16281759    }
     1760    else if( !strcmp( action_name, "force-start-torrent" ) )
     1761    {
     1762        if( action_get_toggle( action_name ) )
     1763            changed |= rpcOnSelectedTorrents( data, "torrent-force-start" );
     1764        else
     1765        {
     1766            GSList * ids = getSelectedTorrentIds( data );
     1767            if( ids )
     1768            {
     1769                GSList * l = ids;
     1770                tr_benc top, *args, *ids;
     1771
     1772                tr_bencInitDict( &top, 2 );
     1773                tr_bencDictAddStr( &top, "method", "torrent-set" );
     1774                args = tr_bencDictAddDict( &top, "arguments", 2 );
     1775                tr_bencDictAddBool( args, "queued", TRUE );
     1776                ids = tr_bencDictAddList( args, "ids", g_slist_length( l ) );
     1777                do
     1778                    tr_bencListAddInt( ids, GPOINTER_TO_INT( l->data ) );
     1779                while(( l = g_slist_next( l ) ));
     1780                tr_core_exec( data->core, &top );
     1781                changed = TRUE;
     1782
     1783                tr_bencFree( &top );
     1784            }
     1785            g_slist_free( ids );
     1786        }
     1787    }
    16291788    else if( !strcmp( action_name, "pause-torrent" ) )
    16301789    {
    16311790        changed |= rpcOnSelectedTorrents( data, "torrent-stop" );
     
    16741833    {
    16751834        removeSelected( data, TRUE );
    16761835    }
     1836    else if( !strcmp( action_name, "queue-up" ) )
     1837    {
     1838        changed |= moveTorrentQueue( data, TR_QUEUE_UP );
     1839    }
     1840    else if( !strcmp( action_name, "queue-down" ) )
     1841    {
     1842        changed |= moveTorrentQueue( data, TR_QUEUE_DOWN );
     1843    }
     1844    else if( !strcmp( action_name, "queue-top" ) )
     1845    {
     1846        changed |= moveTorrentQueue( data, TR_QUEUE_TOP );
     1847    }
     1848    else if( !strcmp( action_name, "queue-bottom" ) )
     1849    {
     1850        changed |= moveTorrentQueue( data, TR_QUEUE_BOTTOM );
     1851    }
    16771852    else if( !strcmp( action_name, "quit" ) )
    16781853    {
    16791854        maybeaskquit( data );
  • gtk/details.c

     
    555555****/
    556556
    557557static const char *
    558 activityString( int activity, tr_bool finished )
     558activityString( const tr_torrent * tor, const tr_stat * torStat )
    559559{
    560     switch( activity )
     560    switch( torStat->activity )
    561561    {
    562562        case TR_STATUS_CHECK_WAIT: return _( "Waiting to verify local data" );
    563563        case TR_STATUS_CHECK:      return _( "Verifying local data" );
    564         case TR_STATUS_DOWNLOAD:   return _( "Downloading" );
    565         case TR_STATUS_SEED:       return _( "Seeding" );
    566         case TR_STATUS_STOPPED:    return finished ? _( "Finished" ) : _( "Paused" );
     564        case TR_STATUS_DOWNLOAD:
     565        {
     566            if( !tr_torrentIsQueued( tor ) && tr_torrentCouldQueue( tor ) )
     567                return _( "Forced downloading" );
     568            else
     569                return _( "Downloading" );
     570        }
     571        case TR_STATUS_SEED:       
     572        {
     573            if( !tr_torrentIsQueued( tor ) && tr_torrentCouldQueue( tor ) )
     574                return _( "Forced seeding" );
     575            else
     576                return _( "Seeding" );
     577        }
     578        case TR_STATUS_STOPPED:
     579        {
     580            if( tr_torrentIsQueued( tor ) && tr_torrentCouldQueue( tor ) )
     581            {
     582                if( torStat->leftUntilDone )
     583                    return _( "Queued to download" );
     584                else
     585                    return _( "Queued to seed" );
     586            }
     587            else
     588            {
     589                if( torStat->finished )
     590                    return _( "Finished" );
     591                else
     592                    return _( "Paused" );
     593            }
     594        }
     595        default:                   return "";
    567596    }
    568 
    569     return "";
    570597}
    571598
    572599/* Only call gtk_label_set_text() if the new text differs from the old.
     
    710737    if( n<=0 )
    711738        str = no_torrent;
    712739    else {
    713         const tr_torrent_activity activity = stats[0]->activity;
    714         tr_bool allFinished = stats[0]->finished;
    715         for( i=1; i<n; ++i ) {
    716             if( activity != stats[i]->activity )
     740        const char * baseline = activityString( torrents[0], stats[0] );
     741        for( i=1; i<n; ++i )
     742            if( strcmp( baseline,  activityString( torrents[i], stats[i] ) ) )
    717743                break;
    718             if( !stats[i]->finished )
    719                 allFinished = FALSE;
    720         }
    721         str = i<n ? mixed : activityString( activity, allFinished );
     744        str = i<n ? mixed : baseline;
    722745    }
    723746    stateString = str;
    724747    gtr_label_set_text( GTK_LABEL( di->state_lb ), str );
  • daemon/transmission-remote.1

     
    2020.Op Fl ASC
    2121.Op Fl b
    2222.Op Fl c Ar path | Fl C
     23.Op Fl cs Ar speed
    2324.Op Fl d Ar number | Fl D
    2425.Op Fl e Ar size
    2526.Op Fl er | ep | et
    2627.Op Fl f
     28.Op Fl fs
    2729.Op Fl g Ar files
    2830.Op Fl G Ar files
    2931.Op Fl gsr Ar ratio
     
    3234.Op Fl i
    3335.Op Fl l
    3436.Op Fl m | M
     37.Op Fl md Ar max
     38.Op Fl ms Ar max
    3539.Op Fl n Ar user:pass
    3640.Op Fl N Ar netrc
    3741.Op Fl o | O
    3842.Op Fl p Ar port
     43.Op Fl q | Q
     44.Op Fl qd | QD
     45.Op Fl qs | QS
     46.Op Fl qmu | qmd | qmt | qmb
     47.Op Fl qnt | qnb
     48.Op Fl qsl | QSL
    3949.Op Fl Bh
    4050.Op Fl Bn
    4151.Op Fl \&Bl
     
    4959.Op Fl sr Ar ratio
    5060.Op Fl SR
    5161.Op Fl srd
     62.Op Fl ss | SS
    5263.Op Fl si
    5364.Op Fl st
    5465.Op Fl t Ar all | Ar id | Ar hash
     
    117128until the torrent is done.
    118129.It Fl C Fl -no-incomplete-dir
    119130Don't store incomplete torrents in a different directory.
     131.It Fl cs Fl -cutoff-speed Ar speed
     132Cutoff speed of slow torrents.
    120133.It Fl d Fl -downlimit Ar limit
    121134Limit the maximum download speed to
    122135.Ar limit
     
    135148Prefer unencrypted peer connections.
    136149.It Fl f Fl -files
    137150Get a file list for the current torrent(s)
     151.It Fl fs Fl -force-start
     152Force start the current torrent(s)
    138153.It Fl g Fl -get Ar all | file-index | files
    139154Mark file(s) for download.
    140155.Ar all
     
    165180Enable portmapping via NAT-PMP or UPnP
    166181.It Fl M Fl -no-portmap
    167182Disable portmapping
     183.It Fl md Fl -max-download Ar max
     184Set the maximum active downloading torrent(s).
     185.It Fl ms Fl -max-seed Ar max
     186Set the maximum active seeding torrent(s).
    168187.It Fl n Fl -auth Ar username:password
    169188Set the
    170189.Ar username
     
    205224.It Fl pr Fl -peers Ar number
    206225Set the maximum number of peers.
    207226If current torrent(s) are selected this operates on them.  Otherwise, it changes the global setting.
     227.It Fl q Fl -queued
     228Queue the torrents(s).
     229.It Fl Q Fl -no-queued
     230Remove the torrent(s) from the queue.
     231.It Fl qd Fl -queue-download
     232Enable the torrent queue for downloads.
     233.It Fl QD Fl -no-queue-download
     234Disable the torrent queue for downloads.
     235.It Fl qs Fl -queue-seed
     236Enable the torrent queue for seeds.
     237.It Fl QS Fl -no-queue-seed
     238Disable the torrent queue for seeds.
     239.It Fl qmu Fl -queue-move-up
     240Move the torrent(s) up in the queue.
     241.It Fl qmd Fl -queue-move-down
     242Move the torrent(s) down in the queue.
     243.It Fl qmt Fl -queue-move-top
     244Move the torrent(s) to the top of the queue.
     245.It Fl qmb Fl -queue-move-bottom
     246Move the torrent(s) to the bottom of the queue.
     247.It Fl qnt Fl -queue-new-top
     248New torrents start at the top of the queue.
     249.It Fl qnb Fl -queue-new-bottom
     250New torrents start at the bottom of the queue.
     251.It Fl qsl Fl -queue-speed-limit
     252Don't start new torrents if the spped limit is reached.
     253.It Fl QSL Fl -no-queue-speed-limit
     254Start new torrents even if the spped limit is reached.
    208255.It Fl r Fl -remove
    209256Remove the current torrent(s).  This does not delete the downloaded data.
    210257.It Fl -remove-and-delete
     
    220267Let the current torrent(s) seed regardless of ratio
    221268.It Fl srd Fl -seedratio-default
    222269Let the current torrent(s) use the global seedratio settings
     270.It Fl ss Fl -skip-slow
     271Skip slow torrents in the queue
     272.It Fl SS Fl -no-skip-slow
     273Don't skip slow torrents in the queue
    223274.It Fl ta Fl -tracker-add Ar tracker
    224275Add a tracker to a torrent
    225276.It Fl ta Fl -tracker-remove Ar trackerId
  • daemon/remote.c

     
    250250    { 920, "session-info",           "Show the session's details", "si", 0, NULL },
    251251    { 921, "session-stats",          "Show the session's statistics", "st", 0, NULL },
    252252    { 'l', "list",                   "List all torrents", "l",  0, NULL },
     253    { 931, "max-download",           "Set the maximum active downloading torrent(s)", "md", 1, "<max>" },
     254    { 932, "max-seed",               "Set the maximum active seeding torrent(s)", "ms", 1, "<max>" },
     255    { 933, "skip-slow",              "Skip slow torrents in the queue", "ss", 0, NULL },
     256    { 934, "no-skip-slow",           "Don't skip slow torrents in the queue", "SS", 0, NULL },
     257    { 935, "cutoff-speed",           "Cutoff speed of slow torrents", "cs", 1, "<speed>" },
     258    { 936, "queue-download",         "Enable the torrent queue for downloads", "qd", 0, NULL },
     259    { 937, "no-queue-download",      "Disable the torrent queue for downloads", "QD", 0, NULL },
     260    { 938, "queue-seed",             "Enable the torrent queue for seeds", "qs", 0, NULL },
     261    { 939, "no-queue-seed",          "Disable the torrent queue for seeds", "QS", 0, NULL },
     262    { 955, "queue-new-top",          "New torrents start at the top of the queue", "qnt", 0, NULL },
     263    { 956, "queue-new-bottom",       "New torrents start at the bottom of the queue", "qnb", 0, NULL },
     264    { 957, "queue-speed-limit",      "Don't start new torrents if the speed limit is reached", "qsl", 0, NULL },
     265    { 958, "no-queue-speed-limit",   "Start new torrents even if the speed limit is reached", "QSL", 0, NULL },
     266    { 944, "queued",                 "Queue the torrent(s)", "q", 0, NULL },
     267    { 945, "no-queued",              "Remove the torrent(s) from the queue", "Q", 0, NULL },
     268    { 946, "queue-move-up",          "Move the torrent(s) up in the queue", "qmu", 0, NULL },
     269    { 947, "queue-move-down",        "Move the torrent(s) down in the queue", "qmd", 0, NULL },
     270    { 948, "queue-move-top",         "Move the torrent(s) to the top of the queue", "qmt", 0, NULL },
     271    { 949, "queue-move-bottom",      "Move the torrent(s) to the bottom of the queue", "qmb", 0, NULL },
    253272    { 960, "move",                   "Move current torrent's data to a new folder", NULL, 1, "<path>" },
    254273    { 961, "find",                   "Tell Transmission where to find a torrent's data", NULL, 1, "<path>" },
    255274    { 'm', "portmap",                "Enable portmapping via NAT-PMP or UPnP", "m",  0, NULL },
     
    280299    { 710, "tracker-add",            "Add a tracker to a torrent", "ta", 1, "<tracker>" },
    281300    { 712, "tracker-remove",         "Remove a tracker from a torrent", "tr", 1, "<trackerId>" },
    282301    { 's', "start",                  "Start the current torrent(s)", "s",  0, NULL },
     302    { 964, "force-start",            "Force start the current torrent(s)", "fs",  0, NULL },
    283303    { 'S', "stop",                   "Stop the current torrent(s)", "S",  0, NULL },
    284304    { 't', "torrent",                "Set the current torrent(s)", "t",  1, "<torrent>" },
    285305    { 990, "start-paused",           "Start added torrents paused", NULL, 0, NULL },
     
    370390        case 'Y': /* no-lpd */
    371391        case 800: /* torrent-done-script */
    372392        case 801: /* no-torrent-done-script */
     393        case 931: /* max-download */
     394        case 932: /* max-seed */
     395        case 933: /* skip-slow */
     396        case 934: /* no-skip-slow */
     397        case 935: /* cutoff-speed */
     398        case 936: /* queue-download */
     399        case 937: /* no-queue-download */
     400        case 938: /* queue-seed */
     401        case 939: /* no-queue-seed */
     402        case 955: /* queue-new-top */
     403        case 956: /* queue-new-bottom */
     404        case 957: /* queue-speed-limit */
     405        case 958: /* no-queue-speed-limit */
    373406        case 970: /* alt-speed */
    374407        case 971: /* no-alt-speed */
    375408        case 972: /* alt-speed-downlimit */
     
    391424            return MODE_SESSION_SET;
    392425
    393426        case 712: /* tracker-remove */
     427        case 944: /* queued */
     428        case 945: /* no-queued */
     429        case 946: /* queue-up */
     430        case 947: /* queue-down */
     431        case 948: /* queue-top */
     432        case 949: /* queue-bottom */
    394433        case 950: /* seedratio */
    395434        case 951: /* seedratio-default */
    396435        case 952: /* no-seedratio */
     
    431470            return MODE_SESSION_SET | MODE_TORRENT_SET;
    432471
    433472        case 's': /* start */
     473        case 964: /* force-start */
    434474            return MODE_TORRENT_START | MODE_TORRENT_ADD;
    435475
    436476        case 'S': /* stop */
     
    655695    "peer-limit",
    656696    "pieceCount",
    657697    "pieceSize",
     698    "queued",
     699    "queueRank",
    658700    "rateDownload",
    659701    "rateUpload",
    660702    "recheckProgress",
     
    821863            printf( "\n" );
    822864
    823865            printf( "TRANSFER\n" );
     866            if( tr_bencDictFindBool( t, "queued", &boolVal ) )
     867                printf( "  Queued: %s\n", boolVal ? "Yes" : "No" );
     868            if( tr_bencDictFindInt( t, "queueRank", &i ) )
     869                printf( "  Queue rank: %" PRId64 "\n", i );
    824870            getStatusString( t, buf, sizeof( buf ) );
    825871            printf( "  State: %s\n", buf );
    826872
     
    15371583        }
    15381584        printf( "\n" );
    15391585
     1586        printf( "QUEUE\n" );
     1587        {
     1588            tr_bool queueDownload, queueSeed, skip, speed, top;
     1589            int64_t maxDown, maxSeed, cutoff;
     1590            if( tr_bencDictFindBool( args, TR_PREFS_KEY_QUEUE_ENABLED_DOWNLOAD, &queueDownload ) &&
     1591                tr_bencDictFindBool( args, TR_PREFS_KEY_QUEUE_ENABLED_SEED, &queueSeed ) &&
     1592                tr_bencDictFindInt ( args, TR_PREFS_KEY_QUEUE_MAX_DOWNLOAD_ACTIVE, &maxDown ) &&
     1593                tr_bencDictFindInt ( args, TR_PREFS_KEY_QUEUE_MAX_SEED_ACTIVE, &maxSeed ) &&
     1594                tr_bencDictFindBool( args, TR_PREFS_KEY_QUEUE_NEW_TORRENTS_TOP, &top ) &&
     1595                tr_bencDictFindBool( args, TR_PREFS_KEY_QUEUE_SKIP_SLOW_TORRENTS, &skip ) &&
     1596                tr_bencDictFindInt ( args, TR_PREFS_KEY_QUEUE_SLOW_CUTOFF_KBps, &cutoff ) &&
     1597                tr_bencDictFindBool( args, TR_PREFS_KEY_QUEUE_SPEED_LIMIT, &speed ) )
     1598            {
     1599                char buf[128];
     1600                printf( "  Download queue: %s ( limit: %" PRId64" )\n",
     1601                        queueDownload ? "Enabled" : "Disabled",
     1602                        maxDown );
     1603                printf( "  Seed queue: %s ( limit: %" PRId64" )\n",
     1604                        queueSeed ? "Enabled" : "Disabled",
     1605                        maxSeed );
     1606
     1607                tr_formatter_speed_KBps( buf, cutoff, sizeof( buf ) );
     1608                printf( "  Skip slow torrents: %s (Slow torrent cutoff: %s)\n",
     1609                        skip ? "Yes" : "No",
     1610                        buf );
     1611
     1612                printf( "  Don't start new torrents if speed limit is reached: %s\n", speed ? "Yes" : "No" );
     1613                printf( "  Queue new torrents at the: %s\n", top ? "Top" : "Bottom" );
     1614
     1615            }
     1616        }
     1617        printf( "\n" );
     1618
    15401619        printf( "MISC\n" );
    15411620        if( tr_bencDictFindBool( args, TR_PREFS_KEY_START, &boolVal ) )
    15421621            printf( "  Autostart added torrents: %s\n", ( boolVal ? "Yes" : "No" ) );
     
    19141993                          break;
    19151994                case 801: tr_bencDictAddBool( args, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_ENABLED, FALSE );
    19161995                          break;
     1996                case 931: tr_bencDictAddInt( args, TR_PREFS_KEY_QUEUE_MAX_DOWNLOAD_ACTIVE, numarg( optarg ) );
     1997                          break;
     1998                case 932: tr_bencDictAddInt( args, TR_PREFS_KEY_QUEUE_MAX_SEED_ACTIVE, numarg( optarg ) );
     1999                          break;
     2000                case 933: tr_bencDictAddBool( args, TR_PREFS_KEY_QUEUE_SKIP_SLOW_TORRENTS, TRUE );
     2001                          break;
     2002                case 934: tr_bencDictAddBool( args, TR_PREFS_KEY_QUEUE_SKIP_SLOW_TORRENTS, FALSE );
     2003                          break;
     2004                case 935: tr_bencDictAddInt( args, TR_PREFS_KEY_QUEUE_SLOW_CUTOFF_KBps, numarg( optarg ) );
     2005                          break;
     2006                case 936: tr_bencDictAddBool( args, TR_PREFS_KEY_QUEUE_ENABLED_DOWNLOAD, TRUE );
     2007                          break;
     2008                case 937: tr_bencDictAddBool( args, TR_PREFS_KEY_QUEUE_ENABLED_DOWNLOAD, FALSE );
     2009                          break;
     2010                case 938: tr_bencDictAddBool( args, TR_PREFS_KEY_QUEUE_ENABLED_SEED, TRUE );
     2011                          break;
     2012                case 939: tr_bencDictAddBool( args, TR_PREFS_KEY_QUEUE_ENABLED_SEED, FALSE );
     2013                          break;
     2014                case 955: tr_bencDictAddBool( args, TR_PREFS_KEY_QUEUE_NEW_TORRENTS_TOP, TRUE );
     2015                          break;
     2016                case 956: tr_bencDictAddBool( args, TR_PREFS_KEY_QUEUE_NEW_TORRENTS_TOP, FALSE );
     2017                          break;
     2018                case 957: tr_bencDictAddBool( args, TR_PREFS_KEY_QUEUE_SPEED_LIMIT, TRUE );
     2019                          break;
     2020                case 958: tr_bencDictAddBool( args, TR_PREFS_KEY_QUEUE_SPEED_LIMIT, FALSE );
     2021                          break;
    19172022                case 970: tr_bencDictAddBool( args, TR_PREFS_KEY_ALT_SPEED_ENABLED, TRUE );
    19182023                          break;
    19192024                case 971: tr_bencDictAddBool( args, TR_PREFS_KEY_ALT_SPEED_ENABLED, FALSE );
     
    20372142            {
    20382143                case 712: tr_bencListAddInt( tr_bencDictAddList( args, "trackerRemove", 1 ), atoi( optarg ) );
    20392144                          break;
     2145                case 944: tr_bencDictAddBool( args, "queued", TRUE );
     2146                          break;
     2147                case 945: tr_bencDictAddBool( args, "queued", FALSE );
     2148                          break;
     2149                case 946: tr_bencDictAddInt( args, "moveQueueRank", TR_QUEUE_UP );
     2150                          break;
     2151                case 947: tr_bencDictAddInt( args, "moveQueueRank", TR_QUEUE_DOWN );
     2152                          break;
     2153                case 948: tr_bencDictAddInt( args, "moveQueueRank", TR_QUEUE_TOP );
     2154                          break;
     2155                case 949: tr_bencDictAddInt( args, "moveQueueRank", TR_QUEUE_BOTTOM );
     2156                          break;
    20402157                case 950: tr_bencDictAddReal( args, "seedRatioLimit", atof(optarg) );
    20412158                          tr_bencDictAddInt( args, "seedRatioMode", TR_RATIOLIMIT_SINGLE );
    20422159                          break;
     
    21302247                }
    21312248                break;
    21322249            }
     2250            case 964: /* force-start */
     2251            {
     2252                if( tadd )
     2253                    tr_bencDictAddBool( tr_bencDictFind( tadd, "arguments" ), "paused", FALSE );
     2254                else {
     2255                    tr_benc * top = tr_new0( tr_benc, 1 );
     2256                    tr_bencInitDict( top, 2 );
     2257                    tr_bencDictAddStr( top, "method", "torrent-force-start" );
     2258                    addIdArg( tr_bencDictAddDict( top, ARGUMENTS, 1 ), id );
     2259                    status |= flush( host, port, &top );
     2260                }
     2261                break;
     2262            }
    21332263            case 'S': /* stop */
    21342264            {
    21352265                if( tadd )
  • extras/rpc-spec.txt

     
    6767   Method name          | libtransmission function
    6868   ---------------------+-------------------------------------------------
    6969   "torrent-start"      | tr_torrentStart
     70   "torrent-force-start"| tr_torrentStart but calls tr_torrentSetQueued( tor, FALSE ) first
    7071   "torrent-stop"       | tr_torrentStop
    7172   "torrent-verify"     | tr_torrentVerify
    7273   "torrent-reannounce" | tr_torrentManualUpdate ("ask tracker for more peers")
     
    9697   "honorsSessionLimits" | boolean    true if session upload limits are honored
    9798   "ids"                 | array      torrent list, as described in 3.1
    9899   "location"            | string     new location of the torrent's content
     100   "moveQueueRank"       | number     move torrent in queue. See tr_queue_direction
    99101   "peer-limit"          | number     maximum number of peers
    100102   "priority-high"       | array      indices of high-priority file(s)
    101103   "priority-low"        | array      indices of low-priority file(s)
    102104   "priority-normal"     | array      indices of normal-priority file(s)
     105   "queued"              | boolean    true if the queue should handle this torrent
     106   "queueRank"           | number     directly set a torrent's queue rank
    103107   "seedIdleLimit"       | number     torrent-level number of minutes of seeding inactivity
    104108   "seedIdleMode"        | number     which seeding inactivity to use.  See tr_inactvelimit
    105109   "seedRatioLimit"      | double     torrent-level seeding ratio
     
    182186   pieceCount                  | number                      | tr_info
    183187   pieceSize                   | number                      | tr_info
    184188   priorities                  | array (see below)           | n/a
     189   queued                      | boolean                     | torrent-set
     190   queueRank                   | number                      | torrent-set
    185191   rateDownload (B/s)          | number                      | tr_stat
    186192   rateUpload (B/s)            | number                      | tr_stat
    187193   recheckProgress             | double                      | tr_stat
     
    428434   "peer-port"                      | number     | port number
    429435   "peer-port-random-on-start"      | boolean    | true means pick a random peer port on launch
    430436   "port-forwarding-enabled"        | boolean    | true means enabled
     437   "queue-enabled-download"         | boolean    | whether the queue is enabled for downloads
     438   "queue-enabled-seed"             | boolean    | whether the queue is enabled for seeds
     439   "queue-max-download-active"      | number     | maximum number of active downloading torrents, includes unqueued downloading torrents
     440   "queue-max-seed-active"          | number     | maximum number of active seeds total, including unqueued seeding torrents
     441   "queue-new-torrents-top"         | boolean    | queue newly added torrents to the top of the queue instead of the bottom
     442   "queue-skip-slow-enabled"        | boolean    | whether or not the queue should skip "slow" torrents when starting/stopping torrents
     443   "queue-slow-count"               | number     | number of minutes a torrent must be "slow" before being skipped
     444   "queue-slow-cutoff"              | number     | speed at which the queue considers torrents "slow" (KBps)
     445   "queue-speed-limit-enabled"      | boolean    | true means the queue will not start new torrents if the current speed limit is reached
    431446   "rename-partial-files"           | boolean    | true means append ".part" to incomplete files
    432447   "rpc-version"                    | number     | the current RPC API version
    433448   "rpc-version-minimum"            | number     | the minimum RPC API version supported
  • web/javascript/torrent.js

     
    4141Torrent._DynamicFields = [ 'downloadedEver', 'error', 'errorString', 'eta',
    4242    'haveUnchecked', 'haveValid', 'leftUntilDone', 'metadataPercentComplete', 'peers',
    4343    'peersConnected', 'peersGettingFromUs', 'peersSendingToUs', 'rateDownload', 'rateUpload',
    44     'recheckProgress', 'sizeWhenDone', 'status', 'trackerStats', 'desiredAvailable',
     44    'recheckProgress', 'sizeWhenDone', 'status', 'trackerStats', 'desiredAvailable', 'queueRank', 'queued',
    4545    'uploadedEver', 'uploadRatio', 'seedRatioLimit', 'seedRatioMode', 'downloadDir', 'isFinished' ]
    4646
    4747Torrent.prototype =
     
    249249        getPercentDoneStr: function() {
    250250                return Transmission.fmt.percentString( 100 * this.getPercentDone() );
    251251        },
     252        isQueued: function() { return this._queued ? true : false; },
     253        queueRank: function() { return this._queueRank; },
    252254        size: function() { return this._size; },
    253255        state: function() { return this._state; },
    254256        stateStr: function() {
    255257                switch( this.state() ) {
    256258                        case Torrent._StatusSeeding:        return 'Seeding';
    257259                        case Torrent._StatusDownloading:    return 'Downloading';
    258                         case Torrent._StatusPaused:         return this.isFinished() ? 'Seeding complete' : 'Paused';
     260                        case Torrent._StatusPaused: {
     261                                var queued = this.isQueued();
     262                                var leech = this._leftUntilDone > 0 || this.needsMetaData();
     263
     264                                if( queued )
     265                                        queued = leech
     266                                                ? this._controller._prefs['queue-enabled-download']
     267                                                : this._controller._prefs['queue-enabled-seed'];
     268
     269                                return queued
     270                                        ? ( leech ? 'Queued to download' : 'Queued to seed' )
     271                                        : ( this.isFinished() ? 'Seeding complete' : 'Paused' );
     272                        }
    259273                        case Torrent._StatusChecking:       return 'Verifying local data';
    260274                        case Torrent._StatusWaitingToCheck: return 'Waiting to verify';
    261275                        default:                            return 'error';
     
    400414                this._error                   = data.error;
    401415                this._error_string            = data.errorString;
    402416                this._eta                     = data.eta;
     417                this._queued                  = data.queued;
     418                this._queueRank               = data.queueRank;
    403419                this._trackerStats            = this.buildTrackerStats(data.trackerStats);
    404420                this._state                   = data.status;
    405421                this._download_dir            = data.downloadDir;
     
    737753};
    738754
    739755/** Helper function for sortTorrents(). */
     756Torrent.compareByQueue = function( a, b ) {
     757        return a.queueRank() - b.queueRank();
     758};
     759
     760/** Helper function for sortTorrents(). */
    740761Torrent.compareByAge = function( a, b ) {
    741762        return a.dateAdded() - b.dateAdded();
    742763};
     
    781802                        torrents.sort( this.compareByAge );
    782803                        break;
    783804                case Prefs._SortByQueue:
    784                         torrents.sort( this.compareById );
     805                        torrents.sort( this.compareByQueue );
    785806                        break;
    786807                case Prefs._SortByProgress:
    787808                        torrents.sort( this.compareByProgress );