Changeset 3


Ignore:
Timestamp:
Jan 12, 2006, 6:29:20 PM (15 years ago)
Author:
root
Message:

Update 2005-11-17

Location:
trunk
Files:
4 added
8 deleted
24 edited

Legend:

Unmodified
Added
Removed
  • trunk/AUTHORS

    r1 r3  
    2727Martin Stadtmueller
    2828 + Icon tweaking
     29
     30John Blitch
     31 + Contextual menu patch
  • trunk/Jamfile

    r2 r3  
    3434        macosx/ProgressCell.m
    3535        macosx/main.m
     36        macosx/TorrentTableView.h
     37        macosx/TorrentTableView.m
    3638        macosx/Transmission.xcodeproj/project.pbxproj
    3739        macosx/Transmission_Prefix.pch
  • trunk/libtransmission/Jamfile

    r1 r3  
    33LIBTRANSMISSION_SRC =
    44    transmission.c bencode.c net.c tracker.c peer.c inout.c
    5     metainfo.c sha1.c utils.c upload.c fdlimit.c clients.c ;
     5    metainfo.c sha1.c utils.c upload.c fdlimit.c clients.c
     6    completion.c ;
    67
    78Library       libtransmission.a       : $(LIBTRANSMISSION_SRC) ;
  • trunk/libtransmission/fastresume.h

    r2 r3  
    102102    char    * path;
    103103    int     * fileMTimes;
    104     int       i;
    105104    uint8_t * blockBitfield;
    106105
     
    131130
    132131    /* Build and write the bitfield for blocks */
    133     blockBitfield = calloc( ( tor->blockCount + 7 ) / 8, 1 );
    134     for( i = 0; i < tor->blockCount; i++ )
    135     {
    136         if( tor->blockHave[i] < 0 )
    137         {
    138             tr_bitfieldAdd( blockBitfield, i );
    139         }
    140     }
     132    blockBitfield = tr_cpBlockBitfield( tor->completion );
    141133    fwrite( blockBitfield, ( tor->blockCount + 7 ) / 8, 1, file );
    142     free( blockBitfield );
    143134
    144135    /* Write the 'slotPiece' table */
     
    223214    blockBitfield = calloc( ( tor->blockCount + 7 ) / 8, 1 );
    224215    fread( blockBitfield, ( tor->blockCount + 7 ) / 8, 1, file );
    225     tor->blockHaveCount = 0;
    226     for( i = 0; i < tor->blockCount; i++ )
    227     {
    228         if( tr_bitfieldHas( blockBitfield, i ) )
    229         {
    230             tor->blockHave[i] = -1;
    231             (tor->blockHaveCount)++;
    232         }
    233     }
     216    tr_cpBlockBitfieldSet( tor->completion, blockBitfield );
    234217    free( blockBitfield );
    235218
     
    254237            }
    255238        }
    256 
    257         for( j = tr_pieceStartBlock( i );
    258              j < tr_pieceStartBlock( i ) + tr_pieceCountBlocks( i );
    259              j++ )
    260         {
    261             if( tor->blockHave[j] > -1 )
    262             {
    263                 break;
    264             }
    265         }
    266         if( j >= tr_pieceStartBlock( i ) + tr_pieceCountBlocks( i ) )
    267         {
    268             // tr_dbg( "Piece %d is complete", i );
    269             tr_bitfieldAdd( tor->bitfield, i );
    270         }
    271239    }
    272240    // tr_dbg( "Slot used: %d", io->slotsUsed );
  • trunk/libtransmission/fdlimit.c

    r1 r3  
    3535#define STATUS_UNUSED  2
    3636#define STATUS_USED    4
     37#define STATUS_CLOSING 8
    3738    int        status;
    3839
     
    112113            !strcmp( path, f->open[i].path ) )
    113114        {
     115            if( f->open[i].status & STATUS_CLOSING )
     116            {
     117                /* Wait until the file is closed */
     118                tr_lockUnlock( f->lock );
     119                tr_wait( 10 );
     120                tr_lockLock( f->lock );
     121                i = -1;
     122                continue;
     123            }
    114124            winner = i;
    115125            goto done;
     
    135145        for( i = 0; i < TR_MAX_OPEN_FILES; i++ )
    136146        {
    137             if( f->open[i].status & STATUS_USED )
     147            if( !( f->open[i].status & STATUS_UNUSED ) )
    138148            {
    139149                continue;
     
    148158        if( winner >= 0 )
    149159        {
     160            /* Close the file: we mark it as closing then release the
     161               lock while doing so, because fclose may take same time
     162               and we don't want to block other threads */
    150163            tr_dbg( "Closing %s", f->open[winner].path );
     164            f->open[winner].status = STATUS_CLOSING;
     165            tr_lockUnlock( f->lock );
    151166            fclose( f->open[winner].file );
     167            tr_lockLock( f->lock );
    152168            goto open;
    153169        }
  • trunk/libtransmission/inout.c

    r1 r3  
    137137    for( i = startBlock; i < endBlock; i++ )
    138138    {
    139         if( tor->blockHave[i] >= 0 )
     139        if( !tr_cpBlockIsComplete( tor->completion, i ) )
    140140        {
    141141            /* The piece is not complete */
     
    160160        for( i = startBlock; i < endBlock; i++ )
    161161        {
    162             tor->blockHave[i]    = 0;
    163             tor->blockHaveCount -= 1;
     162            tr_cpBlockRem( tor->completion, i );
    164163        }
    165164    }
     
    168167        tr_inf( "Piece %d (slot %d): hash OK", index,
    169168                io->pieceSlot[index] );
    170         tr_bitfieldAdd( tor->bitfield, index );
     169        tr_cpPieceAdd( tor->completion, index );
    171170    }
    172171
     
    267266    uint8_t * buf;
    268267    uint8_t hash[SHA_DIGEST_LENGTH];
    269     int startBlock, endBlock;
    270268
    271269    io->pieceSlot = malloc( inf->pieceCount * sizeof( int ) );
     
    282280    memset( io->pieceSlot, 0xFF, inf->pieceCount * sizeof( int ) );
    283281    memset( io->slotPiece, 0xFF, inf->pieceCount * sizeof( int ) );
    284     memset( tor->bitfield, 0, ( inf->pieceCount + 7 ) / 8 );
    285     memset( tor->blockHave, 0, tor->blockCount );
    286     tor->blockHaveCount = 0;
    287282
    288283    /* Check pieces */
     
    305300            if( !memcmp( hash, &inf->pieces[20*j], SHA_DIGEST_LENGTH ) )
    306301            {
    307                 int k;
    308302                io->pieceSlot[j] = i;
    309303                io->slotPiece[i] = j;
    310                 tr_bitfieldAdd( tor->bitfield, j );
    311 
    312                 startBlock = tr_pieceStartBlock( j );
    313                 endBlock   = startBlock + tr_pieceCountBlocks( j );
    314                 for( k = startBlock; k < endBlock; k++ )
    315                 {
    316                     tor->blockHave[k] = -1;
    317                     tor->blockHaveCount++;
    318                 }
     304
     305                tr_cpPieceAdd( tor->completion, j );
    319306                break;
    320307            }
     
    333320            io->pieceSlot[inf->pieceCount - 1] = i;
    334321            io->slotPiece[i]                   = inf->pieceCount - 1;
    335             tr_bitfieldAdd( tor->bitfield, inf->pieceCount - 1 );
    336 
    337             startBlock = tr_pieceStartBlock( inf->pieceCount - 1 );
    338             endBlock   = startBlock +
    339                 tr_pieceCountBlocks( inf->pieceCount - 1 );
    340             for( j = startBlock; j < endBlock; j++ )
    341             {
    342                 tor->blockHave[j] = -1;
    343                 tor->blockHaveCount++;
    344             }
     322
     323            tr_cpPieceAdd( tor->completion, inf->pieceCount - 1 );
    345324        }
    346325    }
     
    411390    {
    412391        asprintf( &path, "%s/%s", tor->destination, inf->files[i].name );
     392        tr_lockUnlock( tor->lock );
    413393        file = tr_fdFileOpen( tor->fdlimit, path );
     394        tr_lockLock( tor->lock );
    414395        free( path );
    415396
     
    422403                          (uint64_t) size );
    423404
     405        tr_lockUnlock( tor->lock );
    424406        if( fseeko( file, posInFile, SEEK_SET ) )
    425407        {
     408            tr_lockLock( tor->lock );
    426409            return 1;
    427410        }
     
    430413            if( fwrite( buf, willRead, 1, file ) != 1 )
    431414            {
     415                tr_lockLock( tor->lock );
    432416                return 1;
    433417            }
     
    437421            if( fread( buf, willRead, 1, file ) != 1 )
    438422            {
     423                tr_lockLock( tor->lock );
    439424                return 1;
    440425            }
    441426        }
    442 
     427        tr_lockLock( tor->lock );
    443428        tr_fdFileRelease( tor->fdlimit, file );
    444429
  • trunk/libtransmission/internal.h

    r2 r3  
    108108
    109109typedef struct tr_torrent_s tr_torrent_t;
     110typedef struct tr_completion_s tr_completion_t;
    110111
    111112#include "bencode.h"
     
    130131
    131132    char            * id;
     133    char            * key;
    132134
    133135    /* An escaped string used to include the hash in HTTP queries */
     
    143145    int               blockCount;
    144146   
     147#if 0
    145148    /* Status for each block
    146149       -1 = we have it
     
    149152    int               blockHaveCount;
    150153    uint8_t         * bitfield;
     154#endif
     155    tr_completion_t * completion;
    151156
    152157    volatile char     die;
     
    171176
    172177#include "utils.h"
     178#include "completion.h"
    173179
    174180struct tr_handle_s
     
    183189
    184190    char           id[21];
     191    char           key[21];
    185192    char           prefsDirectory[256];
    186193};
  • trunk/libtransmission/peer.c

    r2 r3  
    170170        r     = &peer->inRequests[j];
    171171        block = tr_block( r->index,r->begin );
    172         if( tor->blockHave[block] > 0 )
    173         {
    174           (tor->blockHave[block])--;
    175         }
     172        tr_cpDownloaderRem( tor->completion, block );
    176173    }
    177174    if( !peer->amChoking )
     
    272269        peer = tor->peers[i];
    273270
    274         /* Connect */
    275         if( ( peer->status & PEER_STATUS_IDLE ) &&
    276             !tr_fdSocketWillCreate( tor->fdlimit, 0 ) )
    277         {
    278             peer->socket = tr_netOpen( peer->addr, peer->port );
    279             if( peer->socket < 0 )
    280             {
    281                 peer_dbg( "connection failed" );
    282                 goto dropPeer;
    283             }
    284             peer->status = PEER_STATUS_CONNECTING;
    285         }
    286 
    287         /* Try to send handshake */
    288         if( peer->status & PEER_STATUS_CONNECTING )
    289         {
    290             uint8_t buf[68];
    291             tr_info_t * inf = &tor->info;
    292 
    293             buf[0] = 19;
    294             memcpy( &buf[1], "BitTorrent protocol", 19 );
    295             memset( &buf[20], 0, 8 );
    296             memcpy( &buf[28], inf->hash, 20 );
    297             memcpy( &buf[48], tor->id, 20 );
    298 
    299             ret = tr_netSend( peer->socket, buf, 68 );
     271        if( peer->status < PEER_STATUS_HANDSHAKE )
     272        {
     273            i++;
     274            continue;
     275        }
     276
     277        /* Try to read */
     278        for( ;; )
     279        {
     280            if( peer->size < 1 )
     281            {
     282                peer->size = 1024;
     283                peer->buf  = malloc( peer->size );
     284            }
     285            else if( peer->pos >= peer->size )
     286            {
     287                peer->size *= 2;
     288                peer->buf   = realloc( peer->buf, peer->size );
     289            }
     290            ret = tr_netRecv( peer->socket, &peer->buf[peer->pos],
     291                              peer->size - peer->pos );
    300292            if( ret & TR_NET_CLOSE )
    301293            {
     
    303295                goto dropPeer;
    304296            }
    305             else if( !( ret & TR_NET_BLOCK ) )
    306             {
    307                 peer_dbg( "SEND handshake" );
    308                 peer->status = PEER_STATUS_HANDSHAKE;
    309             }
    310         }
    311 
    312         /* Try to read */
    313         if( peer->status >= PEER_STATUS_HANDSHAKE )
    314         {
    315             for( ;; )
    316             {
    317                 if( peer->size < 1 )
    318                 {
    319                     peer->size = 1024;
    320                     peer->buf  = malloc( peer->size );
    321                 }
    322                 else if( peer->pos >= peer->size )
    323                 {
    324                     peer->size *= 2;
    325                     peer->buf   = realloc( peer->buf, peer->size );
    326                 }
    327                 ret = tr_netRecv( peer->socket, &peer->buf[peer->pos],
    328                                   peer->size - peer->pos );
    329                 if( ret & TR_NET_CLOSE )
    330                 {
    331                     peer_dbg( "connection closed" );
    332                     goto dropPeer;
    333                 }
    334                 else if( ret & TR_NET_BLOCK )
     297            else if( ret & TR_NET_BLOCK )
     298            {
     299                break;
     300            }
     301            peer->date  = tr_date();
     302            peer->pos  += ret;
     303            if( parseBuf( tor, peer, ret ) )
     304            {
     305                goto dropPeer;
     306            }
     307        }
     308
     309        if( peer->status < PEER_STATUS_CONNECTED )
     310        {
     311            i++;
     312            continue;
     313        }
     314
     315        /* Try to write */
     316writeBegin:
     317
     318        /* Send all smaller messages regardless of the upload cap */
     319        while( ( p = messagesPending( peer, &size ) ) )
     320        {
     321            ret = tr_netSend( peer->socket, p, size );
     322            if( ret & TR_NET_CLOSE )
     323            {
     324                goto dropPeer;
     325            }
     326            else if( ret & TR_NET_BLOCK )
     327            {
     328                goto writeEnd;
     329            }
     330            messagesSent( peer, ret );
     331        }
     332
     333        /* Send pieces if we can */
     334        while( ( p = blockPending( tor, peer, &size ) ) )
     335        {
     336            if( !tr_uploadCanUpload( tor->upload ) )
     337            {
     338                break;
     339            }
     340
     341            ret = tr_netSend( peer->socket, p, size );
     342            if( ret & TR_NET_CLOSE )
     343            {
     344                goto dropPeer;
     345            }
     346            else if( ret & TR_NET_BLOCK )
     347            {
     348                break;
     349            }
     350
     351            blockSent( peer, ret );
     352            tr_uploadUploaded( tor->upload, ret );
     353
     354            tor->uploaded[9] += ret;
     355            peer->outTotal   += ret;
     356            peer->outDate     = tr_date();
     357
     358            /* In case this block is done, you may have messages
     359               pending. Send them before we start the next block */
     360            goto writeBegin;
     361        }
     362writeEnd:
     363
     364        /* Ask for a block whenever possible */
     365        if( !tr_cpIsSeeding( tor->completion ) &&
     366            !peer->amInterested && tor->peerCount > TR_MAX_PEER_COUNT - 2 )
     367        {
     368            /* This peer is no use to us, and it seems there are
     369               more */
     370            peer_dbg( "not interesting" );
     371            tr_peerRem( tor, i );
     372            continue;
     373        }
     374
     375        if( peer->amInterested && !peer->peerChoking )
     376        {
     377            int block;
     378            while( peer->inRequestCount < OUR_REQUEST_COUNT )
     379            {
     380                block = chooseBlock( tor, peer );
     381                if( block < 0 )
    335382                {
    336383                    break;
    337384                }
    338                 peer->date  = tr_date();
    339                 peer->pos  += ret;
    340                 if( parseBuf( tor, peer, ret ) )
    341                 {
    342                     goto dropPeer;
    343                 }
    344             }
    345         }
    346 
    347         /* Try to write */
    348 writeBegin:
    349 
    350         /* Send all smaller messages regardless of the upload cap */
    351         while( ( p = messagesPending( peer, &size ) ) )
    352         {
    353             ret = tr_netSend( peer->socket, p, size );
    354             if( ret & TR_NET_CLOSE )
    355             {
    356                 goto dropPeer;
    357             }
    358             else if( ret & TR_NET_BLOCK )
    359             {
    360                 goto writeEnd;
    361             }
    362             messagesSent( peer, ret );
    363         }
    364 
    365         /* Send pieces if we can */
    366         while( ( p = blockPending( tor, peer, &size ) ) )
    367         {
    368             if( !tr_uploadCanUpload( tor->upload ) )
    369             {
    370                 break;
    371             }
    372 
    373             ret = tr_netSend( peer->socket, p, size );
    374             if( ret & TR_NET_CLOSE )
    375             {
    376                 goto dropPeer;
    377             }
    378             else if( ret & TR_NET_BLOCK )
    379             {
    380                 break;
    381             }
    382 
    383             blockSent( peer, ret );
    384             tr_uploadUploaded( tor->upload, ret );
    385 
    386             tor->uploaded[9] += ret;
    387             peer->outTotal   += ret;
    388             peer->outDate     = tr_date();
    389 
    390             /* In case this block is done, you may have messages
    391                pending. Send them before we start the next block */
    392             goto writeBegin;
    393         }
    394 writeEnd:
    395 
    396         /* Connected peers: ask for a block whenever possible */
    397         if( peer->status & PEER_STATUS_CONNECTED )
    398         {
    399             if( tor->blockHaveCount < tor->blockCount &&
    400                 !peer->amInterested && tor->peerCount > TR_MAX_PEER_COUNT - 2 )
    401             {
    402                 /* This peer is no use to us, and it seems there are
    403                    more */
    404                 peer_dbg( "not interesting" );
    405                 tr_peerRem( tor, i );
    406                 continue;
    407             }
    408 
    409             if( peer->amInterested && !peer->peerChoking )
    410             {
    411                 int block;
    412                 while( peer->inRequestCount < OUR_REQUEST_COUNT )
    413                 {
    414                     block = chooseBlock( tor, peer );
    415                     if( block < 0 )
    416                     {
    417                         break;
    418                     }
    419                     sendRequest( tor, peer, block );
    420                 }
     385                sendRequest( tor, peer, block );
    421386            }
    422387        }
  • trunk/libtransmission/peermessages.h

    r2 r3  
    216216    TR_HTONL( 1 + bitfieldSize, p );
    217217    p[4] = 5;
    218     memcpy( &p[5], tor->bitfield, bitfieldSize );
     218    memcpy( &p[5], tr_cpPieceBitfield( tor->completion ), bitfieldSize );
    219219
    220220    peer_dbg( "SEND bitfield" );
     
    258258    TR_HTONL( r->length, p + 13 );
    259259
    260     /* Remember that we have one more uploader for this block */
    261     (tor->blockHave[block])++;
     260    tr_cpDownloaderAdd( tor->completion, block );
    262261
    263262    peer_dbg( "SEND request %d/%d (%d bytes)",
  • trunk/libtransmission/peerparse.h

    r2 r3  
    5252        {
    5353            r = &peer->inRequests[i];
    54             if( tor->blockHave[tr_block(r->index,r->begin)] > 0 )
    55             {
    56                 tor->blockHave[tr_block(r->index,r->begin)]--;
    57             }
     54            tr_cpDownloaderRem( tor->completion, tr_block(r->index,r->begin) );
    5855        }
    5956        peer->inRequestCount = 0;
     
    246243            {
    247244                r = &peer->inRequests[j];
    248                 if( tor->blockHave[tr_block(r->index,r->begin)] > 0 )
    249                 {
    250                     tor->blockHave[tr_block(r->index,r->begin)]--;
    251                 }
     245                tr_cpDownloaderRem( tor->completion,
     246                                    tr_block(r->index,r->begin) );
    252247            }
    253248            suckyClient = 1;
     
    275270
    276271    block = tr_block( r->index, r->begin );
    277     if( tor->blockHave[block] < 0 )
     272    if( tr_cpBlockIsComplete( tor->completion, block ) )
    278273    {
    279274        peer_dbg( "have this block already" );
     
    284279    }
    285280
    286     tor->blockHave[block]  = -1;
    287     tor->blockHaveCount   +=  1;
     281    tr_cpBlockAdd( tor->completion, block );
    288282    tr_ioWrite( tor->io, index, begin, len - 9, &p[8] );
     283    tr_cpDownloaderRem( tor->completion, block );
    289284
    290285    sendCancel( tor, block );
    291286
    292     if( tr_bitfieldHas( tor->bitfield, index ) )
     287    if( tr_cpPieceIsComplete( tor->completion, index ) )
    293288    {
    294289        tr_peer_t * otherPeer;
  • trunk/libtransmission/peerutils.h

    r2 r3  
    164164    }
    165165
     166    /* Connect */
     167    if( ( peer->status & PEER_STATUS_IDLE ) &&
     168        !tr_fdSocketWillCreate( tor->fdlimit, 0 ) )
     169    {
     170        peer->socket = tr_netOpen( peer->addr, peer->port );
     171        if( peer->socket < 0 )
     172        {
     173            peer_dbg( "connection failed" );
     174            tr_fdSocketClosed( tor->fdlimit, 0 );
     175            return 1;
     176        }
     177        peer->status = PEER_STATUS_CONNECTING;
     178    }
     179
     180    /* Try to send handshake */
     181    if( peer->status & PEER_STATUS_CONNECTING )
     182    {
     183        uint8_t buf[68];
     184        tr_info_t * inf = &tor->info;
     185        int ret;
     186
     187        buf[0] = 19;
     188        memcpy( &buf[1], "BitTorrent protocol", 19 );
     189        memset( &buf[20], 0, 8 );
     190        memcpy( &buf[28], inf->hash, 20 );
     191        memcpy( &buf[48], tor->id, 20 );
     192
     193        ret = tr_netSend( peer->socket, buf, 68 );
     194        if( ret & TR_NET_CLOSE )
     195        {
     196            peer_dbg( "connection closed" );
     197            return 1;
     198        }
     199        else if( !( ret & TR_NET_BLOCK ) )
     200        {
     201            peer_dbg( "SEND handshake" );
     202            peer->status = PEER_STATUS_HANDSHAKE;
     203        }
     204    }
     205
    166206    return 0;
    167207}
     
    179219    int i;
    180220    int bitfieldSize = ( inf->pieceCount + 7 ) / 8;
     221    uint8_t * bitfield = tr_cpPieceBitfield( tor->completion );
    181222
    182223    if( !peer->bitfield )
     
    188229    for( i = 0; i < bitfieldSize; i++ )
    189230    {
    190         if( ( peer->bitfield[i] & ~(tor->bitfield[i]) ) & 0xFF )
     231        if( ( peer->bitfield[i] & ~(bitfield[i]) ) & 0xFF )
    191232        {
    192233            return 1;
     
    223264    tr_info_t * inf = &tor->info;
    224265
    225     int i, j;
    226     int startBlock, endBlock, countBlocks;
     266    int i;
    227267    int missingBlocks, minMissing;
    228268    int poolSize, * pool;
     
    235275    for( i = 0; i < inf->pieceCount; i++ )
    236276    {
     277        missingBlocks = tr_cpMissingBlocksForPiece( tor->completion, i );
     278        if( missingBlocks < 1 )
     279        {
     280            /* We already have or are downloading all blocks */
     281            continue;
     282        }
    237283        if( !tr_bitfieldHas( peer->bitfield, i ) )
    238284        {
    239285            /* The peer doesn't have this piece */
    240             continue;
    241         }
    242         if( tr_bitfieldHas( tor->bitfield, i ) )
    243         {
    244             /* We already have it */
    245             continue;
    246         }
    247 
    248         /* Count how many blocks from this piece are missing */
    249         startBlock    = tr_pieceStartBlock( i );
    250         countBlocks   = tr_pieceCountBlocks( i );
    251         endBlock      = startBlock + countBlocks;
    252         missingBlocks = countBlocks;
    253         for( j = startBlock; j < endBlock; j++ )
    254         {
    255             /* TODO: optimize */
    256             if( tor->blockHave[j] )
    257             {
    258                 missingBlocks--;
    259             }
    260             if( missingBlocks > minMissing )
    261             {
    262                 break;
    263             }
    264         }
    265 
    266         if( missingBlocks < 1 )
    267         {
    268             /* We are already downloading all blocks */
    269286            continue;
    270287        }
     
    332349
    333350        /* Pick a block in this piece */
    334         startBlock = tr_pieceStartBlock( piece );
    335         endBlock   = startBlock + tr_pieceCountBlocks( piece );
    336         for( i = startBlock; i < endBlock; i++ )
    337         {
    338             if( !tor->blockHave[i] )
    339             {
    340                 block = i;
    341                 goto check;
    342             }
    343         }
    344 
     351        block = tr_cpMissingBlockInPiece( tor->completion, piece );
     352        goto check;
     353    }
     354
     355    free( pool );
     356
     357    /* "End game" mode */
     358    minDownloading = 255;
     359    block = -1;
     360    for( i = 0; i < inf->pieceCount; i++ )
     361    {
     362        int downloaders, block2;
     363        if( !tr_bitfieldHas( peer->bitfield, i ) )
     364        {
     365            /* The peer doesn't have this piece */
     366            continue;
     367        }
     368        if( tr_cpPieceIsComplete( tor->completion, i ) )
     369        {
     370            /* We already have it */
     371            continue;
     372        }
     373        block2 = tr_cpMostMissingBlockInPiece( tor->completion, i, &downloaders );
     374        if( block2 > -1 && downloaders < minDownloading )
     375        {
     376            block = block2;
     377            minDownloading = downloaders;
     378        }
     379    }
     380
     381check:
     382    if( block < 0 )
     383    {
    345384        /* Shouldn't happen */
    346385        return -1;
    347386    }
    348387
    349     free( pool );
    350 
    351     /* "End game" mode */
    352     block          = -1;
    353     minDownloading = TR_MAX_PEER_COUNT + 1;
    354     for( i = 0; i < tor->blockCount; i++ )
    355     {
    356         /* TODO: optimize */
    357         if( tr_bitfieldHas( peer->bitfield, tr_blockPiece( i ) ) &&
    358             tor->blockHave[i] >= 0 && tor->blockHave[i] < minDownloading )
    359         {
    360             block          = i;
    361             minDownloading = tor->blockHave[i];
    362         }
    363     }
    364 
    365     if( block < 0 )
    366     {
    367         /* Shouldn't happen */
    368         return -1;
    369     }
    370 
    371 check:
    372388    for( i = 0; i < peer->inRequestCount; i++ )
    373389    {
  • trunk/libtransmission/tracker.c

    r1 r3  
    246246        event = "";
    247247
    248     left  = (uint64_t) ( tor->blockCount - tor->blockHaveCount ) *
    249             (uint64_t) tor->blockSize;
    250     left  = MIN( left, inf->totalSize );
     248    left = tr_cpLeftBytes( tor->completion );
    251249
    252250    ret = snprintf( (char *) tc->buf, tc->size,
    253               "GET %s?info_hash=%s&peer_id=%s&port=%d&uploaded=%lld&"
    254               "downloaded=%lld&left=%lld&compact=1&numwant=50%s "
    255               "HTTP/1.1\r\nHost: %s\r\nConnection: close\r\n\r\n",
    256               inf->trackerAnnounce, tor->hashString, tc->id,
    257               tor->bindPort, tor->uploaded[9], tor->downloaded[9],
    258               left, event, inf->trackerAddress );
     251            "GET %s?"
     252            "info_hash=%s&"
     253            "peer_id=%s&"
     254            "port=%d&"
     255            "uploaded=%lld&"
     256            "downloaded=%lld&"
     257            "left=%lld&"
     258            "compact=1&"
     259            "numwant=50&"
     260            "key=%s"
     261            "%s "
     262            "HTTP/1.1\r\n"
     263            "Host: %s\r\n"
     264            "User-Agent: Transmission/%d.%d\r\n"
     265            "Connection: close\r\n\r\n",
     266            inf->trackerAnnounce, tor->hashString, tc->id,
     267            tor->bindPort, tor->uploaded[9], tor->downloaded[9],
     268            left, tor->key, event, inf->trackerAddress,
     269            VERSION_MAJOR, VERSION_MINOR );
    259270
    260271    ret = tr_netSend( tc->socket, tc->buf, ret );
     
    382393        tc->leechers = beFoo->val.i;
    383394    }
    384     if( tc->seeders + tc->seeders >= 50 )
     395    if( tc->seeders + tc->leechers >= 50 )
    385396    {
    386397        tc->hasManyPeers = 1;
  • trunk/libtransmission/transmission.c

    r2 r3  
    5252    }
    5353
     54    /* Random key */
     55    for( i = 0; i < 20; i++ )
     56    {
     57        r         = tr_rand( 36 );
     58        h->key[i] = ( r < 26 ) ? ( 'a' + r ) : ( '0' + r - 26 ) ;
     59    }
     60
    5461    /* Don't exit when writing on a broken socket */
    5562    signal( SIGPIPE, SIG_IGN );
     
    165172    tor->status = TR_STATUS_PAUSE;
    166173    tor->id     = h->id;
     174    tor->key    = h->key;
    167175
    168176    /* Guess scrape URL */
     
    192200    tor->blockCount = ( inf->totalSize + tor->blockSize - 1 ) /
    193201                        tor->blockSize;
    194     tor->blockHave  = calloc( tor->blockCount, 1 );
    195     tor->bitfield   = calloc( ( inf->pieceCount + 7 ) / 8, 1 );
     202    tor->completion = tr_cpInit( tor );
    196203
    197204    tr_lockInit( &tor->lock );
     
    331338        inf = &tor->info;
    332339
    333         tr_lockLock( tor->lock );
    334 
    335340        if( ( tor->status & TR_STATUS_STOPPED ) ||
    336341            ( ( tor->status & TR_STATUS_STOPPING ) &&
     
    340345            tor->status = TR_STATUS_PAUSE;
    341346        }
     347
     348        tr_lockLock( tor->lock );
    342349
    343350        memcpy( &s[i].info, &tor->info, sizeof( tr_info_t ) );
     
    365372        }
    366373
    367         s[i].progress = (float) tor->blockHaveCount / (float) tor->blockCount;
    368 
     374        s[i].progress     = tr_cpCompletionAsFloat( tor->completion );
    369375        s[i].rateDownload = rateDownload( tor );
    370376        s[i].rateUpload   = rateUpload( tor );
     
    376382        else
    377383        {
    378             s[i].eta = (float) (tor->blockCount - tor->blockHaveCount ) *
    379                 (float) tor->blockSize / s[i].rateDownload / 1024.0;
     384            s[i].eta = (float) ( 1.0 - s[i].progress ) *
     385                (float) inf->totalSize / s[i].rateDownload / 1024.0;
    380386            if( s[i].eta > 99 * 3600 + 59 * 60 + 59 )
    381387            {
     
    388394            piece = j * inf->pieceCount / 120;
    389395
    390             if( tr_bitfieldHas( tor->bitfield, piece ) )
     396            if( tr_cpPieceIsComplete( tor->completion, piece ) )
    391397            {
    392398                s[i].pieces[j] = -1;
     
    431437    {
    432438        /* Join the thread first */
    433         tr_lockLock( tor->lock );
    434439        torrentReallyStop( h, t );
    435         tr_lockUnlock( tor->lock );
    436440    }
    437441
     
    439443
    440444    tr_lockClose( tor->lock );
     445    tr_cpClose( tor->completion );
    441446
    442447    if( tor->destination )
     
    446451    free( inf->pieces );
    447452    free( inf->files );
    448     free( tor->blockHave );
    449     free( tor->bitfield );
    450453    free( tor );
    451454
     
    477480#endif
    478481
     482    tr_lockLock( tor->lock );
     483
    479484    tor->io     = tr_ioInit( tor );
    480     tor->status = ( tor->blockHaveCount < tor->blockCount ) ?
    481                       TR_STATUS_DOWNLOAD : TR_STATUS_SEED;
    482    
     485    tor->status = tr_cpIsSeeding( tor->completion ) ?
     486                      TR_STATUS_SEED : TR_STATUS_DOWNLOAD;
     487
    483488    while( !tor->die )
    484489    {
    485490        date1 = tr_date();
    486 
    487         tr_lockLock( tor->lock );
    488491
    489492        /* Are we finished ? */
    490493        if( ( tor->status & TR_STATUS_DOWNLOAD ) &&
    491             tor->blockHaveCount >= tor->blockCount )
     494            tr_cpIsSeeding( tor->completion ) )
    492495        {
    493496            /* Done */
     
    502505        tr_trackerPulse( tor->tracker );
    503506
    504         tr_lockUnlock( tor->lock );
    505 
    506507        if( tor->status & TR_STATUS_STOPPED )
    507508        {
     
    513514        if( date2 < date1 + 20 )
    514515        {
     516            tr_lockUnlock( tor->lock );
    515517            tr_wait( date1 + 20 - date2 );
    516         }
    517     }
     518            tr_lockLock( tor->lock );
     519        }
     520    }
     521
     522    tr_lockUnlock( tor->lock );
    518523
    519524    tr_ioClose( tor->io );
  • trunk/libtransmission/utils.h

    r1 r3  
    7676}
    7777
     78static inline void tr_bitfieldRem( uint8_t * bitfield, int piece )
     79{
     80    bitfield[ piece / 8 ] &= ~( 1 << ( 7 - ( piece % 8 ) ) );
     81}
     82
    7883#define tr_blockPiece(a) _tr_blockPiece(tor,a)
    7984static inline int _tr_blockPiece( tr_torrent_t * tor, int block )
  • trunk/macosx/Controller.h

    r2 r3  
    2828#include "PrefsController.h"
    2929
     30@class TorrentTableView;
     31
    3032@interface Controller : NSObject
    3133{
     
    3436    tr_stat_t                    * fStat;
    3537    int                            fResumeOnWake[TR_MAX_TORRENT_COUNT];
    36    
     38
    3739    NSToolbar                    * fToolbar;
    3840
     
    4244
    4345    IBOutlet NSWindow            * fWindow;
    44     IBOutlet NSTableView         * fTableView;
     46    IBOutlet TorrentTableView    * fTableView;
    4547    IBOutlet NSTextField         * fTotalDLField;
    4648    IBOutlet NSTextField         * fTotalULField;
     49    IBOutlet NSMenu              * fContextMenu;
    4750
    4851    IBOutlet NSPanel             * fInfoPanel;
     
    7174- (void) resumeTorrentWithIndex: (int) index;
    7275- (void) removeTorrent:   (id) sender;
     76- (void) removeTorrentDeleteFile: (id) sender;
     77- (void) removeTorrentDeleteData: (id) sender;
     78- (void) removeTorrentDeleteBoth: (id) sender;
     79- (void) removeTorrentWithIndex: (int) idx
     80                  deleteTorrent: (BOOL) deleteTorrent
     81                     deleteData: (BOOL) deleteData;
    7382- (void) showInfo:        (id) sender;
    7483
     
    7685- (void) sleepCallBack:   (natural_t) messageType argument:
    7786                            (void *) messageArgument;
     87
     88- (NSMenu *) menuForIndex: (int) idx;
    7889
    7990- (void) showMainWindow:  (id) sender;
  • trunk/macosx/Controller.m

    r2 r3  
    2626#include "ProgressCell.h"
    2727#include "Utils.h"
     28#include "TorrentTableView.h"
    2829
    2930#define TOOLBAR_OPEN   @"Toolbar Open"
     
    3132#define TOOLBAR_PREFS  @"Toolbar Preferences"
    3233#define TOOLBAR_INFO   @"Toolbar Info"
     34
     35#define CONTEXT_PAUSE           1
     36#define CONTEXT_REMOVE          2
     37#define CONTEXT_REMOVE_TORRENT  3
     38#define CONTEXT_REMOVE_DATA     4
     39#define CONTEXT_REMOVE_BOTH     5
     40#define CONTEXT_INFO            6                                             
    3341
    3442static void sleepCallBack( void * controller, io_service_t y,
     
    8391    [fWindow  setDelegate: self];
    8492
    85     [fTableView setDataSource: self];
    86     [fTableView setDelegate:   self];
    87 
    8893    NSTableColumn * tableColumn;
    8994    NameCell      * nameCell;
     
    9398    progressCell = [[ProgressCell alloc] init];
    9499    tableColumn  = [fTableView tableColumnWithIdentifier: @"Name"];
    95     [nameCell    setController: self];
    96100    [tableColumn setDataCell: nameCell];
    97101    [tableColumn setMinWidth: 10.0];
     
    397401}
    398402
     403
     404- (void) removeTorrentWithIndex: (int) idx
     405                  deleteTorrent: (BOOL) deleteTorrent
     406                     deleteData: (BOOL) deleteData
     407{
     408    BOOL torrentWarning = ![[NSUserDefaults standardUserDefaults]
     409                            boolForKey:@"SkipTorrentDeletionWarning"];
     410    BOOL dataWarning = ![[NSUserDefaults standardUserDefaults]
     411                            boolForKey:@"SkipDataDeletionWarning"];
     412   
     413    if ( ( torrentWarning && deleteTorrent ) || (dataWarning && deleteData ) )
     414    {
     415        NSAlert *alert = [[[NSAlert alloc] init] autorelease];
     416       
     417        [alert addButtonWithTitle:@"No"];
     418        [alert addButtonWithTitle:@"Yes"];
     419        [alert setAlertStyle:NSWarningAlertStyle];
     420        [alert setMessageText:@"Are you sure you want to remove and delete?"];
     421       
     422        if ( (deleteTorrent && torrentWarning) &&
     423             !( dataWarning && deleteData ) )
     424        {
     425            /* delete torrent warning YES, delete data warning NO */
     426            [alert setInformativeText:@"If you choose yes, the .torrent file will "
     427                "be deleted. This cannot be undone."];
     428        }
     429        else if( (deleteData && dataWarning) &&
     430                 !( torrentWarning && deleteTorrent ) )
     431        {
     432            /* delete torrent warning NO, delete data warning YES */
     433            [alert setInformativeText:@"If you choose yes, the downloaded data will "
     434                "be deleted. This cannot be undone."];
     435        }
     436        else
     437        {
     438            /* delete torrent warning YES, delete data warning YES */
     439            [alert setInformativeText:@"If you choose yes, both downloaded data and "
     440                "torrent file will be deleted. This cannot be undone."];
     441        }
     442       
     443        if ( [alert runModal] == NSAlertFirstButtonReturn )
     444            return;
     445    }
     446   
     447    if ( deleteData )
     448    {
     449        tr_file_t * files = fStat[idx].info.files;
     450        int i;
     451       
     452        for ( i = 0; i < fStat[idx].info.fileCount; i++ )
     453        {
     454            if ( -1 == remove([[NSString stringWithFormat:@"%s/%s",
     455                                        fStat[idx].folder,
     456                                        files[i].name]
     457                                cString]) )
     458            {
     459                NSLog(@"remove(%s) failed, errno = %i",
     460                      files[i].name,
     461                      errno);
     462            }
     463        }
     464       
     465        /* in some cases, we should remove fStat[idx].folder also. When? */
     466    }
     467   
     468    if ( deleteTorrent )
     469    {
     470        if ( -1 == remove( fStat[idx].info.torrent ) )
     471        {
     472            NSLog(@"remove(%s) failed, errno = %i",
     473                  fStat[idx].info.torrent,
     474                  errno);
     475        }
     476    }
     477   
     478    tr_torrentClose( fHandle, idx );
     479    [self updateUI: NULL];
     480}
     481
    399482- (void) removeTorrent: (id) sender
    400483{
    401     tr_torrentClose( fHandle, [fTableView selectedRow] );
    402     [self updateUI: NULL];
     484    [self removeTorrentWithIndex: [fTableView selectedRow] deleteTorrent: NO deleteData: NO ];
     485}
     486
     487- (void) removeTorrentDeleteFile: (id) sender
     488{
     489    [self removeTorrentWithIndex: [fTableView selectedRow] deleteTorrent: YES deleteData: NO];
     490}
     491
     492- (void) removeTorrentDeleteData: (id) sender
     493{
     494    [self removeTorrentWithIndex: [fTableView selectedRow] deleteTorrent: NO deleteData: YES];
     495}
     496
     497- (void) removeTorrentDeleteBoth: (id) sender
     498{
     499    [self removeTorrentWithIndex: [fTableView selectedRow] deleteTorrent: YES deleteData: YES];
    403500}
    404501
     
    426523    }
    427524    fCount = tr_torrentStat( fHandle, &fStat );
    428     [fTableView reloadData];
     525    [fTableView updateUI: fStat];
    429526
    430527    /* Update the global DL/UL rates */
     
    449546}
    450547
     548
     549- (NSMenu *) menuForIndex: (int) idx
     550{
     551    if ( idx < 0 || idx >= fCount )
     552    {
     553        return nil;
     554    }
     555   
     556    int status = fStat[idx].status;
     557   
     558    NSMenuItem *pauseItem = [fContextMenu itemWithTag: CONTEXT_PAUSE];
     559    NSMenuItem *removeItem = [fContextMenu itemAtIndex: 1];
     560   
     561    [pauseItem setTarget: self];
     562   
     563    if ( status & TR_STATUS_CHECK ||
     564         status & TR_STATUS_DOWNLOAD ||
     565         status & TR_STATUS_SEED )
     566    {
     567        /* we can stop */
     568        [removeItem setEnabled: NO];
     569        [pauseItem setTitle: @"Pause"];
     570        [pauseItem setAction: @selector(stopTorrent:)];
     571        [pauseItem setEnabled: YES];
     572    } else {
     573        /* we are stopped */
     574        [removeItem setEnabled: YES];
     575        [pauseItem setTitle: @"Resume"];
     576        [pauseItem setAction: @selector(resumeTorrent:)];
     577        /* don't allow resuming if we aren't in PAUSE */
     578        if ( !(status & TR_STATUS_PAUSE) )
     579            [pauseItem setEnabled: NO];
     580    }
     581   
     582    return fContextMenu;
     583}
     584
    451585- (int) numberOfRowsInTableView: (NSTableView *) t
    452586{
     
    465599    if( [[tableColumn identifier] isEqualToString: @"Name"] )
    466600    {
    467         [(NameCell *) cell setStat: &fStat[rowIndex] index: rowIndex];
     601        [(NameCell *) cell setStat: &fStat[rowIndex]];
    468602    }
    469603    else if( [[tableColumn identifier] isEqualToString: @"Progress"] )
     
    519653
    520654    /* Update info window */
    521     [fInfoTitle setStringValue: [NSString stringWithCString:
     655    [fInfoTitle setStringValue: [NSString stringWithUTF8String:
    522656        fStat[row].info.name]];
    523657    [fInfoTracker setStringValue: [NSString stringWithFormat:
  • trunk/macosx/English.lproj/MainMenu.nib/classes.nib

    r1 r3  
    88                openShowSheet = id;
    99                removeTorrent = id;
     10                removeTorrentDeleteBoth = id;
     11                removeTorrentDeleteData = id;
     12                removeTorrentDeleteFile = id;
    1013                resumeTorrent = id;
    1114                showInfo = id;
     
    1720            OUTLETS = {
    1821                fAdvancedBarItem = NSMenuItem;
     22                fContextMenu = NSMenu;
    1923                fInfoAnnounce = NSTextField;
    2024                fInfoDownloaded = NSTextField;
     
    5054            };
    5155            SUPERCLASS = NSObject;
     56        },
     57        {
     58            CLASS = TorrentTableView;
     59            LANGUAGE = ObjC;
     60            OUTLETS = {fController = Controller; };
     61            SUPERCLASS = NSTableView;
    5262        }
    5363    );
  • trunk/macosx/English.lproj/MainMenu.nib/info.nib

    r1 r3  
    44<dict>
    55        <key>IBDocumentLocation</key>
    6         <string>204 84 361 432 0 0 1440 878 </string>
     6        <string>188 334 361 432 0 0 1280 832 </string>
    77        <key>IBEditorPositions</key>
    88        <dict>
    99                <key>29</key>
    1010                <string>105 768 371 44 0 0 1280 832 </string>
     11                <key>456</key>
     12                <string>174 512 147 87 0 0 1280 832 </string>
    1113        </dict>
    1214        <key>IBFramework Version</key>
    13         <string>439.0</string>
     15        <string>443.0</string>
    1416        <key>IBOldestOS</key>
    1517        <integer>3</integer>
    1618        <key>IBOpenObjects</key>
    1719        <array>
     20                <integer>456</integer>
    1821                <integer>21</integer>
    19                 <integer>29</integer>
    20                 <integer>273</integer>
    21                 <integer>343</integer>
    2222        </array>
    2323        <key>IBSystem Version</key>
    24         <string>8C46</string>
     24        <string>8F46</string>
    2525</dict>
    2626</plist>
  • trunk/macosx/NameCell.h

    r2 r3  
    3030@interface NameCell : NSCell
    3131{
    32     Controller * fController;
    33     tr_stat_t  * fStat;
    34     int          fIndex;
    35     NSRect       fPauseRect;
    36     NSRect       fRevealRect;
    37     NSPoint      fClickPoint;
     32    NSString * fNameString;
     33    NSString * fSizeString;
     34    NSString * fTimeString;
     35    NSString * fPeersString;
    3836}
    39 - (void) setController: (Controller *) controller;
    40 - (void) setStat: (tr_stat_t *) stat index: (int) index;
     37- (void) setStat: (tr_stat_t *) stat;
    4138@end
    4239
  • trunk/macosx/NameCell.m

    r2 r3  
    2626@implementation NameCell
    2727
    28 - (void) setController: (Controller *) controller
     28- (void) setStat: (tr_stat_t *) stat
    2929{
    30     fController = controller;
    31 }
     30    fNameString  = [NSString stringWithUTF8String: stat->info.name];
     31    fSizeString  = [NSString stringWithFormat: @" (%@)",
     32                    stringForFileSize( stat->info.totalSize )];
     33    fTimeString  = @"";
     34    fPeersString = @"";
    3235
    33 - (void) setStat: (tr_stat_t *) stat index: (int) idx
    34 {
    35     fStat  = stat;
    36     fIndex = idx;
     36    if( stat->status & TR_STATUS_PAUSE )
     37    {
     38        fTimeString = [NSString stringWithFormat:
     39            @"Paused (%.2f %%)", 100 * stat->progress];
     40    }
     41    else if( stat->status & TR_STATUS_CHECK )
     42    {
     43        fTimeString = [NSString stringWithFormat:
     44            @"Checking existing files (%.2f %%)", 100 * stat->progress];
     45    }
     46    else if( stat->status & TR_STATUS_DOWNLOAD )
     47    {
     48        if( stat->eta < 0 )
     49        {
     50            fTimeString = [NSString stringWithFormat:
     51                @"Finishing in --:--:-- (%.2f %%)", 100 * stat->progress];
     52        }
     53        else
     54        {
     55            fTimeString = [NSString stringWithFormat:
     56                @"Finishing in %02d:%02d:%02d (%.2f %%)",
     57                stat->eta / 3600, ( stat->eta / 60 ) % 60,
     58                stat->eta % 60, 100 * stat->progress];
     59        }
     60        fPeersString = [NSString stringWithFormat:
     61            @"Downloading from %d of %d peer%s",
     62            stat->peersUploading, stat->peersTotal,
     63            ( stat->peersTotal == 1 ) ? "" : "s"];
     64    }
     65    else if( stat->status & TR_STATUS_SEED )
     66    {
     67        fTimeString  = [NSString stringWithFormat:
     68            @"Seeding, uploading to %d of %d peer%s",
     69            stat->peersDownloading, stat->peersTotal,
     70            ( stat->peersTotal == 1 ) ? "" : "s"];
     71    }
     72    else if( stat->status & TR_STATUS_STOPPING )
     73    {
     74        fTimeString  = @"Stopping...";
     75    }
     76
     77    if( ( stat->status & ( TR_STATUS_DOWNLOAD | TR_STATUS_SEED ) ) &&
     78        ( stat->status & TR_TRACKER_ERROR ) )
     79    {
     80        fPeersString = [NSString stringWithFormat: @"%@%@",
     81            @"Error: ", [NSString stringWithUTF8String: stat->error]];
     82    }
    3783}
    3884
    3985- (void) drawWithFrame: (NSRect) cellFrame inView: (NSView *) view
    4086{
     87    NSString * string;
     88    NSPoint pen;
     89    NSMutableDictionary * attributes;
     90
    4191    if( ![view lockFocusIfCanDraw] )
    4292    {
     
    4494    }
    4595
    46     NSString * nameString = NULL, * timeString = @"", * peersString = @"";
    47     NSMutableDictionary * attributes;
     96    pen = cellFrame.origin;
     97
    4898    attributes = [NSMutableDictionary dictionaryWithCapacity: 1];
    49     NSPoint pen = cellFrame.origin;
    50    
    51     NSString * sizeString = [NSString stringWithFormat: @" (%@)",
    52         stringForFileSize( fStat->info.totalSize )];
    53 
    54     nameString = [NSString stringWithFormat: @"%@%@",
    55         stringFittingInWidth( fStat->info.name, cellFrame.size.width -
    56                 35 - widthForString( sizeString, 12 ), 12 ),
    57         sizeString];
    58 
    59     if( fStat->status & TR_STATUS_PAUSE )
    60     {
    61         timeString = [NSString stringWithFormat:
    62             @"Paused (%.2f %%)", 100 * fStat->progress];
    63         peersString = @"";
    64     }
    65     else if( fStat->status & TR_STATUS_CHECK )
    66     {
    67         timeString = [NSString stringWithFormat:
    68             @"Checking existing files (%.2f %%)", 100 * fStat->progress];
    69         peersString = @"";
    70     }
    71     else if( fStat->status & TR_STATUS_DOWNLOAD )
    72     {
    73         if( fStat->eta < 0 )
    74         {
    75             timeString = [NSString stringWithFormat:
    76                 @"Finishing in --:--:-- (%.2f %%)", 100 * fStat->progress];
    77         }
    78         else
    79         {
    80             timeString = [NSString stringWithFormat:
    81                 @"Finishing in %02d:%02d:%02d (%.2f %%)",
    82                 fStat->eta / 3600, ( fStat->eta / 60 ) % 60,
    83                 fStat->eta % 60, 100 * fStat->progress];
    84         }
    85         peersString = [NSString stringWithFormat:
    86             @"Downloading from %d of %d peer%s",
    87             fStat->peersUploading, fStat->peersTotal,
    88             ( fStat->peersTotal == 1 ) ? "" : "s"];
    89     }
    90     else if( fStat->status & TR_STATUS_SEED )
    91     {
    92         timeString  = [NSString stringWithFormat:
    93             @"Seeding, uploading to %d of %d peer%s",
    94             fStat->peersDownloading, fStat->peersTotal,
    95             ( fStat->peersTotal == 1 ) ? "" : "s"];
    96         peersString = @"";
    97     }
    98     else if( fStat->status & TR_STATUS_STOPPING )
    99     {
    100         timeString  = @"Stopping...";
    101         peersString = @"";
    102     }
    103 
    104     if( ( fStat->status & ( TR_STATUS_DOWNLOAD | TR_STATUS_SEED ) ) &&
    105         ( fStat->status & TR_TRACKER_ERROR ) )
    106     {
    107         peersString = [NSString stringWithFormat: @"%@%@",
    108         @"Error: ", stringFittingInWidth( fStat->error,
    109             cellFrame.size.width - 40 -
    110             widthForString( @"Error: ", 10 ), 10 )];
    111     }
    112 
    11399    [attributes setObject: [NSFont messageFontOfSize:12.0]
    114100        forKey: NSFontAttributeName];
    115101
    116102    pen.x += 5; pen.y += 5;
    117     [nameString drawAtPoint: pen withAttributes: attributes];
     103    string = [NSString stringWithFormat: @"%@%@",
     104        stringFittingInWidth( fNameString, cellFrame.size.width -
     105        35 - widthForString( fSizeString, 12 ), 12 ), fSizeString];
     106    [string drawAtPoint: pen withAttributes: attributes];
    118107
    119108    [attributes setObject: [NSFont messageFontOfSize:10.0]
     
    121110
    122111    pen.x += 5; pen.y += 20;
    123     [timeString drawAtPoint: pen withAttributes: attributes];
     112    [fTimeString drawAtPoint: pen withAttributes: attributes];
    124113
    125114    pen.x += 0; pen.y += 15;
    126     [peersString drawAtPoint: pen withAttributes: attributes];
    127 
    128     /* "Pause" button */
    129     fPauseRect = NSMakeRect( cellFrame.origin.x + cellFrame.size.width - 19,
    130                              cellFrame.origin.y + cellFrame.size.height - 38,
    131                              14, 14 );
    132     NSImage * pauseImage = NULL;
    133     if( fStat->status & TR_STATUS_PAUSE )
    134     {
    135         if( NSPointInRect( fClickPoint, fPauseRect ) )
    136         {
    137             pauseImage = [NSImage imageNamed: @"ResumeOn.png"];
    138         }
    139         else
    140         {
    141             pauseImage = [NSImage imageNamed: @"ResumeOff.png"];
    142         }
    143     }
    144     else if( fStat->status &
    145              ( TR_STATUS_CHECK | TR_STATUS_DOWNLOAD | TR_STATUS_SEED ) )
    146     {
    147         if( NSPointInRect( fClickPoint, fPauseRect ) )
    148         {
    149             pauseImage = [NSImage imageNamed: @"PauseOn.png"];
    150         }
    151         else
    152         {
    153             pauseImage = [NSImage imageNamed: @"PauseOff.png"];
    154         }
    155     }
    156     if( pauseImage )
    157     {
    158         pen.x = fPauseRect.origin.x;
    159         pen.y = fPauseRect.origin.y + 14;
    160         [pauseImage compositeToPoint: pen operation: NSCompositeSourceOver];
    161     }
    162 
    163     /* "Reveal in Finder" button */
    164     fRevealRect = NSMakeRect( cellFrame.origin.x + cellFrame.size.width - 19,
    165                               cellFrame.origin.y + cellFrame.size.height - 19,
    166                               14, 14 );
    167     NSImage * revealImage;
    168     if( NSPointInRect( fClickPoint, fRevealRect ) )
    169     {
    170         revealImage = [NSImage imageNamed: @"RevealOn.png"];
    171     }
    172     else
    173     {
    174         revealImage = [NSImage imageNamed: @"RevealOff.png"];
    175     }
    176     pen.x = fRevealRect.origin.x;
    177     pen.y = fRevealRect.origin.y + 14;
    178     [revealImage compositeToPoint: pen operation: NSCompositeSourceOver];
     115    string = stringFittingInWidth( fPeersString,
     116                cellFrame.size.width - 40, 10 );
     117    [string drawAtPoint: pen withAttributes: attributes];
    179118
    180119    [view unlockFocus];
    181120}
    182121
    183 /* Track mouse as long as button is down */
    184 - (BOOL) startTrackingAt: (NSPoint) start inView: (NSView *) v
    185 {
    186     fClickPoint = start;
    187     return YES;
    188 }
    189 - (BOOL) continueTracking: (NSPoint) last at: (NSPoint) current
    190     inView: (NSView *) v
    191 {
    192     fClickPoint = current;
    193     return YES;
    194 }
    195 
    196 - (void) stopTracking: (NSPoint) last at:(NSPoint) stop
    197     inView: (NSView *) v mouseIsUp: (BOOL) flag
    198 {
    199     if( flag )
    200     {
    201         if( NSPointInRect( stop, fRevealRect ) )
    202         {
    203             /* Reveal in Finder */
    204             NSString * string = [NSString stringWithFormat:
    205                 @"tell application \"Finder\"\nactivate\nreveal (POSIX file \"%s/%s\")\nend tell",
    206                 fStat->folder, fStat->info.name];
    207             NSAppleScript * appleScript;
    208             appleScript = [[NSAppleScript alloc] initWithSource: string];
    209             NSDictionary * error;
    210             if( ![appleScript executeAndReturnError: &error] )
    211             {
    212                 printf( "Reveal in Finder: AppleScript failed\n" );
    213             }
    214             [appleScript release];
    215         }
    216         else if( NSPointInRect( stop, fPauseRect ) )
    217         {
    218             /* Pause, resume */
    219             if( fStat->status & TR_STATUS_PAUSE )
    220             {
    221                 [fController resumeTorrentWithIndex: fIndex];
    222             }
    223             else if( fStat->status & ( TR_STATUS_CHECK |
    224                       TR_STATUS_DOWNLOAD | TR_STATUS_SEED ) )
    225             {
    226                 [fController stopTorrentWithIndex: fIndex];
    227             }
    228         }
    229     }
    230     fClickPoint = NSMakePoint(0,0);
    231 }
    232 
    233122@end
  • trunk/macosx/Transmission.xcodeproj/project.pbxproj

    r2 r3  
    1313                4D118E1A08CB46B20033958F /* PrefsController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4D118E1908CB46B20033958F /* PrefsController.m */; };
    1414                4D2784370905709500687951 /* Transmission.icns in Resources */ = {isa = PBXBuildFile; fileRef = 4D2784360905709500687951 /* Transmission.icns */; };
     15                4D364DA0091FBB2C00377D12 /* TorrentTableView.m in Sources */ = {isa = PBXBuildFile; fileRef = 4D364D9F091FBB2C00377D12 /* TorrentTableView.m */; };
    1516                4D3EA0AA08AE13C600EA10C2 /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4D3EA0A908AE13C600EA10C2 /* IOKit.framework */; };
    1617                4D6DAAC6090CE00500F43C22 /* RevealOff.png in Resources */ = {isa = PBXBuildFile; fileRef = 4D6DAAC4090CE00500F43C22 /* RevealOff.png */; };
     
    7778                4D118E1908CB46B20033958F /* PrefsController.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = PrefsController.m; sourceTree = "<group>"; };
    7879                4D2784360905709500687951 /* Transmission.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; name = Transmission.icns; path = Images/Transmission.icns; sourceTree = "<group>"; };
     80                4D364D9E091FBB2C00377D12 /* TorrentTableView.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = TorrentTableView.h; sourceTree = "<group>"; };
     81                4D364D9F091FBB2C00377D12 /* TorrentTableView.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = TorrentTableView.m; sourceTree = "<group>"; };
    7982                4D3EA0A908AE13C600EA10C2 /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = /System/Library/Frameworks/IOKit.framework; sourceTree = "<absolute>"; };
    8083                4D6DAAC4090CE00500F43C22 /* RevealOff.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = RevealOff.png; path = Images/RevealOff.png; sourceTree = "<group>"; };
     
    121124                                4D118E1808CB46B20033958F /* PrefsController.h */,
    122125                                4D118E1908CB46B20033958F /* PrefsController.m */,
     126                                4D364D9E091FBB2C00377D12 /* TorrentTableView.h */,
     127                                4D364D9F091FBB2C00377D12 /* TorrentTableView.m */,
    123128                        );
    124129                        name = Classes;
     
    294299                                4D096C13089FB4E20091B166 /* ProgressCell.m in Sources */,
    295300                                4D118E1A08CB46B20033958F /* PrefsController.m in Sources */,
     301                                4D364DA0091FBB2C00377D12 /* TorrentTableView.m in Sources */,
    296302                        );
    297303                        runOnlyForDeploymentPostprocessing = 0;
  • trunk/macosx/Utils.h

    r1 r3  
    5151}
    5252
    53 static NSString * stringFittingInWidth( char * string, float width,
     53#define NS_ELLIPSIS [NSString stringWithUTF8String: "\xE2\x80\xA6"]
     54
     55static NSString * stringFittingInWidth( NSString * oldString, float width,
    5456                                        float fontSize )
    5557{
    56     NSString * nsString = NULL;
    57     char     * foo      = strdup( string );
    58     int        i;
     58    NSString * newString  = NULL;
     59    unsigned   i;
    5960
    60     for( i = strlen( string ); i > 0; i-- )
     61    for( i = 0; i < [oldString length]; i++ )
    6162    {
    62         foo[i] = '\0';
    63         nsString = [NSString stringWithFormat: @"%s%@",
    64             foo, ( i - strlen( string ) ? [NSString
    65             stringWithUTF8String:"\xE2\x80\xA6"] : @"" )];
     63        newString = [NSString stringWithFormat: @"%@%@",
     64            [oldString substringToIndex: [oldString length] - i],
     65            i ? NS_ELLIPSIS : @""];
    6666
    67         if( widthForString( nsString, fontSize ) <= width )
     67        if( widthForString( newString, fontSize ) <= width )
    6868        {
    6969            break;
    7070        }
    7171    }
    72     free( foo );
    73     return nsString;
     72    return newString;
    7473}
  • trunk/transmissioncli.c

    r1 r3  
    2828#include <signal.h>
    2929#include <transmission.h>
     30#ifdef SYS_BEOS
     31#include <kernel/OS.h>
     32#define usleep snooze
     33#endif
    3034
    3135#define USAGE \
Note: See TracChangeset for help on using the changeset viewer.