Ticket #671: queue.2.patch

File queue.2.patch, 113.8 KB (added by Longinus00, 11 years ago)
  • daemon/remote.c

    diff --git daemon/remote.c daemon/remote.c
    index d0346ff..6803725 100644
    static tr_option opts[] = 
    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    { 964, "max-alt-download",       "Set the maximum alternate active downloading torrent(s)", "mad", 1, "<max>" },
     254    { 965, "max-alt-seed",           "Set the maximum alternate active seeding torrent(s)", "mas", 1, "<max>" },
     255    { 931, "max-download",           "Set the maximum active downloading torrent(s)", "md", 1, "<max>" },
     256    { 932, "max-seed",               "Set the maximum active seeding torrent(s)", "ms", 1, "<max>" },
     257    { 933, "skip-slow",              "Skip slow torrents in the queue", "ss", 0, NULL },
     258    { 934, "no-skip-slow",           "Don't skip slow torrents in the queue", "SS", 0, NULL },
     259    { 935, "cutoff-speed",           "Cutoff speed of slow torrents", "cs", 1, "<speed>" },
     260    { 936, "torrent-queue",          "Enable the torrent queue", "tq", 0, NULL },
     261    { 937, "no-torrent-queue",       "Disable the torrent queue", "TQ", 0, NULL },
     262    { 938, "enqueue-new",            "Enqueue new torrents", "en", 0, NULL },
     263    { 939, "no-enqueue-new",         "Don't enqueue new torrents", "EN", 0, NULL },
     264    { 955, "queue-new-top",          "New torrents start at the top of the queue", "qnt", 0, NULL },
     265    { 956, "queue-new-bottom",       "New torrents start at the bottom of the queue", "qnb", 0, NULL },
     266    { 957, "queue-speed-limit",      "Don't start new torrents if the spped limit is reached", "qsl", 0, NULL },
     267    { 958, "no-queue-speed-limit",   "Start new torrents even if the spped limit is reached", "QSL", 0, NULL },
     268    { 944, "queued",                 "Queue the torrent(s)", "q", 0, NULL },
     269    { 945, "no-queued",              "Remove the torrent(s) from the queue", "Q", 0, NULL },
     270    { 946, "queue-up",               "Move the torrent(s) up in the queue", "qu", 0, NULL },
     271    { 947, "queue-down",             "Move the torrent(s) down in the queue", "qd", 0, NULL },
     272    { 948, "queue-top",              "Move the torrent(s) to the top of the queue", "qt", 0, NULL },
     273    { 949, "queue-bottom",           "Move the torrent(s) to the bottom of the queue", "qb", 0, NULL },
    253274    { 960, "move",                   "Move current torrent's data to a new folder", NULL, 1, "<path>" },
    254275    { 961, "find",                   "Tell Transmission where to find a torrent's data", NULL, 1, "<path>" },
    255276    { 'm', "portmap",                "Enable portmapping via NAT-PMP or UPnP", "m",  0, NULL },
    getOptMode( int val ) 
    370391        case 'Y': /* no-lpd */
    371392        case 800: /* torrent-done-script */
    372393        case 801: /* no-torrent-done-script */
     394        case 964: /* max-alt-download */
     395        case 965: /* max-alt-seed */
     396        case 931: /* max-download */
     397        case 932: /* max-seed */
     398        case 933: /* skip-slow */
     399        case 934: /* no-skip-slow */
     400        case 935: /* cutoff-speed */
     401        case 936: /* torrent-queue */
     402        case 937: /* no-torrent-queue */
     403        case 938: /* enqueue-new */
     404        case 939: /* no-enqueue-new */
     405        case 955: /* queue-new-top */
     406        case 956: /* queue-new-bottom */
     407        case 957: /* queue-speed-limit */
     408        case 958: /* no-queue-speed-limit */
    373409        case 970: /* alt-speed */
    374410        case 971: /* no-alt-speed */
    375411        case 972: /* alt-speed-downlimit */
    getOptMode( int val ) 
    391427            return MODE_SESSION_SET;
    392428
    393429        case 712: /* tracker-remove */
     430        case 944: /* queued */
     431        case 945: /* no-queued */
     432        case 946: /* queue-up */
     433        case 947: /* queue-down */
     434        case 948: /* queue-top */
     435        case 949: /* queue-bottom */
    394436        case 950: /* seedratio */
    395437        case 951: /* seedratio-default */
    396438        case 952: /* no-seedratio */
    static const char * details_keys[] = { 
    655697    "peer-limit",
    656698    "pieceCount",
    657699    "pieceSize",
     700    "queued",
     701    "queueRank",
    658702    "rateDownload",
    659703    "rateUpload",
    660704    "recheckProgress",
    static const char * list_keys[] = { 
    681725    "name",
    682726    "peersGettingFromUs",
    683727    "peersSendingToUs",
     728    "queued",
     729    "queueRank",
    684730    "rateDownload",
    685731    "rateUpload",
    686732    "sizeWhenDone",
    printDetails( tr_benc * top ) 
    821867            printf( "\n" );
    822868
    823869            printf( "TRANSFER\n" );
     870            if( tr_bencDictFindBool( t, "queued", &boolVal ) )
     871                printf( "  Queued: %s\n", boolVal ? "Yes" : "No" );
     872            if( tr_bencDictFindInt( t, "queueRank", &i ) )
     873                printf( "  Queue rank: %" PRId64 "\n", i );
    824874            getStatusString( t, buf, sizeof( buf ) );
    825875            printf( "  State: %s\n", buf );
    826876
    printTorrentList( tr_benc * top ) 
    11911241      && ( tr_bencDictFindList( args, "torrents", &list ) ) )
    11921242    {
    11931243        int i, n;
    1194         int64_t total_size=0;
     1244        int64_t total_size=0, rank;
    11951245        double total_up=0, total_down=0;
    11961246        char haveStr[32];
    11971247
    1198         printf( "%-4s   %-4s  %9s  %-8s  %6s  %6s  %-5s  %-11s  %s\n",
    1199                 "ID", "Done", "Have", "ETA", "Up", "Down", "Ratio", "Status",
     1248        printf( "%6s  %4s  %-4s  %9s  %-8s  %6s  %6s  %-5s  %-11s  %s\n",
     1249                "Rank", "ID", "Done", "Have", "ETA", "Up", "Down", "Ratio", "Status",
    12001250                "Name" );
    12011251
    12021252        for( i = 0, n = tr_bencListSize( list ); i < n; ++i )
    printTorrentList( tr_benc * top ) 
    12071257            const char * name;
    12081258            tr_benc *   d = tr_bencListChild( list, i );
    12091259            if( tr_bencDictFindInt( d, "eta", &eta )
     1260              && tr_bencDictFindInt( d, "queueRank", &rank )
    12101261              && tr_bencDictFindInt( d, "id", &id )
    12111262              && tr_bencDictFindInt( d, "leftUntilDone", &leftUntilDone )
    12121263              && tr_bencDictFindStr( d, "name", &name )
    printTorrentList( tr_benc * top ) 
    12211272                char ratioStr[32];
    12221273                char doneStr[8];
    12231274                int64_t error;
     1275                tr_bool queued;
    12241276                char errorMark;
     1277                char queuedMark;
    12251278
    12261279                if( sizeWhenDone )
    12271280                    tr_snprintf( doneStr, sizeof( doneStr ), "%d%%", (int)( 100.0 * ( sizeWhenDone - leftUntilDone ) / sizeWhenDone ) );
    printTorrentList( tr_benc * top ) 
    12381291                    errorMark = '*';
    12391292                else
    12401293                    errorMark = ' ';
     1294                if( tr_bencDictFindBool( d, "queued", &queued ) && queued )
     1295                    queuedMark = '*';
     1296                else
     1297                    queuedMark = ' ';
    12411298                printf(
    1242                     "%4d%c  %4s  %9s  %-8s  %6.1f  %6.1f  %5s  %-11s  %s\n",
     1299                    "%6d%c %4d%c %4s  %9s  %-8s  %6.1f  %6.1f  %5s  %-11s  %s\n",
     1300                    (int)rank, queuedMark,
    12431301                    (int)id, errorMark,
    12441302                    doneStr,
    12451303                    haveStr,
    printTorrentList( tr_benc * top ) 
    12561314            }
    12571315        }
    12581316
    1259         printf( "Sum:         %9s            %6.1f  %6.1f\n",
     1317        printf( "Sum:                %9s            %6.1f  %6.1f\n",
    12601318                strlsize( haveStr, total_size, sizeof( haveStr ) ),
    12611319                total_up/(double)tr_speed_K,
    12621320                total_down/(double)tr_speed_K );
    printSession( tr_benc * top ) 
    15371595        }
    15381596        printf( "\n" );
    15391597
     1598        printf( "QUEUE\n" );
     1599        {
     1600            tr_bool enqueue, queue, skip, speed, top;
     1601            int64_t altDown, altSeed, maxDown, maxSeed, cutoff;
     1602            if( tr_bencDictFindInt ( args, TR_PREFS_KEY_QUEUE_ALT_DOWNLOAD_ACTIVE, &altDown ) &&
     1603                tr_bencDictFindInt ( args, TR_PREFS_KEY_QUEUE_ALT_SEED_ACTIVE, &altSeed ) &&
     1604                tr_bencDictFindBool( args, TR_PREFS_KEY_QUEUE_ENABLED, &queue ) &&
     1605                tr_bencDictFindBool( args, TR_PREFS_KEY_QUEUE_ENQUEUE_NEW_TORRENTS, &enqueue ) &&
     1606                tr_bencDictFindInt ( args, TR_PREFS_KEY_QUEUE_MAX_DOWNLOAD_ACTIVE, &maxDown ) &&
     1607                tr_bencDictFindInt ( args, TR_PREFS_KEY_QUEUE_MAX_SEED_ACTIVE, &maxSeed ) &&
     1608                tr_bencDictFindBool( args, TR_PREFS_KEY_QUEUE_NEW_TORRENTS_TOP, &top ) &&
     1609                tr_bencDictFindBool( args, TR_PREFS_KEY_QUEUE_SKIP_SLOW_TORRENTS, &skip ) &&
     1610                tr_bencDictFindInt ( args, TR_PREFS_KEY_QUEUE_SLOW_CUTOFF_KBps, &cutoff ) &&
     1611                tr_bencDictFindBool( args, TR_PREFS_KEY_QUEUE_SPEED_LIMIT, &speed ) )
     1612            {
     1613                char buf[128];
     1614                printf( "  Queue: %s\n", queue ? "Enabled" : "Disabled" );
     1615                printf( "  Download ( limit: %" PRId64" turtle limit: %" PRId64" )\n",
     1616                        maxDown,
     1617                        altDown );
     1618                printf( "  Seed ( limit: %" PRId64" turtle limit: %" PRId64" )\n",
     1619                        maxSeed,
     1620                        altSeed);
     1621
     1622                tr_formatter_speed_KBps( buf, cutoff, sizeof( buf ) );
     1623                printf( "  Skip slow torrents: %s (Slow torrent cutoff: %s)\n",
     1624                        skip ? "Yes" : "No",
     1625                        buf );
     1626
     1627                printf( "  Start new torrents if speed limit is reached: %s\n", speed ? "Yes" : "No" );
     1628                printf( "  Queue new torrents at the: %s\n", top ? "Top" : "Bottom" );
     1629                printf( "  Enqueue new torrents: %s\n", enqueue ? "Yes" : "No" );
     1630
     1631            }
     1632        }
     1633        printf( "\n" );
     1634
    15401635        printf( "MISC\n" );
    15411636        if( tr_bencDictFindBool( args, TR_PREFS_KEY_START, &boolVal ) )
    15421637            printf( "  Autostart added torrents: %s\n", ( boolVal ? "Yes" : "No" ) );
    processArgs( const char * host, int port, int argc, const char ** argv ) 
    19142009                          break;
    19152010                case 801: tr_bencDictAddBool( args, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_ENABLED, FALSE );
    19162011                          break;
     2012                case 964: tr_bencDictAddInt( args, TR_PREFS_KEY_QUEUE_ALT_DOWNLOAD_ACTIVE, numarg( optarg ) );
     2013                          break;
     2014                case 965: tr_bencDictAddInt( args, TR_PREFS_KEY_QUEUE_ALT_SEED_ACTIVE, numarg( optarg ) );
     2015                          break;
     2016                case 931: tr_bencDictAddInt( args, TR_PREFS_KEY_QUEUE_MAX_DOWNLOAD_ACTIVE, numarg( optarg ) );
     2017                          break;
     2018                case 932: tr_bencDictAddInt( args, TR_PREFS_KEY_QUEUE_MAX_SEED_ACTIVE, numarg( optarg ) );
     2019                          break;
     2020                case 933: tr_bencDictAddBool( args, TR_PREFS_KEY_QUEUE_SKIP_SLOW_TORRENTS, TRUE );
     2021                          break;
     2022                case 934: tr_bencDictAddBool( args, TR_PREFS_KEY_QUEUE_SKIP_SLOW_TORRENTS, FALSE );
     2023                          break;
     2024                case 935: tr_bencDictAddInt( args, TR_PREFS_KEY_QUEUE_SLOW_CUTOFF_KBps, numarg( optarg ) );
     2025                          break;
     2026                case 936: tr_bencDictAddBool( args, TR_PREFS_KEY_QUEUE_ENABLED, TRUE );
     2027                          break;
     2028                case 937: tr_bencDictAddBool( args, TR_PREFS_KEY_QUEUE_ENABLED, FALSE );
     2029                          break;
     2030                case 938: tr_bencDictAddBool( args, TR_PREFS_KEY_QUEUE_ENQUEUE_NEW_TORRENTS, TRUE );
     2031                          break;
     2032                case 939: tr_bencDictAddBool( args, TR_PREFS_KEY_QUEUE_ENQUEUE_NEW_TORRENTS, FALSE );
     2033                          break;
     2034                case 955: tr_bencDictAddBool( args, TR_PREFS_KEY_QUEUE_NEW_TORRENTS_TOP, TRUE );
     2035                          break;
     2036                case 956: tr_bencDictAddBool( args, TR_PREFS_KEY_QUEUE_NEW_TORRENTS_TOP, FALSE );
     2037                          break;
     2038                case 957: tr_bencDictAddBool( args, TR_PREFS_KEY_QUEUE_SPEED_LIMIT, TRUE );
     2039                          break;
     2040                case 958: tr_bencDictAddBool( args, TR_PREFS_KEY_QUEUE_SPEED_LIMIT, FALSE );
     2041                          break;
    19172042                case 970: tr_bencDictAddBool( args, TR_PREFS_KEY_ALT_SPEED_ENABLED, TRUE );
    19182043                          break;
    19192044                case 971: tr_bencDictAddBool( args, TR_PREFS_KEY_ALT_SPEED_ENABLED, FALSE );
    processArgs( const char * host, int port, int argc, const char ** argv ) 
    20412166                        tr_bencListAddInt( trackers, atoi(optarg) );
    20422167                        break;
    20432168                    }
     2169                case 944: tr_bencDictAddBool( args, "queued", TRUE );
     2170                          break;
     2171                case 945: tr_bencDictAddBool( args, "queued", FALSE );
     2172                          break;
     2173                case 946: tr_bencDictAddInt( args, "moveQueueRank", TR_QUEUE_UP );
     2174                          break;
     2175                case 947: tr_bencDictAddInt( args, "moveQueueRank", TR_QUEUE_DOWN );
     2176                          break;
     2177                case 948: tr_bencDictAddInt( args, "moveQueueRank", TR_QUEUE_TOP );
     2178                          break;
     2179                case 949: tr_bencDictAddInt( args, "moveQueueRank", TR_QUEUE_BOTTOM );
     2180                          break;
    20442181                case 950: tr_bencDictAddReal( args, "seedRatioLimit", atof(optarg) );
    20452182                          tr_bencDictAddInt( args, "seedRatioMode", TR_RATIOLIMIT_SINGLE );
    20462183                          break;
  • daemon/transmission-remote.1

    diff --git daemon/transmission-remote.1 daemon/transmission-remote.1
    index ed6fdbd..34ea5a0 100644
    and 
    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
     26.Op Fl en | EN
    2527.Op Fl er | ep | et
    2628.Op Fl f
    2729.Op Fl g Ar files
    and 
    3234.Op Fl i
    3335.Op Fl l
    3436.Op Fl m | M
     37.Op Fl mad Ar max
     38.Op Fl mas Ar max
     39.Op Fl md Ar max
     40.Op Fl ms Ar max
    3541.Op Fl n Ar user:pass
    3642.Op Fl N Ar netrc
    3743.Op Fl o | O
    3844.Op Fl p Ar port
     45.Op Fl q | Q
     46.Op Fl qu | qd | qt | qb
     47.Op Fl qnt | qnb
     48.Op Fl qsl | QSL
    3949.Op Fl Bh
    4050.Op Fl Bn
    4151.Op Fl \&Bl
    and 
    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
     66.Op Fl tq | TQ
    5567.Op Fl hl
    5668.Op Fl HL
    5769.Op Fl u Ar number | Fl U
    When adding new torrents, store their contents in 
    117129until the torrent is done.
    118130.It Fl C Fl -no-incomplete-dir
    119131Don't store incomplete torrents in a different directory.
     132.It Fl cs Fl -cutoff-speed Ar speed
     133Cutoff speed of slow torrents.
    120134.It Fl d Fl -downlimit Ar limit
    121135Limit the maximum download speed to
    122136.Ar limit
    Disable download speed limits. 
    127141If current torrent(s) are selected this operates on them.  Otherwise, it changes the global setting.
    128142.It Fl e Fl -cache Ar size
    129143Set the session's maximum memory cache size in MiB.  This cache is used to reduce disk IO.
     144.It Fl en Fl -enqueue-new
     145Enqueue new torrents
     146.It Fl EN Fl -no-ignore-new
     147Don't enqueue new torrents
    130148.It Fl er Fl -encryption-required
    131149Encrypt all peer connections.
    132150.It Fl ep Fl -encryption-preferred
    List all torrents 
    165183Enable portmapping via NAT-PMP or UPnP
    166184.It Fl M Fl -no-portmap
    167185Disable portmapping
     186.It Fl mad Fl -max-alt-download Ar max
     187Set the maximum alternate active downloading torrent(s).
     188.It Fl mas Fl -max-alt-seed Ar max
     189Set the maximum alternate active seeding torrent(s).
     190.It Fl md Fl -max-download Ar max
     191Set the maximum active downloading torrent(s).
     192.It Fl ms Fl -max-seed Ar max
     193Set the maximum active seeding torrent(s).
    168194.It Fl n Fl -auth Ar username:password
    169195Set the
    170196.Ar username
    Try to download the specified files last 
    205231.It Fl pr Fl -peers Ar number
    206232Set the maximum number of peers.
    207233If current torrent(s) are selected this operates on them.  Otherwise, it changes the global setting.
     234.It Fl q Fl -queued
     235Queue the torrents(s)
     236.It Fl Q Fl -no-queued
     237Remove the torrent(s) from the queue
     238.It Fl qu Fl -queue-up
     239Move the torrent(s) up in the queue.
     240.It Fl qd Fl -queue-down
     241Move the torrent(s) down in the queue.
     242.It Fl qt Fl -queue-top
     243Move the torrent(s) to the top of the queue.
     244.It Fl qb Fl -queue-bottom
     245Move the torrent(s) to the bottom of the queue.
     246.It Fl qnt Fl -queue-new-top
     247New torrents start at the top of the queue.
     248.It Fl qnb Fl -queue-new-bottom
     249New torrents start at the bottom of the queue.
     250.It Fl qsl Fl -queue-speed-limit
     251Don't start new torrents if the spped limit is reached.
     252.It Fl QSL Fl -no-queue-speed-limit
     253Start new torrents even if the spped limit is reached.
    208254.It Fl r Fl -remove
    209255Remove the current torrent(s).  This does not delete the downloaded data.
    210256.It Fl -remove-and-delete
    Let the current torrent(s) seed until a specific 
    220266Let the current torrent(s) seed regardless of ratio
    221267.It Fl srd Fl -seedratio-default
    222268Let the current torrent(s) use the global seedratio settings
     269.It Fl ss Fl -skip-slow
     270Skip slow torrents in the queue
     271.It Fl SS Fl -no-skip-slow
     272Don't skip slow torrents in the queue
    223273.It Fl ta Fl -tracker-add Ar tracker
    224274Add a tracker to a torrent
    225275.It Fl ta Fl -tracker-remove Ar trackerId
    226276Remove a tracker from a torrent
     277.It Fl tq Fl -torrent-queue
     278Enable the torrent queue
     279.It Fl TQ Fl -no-torrent-queue
     280Disable the torrent queue
    227281.It Fl s Fl -start
    228282Start the current torrent(s)
    229283.It Fl S Fl -stop
  • extras/rpc-spec.txt

    diff --git extras/rpc-spec.txt extras/rpc-spec.txt
    index 418a729..c5c5677 100644
     
    9696   "honorsSessionLimits" | boolean    true if session upload limits are honored
    9797   "ids"                 | array      torrent list, as described in 3.1
    9898   "location"            | string     new location of the torrent's content
     99   "moveQueueRank"       | number     move torrent in queue. See tr_queue_direction
    99100   "peer-limit"          | number     maximum number of peers
    100101   "priority-high"       | array      indices of high-priority file(s)
    101102   "priority-low"        | array      indices of low-priority file(s)
    102103   "priority-normal"     | array      indices of normal-priority file(s)
     104   "queued"              | boolean    true if the queue should handle this torrent
     105   "queueRank"           | number     directly set a torrent's queue rank
    103106   "seedIdleLimit"       | number     torrent-level number of minutes of seeding inactivity
    104107   "seedIdleMode"        | number     which seeding inactivity to use.  See tr_inactvelimit
    105108   "seedRatioLimit"      | double     torrent-level seeding ratio
     
    182185   pieceCount                  | number                      | tr_info
    183186   pieceSize                   | number                      | tr_info
    184187   priorities                  | array (see below)           | n/a
     188   queued                      | boolean                     | torrent-set
     189   queueRank                   | number                      | torrent-set
    185190   rateDownload (B/s)          | number                      | tr_stat
    186191   rateUpload (B/s)            | number                      | tr_stat
    187192   recheckProgress             | double                      | tr_stat
     
    427432   "peer-port"                      | number     | port number
    428433   "peer-port-random-on-start"      | boolean    | true means pick a random peer port on launch
    429434   "port-forwarding-enabled"        | boolean    | true means enabled
     435   "queue-enabled"                  | boolean    | whether the queue is enabled
     436   "queue-enqueue-new-torrents"     | boolean    | if true then newly added torrents start queued
     437   "queue-alt-download-active"      | number     | alternate maximum number of active downloading torrents, includes unqueued downloading torrents
     438   "queue-alt-seed-active"          | number     | alternate maximum number of active seeds total, including unqueued seeding torrents
     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-cutoff"              | number     | speed at which the queue considers torrents "slow" (KBps)
     444   "queue-speed-limit-enabled"      | boolean    | true means the queue will not start new torrents if the current speed limit is reached
    430445   "rename-partial-files"           | boolean    | true means append ".part" to incomplete files
    431446   "rpc-version"                    | number     | the current RPC API version
    432447   "rpc-version-minimum"            | number     | the minimum RPC API version supported
     
    636651         |         | yes       | session-get    | new arg "units"
    637652         |         | yes       | torrent-set    | new arg "seedIdleLimit"
    638653         |         | yes       | torrent-set    | new arg "seedIdleMode"
     654         |         | yes       | torrent-get    | new arg "queued"
     655         |         | yes       | torrent-get    | new arg "queueRank"
     656         |         | yes       | torrent-set    | new arg "moveQueueRank"
     657         |         | yes       | torrent-set    | new arg "queued"
     658         |         | yes       | torrent-set    | new arg "queueRank"
     659         |         | yes       | session-get    | new arg "queue-enabled"
     660         |         | yes       | session-get    | new arg "queue-enqueue-new-torrents"
     661         |         | yes       | session-get    | new arg "queue-max-download-active"
     662         |         | yes       | session-get    | new arg "queue-max-seed-active"
     663         |         | yes       | session-get    | new arg "queue-new-torrents-top"
     664         |         | yes       | session-get    | new arg "queue-skip-slow-enabled"
     665         |         | yes       | session-get    | new arg "queue-slow-cutoff"
     666         |         | yes       | session-get    | new arg "queue-slow-speed-limit-enabled"
     667         |         | yes       | session-set    | new arg "queue-enabled"
     668         |         | yes       | session-set    | new arg "queue-enqueue-new-torrents"
     669         |         | yes       | session-set    | new arg "queue-max-download-active"
     670         |         | yes       | session-set    | new arg "queue-max-seed-active"
     671         |         | yes       | session-set    | new arg "queue-new-torrents-top"
     672         |         | yes       | session-set    | new arg "queue-skip-slow-enabled"
     673         |         | yes       | session-set    | new arg "queue-slow-cutoff"
     674         |         | yes       | session-set    | new arg "queue-slow-speed-limit-enabled"
  • gtk/actions.c

    diff --git gtk/actions.c gtk/actions.c
    index 7a11b5c..dd87e29 100644
    static GtkRadioActionEntry sort_radio_entries[] = 
    5757    { "sort-by-progress",  NULL, N_( "Sort by _Progress" ),  NULL, NULL, 2 },
    5858    { "sort-by-ratio",     NULL, N_( "Sort by Rati_o" ),     NULL, NULL, 3 },
    5959    { "sort-by-state",     NULL, N_( "Sort by Stat_e" ),     NULL, NULL, 4 },
    60     { "sort-by-age",       NULL, N_( "Sort by A_ge" ),       NULL, NULL, 5 },
    61     { "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 }
     60    { "sort-by-age",       NULL, N_( "Sort by A_ge" ),       NULL, NULL, 6 },
     61    { "sort-by-time-left", NULL, N_( "Sort by Time _Left" ), NULL, NULL, 7 },
     62    { "sort-by-size",      NULL, N_( "Sort by Si_ze" ),      NULL, NULL, 8 },
     63    { "sort-by-queue",     NULL, N_( "Sort by _Queue" ),     NULL, NULL, 9 }
    6364};
    6465
    6566static void
    static GtkToggleActionEntry pref_toggle_entries[] = 
    9495{
    9596    { "alt-speed-enabled", NULL, N_( "Enable Temporary Speed _Limits" ), NULL, NULL, G_CALLBACK( toggle_pref_cb ), FALSE },
    9697    { "compact-view",      NULL, N_( "_Compact View" ), "<alt>C", NULL, G_CALLBACK( toggle_pref_cb ), FALSE },
     98    { "queued",            NULL, N_( "_Queued" ), NULL, NULL, G_CALLBACK( action_cb ), FALSE },
    9799    { "sort-reversed",     NULL, N_( "Re_verse Sort Order" ), NULL, NULL, G_CALLBACK( toggle_pref_cb ), FALSE },
    98100    { "show-filterbar",    NULL, N_( "_Filterbar" ), NULL, NULL, G_CALLBACK( toggle_pref_cb ), FALSE },
    99101    { "show-statusbar",    NULL, N_( "_Statusbar" ), NULL, NULL, G_CALLBACK( toggle_pref_cb ), FALSE },
    static GtkActionEntry entries[] = 
    121123    { "start-all-torrents", GTK_STOCK_MEDIA_PLAY, N_( "_Start All" ), NULL, N_( "Start all torrents" ), G_CALLBACK( action_cb ) },
    122124    { "relocate-torrent", NULL, N_("Set _Location..." ), NULL, NULL, G_CALLBACK( action_cb ) },
    123125    { "remove-torrent", GTK_STOCK_REMOVE, NULL, "Delete", N_( "Remove torrent" ), G_CALLBACK( action_cb ) },
     126    { "queue-up", GTK_STOCK_GO_UP, NULL, NULL, N_( "Move torrent up queue" ), G_CALLBACK( action_cb ) },
     127    { "queue-down", GTK_STOCK_GO_DOWN, NULL, NULL, N_( "Move torrent down queue " ), G_CALLBACK( action_cb ) },
     128    { "queue-top", GTK_STOCK_GOTO_TOP, NULL, NULL, N_( "Move torrent to of top queue" ), G_CALLBACK( action_cb ) },
     129    { "queue-bottom", GTK_STOCK_GOTO_BOTTOM, NULL, NULL, N_( "Move torrent to of bottom queue" ), G_CALLBACK( action_cb ) },
    124130    { "delete-torrent", GTK_STOCK_DELETE, N_( "_Delete Files and Remove" ), "<shift>Delete", NULL, G_CALLBACK( action_cb ) },
    125131    { "new-torrent", GTK_STOCK_NEW, N_( "_New..." ), NULL, N_( "Create a torrent" ), G_CALLBACK( action_cb ) },
    126132    { "quit", GTK_STOCK_QUIT, N_( "_Quit" ), NULL, NULL, G_CALLBACK( action_cb ) },
    action_toggle( const char * name, 
    328334    gtk_toggle_action_set_active( GTK_TOGGLE_ACTION( action ), b );
    329335}
    330336
     337gboolean
     338action_get_toggle( const char * name )
     339{
     340    GtkAction * action = get_action( name );
     341
     342    return gtk_toggle_action_get_active( GTK_TOGGLE_ACTION( action ) );
     343}
     344
    331345GtkWidget*
    332346action_get_widget( const char * path )
    333347{
  • gtk/actions.h

    diff --git gtk/actions.h gtk/actions.h
    index 3d41906..324bfbc 100644
    void action_sensitize( const char * name, 
    3434void       action_toggle( const char * name,
    3535                          gboolean     b );
    3636
     37gboolean   action_get_toggle( const char * name );
     38
    3739void       action_set_important( const char * name, gboolean b );
    3840
    3941
  • gtk/details.c

    diff --git gtk/details.c gtk/details.c
    index 105cf78..d39478f 100644
    struct DetailsImpl 
    3434{
    3535    GtkWidget * dialog;
    3636
     37    GtkWidget * queued_check;
    3738    GtkWidget * honor_limits_check;
    3839    GtkWidget * up_limited_check;
    3940    GtkWidget * up_limit_sping;
    struct DetailsImpl 
    4748    GtkWidget * idle_spin;
    4849    GtkWidget * max_peers_spin;
    4950
     51    guint queued_check_tag;
    5052    guint honor_limits_check_tag;
    5153    guint up_limited_check_tag;
    5254    guint down_limited_check_tag;
    refreshOptions( struct DetailsImpl * di, tr_torrent ** torrents, int n ) 
    308310                                   di->idle_spin_tag, baseline );
    309311    }
    310312
     313    /* queued_check  */
     314    if( n ) {
     315        const tr_bool baseline = tr_torrentIsQueued( torrents[0] );
     316        int i;
     317        for( i=1; i<n; ++i )
     318            if( baseline != tr_torrentIsQueued( torrents[i] ) )
     319                break;
     320        if( i == n )
     321            set_togglebutton_if_different( di->queued_check,
     322                                           di->queued_check_tag, baseline );
     323    }
     324
    311325    /* max_peers_spin */
    312326    if( n ) {
    313327        const int baseline = tr_torrentGetPeerLimit( torrents[0] );
    global_speed_toggled_cb( GtkToggleButton * tb, gpointer d ) 
    389403}
    390404
    391405static void
     406queued_toggled_cb( GtkToggleButton * tb, gpointer d )
     407{
     408    torrent_set_bool( d, "queued", gtk_toggle_button_get_active( tb ) );
     409}
     410
     411static void
    392412up_speed_spun_cb( GtkSpinButton * s, struct DetailsImpl * di )
    393413{
    394414    torrent_set_int( di, "uploadLimit", gtk_spin_button_get_value_as_int( s ) );
    options_page_new( struct DetailsImpl * d ) 
    536556    hig_workarea_add_row( t, &row, _( "_Idle:" ), h, NULL );
    537557
    538558    hig_workarea_add_section_divider( t, &row );
     559    hig_workarea_add_section_title( t, &row, _( "Queue" ) );
     560
     561    tb = hig_workarea_add_wide_checkbutton( t, &row, _( "_Queued" ), 0 );
     562    d->queued_check = tb;
     563    tag = g_signal_connect( tb, "toggled", G_CALLBACK( queued_toggled_cb ), d );
     564    d->queued_check_tag = tag;
     565
     566    hig_workarea_add_section_divider( t, &row );
    539567    hig_workarea_add_section_title( t, &row, _( "Peer Connections" ) );
    540568
    541569    w = gtk_spin_button_new_with_range( 1, 3000, 5 );
    options_page_new( struct DetailsImpl * d ) 
    555583****/
    556584
    557585static const char *
    558 activityString( int activity, tr_bool finished )
     586activityString( int activity, tr_bool finished, tr_bool queued )
    559587{
    560588    switch( activity )
    561589    {
    activityString( int activity, tr_bool finished ) 
    563591        case TR_STATUS_CHECK:      return _( "Verifying local data" );
    564592        case TR_STATUS_DOWNLOAD:   return _( "Downloading" );
    565593        case TR_STATUS_SEED:       return _( "Seeding" );
    566         case TR_STATUS_STOPPED:    return finished ? _( "Finished" ) : _( "Paused" );
     594        case TR_STATUS_STOPPED:    return queued ? _( "Queued" ) : ( finished ? _( "Finished" ) : _( "Paused" ) );
    567595    }
    568596
    569597    return "";
    refreshInfo( struct DetailsImpl * di, tr_torrent ** torrents, int n ) 
    711739        str = no_torrent;
    712740    else {
    713741        const tr_torrent_activity activity = stats[0]->activity;
     742        const tr_session * session = tr_core_session( di->core );
    714743        tr_bool allFinished = stats[0]->finished;
     744        tr_bool allQueued = tr_sessionIsQueueEnabled( session ) && !tr_torrentIsQueued( torrents[0] );
    715745        for( i=1; i<n; ++i ) {
    716746            if( activity != stats[i]->activity )
    717747                break;
    718748            if( !stats[i]->finished )
    719749                allFinished = FALSE;
    720750        }
    721         str = i<n ? mixed : activityString( activity, allFinished );
     751        for( i=1; i<n; ++i ) {
     752            if( !allQueued )
     753                break;
     754            if( allQueued != !tr_torrentIsQueued( torrents[i] ) )
     755                allQueued = FALSE;
     756        }
     757        str = i<n ? mixed : activityString( activity, allFinished, allQueued );
    722758    }
    723759    stateString = str;
    724760    gtr_label_set_text( GTK_LABEL( di->state_lb ), str );
  • gtk/filter.c

    diff --git gtk/filter.c gtk/filter.c
    index 77203eb..4dfd865 100644
    test_torrent_activity( tr_torrent * tor, int type ) 
    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/main.c

    diff --git gtk/main.c gtk/main.c
    index 155d2a4..9bd17bc 100644
    struct counts_data 
    263263    int    totalCount;
    264264    int    activeCount;
    265265    int    inactiveCount;
     266    int    queuedCount;
    266267};
    267268
    268269static void
    accumulateStatusForeach( GtkTreeModel * model, 
    273274{
    274275    int activity = 0;
    275276    struct counts_data * counts = user_data;
     277    tr_torrent * tor;
    276278
    277279    ++counts->totalCount;
    278280
    279281    gtk_tree_model_get( model, iter, MC_ACTIVITY, &activity, -1 );
     282    gtk_tree_model_get( model, iter, MC_TORRENT_RAW, &tor, -1 );
    280283
    281284    if( activity == TR_STATUS_STOPPED )
    282285        ++counts->inactiveCount;
    283286    else
    284287        ++counts->activeCount;
     288
     289    counts->queuedCount += tr_torrentIsQueued( tor ) ? 1 : -1;
    285290}
    286291
    287292static void
    getTorrentCounts( struct cbdata * data, struct counts_data * counts ) 
    290295    counts->activeCount = 0;
    291296    counts->inactiveCount = 0;
    292297    counts->totalCount = 0;
     298    counts->queuedCount = 0;
    293299
    294300    gtk_tree_selection_selected_foreach( data->sel, accumulateStatusForeach, counts );
    295301}
    refreshActions( gpointer gdata ) 
    311317    int canUpdate;
    312318    struct counts_data counts;
    313319    struct cbdata * data = gdata;
     320    const char * sort_mode = pref_string_get( PREF_KEY_SORT_MODE );
     321    gboolean sort_queue = !strcmp( sort_mode, "sort-by-queue" );
    314322
    315323    getTorrentCounts( data, &counts );
     324    sort_queue = sort_queue && ( counts.totalCount != 0 );
    316325    action_sensitize( "pause-torrent", counts.activeCount != 0 );
    317326    action_sensitize( "start-torrent", counts.inactiveCount != 0 );
    318327    action_sensitize( "remove-torrent", counts.totalCount != 0 );
    319328    action_sensitize( "delete-torrent", counts.totalCount != 0 );
     329    action_sensitize( "queue-up", sort_queue );
     330    action_sensitize( "queue-down", sort_queue );
     331    action_sensitize( "queue-top", sort_queue );
     332    action_sensitize( "queue-bottom", sort_queue );
     333    action_sensitize( "queued", counts.totalCount != 0 );
    320334    action_sensitize( "verify-torrent", counts.totalCount != 0 );
    321335    action_sensitize( "relocate-torrent", counts.totalCount != 0 );
    322336    action_sensitize( "show-torrent-properties", counts.totalCount != 0 );
    323337    action_sensitize( "open-torrent-folder", counts.totalCount == 1 );
    324338    action_sensitize( "copy-magnet-link-to-clipboard", counts.totalCount == 1 );
    325339
     340    if( abs( counts.queuedCount ) == counts.totalCount )
     341        action_toggle( "queued", counts.queuedCount > 0 ? TRUE : FALSE );
     342    else
     343        action_toggle( "queued", FALSE );
     344
    326345    canUpdate = 0;
    327346    gtk_tree_selection_selected_foreach( data->sel, accumulateCanUpdateForeach, &canUpdate );
    328347    action_sensitize( "update-tracker", canUpdate != 0 );
    prefschanged( TrCore * core UNUSED, const char * key, gpointer data ) 
    13651384    {
    13661385        tr_sessionSetDeleteSource( tr, pref_flag_get( key ) );
    13671386    }
     1387    else if( !strcmp( key, TR_PREFS_KEY_QUEUE_ALT_DOWNLOAD_ACTIVE ) )
     1388    {
     1389        tr_sessionSetQueueAltDownloadActive( tr, pref_int_get( key ) );
     1390    }
     1391    else if( !strcmp( key, TR_PREFS_KEY_QUEUE_ALT_SEED_ACTIVE ) )
     1392    {
     1393        tr_sessionSetQueueAltSeedActive( tr, pref_int_get( key ) );
     1394    }
     1395    else if( !strcmp( key, TR_PREFS_KEY_QUEUE_ENABLED ) )
     1396    {
     1397        tr_sessionSetQueueEnabled( tr, pref_flag_get( key ) );
     1398    }
     1399    else if( !strcmp( key, TR_PREFS_KEY_QUEUE_ENQUEUE_NEW_TORRENTS ) )
     1400    {
     1401        tr_sessionSetQueueEnqueueNewTorrents( tr, pref_flag_get( key ) );
     1402    }
     1403    else if( !strcmp( key, TR_PREFS_KEY_QUEUE_MAX_DOWNLOAD_ACTIVE ) )
     1404    {
     1405        tr_sessionSetQueueMaxDownloadActive( tr, pref_int_get( key ) );
     1406    }
     1407    else if( !strcmp( key, TR_PREFS_KEY_QUEUE_MAX_SEED_ACTIVE ) )
     1408    {
     1409        tr_sessionSetQueueMaxSeedActive( tr, pref_int_get( key ) );
     1410    }
     1411    else if( !strcmp( key, TR_PREFS_KEY_QUEUE_NEW_TORRENTS_TOP ) )
     1412    {
     1413        tr_sessionSetQueueNewTorrentsTop( tr, pref_flag_get( key ) );
     1414    }
     1415    else if( !strcmp( key, TR_PREFS_KEY_QUEUE_SKIP_SLOW_TORRENTS ) )
     1416    {
     1417        tr_sessionSetQueueSkipSlowTorrentsEnabled( tr, pref_flag_get( key ) );
     1418    }
     1419    else if( !strcmp( key, TR_PREFS_KEY_QUEUE_SLOW_CUTOFF_KBps ) )
     1420    {
     1421        tr_sessionSetQueueSlowCutoff( tr, pref_int_get( key ) );
     1422    }
     1423    else if( !strcmp( key, TR_PREFS_KEY_QUEUE_SPEED_LIMIT ) )
     1424    {
     1425        tr_sessionSetQueueSpeedLimitEnabled( tr, pref_flag_get( key ) );
     1426    }
    13681427}
    13691428
    13701429static gboolean
    removeSelected( struct cbdata * data, gboolean delete_files ) 
    15241583}
    15251584
    15261585static void
     1586accumulateSelectedTorrentsRaw( GtkTreeModel *      model,
     1587                               GtkTreePath  * path UNUSED,
     1588                               GtkTreeIter *       iter,
     1589                               gpointer            gdata )
     1590{
     1591    GSList ** data = ( GSList** ) gdata;
     1592    tr_torrent * tor = NULL;
     1593
     1594    gtk_tree_model_get( model, iter, MC_TORRENT_RAW, &tor, -1 );
     1595    *data = g_slist_prepend( *data, tor );
     1596}
     1597
     1598static int
     1599compareTorrentByQueueRank( const void * a, const void * b )
     1600{
     1601    return tr_sessionCompareTorrentByQueueRank( &a, &b );
     1602}
     1603
     1604static gboolean
     1605moveTorrentQueue( struct cbdata      * data,
     1606                  tr_queue_direction   dir )
     1607{
     1608    gboolean changed = FALSE;
     1609    tr_session * session = tr_core_session( data->core );
     1610    GtkTreeSelection * s = tr_window_get_selection( data->wind );
     1611    GSList * l = NULL;
     1612
     1613    gtk_tree_selection_selected_foreach( s, accumulateSelectedTorrentsRaw, &l );
     1614    l = g_slist_sort( l, compareTorrentByQueueRank );
     1615
     1616    switch( dir )
     1617    {
     1618        case TR_QUEUE_DOWN:
     1619            if( tr_torrentGetQueueRank( g_slist_last( l )->data ) == tr_sessionCountTorrents( session ) )
     1620                break;
     1621        case TR_QUEUE_TOP:
     1622            l = g_slist_reverse( l );
     1623            changed = TRUE;
     1624            break;
     1625        case TR_QUEUE_UP:
     1626            if( tr_torrentGetQueueRank( l->data ) == 1 )
     1627                break;
     1628        case TR_QUEUE_BOTTOM:
     1629            changed = TRUE;
     1630            break;
     1631    }
     1632
     1633    if( changed )
     1634    {
     1635        int json_len;
     1636        char * json;
     1637        tr_benc top, *args, *ids;
     1638        GSList * le = l;
     1639
     1640        tr_bencInitDict( &top, 2 );
     1641        tr_bencDictAddStr( &top, "method", "torrent-set" );
     1642        args = tr_bencDictAddDict( &top, "arguments", 1 );
     1643        tr_bencDictAddInt( args, "moveQueueRank", (int)dir );
     1644        ids = tr_bencDictAddList( args, "ids", 0 );
     1645
     1646        do
     1647        {
     1648            const tr_torrent * tor = ( tr_torrent* )le->data;
     1649            tr_bencListAddInt( ids, tr_torrentId( tor ) );
     1650        }
     1651        while(( le = g_slist_next( le ) ));
     1652
     1653        json = tr_bencToStr( &top, TR_FMT_JSON_LEAN, &json_len );
     1654        tr_rpc_request_exec_json( session, json, json_len, NULL, NULL );
     1655
     1656        g_free( json );
     1657        tr_bencFree( &top );
     1658    }
     1659
     1660    g_slist_free( l );
     1661    return changed;
     1662}
     1663
     1664static void
    15271665startAllTorrents( struct cbdata * data )
    15281666{
    15291667    tr_session * session = tr_core_session( data->core );
    doAction( const char * action_name, gpointer user_data ) 
    16951833    {
    16961834        removeSelected( data, TRUE );
    16971835    }
     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    }
     1852    else if( !strcmp( action_name, "queued" ) )
     1853    {
     1854        const gboolean queued = action_get_toggle( "queued" );
     1855        GtkTreeSelection * s = tr_window_get_selection( data->wind );
     1856        GSList * l = NULL;
     1857
     1858        gtk_tree_selection_selected_foreach( s, accumulateSelectedTorrentsRaw, &l );
     1859        if( l )
     1860        {
     1861            GSList * le = l;
     1862
     1863            do
     1864            {
     1865                tr_torrent * tor = ( tr_torrent* )le->data;
     1866                tr_torrentSetQueued( tor, queued );
     1867            }
     1868            while(( le = g_slist_next( le ) ));
     1869
     1870            g_slist_free( l );
     1871            changed = TRUE;
     1872        }
     1873    }
    16981874    else if( !strcmp( action_name, "quit" ) )
    16991875    {
    17001876        maybeaskquit( data );
  • gtk/tr-core.c

    diff --git gtk/tr-core.c gtk/tr-core.c
    index 76dabb2..1727591 100644
    compareByETA( GtkTreeModel * model, 
    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,
    setSort( TrCore * core, 
    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" ) )
  • gtk/tr-prefs.c

    diff --git gtk/tr-prefs.c gtk/tr-prefs.c
    index 8f690ab..640d776 100644
    torrentPage( GObject * core ) 
    340340}
    341341
    342342/****
     343*****  Queue Tab
     344****/
     345
     346static GtkWidget*
     347queuePage( GObject * core )
     348{
     349    int          row = 0;
     350    const char * s;
     351    GtkWidget *  t;
     352    GtkWidget *  w;
     353    GtkWidget *  w2;
     354    GtkWidget *  l;
     355    char buf[512];
     356
     357    t = hig_workarea_create( );
     358
     359    hig_workarea_add_section_title( t, &row, _( "Queue Options" ) );
     360
     361        s = _( "Enable _Queue" );
     362        l = new_check_button( s, TR_PREFS_KEY_QUEUE_ENABLED, core );
     363        hig_workarea_add_row_w( t, &row, l, NULL, NULL );
     364
     365        s = _( "Don't start _new torrents if a speed limit is reached" );
     366        w = new_check_button( s, TR_PREFS_KEY_QUEUE_SPEED_LIMIT, core );
     367        hig_workarea_add_row_w( t, &row, w, NULL, NULL );
     368
     369        s = _( "_Enqueue new torrents" );
     370        w = new_check_button( s, TR_PREFS_KEY_QUEUE_ENQUEUE_NEW_TORRENTS, core );
     371        hig_workarea_add_row_w( t, &row, w, NULL, NULL );
     372
     373        s = _( "_Add new torrents to the top of the queue" );
     374        w = new_check_button( s, TR_PREFS_KEY_QUEUE_NEW_TORRENTS_TOP, core );
     375        hig_workarea_add_row_w( t, &row, w, NULL, NULL );
     376
     377        g_snprintf( buf, sizeof( buf ), _( "Slo_w torrents don't use a queue slot (%s):" ), _(speed_K_str) );
     378        l = new_check_button( buf, TR_PREFS_KEY_QUEUE_SKIP_SLOW_TORRENTS, core );
     379        w = new_spin_button( TR_PREFS_KEY_QUEUE_SLOW_CUTOFF_KBps, core, 0, 9999, 1 );
     380        gtk_widget_set_sensitive( GTK_WIDGET( w ), pref_flag_get( TR_PREFS_KEY_QUEUE_SKIP_SLOW_TORRENTS ) );
     381        g_signal_connect( l, "toggled", G_CALLBACK( target_cb ), w );
     382        hig_workarea_add_row_w( t, &row, l, w, NULL );
     383
     384    hig_workarea_add_section_title( t, &row, _( "Queue Limits" ) );
     385
     386        s = _( "Maxiumum downloads active:" );
     387        w = gtk_label_new( s );
     388        w2 = new_spin_button( TR_PREFS_KEY_QUEUE_MAX_DOWNLOAD_ACTIVE, core, 0, 9999, 1 );
     389        hig_workarea_add_row_w( t, &row, w, w2, NULL );
     390
     391        s = _( "Maxiumum seeds active:" );
     392        w = gtk_label_new( s );
     393        w2 = new_spin_button( TR_PREFS_KEY_QUEUE_MAX_SEED_ACTIVE, core, 0, 9999, 1 );
     394        hig_workarea_add_row_w( t, &row, w, w2, NULL );
     395
     396    hig_workarea_add_section_title( t, &row, _( "Temporary Queue Limits" ) );
     397
     398        s = _( "Queue limits used when temporary speed limit is active" );
     399        g_snprintf( buf, sizeof( buf ), "<small>%s</small>", s );
     400        w = gtk_label_new( buf );
     401        gtk_label_set_use_markup( GTK_LABEL( w ), TRUE );
     402        gtk_misc_set_alignment( GTK_MISC( w ), 0.5f, 0.5f );
     403        hig_workarea_add_wide_control( t, &row, w );
     404
     405        s = _( "Maxiumum downloads active:" );
     406        w = gtk_label_new( s );
     407        w2 = new_spin_button( TR_PREFS_KEY_QUEUE_ALT_DOWNLOAD_ACTIVE, core, 0, 9999, 1 );
     408        hig_workarea_add_row_w( t, &row, w, w2, NULL );
     409
     410        s = _( "Maxiumum seeds active:" );
     411        w = gtk_label_new( s );
     412        w2 = new_spin_button( TR_PREFS_KEY_QUEUE_ALT_SEED_ACTIVE, core, 0, 9999, 1 );
     413        hig_workarea_add_row_w( t, &row, w, w2, NULL );
     414
     415    hig_workarea_finish( t, &row );
     416    return t;
     417}
     418
     419/****
    343420*****  Desktop Tab
    344421****/
    345422
    tr_prefs_dialog_new( GObject * core, 
    13361413                              torrentPage( core ),
    13371414                              gtk_label_new ( _( "Torrents" ) ) );
    13381415    gtk_notebook_append_page( GTK_NOTEBOOK( n ),
     1416                              queuePage( core ),
     1417                              gtk_label_new ( _( "Queue" ) ) );
     1418    gtk_notebook_append_page( GTK_NOTEBOOK( n ),
    13391419                              bandwidthPage( core ),
    13401420                              gtk_label_new ( _( "Speed" ) ) );
    13411421    gtk_notebook_append_page( GTK_NOTEBOOK( n ),
  • gtk/ui.h

    diff --git gtk/ui.h gtk/ui.h
    index 26bc18f..66dddd9 100644
    static const char * fallback_ui_file = 
    2626    "      <menuitem action='pause-torrent'/>\n"
    2727    "      <menuitem action='copy-magnet-link-to-clipboard'/>\n"
    2828    "      <separator/>\n"
     29    "      <menuitem action='queued'/>\n"
    2930    "      <menuitem action='relocate-torrent'/>\n"
    3031    "      <menuitem action='verify-torrent'/>\n"
    3132    "      <separator/>\n"
    static const char * fallback_ui_file = 
    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"
    static const char * fallback_ui_file = 
    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"
    static const char * fallback_ui_file = 
    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"
    static const char * fallback_ui_file = 
    92100    "    <menuitem action='pause-torrent'/>\n"
    93101    "    <menuitem action='copy-magnet-link-to-clipboard'/>\n"
    94102    "    <separator/>\n"
     103    "    <menuitem action='queued'/>\n"
    95104    "    <menuitem action='verify-torrent'/>\n"
    96105    "    <menuitem action='relocate-torrent'/>\n"
    97106    "    <separator/>\n"
  • gtk/util.c

    diff --git gtk/util.c gtk/util.c
    index 4bb46da..89fc1c1 100644
    gtr_strcmp0( const char * str1, const char * str2 ) 
    419419#if GLIB_CHECK_VERSION( 2, 16, 0 )
    420420    return g_strcmp0( str1, str2 );
    421421#else
    422     if( str1 && str2 ) return strcmp( str1, str2 );
    423     if( str1 ) return 1;
    424     if( str2 ) return -1;
    425     return 0;
     422    return tr_strcmp0( str1, str2 );
    426423#endif
    427424}
    428425
  • libtransmission/resume.c

    diff --git libtransmission/resume.c libtransmission/resume.c
    index fcf84a7..91e4472 100644
     
    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
    tr_torrentSaveResume( tr_torrent * tor ) 
    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    {
    loadFromFile( tr_torrent * tor, 
    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

    diff --git libtransmission/resume.h libtransmission/resume.h
    index c7dbdd9..34f5f53 100644
    enum 
    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/rpcimpl.c

    diff --git libtransmission/rpcimpl.c libtransmission/rpcimpl.c
    index 9258c44..77b9a0a 100644
    addField( const tr_torrent * tor, tr_benc * d, const char * key ) 
    569569        for( i = 0; i < inf->fileCount; ++i )
    570570            tr_bencListAddInt( p, inf->files[i].priority );
    571571    }
     572    else if( tr_streq( key, keylen, "queued" ) )
     573        tr_bencDictAddBool( d, key, tr_torrentIsQueued( tor ) );
     574    else if( tr_streq( key, keylen, "queueRank" ) )
     575        tr_bencDictAddInt( d, key, tr_torrentGetQueueRank( tor ) );
    572576    else if( tr_streq( key, keylen, "rateDownload" ) )
    573577        tr_bencDictAddInt( d, key, toSpeedBytes( st->pieceDownloadSpeed_KBps ) );
    574578    else if( tr_streq( key, keylen, "rateUpload" ) )
    removeTrackers( tr_torrent * tor, tr_benc * ids ) 
    942946    return errmsg;
    943947}
    944948
     949static void
     950torrentMoveQueueRank( tr_torrent * tor , const tr_queue_direction dir )
     951{
     952    switch(dir)
     953    {
     954        case TR_QUEUE_UP        : tr_torrentMoveQueueRankUp( tor ); break;
     955        case TR_QUEUE_DOWN      : tr_torrentMoveQueueRankDown( tor ); break;
     956        case TR_QUEUE_TOP       : tr_torrentMoveQueueRankTop( tor ); break;
     957        case TR_QUEUE_BOTTOM    : tr_torrentMoveQueueRankBottom( tor ); break;
     958    }
     959}
     960
    945961static const char*
    946962torrentSet( tr_session               * session,
    947963            tr_benc                  * args_in,
    torrentSet( tr_session * session, 
    10021018            errmsg = removeTrackers( tor, trackers );
    10031019        if( !errmsg && tr_bencDictFindList( args_in, "trackerReplace", &trackers ) )
    10041020            errmsg = replaceTrackers( tor, trackers );
     1021        if( tr_bencDictFindInt( args_in, "moveQueueRank", &tmp ) )
     1022            torrentMoveQueueRank( tor, (tr_queue_direction)tmp );
     1023        if( tr_bencDictFindBool( args_in, "queued", &boolVal) )
     1024            tr_torrentSetQueued( tor, boolVal );
     1025        if( tr_bencDictFindInt( args_in, "queueRank", &tmp ) )
     1026            tr_torrentSetQueueRank( tor, tmp );
    10051027        notify( session, TR_RPC_TORRENT_CHANGED, tor );
    10061028    }
    10071029
    sessionSet( tr_session * session, 
    14601482        else
    14611483            tr_sessionSetEncryption( session, TR_ENCRYPTION_PREFERRED );
    14621484    }
     1485    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_QUEUE_ALT_DOWNLOAD_ACTIVE, &i ) )
     1486        tr_sessionSetQueueAltDownloadActive( session, i );
     1487    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_QUEUE_ALT_SEED_ACTIVE, &i ) )
     1488        tr_sessionSetQueueAltSeedActive( session, i );
     1489    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_QUEUE_ENABLED, &boolVal ) )
     1490        tr_sessionSetQueueEnabled( session, boolVal );
     1491    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_QUEUE_ENQUEUE_NEW_TORRENTS, &boolVal ) )
     1492        tr_sessionSetQueueEnqueueNewTorrents( session, boolVal );
     1493    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_QUEUE_MAX_DOWNLOAD_ACTIVE, &i ) )
     1494        tr_sessionSetQueueMaxDownloadActive( session, i );
     1495    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_QUEUE_MAX_SEED_ACTIVE, &i ) )
     1496        tr_sessionSetQueueMaxSeedActive( session, i );
     1497    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_QUEUE_NEW_TORRENTS_TOP, &boolVal ) )
     1498        tr_sessionSetQueueNewTorrentsTop( session, boolVal );
     1499    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_QUEUE_SKIP_SLOW_TORRENTS, &boolVal ) )
     1500        tr_sessionSetQueueSkipSlowTorrentsEnabled( session, boolVal );
     1501    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_QUEUE_SLOW_CUTOFF_KBps, &i ) )
     1502        tr_sessionSetQueueSlowCutoff( session, i );
     1503    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_QUEUE_SPEED_LIMIT, &boolVal ) )
     1504        tr_sessionSetQueueSpeedLimitEnabled( session, boolVal );
    14631505
    14641506    notify( session, TR_RPC_SESSION_CHANGED, NULL );
    14651507
    sessionGet( tr_session * s, 
    15671609        case TR_ENCRYPTION_REQUIRED: str = "required"; break;
    15681610        default: str = "preferred"; break;
    15691611    }
    1570     tr_bencDictAddStr( d, TR_PREFS_KEY_ENCRYPTION, str );
     1612    tr_bencDictAddStr ( d, TR_PREFS_KEY_ENCRYPTION, str );
     1613    tr_bencDictAddInt ( d, TR_PREFS_KEY_QUEUE_ALT_DOWNLOAD_ACTIVE, tr_sessionGetQueueAltDownloadActive( s ) );
     1614    tr_bencDictAddInt ( d, TR_PREFS_KEY_QUEUE_ALT_SEED_ACTIVE, tr_sessionGetQueueAltSeedActive( s ) );
     1615    tr_bencDictAddBool( d, TR_PREFS_KEY_QUEUE_ENABLED, tr_sessionIsQueueEnabled( s ) );
     1616    tr_bencDictAddBool( d, TR_PREFS_KEY_QUEUE_ENQUEUE_NEW_TORRENTS, tr_sessionGetQueueEnqueueNewTorrents( s ) );
     1617    tr_bencDictAddInt ( d, TR_PREFS_KEY_QUEUE_MAX_DOWNLOAD_ACTIVE, tr_sessionGetQueueMaxDownloadActive( s ) );
     1618    tr_bencDictAddInt ( d, TR_PREFS_KEY_QUEUE_MAX_SEED_ACTIVE, tr_sessionGetQueueMaxSeedActive( s ) );
     1619    tr_bencDictAddBool( d, TR_PREFS_KEY_QUEUE_NEW_TORRENTS_TOP, tr_sessionGetQueueNewTorrentsTop( s ) );
     1620    tr_bencDictAddBool( d, TR_PREFS_KEY_QUEUE_SKIP_SLOW_TORRENTS, tr_sessionIsQueueSkipSlowTorrentsEnabled( s ) );
     1621    tr_bencDictAddInt ( d, TR_PREFS_KEY_QUEUE_SLOW_CUTOFF_KBps, tr_sessionGetQueueSlowCutoff( s ) );
     1622    tr_bencDictAddBool( d, TR_PREFS_KEY_QUEUE_SPEED_LIMIT, tr_sessionIsQueueSpeedLimitEnabled( s ) );
    15711623
    15721624    return NULL;
    15731625}
  • libtransmission/session.c

    diff --git libtransmission/session.c libtransmission/session.c
    index a9378ec..8a44202 100644
    enum 
    5555{
    5656    SAVE_INTERVAL_SECS = 120,
    5757
     58    QUEUE_INTERVAL_SECS = 60,
     59
    5860    DEFAULT_CACHE_SIZE_MB = 2
    5961};
    6062
    tr_sessionGetDefaultSettings( const char * configDir UNUSED, tr_benc * d ) 
    246248{
    247249    assert( tr_bencIsDict( d ) );
    248250
    249     tr_bencDictReserve( d, 60 );
     251    tr_bencDictReserve( d, 70 );
    250252    tr_bencDictAddBool( d, TR_PREFS_KEY_BLOCKLIST_ENABLED,        FALSE );
    251253    tr_bencDictAddInt ( d, TR_PREFS_KEY_MAX_CACHE_SIZE_MB,        DEFAULT_CACHE_SIZE_MB );
    252254    tr_bencDictAddBool( d, TR_PREFS_KEY_DHT_ENABLED,              TRUE );
    tr_sessionGetDefaultSettings( const char * configDir UNUSED, tr_benc * d ) 
    279281    tr_bencDictAddInt ( d, TR_PREFS_KEY_PROXY_PORT,               80 );
    280282    tr_bencDictAddInt ( d, TR_PREFS_KEY_PROXY_TYPE,               TR_PROXY_HTTP );
    281283    tr_bencDictAddStr ( d, TR_PREFS_KEY_PROXY_USERNAME,           "" );
     284    tr_bencDictAddInt ( d, TR_PREFS_KEY_QUEUE_ALT_DOWNLOAD_ACTIVE,2 );
     285    tr_bencDictAddInt ( d, TR_PREFS_KEY_QUEUE_ALT_SEED_ACTIVE,    2 );
     286    tr_bencDictAddBool( d, TR_PREFS_KEY_QUEUE_ENABLED,            FALSE );
     287    tr_bencDictAddBool( d, TR_PREFS_KEY_QUEUE_ENQUEUE_NEW_TORRENTS,FALSE );
     288    tr_bencDictAddInt ( d, TR_PREFS_KEY_QUEUE_MAX_DOWNLOAD_ACTIVE,2 );
     289    tr_bencDictAddInt ( d, TR_PREFS_KEY_QUEUE_MAX_SEED_ACTIVE,    2 );
     290    tr_bencDictAddBool( d, TR_PREFS_KEY_QUEUE_NEW_TORRENTS_TOP,   FALSE );
     291    tr_bencDictAddBool( d, TR_PREFS_KEY_QUEUE_SKIP_SLOW_TORRENTS, FALSE );
     292    tr_bencDictAddInt ( d, TR_PREFS_KEY_QUEUE_SLOW_CUTOFF_KBps,   5 );
     293    tr_bencDictAddBool( d, TR_PREFS_KEY_QUEUE_SPEED_LIMIT,        FALSE );
    282294    tr_bencDictAddReal( d, TR_PREFS_KEY_RATIO,                    2.0 );
    283295    tr_bencDictAddBool( d, TR_PREFS_KEY_RATIO_ENABLED,            FALSE );
    284296    tr_bencDictAddBool( d, TR_PREFS_KEY_RENAME_PARTIAL_FILES,     TRUE );
    tr_sessionGetSettings( tr_session * s, struct tr_benc * d ) 
    314326{
    315327    assert( tr_bencIsDict( d ) );
    316328
    317     tr_bencDictReserve( d, 60 );
     329    tr_bencDictReserve( d, 70 );
    318330    tr_bencDictAddBool( d, TR_PREFS_KEY_BLOCKLIST_ENABLED,        tr_blocklistIsEnabled( s ) );
    319331    tr_bencDictAddInt ( d, TR_PREFS_KEY_MAX_CACHE_SIZE_MB,        tr_sessionGetCacheLimit_MB( s ) );
    320332    tr_bencDictAddBool( d, TR_PREFS_KEY_DHT_ENABLED,              s->isDHTEnabled );
    tr_sessionGetSettings( tr_session * s, struct tr_benc * d ) 
    349361    tr_bencDictAddInt ( d, TR_PREFS_KEY_PROXY_PORT,               s->proxyPort );
    350362    tr_bencDictAddInt ( d, TR_PREFS_KEY_PROXY_TYPE,               s->proxyType );
    351363    tr_bencDictAddStr ( d, TR_PREFS_KEY_PROXY_USERNAME,           s->proxyUsername );
     364    tr_bencDictAddInt ( d, TR_PREFS_KEY_QUEUE_ALT_DOWNLOAD_ACTIVE,tr_sessionGetQueueAltDownloadActive( s ) );
     365    tr_bencDictAddInt ( d, TR_PREFS_KEY_QUEUE_ALT_SEED_ACTIVE,    tr_sessionGetQueueAltSeedActive( s ) );
     366    tr_bencDictAddBool( d, TR_PREFS_KEY_QUEUE_ENABLED,            tr_sessionIsQueueEnabled( s ) );
     367    tr_bencDictAddBool( d, TR_PREFS_KEY_QUEUE_ENQUEUE_NEW_TORRENTS,tr_sessionGetQueueEnqueueNewTorrents( s ) );
     368    tr_bencDictAddInt ( d, TR_PREFS_KEY_QUEUE_MAX_DOWNLOAD_ACTIVE,tr_sessionGetQueueMaxDownloadActive( s ) );
     369    tr_bencDictAddInt ( d, TR_PREFS_KEY_QUEUE_MAX_SEED_ACTIVE,    tr_sessionGetQueueMaxSeedActive( s ) );
     370    tr_bencDictAddBool( d, TR_PREFS_KEY_QUEUE_NEW_TORRENTS_TOP,   tr_sessionGetQueueNewTorrentsTop( s ) );
     371    tr_bencDictAddBool( d, TR_PREFS_KEY_QUEUE_SKIP_SLOW_TORRENTS, tr_sessionIsQueueSkipSlowTorrentsEnabled( s ) );
     372    tr_bencDictAddInt ( d, TR_PREFS_KEY_QUEUE_SLOW_CUTOFF_KBps,   tr_sessionGetQueueSlowCutoff( s ) );
     373    tr_bencDictAddBool( d, TR_PREFS_KEY_QUEUE_SPEED_LIMIT,        tr_sessionIsQueueSpeedLimitEnabled( s ) );
    352374    tr_bencDictAddReal( d, TR_PREFS_KEY_RATIO,                    s->desiredRatio );
    353375    tr_bencDictAddBool( d, TR_PREFS_KEY_RATIO_ENABLED,            s->isRatioLimited );
    354376    tr_bencDictAddBool( d, TR_PREFS_KEY_RENAME_PARTIAL_FILES,     tr_sessionIsIncompleteFileNamingEnabled( s ) );
    onSaveTimer( int foo UNUSED, short bar UNUSED, void * vsession ) 
    490512****
    491513***/
    492514
     515int
     516tr_sessionCompareTorrentByQueueRank( const void * va, const void * vb )
     517{
     518    const tr_torrent * a = *(const tr_torrent**)va;
     519    const tr_torrent * b = *(const tr_torrent**)vb;
     520    int ret = a->queueRank - b->queueRank;
     521    if( ret == 0 ){
     522        const tr_info * ia = tr_torrentInfo( a );
     523        const tr_info * ib = tr_torrentInfo( b );
     524        ret = tr_strcmp0( ia->name, ib->name );
     525        if( ret == 0 )
     526            ret = strcmp( ia->hashString, ib->hashString );
     527    }
     528    return ret;
     529}
     530
     531static tr_torrent **
     532getQueueArray( tr_session          * session,
     533               int                 * count,
     534               const tr_queueType    type,
     535               tr_bool               sort )
     536{
     537    int i = 0;
     538    tr_torrent ** torrents, * tor = NULL;
     539
     540    assert( tr_isSession( session ) );
     541
     542    torrents = tr_new( tr_torrent*, session->torrentCount );
     543    while(( tor = tr_torrentNext( session, tor )))
     544    {
     545        switch( type )
     546        {
     547            case TR_QUEUE_IGNORED:
     548                if( !tor->isQueued )
     549                    torrents[i++] = tor;
     550                break;
     551            case TR_QUEUE_SEED:
     552                if( tor->isQueued && tor->completeness != TR_LEECH )
     553                    torrents[i++] = tor;
     554                break;
     555            case TR_QUEUE_DOWNLOAD:
     556                if( tor->isQueued && tor->completeness == TR_LEECH )
     557                    torrents[i++] = tor;
     558                break;
     559            case TR_QUEUE_QUEUED:
     560                if( tor->isQueued )
     561                    torrents[i++] = tor;
     562                break;
     563            case TR_QUEUE_ALL:
     564                torrents[i++] = tor;
     565                break;
     566        }
     567    }
     568
     569    if( sort )
     570        qsort( torrents, i, sizeof( tr_torrent* ), tr_sessionCompareTorrentByQueueRank );
     571
     572    *count = i;
     573    return torrents;
     574}
     575
     576static void
     577compactQueue( tr_session * session )
     578{
     579    int i, j;
     580    tr_torrent ** torrents;
     581
     582    assert( tr_isSession( session ) );
     583
     584    torrents = getQueueArray( session, &j, TR_QUEUE_ALL, TRUE );
     585    for( i = 0; i < j; ++i )
     586    {
     587        if( torrents[i]->queueRank != i + 1 )
     588        {
     589            torrents[i]->queueRank = i + 1;
     590            tr_torrentSetDirty( torrents[i] );
     591        }
     592    }
     593
     594    tr_free( torrents );
     595}
     596
     597static tr_bool
     598testTorrentCutoff( tr_torrent * tor, const int cutoff, const tr_direction dir )
     599{
     600    tr_bool pass;
     601    const tr_stat * stat = tr_torrentStatCached( tor );
     602
     603    if( dir == TR_DOWN )
     604        pass = tor->isRunning && stat->pieceDownloadSpeed_KBps < cutoff;
     605    else
     606        pass = tor->isRunning && stat->pieceUploadSpeed_KBps < cutoff;
     607
     608    return pass;
     609}
     610
     611static void
     612processQueue( tr_session * session )
     613{
     614    tr_bool skip, speedLimit = FALSE;
     615    int count, cutoff, i, maxDown, maxSeed;
     616    tr_torrent ** torrents, * tor;
     617    tr_torrent_activity activity;
     618
     619    assert( tr_isSession( session ) );
     620
     621    cutoff      = tr_sessionGetQueueSlowCutoff( session );
     622    skip        = tr_sessionIsQueueSkipSlowTorrentsEnabled( session );
     623    if( tr_sessionUsesAltSpeed( session ) )
     624    {
     625        maxDown = tr_sessionGetQueueAltDownloadActive( session );
     626        maxSeed = tr_sessionGetQueueAltSeedActive( session );
     627    }
     628    else
     629    {
     630        maxDown = tr_sessionGetQueueMaxDownloadActive( session );
     631        maxSeed = tr_sessionGetQueueMaxSeedActive( session );
     632    }
     633
     634    if( tr_sessionIsQueueSpeedLimitEnabled( session ) )
     635    {
     636#define DEV 0.9 /* allow for a little deviation in speeds from the limit */
     637        double speed;
     638        uint64_t uploadedBytes, downloadedBytes;
     639        static tr_session_stats oldStats = { 0.0f, 0, 0, 0, 0, 0 };
     640        tr_session_stats stats;
     641
     642        tr_sessionGetStats( session, &stats );
     643        uploadedBytes = stats.uploadedBytes - oldStats.uploadedBytes;
     644        downloadedBytes = stats.downloadedBytes - oldStats.downloadedBytes;
     645
     646        if( tr_sessionGetActiveSpeedLimit_KBps( session, TR_UP, &speed )
     647            && ( speed * DEV ) < toSpeedKBps( uploadedBytes / QUEUE_INTERVAL_SECS ) )
     648            speedLimit = TRUE;
     649        else if( tr_sessionGetActiveSpeedLimit_KBps( session, TR_DOWN, &speed )
     650            && ( speed * DEV ) < toSpeedKBps( downloadedBytes / QUEUE_INTERVAL_SECS ) )
     651            speedLimit = TRUE;
     652
     653        oldStats = stats;
     654    }
     655
     656    torrents = getQueueArray( session, &count, TR_QUEUE_IGNORED, FALSE );
     657    for( i = 0; i < count; ++i )
     658    {
     659        tor = torrents[i];
     660        activity = tr_torrentGetActivity( tor );
     661        if( activity == TR_STATUS_DOWNLOAD )
     662        {
     663            if( skip && testTorrentCutoff( tor, cutoff, TR_DOWN ) )
     664                continue;
     665            if( tor->isRunning )
     666                --maxDown;
     667        }
     668        else if( activity == TR_STATUS_SEED )
     669        {
     670            if( skip && testTorrentCutoff( tor, cutoff, TR_UP ) )
     671                continue;
     672            if( tor->isRunning )
     673                --maxSeed;
     674        }
     675    }
     676    tr_free( torrents );
     677
     678    torrents = getQueueArray( session, &count, TR_QUEUE_QUEUED, TRUE );
     679    for( i = 0; i < count; ++i )
     680    {
     681        tor = torrents[i];
     682        if( tor->error == TR_STAT_LOCAL_ERROR
     683            || tor->verifyState != TR_VERIFY_NONE )
     684            continue;
     685        if( tor->completeness == TR_LEECH )
     686        {
     687            if( skip && testTorrentCutoff( tor, cutoff, TR_DOWN ) )
     688                continue;
     689            if( maxDown > 0 )
     690            {
     691                if( !speedLimit )
     692                    tr_torrentStart( tor );
     693                --maxDown;
     694            }
     695            else if( tor->isRunning )
     696                tr_torrentStop( tor );
     697        }
     698        else
     699        {
     700            if( skip && testTorrentCutoff( tor, cutoff, TR_UP ) )
     701                continue;
     702            if( maxSeed > 0 )
     703            {
     704                if( !speedLimit )
     705                    tr_torrentStart( tor );
     706                --maxSeed;
     707            }
     708            else if( tor->isRunning )
     709                tr_torrentStop( tor );
     710        }
     711    }
     712    tr_free( torrents );
     713}
     714
     715/***
     716****
     717***/
     718
     719static void
     720onQueueTimer( int foo UNUSED, short bar UNUSED, void * vsession )
     721{
     722    tr_session * session = vsession;
     723
     724    assert( tr_isSession( session ) );
     725
     726    if( tr_sessionIsQueueEnabled( session ) )
     727        processQueue( session );
     728
     729    tr_timerAdd( session->queueTimer, QUEUE_INTERVAL_SECS, 0 );
     730}
     731
     732/***
     733****
     734***/
     735
    493736static void tr_sessionInitImpl( void * );
    494737
    495738struct init_data
    tr_sessionInitImpl( void * vdata ) 
    625868
    626869    assert( tr_isSession( session ) );
    627870
     871    session->queueTimer = tr_new0( struct event, 1 );
     872    evtimer_set( session->queueTimer, onQueueTimer, session );
     873    tr_timerAdd( session->queueTimer, QUEUE_INTERVAL_SECS , 0 );
     874
    628875    session->saveTimer = tr_new0( struct event, 1 );
    629876    evtimer_set( session->saveTimer, onSaveTimer, session );
    630877    tr_timerAdd( session->saveTimer, SAVE_INTERVAL_SECS, 0 );
    sessionSetImpl( void * vdata ) 
    734981    if( tr_bencDictFindStr( settings, TR_PREFS_KEY_PROXY_PASSWORD, &str ) )
    735982        tr_sessionSetProxyPassword( session, str );
    736983
     984    /* torrent queueing */
     985    if( tr_bencDictFindInt( settings, TR_PREFS_KEY_QUEUE_ALT_DOWNLOAD_ACTIVE, &i ) )
     986        tr_sessionSetQueueAltDownloadActive( session, i );
     987    if( tr_bencDictFindInt( settings, TR_PREFS_KEY_QUEUE_ALT_SEED_ACTIVE, &i ) )
     988        tr_sessionSetQueueAltSeedActive( session, i );
     989    if( tr_bencDictFindBool( settings, TR_PREFS_KEY_QUEUE_ENABLED, &boolVal ) )
     990        tr_sessionSetQueueEnabled( session, boolVal );
     991    if( tr_bencDictFindBool( settings, TR_PREFS_KEY_QUEUE_ENQUEUE_NEW_TORRENTS, &boolVal ) )
     992        tr_sessionSetQueueEnqueueNewTorrents( session, boolVal );
     993    if( tr_bencDictFindInt( settings, TR_PREFS_KEY_QUEUE_MAX_DOWNLOAD_ACTIVE, &i ) )
     994        tr_sessionSetQueueMaxDownloadActive( session, i );
     995    if( tr_bencDictFindInt( settings, TR_PREFS_KEY_QUEUE_MAX_SEED_ACTIVE, &i ) )
     996        tr_sessionSetQueueMaxSeedActive( session, i );
     997    if( tr_bencDictFindBool( settings, TR_PREFS_KEY_QUEUE_NEW_TORRENTS_TOP, &boolVal ) )
     998        tr_sessionSetQueueNewTorrentsTop( session, boolVal );
     999    if( tr_bencDictFindBool( settings, TR_PREFS_KEY_QUEUE_SKIP_SLOW_TORRENTS, &boolVal ) )
     1000        tr_sessionSetQueueSkipSlowTorrentsEnabled( session, boolVal );
     1001    if( tr_bencDictFindInt ( settings, TR_PREFS_KEY_QUEUE_SLOW_CUTOFF_KBps, &i ) )
     1002        tr_sessionSetQueueSlowCutoff( session, i );
     1003    if( tr_bencDictFindBool( settings, TR_PREFS_KEY_QUEUE_SPEED_LIMIT, &boolVal ) )
     1004        tr_sessionSetQueueSpeedLimitEnabled( session, boolVal );
     1005
    7371006    /* rpc server */
    7381007    if( session->rpcServer != NULL ) /* close the old one */
    7391008        tr_rpcClose( &session->rpcServer );
    tr_sessionGetRawSpeed_KBps( const tr_session * session, tr_direction dir ) 
    16311900    return toSpeedKBps( tr_sessionGetRawSpeed_Bps( session, dir ) );
    16321901}
    16331902
    1634 
    16351903int
    16361904tr_sessionCountTorrents( const tr_session * session )
    16371905{
    sessionCloseImpl( void * vsession ) 
    16721940    if( session->isDHTEnabled )
    16731941        tr_dhtUninit( session );
    16741942
     1943    evtimer_del( session->queueTimer );
     1944    tr_free( session->queueTimer );
     1945    session->queueTimer = NULL;
     1946
    16751947    evtimer_del( session->saveTimer );
    16761948    tr_free( session->saveTimer );
    16771949    session->saveTimer = NULL;
    tr_sessionLoadTorrents( tr_session * session, 
    18432115    if( n )
    18442116        tr_inf( _( "Loaded %d torrents" ), n );
    18452117
     2118    compactQueue( session );
     2119
    18462120    if( setmeCount )
    18472121        *setmeCount = n;
    18482122    return torrents;
    tr_sessionSetTorrentDoneScript( tr_session * session, const char * scriptFilenam 
    26202894        session->torrentDoneScript = tr_strdup( scriptFilename );
    26212895    }
    26222896}
     2897
     2898/***
     2899****
     2900***/
     2901
     2902int
     2903tr_sessionGetQueueAltDownloadActive( const tr_session * session )
     2904{
     2905    assert( tr_isSession( session ) );
     2906
     2907    return session->queueAltDownloadActive;
     2908}
     2909
     2910void
     2911tr_sessionSetQueueAltDownloadActive( tr_session * session, int maxActive )
     2912{
     2913    assert( tr_isSession( session ) );
     2914
     2915    session->queueAltDownloadActive = maxActive;
     2916}
     2917
     2918int
     2919tr_sessionGetQueueAltSeedActive( const tr_session * session )
     2920{
     2921    assert( tr_isSession( session ) );
     2922
     2923    return session->queueAltSeedActive;
     2924}
     2925
     2926void
     2927tr_sessionSetQueueAltSeedActive( tr_session * session , int maxActive )
     2928{
     2929    assert( tr_isSession( session ) );
     2930
     2931    session->queueAltSeedActive = maxActive;
     2932}
     2933
     2934tr_bool
     2935tr_sessionIsQueueEnabled( const tr_session* session )
     2936{
     2937    assert( tr_isSession( session ) );
     2938
     2939    return session->queueEnabled;
     2940}
     2941
     2942void
     2943tr_sessionSetQueueEnabled( tr_session * session, tr_bool enabled )
     2944{
     2945    assert( tr_isSession( session ) );
     2946
     2947    session->queueEnabled = enabled;
     2948}
     2949
     2950tr_bool
     2951tr_sessionGetQueueEnqueueNewTorrents( const tr_session* session )
     2952{
     2953    assert( tr_isSession( session ) );
     2954
     2955    return session->queueEnqueueNew;
     2956}
     2957
     2958void
     2959tr_sessionSetQueueEnqueueNewTorrents( tr_session * session, tr_bool enqueue )
     2960{
     2961    assert( tr_isSession( session ) );
     2962
     2963    session->queueEnqueueNew = enqueue;
     2964}
     2965
     2966int
     2967tr_sessionGetQueueMaxDownloadActive( const tr_session * session )
     2968{
     2969    assert( tr_isSession( session ) );
     2970
     2971    return session->queueMaxDownloadActive;
     2972}
     2973
     2974void
     2975tr_sessionSetQueueMaxDownloadActive( tr_session * session, int maxActive )
     2976{
     2977    assert( tr_isSession( session ) );
     2978
     2979    session->queueMaxDownloadActive = maxActive;
     2980}
     2981
     2982int
     2983tr_sessionGetQueueMaxSeedActive( const tr_session * session )
     2984{
     2985    assert( tr_isSession( session ) );
     2986
     2987    return session->queueMaxSeedActive;
     2988}
     2989
     2990void
     2991tr_sessionSetQueueMaxSeedActive( tr_session * session , int maxActive )
     2992{
     2993    assert( tr_isSession( session ) );
     2994
     2995    session->queueMaxSeedActive = maxActive;
     2996}
     2997
     2998tr_bool
     2999tr_sessionGetQueueNewTorrentsTop( const tr_session * session )
     3000{
     3001    assert( tr_isSession( session ) );
     3002
     3003    return session->queueNewTorrentsTop;
     3004}
     3005
     3006void
     3007tr_sessionSetQueueNewTorrentsTop( tr_session * session, tr_bool enabled )
     3008{
     3009    assert( tr_isSession( session ) );
     3010
     3011    session->queueNewTorrentsTop = enabled;
     3012}
     3013
     3014tr_bool
     3015tr_sessionIsQueueSkipSlowTorrentsEnabled( const tr_session * session )
     3016{
     3017    assert( tr_isSession( session ) );
     3018
     3019    return session->queueSkipSlowTorrents;
     3020}
     3021
     3022void
     3023tr_sessionSetQueueSkipSlowTorrentsEnabled( tr_session * session, tr_bool enabled )
     3024{
     3025    assert( tr_isSession( session ) );
     3026
     3027    session->queueSkipSlowTorrents = enabled;
     3028}
     3029
     3030int
     3031tr_sessionGetQueueSlowCutoff( const tr_session * session )
     3032{
     3033    assert( tr_isSession( session ) );
     3034
     3035    return session->queueSlowCutoff;
     3036}
     3037
     3038void
     3039tr_sessionSetQueueSlowCutoff( tr_session * session, int cutoff )
     3040{
     3041    assert( tr_isSession( session ) );
     3042
     3043    session->queueSlowCutoff = cutoff;
     3044}
     3045
     3046tr_bool
     3047tr_sessionIsQueueSpeedLimitEnabled( const tr_session * session )
     3048{
     3049    assert( tr_isSession( session ) );
     3050
     3051    return session->queueSpeedLimit;
     3052}
     3053
     3054void
     3055tr_sessionSetQueueSpeedLimitEnabled( tr_session * session, tr_bool enabled )
     3056{
     3057    assert( tr_isSession( session ) );
     3058
     3059    session->queueSpeedLimit = enabled;
     3060}
  • libtransmission/session.h

    diff --git libtransmission/session.h libtransmission/session.h
    index b8a218a..3f1acca 100644
    struct tr_session 
    141141    int                          peerSocketTOS;
    142142    char *                       peer_congestion_algorithm;
    143143
     144    int                          queueAltDownloadActive;
     145    int                          queueAltSeedActive;
     146    tr_bool                      queueEnabled;
     147    tr_bool                      queueEnqueueNew;
     148    int                          queueMaxDownloadActive;
     149    int                          queueMaxSeedActive;
     150    tr_bool                      queueNewTorrentsTop;
     151    tr_bool                      queueSkipSlowTorrents;
     152    double                       queueSlowCutoff;
     153    tr_bool                      queueSpeedLimit;
     154
    144155    int                          torrentCount;
    145156    tr_torrent *                 torrentList;
    146157
    struct tr_session 
    180191
    181192    struct event               * nowTimer;
    182193    struct event               * saveTimer;
     194    struct event               * queueTimer;
    183195
    184196    /* monitors the "global pool" speeds */
    185197    struct tr_bandwidth        * bandwidth;
    const struct tr_address* tr_sessionGetPublicAddress( const tr_session *, int tr 
    228240
    229241struct tr_bindsockets * tr_sessionGetBindSockets( tr_session * );
    230242
    231 int tr_sessionCountTorrents( const tr_session * session );
    232 
    233243enum
    234244{
    235245    SESSION_MAGIC_NUMBER = 3845,
  • libtransmission/torrent.c

    diff --git libtransmission/torrent.c libtransmission/torrent.c
    index 49f63bd..bbb54db 100644
    tr_torrentCheckSeedLimit( tr_torrent * tor ) 
    428428        /* maybe notify the client */
    429429        if( tor->ratio_limit_hit_func != NULL )
    430430            tor->ratio_limit_hit_func( tor, tor->ratio_limit_hit_func_user_data );
     431
     432        tr_torrentSetQueued( tor, FALSE );
    431433    }
    432434    /* if we're seeding and reach our inactiviy limit, stop the torrent */
    433435    else if( tr_torrentIsSeedIdleLimitDone( tor ) )
    tr_torrentCheckSeedLimit( tr_torrent * tor ) 
    440442        /* maybe notify the client */
    441443        if( tor->idle_limit_hit_func != NULL )
    442444            tor->idle_limit_hit_func( tor, tor->idle_limit_hit_func_user_data );
     445
     446        tr_torrentSetQueued( tor, FALSE );
     447    }
     448
     449}
     450
     451/***
     452****
     453***/
     454
     455static void
     456torrentSetQueueRank( tr_torrent * tor, int rank )
     457{
     458    tr_session * session;
     459    tr_torrent * t = NULL;
     460
     461    assert( tr_isTorrent( tor ) );
     462
     463    if( rank == tor->queueRank )
     464        return;
     465
     466    session = tor->session;
     467
     468    if( rank > tor->queueRank && rank > 0 )
     469    {
     470        int maxRank = 0;
     471        if( tor->queueRank <= 0 )
     472        {
     473            while(( t = tr_torrentNext( session, t )))
     474            {
     475                if( t == tor ) continue;
     476                if( t->queueRank > maxRank )
     477                    maxRank = t->queueRank;
     478                if( t->queueRank >= rank )
     479                {
     480                    ++t->queueRank;
     481                    tr_torrentSetDirty( t );
     482                }
     483            }
     484            rank = MIN( maxRank + 1, rank );
     485        }
     486        else
     487        {
     488            while(( t = tr_torrentNext( session, t )))
     489            {
     490                if( t == tor ) continue;
     491                if( t->queueRank > maxRank )
     492                    maxRank = t->queueRank;
     493                if( t->queueRank > tor->queueRank
     494                    && t->queueRank <= rank )
     495                {
     496                    --t->queueRank;
     497                    tr_torrentSetDirty( t );
     498                }
     499            }
     500            if( tor->queueRank > maxRank )
     501                maxRank = tor->queueRank;
     502            rank = MIN( maxRank, rank );
     503        }
     504    }
     505    else if ( rank < tor->queueRank && tor->queueRank > 0 )
     506    {
     507        if( rank <= 0 )
     508        {
     509            while(( t = tr_torrentNext( session, t )))
     510            {
     511                if( t == tor ) continue;
     512                if( t->queueRank > tor->queueRank )
     513                {
     514                    --t->queueRank;
     515                    tr_torrentSetDirty( t );
     516                }
     517            }
     518        }
     519        else
     520        {
     521            while(( t = tr_torrentNext( session, t )))
     522            {
     523                if( t == tor ) continue;
     524                if( t->queueRank >= rank
     525                    && t->queueRank < tor->queueRank )
     526                {
     527                    ++t->queueRank;
     528                    tr_torrentSetDirty( t );
     529                }
     530            }
     531        }
     532    }
     533
     534    tor->queueRank = rank;
     535    tr_torrentSetDirty( tor );
     536}
     537
     538static void
     539torrentRemoveFromQueue( tr_torrent * tor )
     540{
     541    assert( tr_isTorrent( tor ) );
     542
     543    torrentSetQueueRank( tor, 0 );
     544}
     545
     546int
     547tr_torrentGetQueueRank( const tr_torrent * tor )
     548{
     549    assert( tr_isTorrent( tor ) );
     550
     551    return tor->queueRank;
     552}
     553
     554void
     555tr_torrentSetQueueRank( tr_torrent * tor, int rank )
     556{
     557    assert( tr_isTorrent( tor ) );
     558
     559    torrentSetQueueRank( tor, rank < 1 ? INT_MAX : rank );
     560}
     561
     562tr_bool
     563tr_torrentIsQueued( const tr_torrent * tor )
     564{
     565    assert( tr_isTorrent( tor ) );
     566
     567    return tor->isQueued;
     568}
     569
     570void
     571tr_torrentSetQueued( tr_torrent * tor, tr_bool queued )
     572{
     573    assert( tr_isTorrent( tor ) );
     574
     575    if( tor->isQueued != queued )
     576    {
     577        tor->isQueued = queued;
     578        tr_torrentSetDirty( tor );
    443579    }
    444580}
    445581
    torrentInit( tr_torrent * tor, const tr_ctor * ctor ) 
    846982
    847983    tor->tiers = tr_announcerAddTorrent( tor->session->announcer, tor, onTrackerResponse, NULL );
    848984
     985    if( !( loaded & TR_FR_QUEUERANK ) )
     986    {
     987        const tr_bool top = tr_sessionGetQueueNewTorrentsTop( session );
     988        tr_torrentSetQueueRank( tor, top ? 1 : INT_MAX );
     989    }
     990    if( !( loaded & TR_FR_QUEUED ) )
     991    {
     992        const tr_stat * stat = tr_torrentStatCached( tor );
     993        tr_torrentSetQueued( tor, stat->finished || tr_sessionGetQueueEnqueueNewTorrents( session ) );
     994    }
     995
    849996    if( doStart )
    850997        torrentStart( tor );
    851998
    tr_torrentRemove( tr_torrent * tor ) 
    17151862    assert( tr_isTorrent( tor ) );
    17161863
    17171864    tor->isDeleting = 1;
     1865    torrentRemoveFromQueue( tor );
    17181866    tr_torrentFree( tor );
    17191867}
    17201868
    tr_torrentRecheckCompleteness( tr_torrent * tor ) 
    18802028            if( recentChange )
    18812029            {
    18822030                tr_announcerTorrentCompleted( tor );
    1883                 tor->doneDate = tor->anyDate = tr_time( );
     2031                tor->doneDate = tr_time( );
    18842032            }
    18852033
    18862034            if( tor->currentDir == tor->incompleteDir )
  • libtransmission/torrent.h

    diff --git libtransmission/torrent.h libtransmission/torrent.h
    index dbb8a89..0bc9da5 100644
    void tr_torrentSave( tr_torrent * tor ); 
    114114
    115115void             tr_torrentSetLocalError( tr_torrent * tor, const char * fmt, ... ) TR_GNUC_PRINTF( 2, 3 );
    116116
    117 
    118 
    119117typedef enum
    120118{
    121119    TR_VERIFY_NONE,
    struct tr_torrent 
    250248    tr_torrent *               next;
    251249
    252250    int                        uniqueId;
     251    tr_bool                    isQueued;
     252    int                        queueRank;
    253253
    254254    struct tr_bandwidth      * bandwidth;
    255255
    static inline tr_bool tr_torrentIsPieceChecked( const tr_torrent * tor, 
    359359    return tr_bitfieldHasFast( &tor->checkedPieces, i );
    360360}
    361361
     362static inline void tr_torrentMoveQueueRankUp( tr_torrent * tor )
     363{
     364    if( tor->queueRank > 1 ) tr_torrentSetQueueRank( tor, tor->queueRank - 1 );
     365}
     366
     367static inline void tr_torrentMoveQueueRankDown( tr_torrent * tor )
     368{
     369    if( tor->queueRank > 0 ) tr_torrentSetQueueRank( tor, tor->queueRank + 1 );
     370}
     371
     372static inline void tr_torrentMoveQueueRankTop( tr_torrent * tor )
     373{
     374    if( tor->queueRank > 1 ) tr_torrentSetQueueRank( tor, 1 );
     375}
     376
     377static inline void tr_torrentMoveQueueRankBottom( tr_torrent * tor )
     378{
     379    if( tor->queueRank > 0 ) tr_torrentSetQueueRank( tor, -1 );
     380}
     381
    362382/***
    363383****
    364384***/
    void tr_torrentSetDirty( tr_torrent * tor ) 
    382402{
    383403    assert( tr_isTorrent( tor ) );
    384404
     405    tor->anyDate = tr_time();
    385406    tor->isDirty = TRUE;
    386407}
    387408
  • libtransmission/transmission.h

    diff --git libtransmission/transmission.h libtransmission/transmission.h
    index b218c33..732e793 100644
    const char* tr_getDefaultDownloadDir( void ); 
    195195#define TR_PREFS_KEY_PROXY                         "proxy"
    196196#define TR_PREFS_KEY_PROXY_TYPE                    "proxy-type"
    197197#define TR_PREFS_KEY_PROXY_USERNAME                "proxy-auth-username"
     198#define TR_PREFS_KEY_QUEUE_ALT_DOWNLOAD_ACTIVE     "queue-alt-download-active"
     199#define TR_PREFS_KEY_QUEUE_ALT_SEED_ACTIVE         "queue-alt-seed-active"
     200#define TR_PREFS_KEY_QUEUE_ENABLED                 "queue-enabled"
     201#define TR_PREFS_KEY_QUEUE_ENQUEUE_NEW_TORRENTS    "queue-enqueue-new-torrents"
     202#define TR_PREFS_KEY_QUEUE_MAX_DOWNLOAD_ACTIVE     "queue-max-download-active"
     203#define TR_PREFS_KEY_QUEUE_MAX_SEED_ACTIVE         "queue-max-seed-active"
     204#define TR_PREFS_KEY_QUEUE_NEW_TORRENTS_TOP        "queue-new-torrents-top"
     205#define TR_PREFS_KEY_QUEUE_SKIP_SLOW_TORRENTS      "queue-skip-slow-enabled"
     206#define TR_PREFS_KEY_QUEUE_SLOW_CUTOFF_KBps        "queue-slow-cutoff"
     207#define TR_PREFS_KEY_QUEUE_SPEED_LIMIT             "queue-speed-limit-enabled"
    198208#define TR_PREFS_KEY_RATIO                         "ratio-limit"
    199209#define TR_PREFS_KEY_RATIO_ENABLED                 "ratio-limit-enabled"
    200210#define TR_PREFS_KEY_RENAME_PARTIAL_FILES          "rename-partial-files"
    tr_bool tr_sessionGetPaused ( const tr_session * ); 
    746756void       tr_sessionSetDeleteSource  ( tr_session *, tr_bool deleteSource );
    747757tr_bool    tr_sessionGetDeleteSource  ( const tr_session * );
    748758
     759/***
     760****
     761***/
     762
     763int         tr_sessionGetQueueAltDownloadActive( const tr_session * session );
     764void        tr_sessionSetQueueAltDownloadActive( tr_session * session, int maxActive );
     765
     766int         tr_sessionGetQueueAltSeedActive( const tr_session * session );
     767void        tr_sessionSetQueueAltSeedActive( tr_session * session, int maxActive );
     768
     769tr_bool     tr_sessionIsQueueEnabled( const tr_session* session );
     770void        tr_sessionSetQueueEnabled( tr_session * session, tr_bool enabled );
     771
     772tr_bool     tr_sessionGetQueueEnqueueNewTorrents( const tr_session* session );
     773void        tr_sessionSetQueueEnqueueNewTorrents( tr_session * session, tr_bool ignore );
     774
     775int         tr_sessionGetQueueMaxDownloadActive( const tr_session * session );
     776void        tr_sessionSetQueueMaxDownloadActive( tr_session * session, int maxActive );
     777
     778int         tr_sessionGetQueueMaxSeedActive( const tr_session * session );
     779void        tr_sessionSetQueueMaxSeedActive( tr_session * session, int maxActive );
     780
     781tr_bool     tr_sessionGetQueueNewTorrentsTop( const tr_session * session );
     782void        tr_sessionSetQueueNewTorrentsTop( tr_session * session, tr_bool enabled );
     783
     784tr_bool     tr_sessionIsQueueSkipSlowTorrentsEnabled( const tr_session * session );
     785void        tr_sessionSetQueueSkipSlowTorrentsEnabled( tr_session * session, tr_bool enabled );
     786
     787int         tr_sessionGetQueueSlowCutoff( const tr_session * session );
     788void        tr_sessionSetQueueSlowCutoff( tr_session * session, int cutoff );
     789
     790tr_bool     tr_sessionIsQueueSpeedLimitEnabled( const tr_session * session );
     791void        tr_sessionSetQueueSpeedLimitEnabled( tr_session * session, tr_bool enabled );
     792
    749793/**
    750794 *  Load all the torrents in tr_getTorrentDir().
    751795 *  This can be used at startup to kickstart all the torrents
    tr_torrent ** tr_sessionLoadTorrents( tr_session * session, 
    755799                                      tr_ctor     * ctor,
    756800                                      int         * setmeCount );
    757801
     802int tr_sessionCountTorrents( const tr_session * session );
     803
    758804/**
    759805***
    760806**/
    tr_torrentSetAnnounceList( tr_torrent * torrent, 
    12971343                           const tr_tracker_info  * trackers,
    12981344                           int                      trackerCount );
    12991345
     1346/**
     1347 * @brief returns if a torrent is queued
     1348 */
     1349tr_bool tr_torrentIsQueued( const tr_torrent * tor );
     1350
     1351/**
     1352 * @brief set if a torrent is queued
     1353 */
     1354void tr_torrentSetQueued( tr_torrent * tor, tr_bool ignore );
     1355
     1356/**
     1357 * @brief returns a torrent's queueRank
     1358 */
     1359int tr_torrentGetQueueRank( const tr_torrent * tor );
     1360
     1361/**
     1362 * @brief sets a torrent's queueRank.
     1363 *
     1364 * The queue starts downloading torrents starting from queueRank 1.
     1365 */
     1366void tr_torrentSetQueueRank( tr_torrent * tor, int rank );
     1367
     1368/**
     1369 * @brief comparator for sorting torrents by queue rank
     1370 *
     1371 * Compares first by queue and then by name and hash for torrents that have
     1372 * equal queue rankings.
     1373 */
     1374int tr_sessionCompareTorrentByQueueRank( const void * va, const void * vb );
     1375
     1376typedef enum
     1377{
     1378    TR_QUEUE_IGNORED,   /* torrents ignored by the queue */
     1379    TR_QUEUE_SEED,      /* queued seeding torrents */
     1380    TR_QUEUE_DOWNLOAD,  /* queued downloading torrents */
     1381    TR_QUEUE_QUEUED,    /* all queued torrents */
     1382    TR_QUEUE_ALL        /* all torrents */
     1383}
     1384tr_queueType;
     1385
     1386typedef enum
     1387{
     1388    TR_QUEUE_UP         = 0,
     1389    TR_QUEUE_DOWN       = 1,
     1390    TR_QUEUE_TOP        = 2,
     1391    TR_QUEUE_BOTTOM     = 3
     1392}
     1393tr_queue_direction;
    13001394
    13011395/**
    13021396***
  • libtransmission/utils.c

    diff --git libtransmission/utils.c libtransmission/utils.c
    index aaa1520..cbc69d0 100644
    tr_realpath( const char * path, char * resolved_path ) 
    15491549#endif
    15501550}
    15511551
     1552int
     1553tr_strcmp0( const char * str1, const char * str2 )
     1554{
     1555    if( str1 && str2 ) return strcmp( str1, str2 );
     1556    if( str1 ) return 1;
     1557    if( str2 ) return -1;
     1558    return 0;
     1559}
     1560
    15521561/***
    15531562****
    15541563****
  • libtransmission/utils.h

    diff --git libtransmission/utils.h libtransmission/utils.h
    index 667f674..f7c220e 100644
    char* tr_formatter_size_B( char * buf, uint64_t bytes, size_t buflen ); 
    592592
    593593void tr_formatter_get_units( struct tr_benc * dict );
    594594
     595int tr_strcmp0( const char * str1, const char * str2 );
     596
    595597/***
    596598****
    597599***/
  • qt/details.cc

    diff --git qt/details.cc qt/details.cc
    index 97d046f..376b2a3 100644
    Details :: refresh( ) 
    723723        myIdleSpin->blockSignals( true );
    724724        myIdleSpin->setValue( tor->seedIdleLimit( ) );
    725725        myIdleSpin->blockSignals( false );
     726
     727        uniform = true;
     728        bool baselineFlag = torrents[0]->queued( );
     729        foreach( tor, torrents ) if( baselineFlag != tor->queued( ) ) { uniform = false; break; }
     730
     731        myQueuedCheck->setChecked( uniform && baselineFlag );
    726732    }
    727733
    728734    ///
    Details :: createInfoTab( ) 
    870876***/
    871877
    872878void
     879Details :: onQueuedToggled( bool val )
     880{
     881    mySession.torrentSet( myIds, "queued", val );
     882    getNewData( );
     883}
     884
     885void
    873886Details :: onShowTrackerScrapesToggled( bool val )
    874887{
    875888    myPrefs.set( Prefs::SHOW_TRACKER_SCRAPES, val );
    Details :: createOptionsTab( ) 
    11461159    h->addWidget( myIdleSpin = s );
    11471160    hig->addRow( tr( "&Idle:" ), h, m );
    11481161
     1162    hig->addSectionDivider( );
     1163    hig->addSectionTitle( tr( "Queue" ) );
     1164
     1165    c = new QCheckBox( tr( "Queued" ) );
     1166    myQueuedCheck = c;
     1167    connect( c, SIGNAL(clicked(bool)), this, SLOT(onQueuedToggled(bool)) );
     1168    hig->addWideControl( c );
    11491169
    11501170    hig->addSectionDivider( );
    11511171    hig->addSectionTitle( tr( "Peer Connections" ) );
  • qt/details.h

    diff --git qt/details.h qt/details.h
    index 8e86855..e5d809e 100644
    class Details: public QDialog 
    8888        QLabel * myETALabel;
    8989        QLabel * myLastActivityLabel;
    9090
     91        QCheckBox * myQueuedCheck;
    9192        QCheckBox * mySessionLimitCheck;
    9293        QCheckBox * mySingleDownCheck;
    9394        QCheckBox * mySingleUpCheck;
    class Details: public QDialog 
    156157        void onEditTrackerClicked( );
    157158        void onRemoveTrackerClicked( );
    158159        void onMaxPeersChanged( int );
     160        void onQueuedToggled( bool );
    159161        void refresh( );
    160162};
    161163
  • qt/filters.cc

    diff --git qt/filters.cc qt/filters.cc
    index a727f76..ca33842 100644
    const QString SortMode::names[NUM_MODES] = { 
    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/filters.h

    diff --git qt/filters.h qt/filters.h
    index 11a3ea4..c3b6e16 100644
    class SortMode 
    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/mainwin.cc

    diff --git qt/mainwin.cc qt/mainwin.cc
    index 69288bf..4f04d56 100644
    TrMainWindow :: TrMainWindow( Session& session, Prefs& prefs, TorrentModel& mode 
    121121    ui.action_Preferences->setIcon( getStockIcon( "preferences-system" ) );
    122122    ui.action_Contents->setIcon( getStockIcon( "help-contents", QStyle::SP_DialogHelpButton ) );
    123123    ui.action_About->setIcon( getStockIcon( "help-about" ) );
     124    ui.action_QueueUp->setIcon( getStockIcon( "go-up", QStyle::SP_ArrowUp ) );
     125    ui.action_QueueDown->setIcon( getStockIcon( "go-down", QStyle::SP_ArrowDown ) );
     126    ui.action_QueueTop->setIcon( getStockIcon( "go-top" ) );
     127    ui.action_QueueBottom->setIcon( getStockIcon( "go-bottom" ) );
    124128
    125129    // ui signals
    126130    connect( ui.action_Toolbar, SIGNAL(toggled(bool)), this, SLOT(setToolbarVisible(bool)));
    TrMainWindow :: TrMainWindow( Session& session, Prefs& prefs, TorrentModel& mode 
    132136    connect( ui.action_SortByETA,      SIGNAL(toggled(bool)), this, SLOT(onSortByETAToggled(bool)));
    133137    connect( ui.action_SortByName,     SIGNAL(toggled(bool)), this, SLOT(onSortByNameToggled(bool)));
    134138    connect( ui.action_SortByProgress, SIGNAL(toggled(bool)), this, SLOT(onSortByProgressToggled(bool)));
     139    connect( ui.action_SortByQueue,    SIGNAL(toggled(bool)), this, SLOT(onSortByQueueToggled(bool)));
    135140    connect( ui.action_SortByRatio,    SIGNAL(toggled(bool)), this, SLOT(onSortByRatioToggled(bool)));
    136141    connect( ui.action_SortBySize,     SIGNAL(toggled(bool)), this, SLOT(onSortBySizeToggled(bool)));
    137142    connect( ui.action_SortByState,    SIGNAL(toggled(bool)), this, SLOT(onSortByStateToggled(bool)));
    138143    connect( ui.action_ReverseSortOrder, SIGNAL(toggled(bool)), this, SLOT(setSortAscendingPref(bool)));
     144    connect( ui.action_Queued, SIGNAL(triggered(bool)), this, SLOT(setQueued(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()));
    140150    connect( ui.action_Pause, SIGNAL(triggered()), this, SLOT(pauseSelected()));
    141151    connect( ui.action_Remove, SIGNAL(triggered()), this, SLOT(removeSelected()));
    TrMainWindow :: TrMainWindow( Session& session, Prefs& prefs, TorrentModel& mode 
    174184            << ui.action_Pause
    175185            << ui.action_CopyMagnetToClipboard
    176186            << sep3
     187            << ui.action_Queued
    177188            << ui.action_Verify
    178189            << ui.action_SetLocation
    179190            << sep
    TrMainWindow :: TrMainWindow( Session& session, Prefs& prefs, TorrentModel& mode 
    209220    actionGroup->addAction( ui.action_SortByETA );
    210221    actionGroup->addAction( ui.action_SortByName );
    211222    actionGroup->addAction( ui.action_SortByProgress );
     223    actionGroup->addAction( ui.action_SortByQueue );
    212224    actionGroup->addAction( ui.action_SortByRatio );
    213225    actionGroup->addAction( ui.action_SortBySize );
    214226    actionGroup->addAction( ui.action_SortByState );
    void 
    520532TrMainWindow :: setSortPref( int i )
    521533{
    522534    myPrefs.set( Prefs::SORT_MODE, SortMode( i ) );
     535    refreshActionSensitivity( );
    523536}
    524537void TrMainWindow :: onSortByActivityToggled ( bool b ) { if( b ) setSortPref( SortMode::SORT_BY_ACTIVITY ); }
    525538void TrMainWindow :: onSortByAgeToggled      ( bool b ) { if( b ) setSortPref( SortMode::SORT_BY_AGE );      }
    526539void TrMainWindow :: onSortByETAToggled      ( bool b ) { if( b ) setSortPref( SortMode::SORT_BY_ETA );      }
    527540void TrMainWindow :: onSortByNameToggled     ( bool b ) { if( b ) setSortPref( SortMode::SORT_BY_NAME );     }
    528541void TrMainWindow :: onSortByProgressToggled ( bool b ) { if( b ) setSortPref( SortMode::SORT_BY_PROGRESS ); }
     542void TrMainWindow :: onSortByQueueToggled    ( bool b ) { if( b ) setSortPref( SortMode::SORT_BY_QUEUE );    }
    529543void TrMainWindow :: onSortByRatioToggled    ( bool b ) { if( b ) setSortPref( SortMode::SORT_BY_RATIO );    }
    530544void TrMainWindow :: onSortBySizeToggled     ( bool b ) { if( b ) setSortPref( SortMode::SORT_BY_SIZE );     }
    531545void TrMainWindow :: onSortByStateToggled    ( bool b ) { if( b ) setSortPref( SortMode::SORT_BY_STATE );    }
    TrMainWindow :: setSortAscendingPref( bool b ) 
    541555****/
    542556
    543557void
     558TrMainWindow :: setQueued( bool b )
     559{
     560    mySession.torrentSet( getSelectedTorrents( ), "queued", b );
     561    mySession.refreshActiveTorrents( );
     562}
     563
     564namespace
     565{
     566    QList<int>
     567    reverse( const QList<int> &list )
     568    {
     569        QList<int> ret;
     570        for( int i = list.count(); i > 0; --i )
     571            ret << list[i-1];
     572        return ret;
     573    }
     574}
     575
     576void
     577TrMainWindow :: moveQueue( int dir )
     578{
     579    QMap<int, int> map;
     580
     581    foreach( int id, getSelectedTorrents( ) )
     582        map.insert( myModel.getTorrentFromId( id )->queueRank(), id );
     583
     584    switch( dir )
     585    {
     586        case TR_QUEUE_DOWN:
     587            if( myModel.getTorrentFromId( map.values().last() )->queueRank() == myModel.rowCount() )
     588                break;
     589        case TR_QUEUE_TOP:
     590            mySession.torrentSet( reverse( map.values() ), "moveQueueRank", dir );
     591            break;
     592        case TR_QUEUE_UP:
     593            if( myModel.getTorrentFromId( map.values().first() )->queueRank() == 1 )
     594                break;
     595        case TR_QUEUE_BOTTOM:
     596            mySession.torrentSet( map.values(), "moveQueueRank", dir );
     597            break;
     598    }
     599
     600    mySession.refreshActiveTorrents( );
     601}
     602
     603void
     604TrMainWindow :: moveQueueUp( )
     605{
     606    moveQueue( TR_QUEUE_UP );
     607}
     608
     609void
     610TrMainWindow :: moveQueueDown( )
     611{
     612    moveQueue( TR_QUEUE_DOWN );
     613}
     614
     615void
     616TrMainWindow :: moveQueueTop( )
     617{
     618    moveQueue( TR_QUEUE_TOP );
     619}
     620
     621void
     622TrMainWindow :: moveQueueBottom( )
     623{
     624    moveQueue( TR_QUEUE_BOTTOM );
     625}
     626
     627/****
     628*****
     629****/
     630
     631void
    544632TrMainWindow :: onPrefsDestroyed( )
    545633{
    546634    myPrefsDialog = 0;
    TrMainWindow :: refreshActionSensitivity( ) 
    700788    int paused( 0 );
    701789    int selectedAndPaused( 0 );
    702790    int canAnnounce( 0 );
     791    int queued( 0 );
    703792    const QAbstractItemModel * model( ui.listView->model( ) );
    704793    const QItemSelectionModel * selectionModel( ui.listView->selectionModel( ) );
    705794    const int rowCount( model->rowCount( ) );
    TrMainWindow :: refreshActionSensitivity( ) 
    709798        const QModelIndex modelIndex( model->index( row, 0 ) );
    710799        assert( model == modelIndex.model( ) );
    711800        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;
     801        const bool isSelected( selectionModel->isSelected( modelIndex ) );
     802        const bool isPaused( tor->isPaused( ) );
     803
     804        if( isSelected )
     805        {
     806            ++selected;
     807            queued += tor->queued( ) ? 1 : 0;
    723808        }
     809        if( isPaused )
     810            ++ paused;
     811        if( isSelected && isPaused )
     812            ++selectedAndPaused;
     813        if( tor->canManualAnnounce( ) )
     814            ++canAnnounce;
    724815    }
    725816
    726817    const bool haveSelection( selected > 0 );
    TrMainWindow :: refreshActionSensitivity( ) 
    735826    ui.action_OpenFolder->setEnabled( oneSelection && mySession.isLocal( ) );
    736827    ui.action_CopyMagnetToClipboard->setEnabled( oneSelection );
    737828
     829    const bool queueSort( ( myPrefs.get<SortMode>(Prefs::SORT_MODE).mode() == SortMode::SORT_BY_QUEUE ) && haveSelection );
     830    ui.action_QueueUp->setEnabled( queueSort );
     831    ui.action_QueueDown->setEnabled( queueSort );
     832    ui.action_QueueTop->setEnabled( queueSort );
     833    ui.action_QueueBottom->setEnabled( queueSort );
     834    ui.action_Queued->setEnabled( haveSelection );
     835
     836    if( abs( queued ) == selected && queued > 0 )
     837        ui.action_Queued->setChecked( true );
     838    else
     839        ui.action_Queued->setChecked( false );
     840
    738841    ui.action_SelectAll->setEnabled( selected < rowCount );
    739842    ui.action_StartAll->setEnabled( paused > 0 );
    740843    ui.action_PauseAll->setEnabled( paused < rowCount );
    TrMainWindow :: refreshPref( int key ) 
    9131016            ui.action_SortByETA->setChecked      ( i == SortMode::SORT_BY_ETA );
    9141017            ui.action_SortByName->setChecked     ( i == SortMode::SORT_BY_NAME );
    9151018            ui.action_SortByProgress->setChecked ( i == SortMode::SORT_BY_PROGRESS );
     1019            ui.action_SortByQueue->setChecked    ( i == SortMode::SORT_BY_QUEUE );
    9161020            ui.action_SortByRatio->setChecked    ( i == SortMode::SORT_BY_RATIO );
    9171021            ui.action_SortBySize->setChecked     ( i == SortMode::SORT_BY_SIZE );
    9181022            ui.action_SortByState->setChecked    ( i == SortMode::SORT_BY_STATE );
  • qt/mainwin.h

    diff --git qt/mainwin.h qt/mainwin.h
    index da95f40..82ee51c 100644
    class TrMainWindow: public QMainWindow 
    8989        QSet<int> getSelectedTorrents( ) const;
    9090        void updateNetworkIcon( );
    9191        QWidgetList myHidden;
     92        void moveQueue( int dir );
    9293
    9394    public slots:
    9495        void openURL( QString );
    class TrMainWindow: public QMainWindow 
    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 setQueued               ( bool );
     144        void moveQueueUp             ( );
     145        void moveQueueDown           ( );
     146        void moveQueueTop            ( );
     147        void moveQueueBottom         ( );
    141148
    142149    private:
    143150        QWidget * myFilterBar;
  • qt/mainwin.ui

    diff --git qt/mainwin.ui qt/mainwin.ui
    index 25a5d8b..203c639 100644
     
    7373    <addaction name="action_Pause"/>
    7474    <addaction name="action_CopyMagnetToClipboard"/>
    7575    <addaction name="separator"/>
     76    <addaction name="action_Queued"/>
    7677    <addaction name="action_SetLocation"/>
    7778    <addaction name="action_Verify"/>
    7879    <addaction name="separator"/>
     
    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">
     
    412419    <string>Sort by &amp;Progress</string>
    413420   </property>
    414421  </action>
     422  <action name="action_SortByQueue">
     423   <property name="checkable">
     424    <bool>true</bool>
     425   </property>
     426   <property name="text">
     427    <string>Sort by &amp;Queue</string>
     428   </property>
     429  </action>
    415430  <action name="action_SortByRatio">
    416431   <property name="checkable">
    417432    <bool>true</bool>
     
    581596    <string>&amp;Donate</string>
    582597   </property>
    583598  </action>
     599  <action name="action_QueueUp">
     600   <property name="text">
     601    <string>Queue &amp;Up</string>
     602   </property>
     603   <property name="toolTip">
     604    <string>Move up queue</string>
     605   </property>
     606   <property name="priority">
     607    <enum>QAction::LowPriority</enum>
     608   </property>
     609  </action>
     610  <action name="action_QueueDown">
     611   <property name="text">
     612    <string>Queue &amp;Down</string>
     613   </property>
     614   <property name="toolTip">
     615    <string>Move down queue</string>
     616   </property>
     617   <property name="priority">
     618    <enum>QAction::LowPriority</enum>
     619   </property>
     620  </action>
     621  <action name="action_QueueTop">
     622   <property name="text">
     623    <string>Queue &amp;Top</string>
     624   </property>
     625   <property name="toolTip">
     626    <string>Move to top of queue</string>
     627   </property>
     628   <property name="priority">
     629    <enum>QAction::LowPriority</enum>
     630   </property>
     631  </action>
     632  <action name="action_QueueBottom">
     633   <property name="text">
     634    <string>Queue &amp;Bottom</string>
     635   </property>
     636   <property name="toolTip">
     637    <string>Move to bottom of  queue</string>
     638   </property>
     639   <property name="priority">
     640    <enum>QAction::LowPriority</enum>
     641   </property>
     642  </action>
     643  <action name="action_Queued">
     644   <property name="text">
     645    <string>&amp;Queued</string>
     646   </property>
     647   <property name="checkable">
     648    <bool>true</bool>
     649   </property>
     650  </action>
    584651 </widget>
    585652 <resources>
    586653  <include location="application.qrc"/>
  • qt/prefs-dialog.cc

    diff --git qt/prefs-dialog.cc qt/prefs-dialog.cc
    index 54109b7..45f1d82 100644
    PrefsDialog :: createWebTab( Session& session ) 
    251251****
    252252***/
    253253
     254QWidget *
     255PrefsDialog :: createQueueTab( )
     256{
     257    QWidget *l, *r;
     258    HIG * hig = new HIG( this );
     259    const QString speed_K_str = Formatter::unitStr( Formatter::SPEED, Formatter::KB );
     260
     261    hig->addSectionTitle( tr( "Queue Options" ) );
     262
     263        l = checkBoxNew( tr( "Enable &Queue" ), Prefs::QUEUE_ENABLED );
     264        hig->addWideControl( l );
     265
     266        l = checkBoxNew( tr( "Don't s&tart new torrents if a speed limit is reached" ), Prefs::QUEUE_SPEED_LIMIT );
     267        hig->addWideControl( l );
     268
     269        l = checkBoxNew( tr( "Enqueue &new torrents" ), Prefs::QUEUE_ENQUEUE_NEW_TORRENTS );
     270        hig->addWideControl( l );
     271
     272        l = checkBoxNew( tr( "&Add new torrents to the top of the queue" ), Prefs::QUEUE_NEW_TORRENTS_TOP );
     273        hig->addWideControl( l );
     274
     275        l = checkBoxNew( tr( "Skip slo&w torrents (%1):" ).arg( speed_K_str ), Prefs::QUEUE_SKIP_SLOW_TORRENTS );
     276        r = spinBoxNew( Prefs::QUEUE_SLOW_CUTOFF, 0, INT_MAX, 1 );
     277        hig->addRow( l, r );
     278        enableBuddyWhenChecked( qobject_cast<QCheckBox*>(l), r );
     279
     280    hig->addSectionTitle( tr( "Queue Limits" ) );
     281
     282        r = spinBoxNew( Prefs::QUEUE_MAX_DOWNLOAD_ACTIVE, 0, INT_MAX, 1 );
     283        hig->addRow( tr( "Maximum &downloads active:" ), r );
     284
     285        r = spinBoxNew( Prefs::QUEUE_MAX_SEED_ACTIVE, 0, INT_MAX, 1 );
     286        hig->addRow( tr( "Maximum &seeds active:" ), r );
     287
     288    hig->addSectionTitle( tr( "Temporary Queue Limits" ) );
     289
     290        QString s = tr( "<small>Queue limits used when temporary speed limit is active</small>" );
     291        hig->addWideControl( new QLabel( s ) );
     292
     293        r = spinBoxNew( Prefs::QUEUE_ALT_DOWNLOAD_ACTIVE, 0, INT_MAX, 1 );
     294        hig->addRow( tr( "Maximum d&ownloads active:" ), r );
     295
     296        r = spinBoxNew( Prefs::QUEUE_ALT_SEED_ACTIVE, 0, INT_MAX, 1 );
     297        hig->addRow( tr( "Maximum s&eeds active:" ), r );
     298
     299    hig->finish( );
     300    return hig;
     301}
     302
     303/***
     304****
     305***/
     306
    254307void
    255308PrefsDialog :: altSpeedDaysEdited( int i )
    256309{
    PrefsDialog :: PrefsDialog( Session& session, Prefs& prefs, QWidget * parent ): 
    634687
    635688    QTabWidget * t = new QTabWidget( this );
    636689    t->addTab( createTorrentsTab( ),     tr( "Torrents" ) );
     690    t->addTab( createQueueTab( ),        tr( "Queue" ) );
    637691    t->addTab( createSpeedTab( ),        tr( "Speed" ) );
    638692    t->addTab( createPrivacyTab( ),      tr( "Privacy" ) );
    639693    t->addTab( createNetworkTab( ),      tr( "Network" ) );
  • qt/prefs-dialog.h

    diff --git qt/prefs-dialog.h qt/prefs-dialog.h
    index 2dc212f..09d7260 100644
    class PrefsDialog: public QDialog 
    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/prefs.cc

    diff --git qt/prefs.cc qt/prefs.cc
    index e917a5d..cce5a15 100644
    Prefs::PrefItem Prefs::myItems[] = 
    9292    { PEER_PORT_RANDOM_ON_START, TR_PREFS_KEY_PEER_PORT_RANDOM_ON_START, QVariant::Bool },
    9393    { PEER_PORT_RANDOM_LOW, TR_PREFS_KEY_PEER_PORT_RANDOM_LOW, QVariant::Int },
    9494    { PEER_PORT_RANDOM_HIGH, TR_PREFS_KEY_PEER_PORT_RANDOM_HIGH, QVariant::Int },
     95    { QUEUE_ALT_DOWNLOAD_ACTIVE, TR_PREFS_KEY_QUEUE_ALT_DOWNLOAD_ACTIVE, QVariant::Int },
     96    { QUEUE_ALT_SEED_ACTIVE, TR_PREFS_KEY_QUEUE_ALT_SEED_ACTIVE, QVariant::Int },
     97    { QUEUE_ENABLED, TR_PREFS_KEY_QUEUE_ENABLED, QVariant::Bool },
     98    { QUEUE_ENQUEUE_NEW_TORRENTS, TR_PREFS_KEY_QUEUE_ENQUEUE_NEW_TORRENTS, QVariant::Bool },
     99    { QUEUE_MAX_DOWNLOAD_ACTIVE, TR_PREFS_KEY_QUEUE_MAX_DOWNLOAD_ACTIVE, QVariant::Int },
     100    { QUEUE_MAX_SEED_ACTIVE, TR_PREFS_KEY_QUEUE_MAX_SEED_ACTIVE, QVariant::Int },
     101    { QUEUE_NEW_TORRENTS_TOP, TR_PREFS_KEY_QUEUE_NEW_TORRENTS_TOP, QVariant::Bool },
     102    { QUEUE_SKIP_SLOW_TORRENTS, TR_PREFS_KEY_QUEUE_SKIP_SLOW_TORRENTS, QVariant::Bool },
     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 },
    95105    { SCRIPT_TORRENT_DONE_ENABLED, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_ENABLED, QVariant::Bool },
    96106    { SCRIPT_TORRENT_DONE_FILENAME, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_FILENAME, QVariant::String },
    97107    { SOCKET_TOS, TR_PREFS_KEY_PEER_SOCKET_TOS, QVariant::Int },
  • qt/prefs.h

    diff --git qt/prefs.h qt/prefs.h
    index f640bfd..b7a0221 100644
    class Prefs: public QObject 
    9797            PEER_PORT_RANDOM_ON_START,
    9898            PEER_PORT_RANDOM_LOW,
    9999            PEER_PORT_RANDOM_HIGH,
     100            QUEUE_ALT_DOWNLOAD_ACTIVE,
     101            QUEUE_ALT_SEED_ACTIVE,
     102            QUEUE_ENABLED,
     103            QUEUE_ENQUEUE_NEW_TORRENTS,
     104            QUEUE_MAX_DOWNLOAD_ACTIVE,
     105            QUEUE_MAX_SEED_ACTIVE,
     106            QUEUE_NEW_TORRENTS_TOP,
     107            QUEUE_SKIP_SLOW_TORRENTS,
     108            QUEUE_SLOW_CUTOFF,
     109            QUEUE_SPEED_LIMIT,
    100110            SCRIPT_TORRENT_DONE_ENABLED,
    101111            SCRIPT_TORRENT_DONE_FILENAME,
    102112            SOCKET_TOS,
  • qt/session.cc

    diff --git qt/session.cc qt/session.cc
    index dc14d24..b8d3add 100644
    Session :: updatePref( int key ) 
    156156        case Prefs :: PEER_PORT_RANDOM_ON_START:
    157157        case Prefs :: PEX_ENABLED:
    158158        case Prefs :: PORT_FORWARDING:
     159        case Prefs :: QUEUE_ALT_DOWNLOAD_ACTIVE:
     160        case Prefs :: QUEUE_ALT_SEED_ACTIVE:
     161        case Prefs :: QUEUE_ENABLED:
     162        case Prefs :: QUEUE_ENQUEUE_NEW_TORRENTS:
     163        case Prefs :: QUEUE_MAX_DOWNLOAD_ACTIVE:
     164        case Prefs :: QUEUE_MAX_SEED_ACTIVE:
     165        case Prefs :: QUEUE_NEW_TORRENTS_TOP:
     166        case Prefs :: QUEUE_SKIP_SLOW_TORRENTS:
     167        case Prefs :: QUEUE_SLOW_CUTOFF:
     168        case Prefs :: QUEUE_SPEED_LIMIT:
    159169        case Prefs :: SCRIPT_TORRENT_DONE_ENABLED:
    160170        case Prefs :: SCRIPT_TORRENT_DONE_FILENAME:
    161171        case Prefs :: START:
    namespace 
    380390                tr_bencListAddInt( idList, i );
    381391        }
    382392    }
     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    }
     404}
     405
     406void
     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 );
    383417}
    384418
    385419void
  • qt/session.h

    diff --git qt/session.h qt/session.h
    index eaa9a41..2c1b793 100644
    class Session: public QObject 
    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 );
  • qt/torrent-filter.cc

    diff --git qt/torrent-filter.cc qt/torrent-filter.cc
    index ed620e1..5cd2313 100644
    TorrentFilter :: lessThan( const QModelIndex& left, const QModelIndex& right ) c 
    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;
    TorrentFilter :: activityFilterAcceptsTorrent( const Torrent * tor, const Filter 
    163166            accepts = tor->isFinished( );
    164167            break;
    165168        case FilterMode::SHOW_QUEUED:
    166             accepts = tor->isWaitingToVerify( );
     169            accepts = tor->queued();
    167170            break;
    168171        case FilterMode::SHOW_VERIFYING:
    169             accepts = tor->isVerifying( );
     172            accepts = tor->isWaitingToVerify( ) || tor->isVerifying( );
    170173            break;
    171174        case FilterMode::SHOW_ERROR:
    172175            accepts = tor->hasError( );
  • qt/torrent.cc

    diff --git qt/torrent.cc qt/torrent.cc
    index f396ce1..de9e3e8 100644
    Torrent :: myProperties[] = 
    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 },
    Torrent :: activityString( ) const 
    687689        case TR_STATUS_CHECK:      str = tr( "Verifying local data" ); break;
    688690        case TR_STATUS_DOWNLOAD:   str = tr( "Downloading" ); break;
    689691        case TR_STATUS_SEED:       str = tr( "Seeding" ); break;
    690         case TR_STATUS_STOPPED:    str = isFinished() ? tr( "Finished" ): tr( "Paused" ); break;
     692        case TR_STATUS_STOPPED:
     693            str = ( myPrefs.getBool( Prefs::QUEUE_ENABLED ) && queued() )
     694                ? tr( "Queued" )
     695                : ( isFinished() ? tr( "Finished" ) : tr( "Paused" ) ); break;
    691696    }
    692697
    693698    return str;
  • qt/torrent.h

    diff --git qt/torrent.h qt/torrent.h
    index 211f949..054d700 100644
    class Torrent: public QObject 
    146146            DATE_CREATED,
    147147            PEERS_CONNECTED,
    148148            ETA,
     149            QUEUED,
     150            QUEUE_RANK,
    149151            RATIO,
    150152            DOWNLOADED_EVER,
    151153            UPLOADED_EVER,
    class Torrent: public QObject 
    272274        int compareETA( const Torrent& ) const;
    273275        bool hasETA( ) const { return getETA( ) >= 0; }
    274276        int getETA( ) const { return getInt( ETA ); }
     277        bool queued( ) const { return getBool( QUEUED ); }
     278        int queueRank( ) const { return getInt( QUEUE_RANK ); }
    275279        QDateTime lastActivity( ) const { return getDateTime( DATE_ACTIVITY ); }
    276280        QDateTime lastStarted( ) const { return getDateTime( DATE_STARTED ); }
    277281        QDateTime dateAdded( ) const { return getDateTime( DATE_ADDED ); }
  • web/javascript/torrent.js

    diff --git web/javascript/torrent.js web/javascript/torrent.js
    index b7ccd4f..cf8cf71 100644
    Torrent._MetaDataFields = [ 'addedDate', 'comment', 'creator', 'dateCreated', 
    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 =
    Torrent.prototype = 
    249249        getPercentDoneStr: function() {
    250250                return Transmission.fmt.percentString( 100 * this.getPercentDone() );
    251251        },
     252        queued: 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                                return ( this._controller._prefs['queue-enabled'] && this.queued() )
     262                                        ? 'Queued'
     263                    : ( this.isFinished() ? 'Seeding complete' : 'Paused' );
    259264                        case Torrent._StatusChecking:       return 'Verifying local data';
    260265                        case Torrent._StatusWaitingToCheck: return 'Waiting to verify';
    261266                        default:                            return 'error';
    Torrent.prototype = 
    400405                this._error                   = data.error;
    401406                this._error_string            = data.errorString;
    402407                this._eta                     = data.eta;
     408                this._queued                  = data.queued;
     409                this._queueRank               = data.queueRank;
    403410                this._trackerStats            = this.buildTrackerStats(data.trackerStats);
    404411                this._state                   = data.status;
    405412                this._download_dir            = data.downloadDir;
    Torrent.compareById = function( a, b ) { 
    749756};
    750757
    751758/** Helper function for sortTorrents(). */
     759Torrent.compareByQueue = function( a, b ) {
     760        return a.queueRank() - b.queueRank();
     761};
     762
     763/** Helper function for sortTorrents(). */
    752764Torrent.compareByAge = function( a, b ) {
    753765        return a.dateAdded() - b.dateAdded();
    754766};
    Torrent.sortTorrents = function( torrents, sortMethod, sortDirection ) 
    793805                        torrents.sort( this.compareByAge );
    794806                        break;
    795807                case Prefs._SortByQueue:
    796                         torrents.sort( this.compareById );
     808                        torrents.sort( this.compareByQueue );
    797809                        break;
    798810                case Prefs._SortByProgress:
    799811                        torrents.sort( this.compareByProgress );