Changes in / [1:3]


Ignore:
Location:
/trunk
Files:
16 added
8 deleted
28 edited

Legend:

Unmodified
Added
Removed
  • /trunk/AUTHORS

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

    r1 r3  
    1616        macosx/English.lproj/MainMenu.nib/info.nib
    1717        macosx/English.lproj/MainMenu.nib/keyedobjects.nib
    18         macosx/Images/Info.tiff
    19         macosx/Images/Open.tiff
    20         macosx/Images/Progress.tiff
    21         macosx/Images/Remove.tiff
    22         macosx/Images/Resume.tiff
    23         macosx/Images/RevealOff.tiff
    24         macosx/Images/RevealOn.tiff
    25         macosx/Images/Stop.tiff
     18        macosx/Images/Info.png
     19        macosx/Images/Open.png
     20        macosx/Images/PauseOff.png
     21        macosx/Images/PauseOn.png
     22        macosx/Images/Progress.png
     23        macosx/Images/Remove.png
     24        macosx/Images/RevealOff.png
     25        macosx/Images/RevealOn.png
    2626        macosx/Images/Transmission.icns
    2727        macosx/Images/TransmissionDocument.icns
     
    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/Jamrules

    r1 r3  
    88VERSION_MAJOR  = 0 ;
    99VERSION_MINOR  = 3 ;
    10 VERSION_STRING = $(VERSION_MAJOR).$(VERSION_MINOR) ;
     10# VERSION_STRING = $(VERSION_MAJOR).$(VERSION_MINOR) ;
     11VERSION_STRING = 0.4-cvs ;
    1112
    1213DEFINES += VERSION_MAJOR=$(VERSION_MAJOR)
    1314           VERSION_MINOR=$(VERSION_MINOR)
    1415           VERSION_STRING=\\\"$(VERSION_STRING)\\\" ;
    15 CCFLAGS  = -g -Wall -W ;
    1616OPTIM    = -O3 ;
    1717RM       = rm -Rf ;
     
    2828    # universal binaries)
    2929    RANLIB     =   ;
     30    NOARSCAN   = 1 ;
    3031    NOARUPDATE = 1 ;
    3132    actions Archive
     
    3738    {
    3839        Depends        $(1)  : $(2) ;
     40        Depends        $(1)  : Jamrules ;
    3941        Clean          clean : $(1) ;
    4042    }
    4143    actions OSXInfoPlist
    4244    {
     45        $(RM) $(1)
    4346        sed "s/%%VERSION%%/$(VERSION_STRING)/" < $(2) > $(1)
    4447    }
  • /trunk/configure

    r1 r3  
    11#! /bin/sh
    22
     3#
     4# Functions
     5#
     6usage()
     7{
     8  cat << EOF
     9
     10  OpenSSL options:
     11    --disable-openssl      Disable OpenSSL, use built-in SHA1 implementation
     12    --openssl-prefix=PATH  Location of OpenSSL headers and library
     13
     14  Some influential environment variables:
     15    CC          C compiler command (default "cc")
     16    CFLAGS      C compiler flags (default "-g -Wall -W")
     17
     18EOF
     19}
     20
     21openssl_test()
     22{
     23  cat > testconf.c << EOF
     24  #include <stdio.h>
     25  #include <openssl/sha.h>
     26  int main()
     27  {
     28      SHA1( 0, 0, 0 );
     29  }
     30EOF
     31  if [ -n "$PREFIX" ]; then
     32    TMPFLAGS="-I$PREFIX/include -L$PREFIX/lib"
     33  fi
     34  if $CC $TMPFLAGS -o testconf testconf.c -lcrypto > /dev/null 2>&1
     35  then
     36    echo "OpenSSL: yes"
     37    DEFINES="$DEFINES HAVE_OPENSSL"
     38    LINKLIBS="$LINKLIBS -lcrypto"
     39  else
     40    echo "OpenSSL: no, using built-in SHA1 implementation"
     41  fi
     42  rm -f testconf.c testconf
     43}
     44
     45#
     46# Defaults settings
     47#
     48CC="${CC-cc}"
     49CFLAGS="${CFLAGS--g -Wall -W}"
    350# For > 2 GB files
    451DEFINES="_FILE_OFFSET_BITS=64 _LARGEFILE_SOURCE"
    5 
    652# For asprintf
    753DEFINES="$DEFINES _GNU_SOURCE"
     54openssl_disable=0
    855
     56#
     57# Parse options
     58#
     59while [ $# -ne 0 ]; do
     60  param=`expr "opt$1" : 'opt[^=]*=\(.*\)'`
     61
     62  case "x$1" in
     63    x--disable-openssl)
     64      openssl_disable="1";
     65      ;;
     66    x--openssl-prefix=*)
     67      OPENSSL_PREFIX="$param";
     68      ;;
     69    x--help)
     70      usage
     71      exit 0
     72      ;;
     73  esac
     74  shift
     75done
     76
     77#
    978# System-specific flags
     79#
    1080SYSTEM=`uname -s`
    1181case $SYSTEM in
     
    56126echo "System:  $SYSTEM"
    57127
    58 # Check for OpenSSL
    59 cat > testconf.c << EOF
    60 #include <stdio.h>
    61 #include <openssl/sha.h>
    62 int main()
    63 {
    64     SHA1( 0, 0, 0 );
    65 }
     128#
     129# OpenSSL settings
     130#
     131if [ ${openssl_disable} = 1 ]; then
     132  echo "OpenSSL: no, using built-in SHA1 implementation"
     133else
     134    openssl_test
     135fi
     136
     137#
     138# Generate config.jam
     139#
     140rm -f config.jam
     141cat > config.jam << EOF
     142CC         = $CC ;
     143LINK       = $CC ;
     144CCFLAGS    = $CFLAGS ;
     145DEFINES    = $DEFINES ;
     146LINKLIBS   = $LINKLIBS ;
    66147EOF
    67 if cc -o testconf testconf.c -lcrypto > /dev/null 2>&1
    68 then
    69   echo "OpenSSL: yes"
    70   DEFINES="$DEFINES HAVE_OPENSSL"
    71   LINKLIBS="$LINKLIBS -lcrypto"
    72 else
    73   echo "OpenSSL: no, using built-in SHA1 implementation"
     148if [ -n "$OPENSSL_PREFIX" ]; then
     149cat >> config.jam << EOF
     150HDRS      += $OPENSSL_PREFIX/include ;
     151LINKFLAGS += -L$OPENSSL_PREFIX/lib ;
     152EOF
    74153fi
    75 rm -f testconf.c testconf
    76 
    77 # Generate config.jam
    78 rm -f config.jam
    79 cat << EOF > config.jam
    80 DEFINES  = $DEFINES ;
    81 LINKLIBS = $LINKLIBS ;
    82 EOF
    83154
    84155echo
  • /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

    r1 r3  
    3131 *    been completed or started in each slot
    3232 *
    33  * The resume file is located in ~/.transmission/. Its name is
    34  * "resume.<hash>".
     33 * The name of the resume file is "resume.<hash>".
    3534 *
    3635 * All values are stored in the native endianness. Moving a
     
    4039 **********************************************************************/
    4140
    42 static char * fastResumeFolderName()
    43 {
    44     char * ret;
    45     asprintf( &ret, "%s/.transmission", getenv( "HOME" ) );
    46     return ret;
    47 }
    48 
    4941static char * fastResumeFileName( tr_io_t * io )
    5042{
     43    tr_torrent_t * tor = io->tor;
    5144    char * ret, * p;
    5245    int i;
    5346
    54     p = fastResumeFolderName();
    55     asprintf( &ret, "%s/resume.%40d", p, 0 );
    56     free( p );
     47    asprintf( &ret, "%s/resume.%40d", tor->prefsDirectory, 0 );
    5748
    5849    p = &ret[ strlen( ret ) - 2 * SHA_DIGEST_LENGTH ];
     
    111102    char    * path;
    112103    int     * fileMTimes;
    113     int       i;
    114104    uint8_t * blockBitfield;
    115105
     
    121111        return;
    122112    }
    123 
    124     /* Create folder if missing */
    125     path = fastResumeFolderName();
    126     mkdir( path, 0755 );
    127     free( path );
    128113
    129114    /* Create/overwrite the resume file */
     
    136121        return;
    137122    }
    138     free( path );
    139123   
    140124    /* Write format version */
     
    146130
    147131    /* Build and write the bitfield for blocks */
    148     blockBitfield = calloc( ( tor->blockCount + 7 ) / 8, 1 );
    149     for( i = 0; i < tor->blockCount; i++ )
    150     {
    151         if( tor->blockHave[i] < 0 )
    152         {
    153             tr_bitfieldAdd( blockBitfield, i );
    154         }
    155     }
     132    blockBitfield = tr_cpBlockBitfield( tor->completion );
    156133    fwrite( blockBitfield, ( tor->blockCount + 7 ) / 8, 1, file );
    157     free( blockBitfield );
    158134
    159135    /* Write the 'slotPiece' table */
     
    161137
    162138    fclose( file );
     139
     140    tr_dbg( "Resume file '%s' written", path );
     141    free( path );
    163142}
    164143
     
    185164        return 1;
    186165    }
     166    tr_dbg( "Resume file '%s' loaded", path );
    187167    free( path );
    188168
     
    234214    blockBitfield = calloc( ( tor->blockCount + 7 ) / 8, 1 );
    235215    fread( blockBitfield, ( tor->blockCount + 7 ) / 8, 1, file );
    236     tor->blockHaveCount = 0;
    237     for( i = 0; i < tor->blockCount; i++ )
    238     {
    239         if( tr_bitfieldHas( blockBitfield, i ) )
    240         {
    241             tor->blockHave[i] = -1;
    242             (tor->blockHaveCount)++;
    243         }
    244     }
     216    tr_cpBlockBitfieldSet( tor->completion, blockBitfield );
    245217    free( blockBitfield );
    246218
     
    265237            }
    266238        }
    267 
    268         for( j = tr_pieceStartBlock( i );
    269              j < tr_pieceStartBlock( i ) + tr_pieceCountBlocks( i );
    270              j++ )
    271         {
    272             if( tor->blockHave[j] > -1 )
    273             {
    274                 break;
    275             }
    276         }
    277         if( j >= tr_pieceStartBlock( i ) + tr_pieceCountBlocks( i ) )
    278         {
    279             // tr_dbg( "Piece %d is complete", i );
    280             tr_bitfieldAdd( tor->bitfield, i );
    281         }
    282239    }
    283240    // 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

    r1 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;
     
    156161    tr_tracker_t    * tracker;
    157162    tr_io_t         * io;
     163    uint64_t          stopDate;
    158164
    159165    int               bindSocket;
     
    165171    uint64_t          downloaded[10];
    166172    uint64_t          uploaded[10];
     173
     174    char            * prefsDirectory;
    167175};
    168176
    169177#include "utils.h"
     178#include "completion.h"
    170179
    171180struct tr_handle_s
     
    180189
    181190    char           id[21];
     191    char           key[21];
     192    char           prefsDirectory[256];
    182193};
    183194
  • /trunk/libtransmission/peer.c

    r1 r3  
    101101#include "peermessages.h"
    102102#include "peerutils.h"
     103#include "peerparse.h"
    103104
    104105/***********************************************************************
     
    169170        r     = &peer->inRequests[j];
    170171        block = tr_block( r->index,r->begin );
    171         if( tor->blockHave[block] > 0 )
    172         {
    173           (tor->blockHave[block])--;
    174         }
     172        tr_cpDownloaderRem( tor->completion, block );
    175173    }
    176174    if( !peer->amChoking )
     
    233231    }
    234232
     233    if( tor->status & TR_STATUS_STOPPING )
     234    {
     235        return;
     236    }
     237
    235238    /* Check for incoming connections */
    236239    if( tor->bindSocket > -1 &&
     
    266269        peer = tor->peers[i];
    267270
    268         /* Connect */
    269         if( ( peer->status & PEER_STATUS_IDLE ) &&
    270             !tr_fdSocketWillCreate( tor->fdlimit, 0 ) )
    271         {
    272             peer->socket = tr_netOpen( peer->addr, peer->port );
    273             if( peer->socket < 0 )
    274             {
    275                 peer_dbg( "connection failed" );
    276                 goto dropPeer;
    277             }
    278             peer->status = PEER_STATUS_CONNECTING;
    279         }
    280 
    281         /* Try to send handshake */
    282         if( peer->status & PEER_STATUS_CONNECTING )
    283         {
    284             uint8_t buf[68];
    285             tr_info_t * inf = &tor->info;
    286 
    287             buf[0] = 19;
    288             memcpy( &buf[1], "BitTorrent protocol", 19 );
    289             memset( &buf[20], 0, 8 );
    290             memcpy( &buf[28], inf->hash, 20 );
    291             memcpy( &buf[48], tor->id, 20 );
    292 
    293             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 );
    294292            if( ret & TR_NET_CLOSE )
    295293            {
     
    297295                goto dropPeer;
    298296            }
    299             else if( !( ret & TR_NET_BLOCK ) )
    300             {
    301                 peer_dbg( "SEND handshake" );
    302                 peer->status = PEER_STATUS_HANDSHAKE;
    303             }
    304         }
    305 
    306         /* Try to read */
    307         if( peer->status >= PEER_STATUS_HANDSHAKE )
    308         {
    309             for( ;; )
    310             {
    311                 if( peer->size < 1 )
    312                 {
    313                     peer->size = 1024;
    314                     peer->buf  = malloc( peer->size );
    315                 }
    316                 else if( peer->pos >= peer->size )
    317                 {
    318                     peer->size *= 2;
    319                     peer->buf   = realloc( peer->buf, peer->size );
    320                 }
    321                 ret = tr_netRecv( peer->socket, &peer->buf[peer->pos],
    322                                   peer->size - peer->pos );
    323                 if( ret & TR_NET_CLOSE )
    324                 {
    325                     peer_dbg( "connection closed" );
    326                     goto dropPeer;
    327                 }
    328                 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 )
    329382                {
    330383                    break;
    331384                }
    332                 peer->date  = tr_date();
    333                 peer->pos  += ret;
    334                 if( parseMessage( tor, peer, ret ) )
    335                 {
    336                     goto dropPeer;
    337                 }
    338             }
    339         }
    340 
    341         /* Try to write */
    342 writeBegin:
    343 
    344         /* Send all smaller messages regardless of the upload cap */
    345         while( ( p = messagesPending( peer, &size ) ) )
    346         {
    347             ret = tr_netSend( peer->socket, p, size );
    348             if( ret & TR_NET_CLOSE )
    349             {
    350                 goto dropPeer;
    351             }
    352             else if( ret & TR_NET_BLOCK )
    353             {
    354                 goto writeEnd;
    355             }
    356             messagesSent( peer, ret );
    357         }
    358 
    359         /* Send pieces if we can */
    360         while( ( p = blockPending( tor, peer, &size ) ) )
    361         {
    362             if( !tr_uploadCanUpload( tor->upload ) )
    363             {
    364                 break;
    365             }
    366 
    367             ret = tr_netSend( peer->socket, p, size );
    368             if( ret & TR_NET_CLOSE )
    369             {
    370                 goto dropPeer;
    371             }
    372             else if( ret & TR_NET_BLOCK )
    373             {
    374                 break;
    375             }
    376 
    377             blockSent( peer, ret );
    378             tr_uploadUploaded( tor->upload, ret );
    379 
    380             tor->uploaded[9] += ret;
    381             peer->outTotal   += ret;
    382             peer->outDate     = tr_date();
    383 
    384             /* In case this block is done, you may have messages
    385                pending. Send them before we start the next block */
    386             goto writeBegin;
    387         }
    388 writeEnd:
    389 
    390         /* Connected peers: ask for a block whenever possible */
    391         if( peer->status & PEER_STATUS_CONNECTED )
    392         {
    393             if( tor->blockHaveCount < tor->blockCount &&
    394                 !peer->amInterested && tor->peerCount > TR_MAX_PEER_COUNT - 2 )
    395             {
    396                 /* This peer is no use to us, and it seems there are
    397                    more */
    398                 peer_dbg( "not interesting" );
    399                 tr_peerRem( tor, i );
    400                 continue;
    401             }
    402 
    403             if( peer->amInterested && !peer->peerChoking )
    404             {
    405                 int block;
    406                 while( peer->inRequestCount < OUR_REQUEST_COUNT )
    407                 {
    408                     block = chooseBlock( tor, peer );
    409                     if( block < 0 )
    410                     {
    411                         break;
    412                     }
    413                     sendRequest( tor, peer, block );
    414                 }
     385                sendRequest( tor, peer, block );
    415386            }
    416387        }
  • /trunk/libtransmission/peermessages.h

    r1 r3  
    2121 *****************************************************************************/
    2222
     23/***********************************************************************
     24 * This file handles all outgoing messages
     25 **********************************************************************/
     26
    2327static uint8_t * messagesPending( tr_peer_t * peer, int * size )
    2428{
     
    212216    TR_HTONL( 1 + bitfieldSize, p );
    213217    p[4] = 5;
    214     memcpy( &p[5], tor->bitfield, bitfieldSize );
     218    memcpy( &p[5], tr_cpPieceBitfield( tor->completion ), bitfieldSize );
    215219
    216220    peer_dbg( "SEND bitfield" );
     
    254258    TR_HTONL( r->length, p + 13 );
    255259
    256     /* Remember that we have one more uploader for this block */
    257     (tor->blockHave[block])++;
     260    tr_cpDownloaderAdd( tor->completion, block );
    258261
    259262    peer_dbg( "SEND request %d/%d (%d bytes)",
  • /trunk/libtransmission/peerutils.h

    r1 r3  
    123123    }
    124124
     125    /* TODO: check for bad downloaders */
     126
    125127#if 0
    126128    /* Choke unchoked peers we are not sending anything to */
     
    162164    }
    163165
    164     return 0;
    165 }
    166 
    167 static int parseMessage( tr_torrent_t * tor, tr_peer_t * peer,
    168                          int newBytes )
    169 {
    170     tr_info_t * inf = &tor->info;
    171 
    172     int       i, j;
    173     int       len;
    174     char      id;
    175     uint8_t * p   = peer->buf;
    176     uint8_t * end = &p[peer->pos];
    177    
    178     for( ;; )
    179     {
    180         if( peer->pos < 4 )
    181         {
    182             break;
    183         }
    184 
    185         if( peer->status & PEER_STATUS_HANDSHAKE )
    186         {
    187             char * client;
    188 
    189             if( p[0] != 19 || memcmp( &p[1], "Bit", 3 ) )
    190             {
    191                 /* Don't wait until we get 68 bytes, this is wrong
    192                    already */
    193                 peer_dbg( "GET  handshake, invalid" );
    194                 tr_netSend( peer->socket, (uint8_t *) "Nice try...\r\n", 13 );
    195                 return 1;
    196             }
    197 
    198             if( peer->pos < 68 )
    199             {
    200                 break;
    201             }
    202 
    203             if( memcmp( &p[4], "Torrent protocol", 16 ) )
    204             {
    205                 peer_dbg( "GET  handshake, invalid" );
    206                 return 1;
    207             }
    208 
    209             if( memcmp( &p[28], inf->hash, 20 ) )
    210             {
    211                 peer_dbg( "GET  handshake, wrong torrent hash" );
    212                 return 1;
    213             }
    214 
    215             if( !memcmp( &p[48], tor->id, 20 ) )
    216             {
    217                 /* We are connected to ourselves... */
    218                 peer_dbg( "GET  handshake, that is us" );
    219                 return 1;
    220             }
    221 
    222             peer->status  = PEER_STATUS_CONNECTED;
    223             memcpy( peer->id, &p[48], 20 );
    224             p            += 68;
    225             peer->pos    -= 68;
    226 
    227             for( i = 0; i < tor->peerCount; i++ )
    228             {
    229                 if( tor->peers[i] == peer )
    230                 {
    231                     continue;
    232                 }
    233                 if( !peerCmp( peer, tor->peers[i] ) )
    234                 {
    235                     peer_dbg( "GET  handshake, duplicate" );
    236                     return 1;
    237                 }
    238             }
    239 
    240             client = tr_clientForId( (uint8_t *) peer->id );
    241             peer_dbg( "GET  handshake, ok (%s)", client );
    242             free( client );
    243 
    244             sendBitfield( tor, peer );
    245 
    246             continue;
    247         }
    248        
    249         /* Get payload size */
    250         TR_NTOHL( p, len );
    251         p += 4;
    252 
    253         if( len > 9 + tor->blockSize )
    254         {
    255             /* This shouldn't happen. Forget about that peer */
    256             peer_dbg( "message too large" );
     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 );
    257175            return 1;
    258176        }
    259 
    260         if( !len )
    261         {
    262             /* keep-alive */
    263             peer_dbg( "GET  keep-alive" );
    264             peer->pos -= 4;
    265             continue;
    266         }
    267 
    268         /* That's a piece coming */
    269         if( p < end && *p == 7 )
    270         {
    271             /* XXX */
    272             tor->downloaded[9] += newBytes;
    273             peer->inTotal      += newBytes;
    274             newBytes            = 0;
    275         }
    276 
    277         if( &p[len] > end )
    278         {
    279             /* We do not have the entire message */
    280             p -= 4;
    281             break;
    282         }
    283 
    284         /* Remaining data after this message */
    285         peer->pos -= 4 + len;
    286 
    287         /* Type of the message */
    288         id = *(p++);
    289 
    290         switch( id )
    291         {
    292             case 0: /* choke */
    293             {
    294                 tr_request_t * r;
    295 
    296                 if( len != 1 )
    297                 {
    298                     peer_dbg( "GET  choke, invalid" );
    299                     return 1;
    300                 }
    301 
    302                 peer_dbg( "GET  choke" );
    303                 peer->peerChoking    = 1;
    304 
    305                 for( i = 0; i < peer->inRequestCount; i++ )
    306                 {
    307                     r = &peer->inRequests[i];
    308                     if( tor->blockHave[tr_block(r->index,r->begin)] > 0 )
    309                     {
    310                         tor->blockHave[tr_block(r->index,r->begin)]--;
    311                     }
    312                 }
    313                 peer->inRequestCount = 0;
    314 
    315                 break;
    316             }
    317             case 1: /* unchoke */
    318                 if( len != 1 )
    319                 {
    320                     peer_dbg( "GET  unchoke, invalid" );
    321                     return 1;
    322                 }
    323                 peer_dbg( "GET  unchoke" );
    324                 peer->peerChoking = 0;
    325                 break;
    326             case 2: /* interested */
    327                 if( len != 1 )
    328                 {
    329                     peer_dbg( "GET  interested, invalid" );
    330                     return 1;
    331                 }
    332                 peer_dbg( "GET  interested" );
    333                 peer->peerInterested = 1;
    334                 break;
    335             case 3: /* uninterested */
    336                 if( len != 1 )
    337                 {
    338                     peer_dbg( "GET  uninterested, invalid" );
    339                     return 1;
    340                 }
    341                 peer_dbg( "GET  uninterested" );
    342                 peer->peerInterested = 0;
    343                 break;
    344             case 4: /* have */
    345             {
    346                 uint32_t piece;
    347                 if( len != 5 )
    348                 {
    349                     peer_dbg( "GET  have, invalid" );
    350                     return 1;
    351                 }
    352                 TR_NTOHL( p, piece );
    353                 if( !peer->bitfield )
    354                 {
    355                     peer->bitfield = calloc( ( inf->pieceCount + 7 ) / 8, 1 );
    356                 }
    357                 tr_bitfieldAdd( peer->bitfield, piece );
    358 
    359                 updateInterest( tor, peer );
    360 
    361                 peer_dbg( "GET  have %d", piece );
    362                 break;
    363             }
    364             case 5: /* bitfield */
    365             {
    366                 int bitfieldSize;
    367 
    368                 bitfieldSize = ( inf->pieceCount + 7 ) / 8;
    369                
    370                 if( len != 1 + bitfieldSize )
    371                 {
    372                     peer_dbg( "GET  bitfield, wrong size" );
    373                     return 1;
    374                 }
    375 
    376                 /* Make sure the spare bits are unset */
    377                 if( ( inf->pieceCount & 0x7 ) )
    378                 {
    379                     uint8_t lastByte;
    380                    
    381                     lastByte   = p[bitfieldSize-1];
    382                     lastByte <<= inf->pieceCount & 0x7;
    383                     lastByte  &= 0xFF;
    384 
    385                     if( lastByte )
    386                     {
    387                         peer_dbg( "GET  bitfield, spare bits set" );
    388                         return 1;
    389                     }
    390                 }
    391 
    392                 if( !peer->bitfield )
    393                 {
    394                     peer->bitfield = malloc( bitfieldSize );
    395                 }
    396                 memcpy( peer->bitfield, p, bitfieldSize );
    397 
    398                 updateInterest( tor, peer );
    399 
    400                 peer_dbg( "GET  bitfield, ok" );
    401                 break;
    402             }
    403             case 6: /* request */
    404             {
    405                 int index, begin, length;
    406 
    407                 if( peer->amChoking )
    408                 {
    409                     /* Didn't he get it? */
    410                     sendChoke( peer, 1 );
    411                     break;
    412                 }
    413                
    414                 TR_NTOHL( p,     index );
    415                 TR_NTOHL( &p[4], begin );
    416                 TR_NTOHL( &p[8], length );
    417 
    418                 peer_dbg( "GET  request %d/%d (%d bytes)",
    419                           index, begin, length );
    420 
    421                 /* TODO sanity checks (do we have the piece, etc) */
    422 
    423                 if( length > 16384 )
    424                 {
    425                     /* Sorry mate */
    426                     return 1;
    427                 }
    428 
    429                 if( peer->outRequestCount < MAX_REQUEST_COUNT )
    430                 {
    431                     tr_request_t * r;
    432                    
    433                     r         = &peer->outRequests[peer->outRequestCount];
    434                     r->index  = index;
    435                     r->begin  = begin;
    436                     r->length = length;
    437 
    438                     (peer->outRequestCount)++;
    439                 }
    440                 else
    441                 {
    442                     tr_err( "Too many requests" );
    443                     return 1;
    444                 }
    445                 break;
    446             }
    447             case 7: /* piece */
    448             {
    449                 int index, begin;
    450                 int block;
    451                 tr_request_t * r;
    452 
    453                 TR_NTOHL( p,     index );
    454                 TR_NTOHL( &p[4], begin );
    455 
    456                 peer_dbg( "GET  piece %d/%d (%d bytes)",
    457                           index, begin, len - 9 );
    458 
    459                 if( peer->inRequestCount < 1 )
    460                 {
    461                     /* Our "cancel" was probably late */
    462                     peer_dbg( "not expecting a block" );
    463                     break;
    464                 }
    465                
    466                 r = &peer->inRequests[0];
    467                 if( index != r->index || begin != r->begin )
    468                 {
    469                     int suckyClient;
    470 
    471                     /* Either our "cancel" was late, or this is a sucky
    472                        client that cannot deal with multiple requests */
    473                     suckyClient = 0;
    474                     for( i = 0; i < peer->inRequestCount; i++ )
    475                     {
    476                         r = &peer->inRequests[i];
    477 
    478                         if( index != r->index || begin != r->begin )
    479                         {
    480                             continue;
    481                         }
    482 
    483                         /* Sucky client, he dropped the previous requests */
    484                         peer_dbg( "block was expected later" );
    485                         for( j = 0; j < i; j++ )
    486                         {
    487                             r = &peer->inRequests[j];
    488                             if( tor->blockHave[tr_block(r->index,r->begin)] > 0 )
    489                             {
    490                                 tor->blockHave[tr_block(r->index,r->begin)]--;
    491                             }
    492                         }
    493                         suckyClient = 1;
    494                         peer->inRequestCount -= i;
    495                         memmove( &peer->inRequests[0], &peer->inRequests[i],
    496                                  peer->inRequestCount * sizeof( tr_request_t ) );
    497                         r = &peer->inRequests[0];
    498                         break;
    499                     }
    500 
    501                     if( !suckyClient )
    502                     {
    503                         r = &peer->inRequests[0];
    504                         peer_dbg( "wrong block (expecting %d/%d)",
    505                                   r->index, r->begin );
    506                         break;
    507                     }
    508                 }
    509 
    510                 if( len - 9 != r->length )
    511                 {
    512                     peer_dbg( "wrong size (expecting %d)", r->length );
    513                     return 1;
    514                 }
    515 
    516                 block = tr_block( r->index, r->begin );
    517                 if( tor->blockHave[block] < 0 )
    518                 {
    519                     peer_dbg( "have this block already" );
    520                     (peer->inRequestCount)--;
    521                     memmove( &peer->inRequests[0], &peer->inRequests[1],
    522                              peer->inRequestCount * sizeof( tr_request_t ) );
    523                     break;
    524                 }
    525 
    526                 tor->blockHave[block]  = -1;
    527                 tor->blockHaveCount   +=  1;
    528                 tr_ioWrite( tor->io, index, begin, len - 9, &p[8] );
    529 
    530                 sendCancel( tor, block );
    531 
    532                 if( tr_bitfieldHas( tor->bitfield, index ) )
    533                 {
    534                     tr_peer_t * otherPeer;
    535 
    536                     for( i = 0; i < tor->peerCount; i++ )
    537                     {
    538                         otherPeer = tor->peers[i];
    539 
    540                         if( otherPeer->status < PEER_STATUS_CONNECTED )
    541                         {
    542                             continue;
    543                         }
    544 
    545                         sendHave( otherPeer, index );
    546                         updateInterest( tor, otherPeer );
    547                     }
    548                 }
    549 
    550                 (peer->inRequestCount)--;
    551                 memmove( &peer->inRequests[0], &peer->inRequests[1],
    552                          peer->inRequestCount * sizeof( tr_request_t ) );
    553                 break;
    554             }
    555             case 8: /* cancel */
    556             {
    557                 int index, begin, length;
    558                 int i;
    559                 tr_request_t * r;
    560 
    561                 TR_NTOHL( p,     index );
    562                 TR_NTOHL( &p[4], begin );
    563                 TR_NTOHL( &p[8], length );
    564 
    565                 peer_dbg( "GET  cancel %d/%d (%d bytes)",
    566                           index, begin, length );
    567 
    568                 for( i = 0; i < peer->outRequestCount; i++ )
    569                 {
    570                     r = &peer->outRequests[i];
    571                     if( r->index == index && r->begin == begin &&
    572                         r->length == length )
    573                     {
    574                         (peer->outRequestCount)--;
    575                         memmove( &r[0], &r[1], sizeof( tr_request_t ) *
    576                                 ( peer->outRequestCount - i ) );
    577                         break;
    578                     }
    579                 }
    580 
    581                 break;
    582             }
    583             case 9:
    584             {
    585                 in_port_t port;
    586 
    587                 if( len != 3 )
    588                 {
    589                     peer_dbg( "GET  port, invalid" );
    590                     return 1;
    591                 }
    592 
    593                 port = *( (in_port_t *) p );
    594                 peer_dbg( "GET  port %d", ntohs( port ) );
    595 
    596                 break;
    597             }
    598             default:
    599             {
    600                 peer_dbg( "Unknown message '%d'", id );
    601                 return 1;
    602             }
    603         }
    604 
    605         p += len - 1;
    606     }
    607 
    608     memmove( peer->buf, p, peer->pos );
     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    }
    609205
    610206    return 0;
     
    623219    int i;
    624220    int bitfieldSize = ( inf->pieceCount + 7 ) / 8;
     221    uint8_t * bitfield = tr_cpPieceBitfield( tor->completion );
    625222
    626223    if( !peer->bitfield )
     
    632229    for( i = 0; i < bitfieldSize; i++ )
    633230    {
    634         if( ( peer->bitfield[i] & ~(tor->bitfield[i]) ) & 0xFF )
     231        if( ( peer->bitfield[i] & ~(bitfield[i]) ) & 0xFF )
    635232        {
    636233            return 1;
     
    663260 * missing less blocks.
    664261 **********************************************************************/
    665 static int chooseBlock( tr_torrent_t * tor, tr_peer_t * peer )
     262static inline int chooseBlock( tr_torrent_t * tor, tr_peer_t * peer )
    666263{
    667264    tr_info_t * inf = &tor->info;
    668265
    669     int i, j;
    670     int startBlock, endBlock, countBlocks;
     266    int i;
    671267    int missingBlocks, minMissing;
    672268    int poolSize, * pool;
     
    679275    for( i = 0; i < inf->pieceCount; i++ )
    680276    {
     277        missingBlocks = tr_cpMissingBlocksForPiece( tor->completion, i );
     278        if( missingBlocks < 1 )
     279        {
     280            /* We already have or are downloading all blocks */
     281            continue;
     282        }
    681283        if( !tr_bitfieldHas( peer->bitfield, i ) )
    682284        {
    683285            /* The peer doesn't have this piece */
    684             continue;
    685         }
    686         if( tr_bitfieldHas( tor->bitfield, i ) )
    687         {
    688             /* We already have it */
    689             continue;
    690         }
    691 
    692         /* Count how many blocks from this piece are missing */
    693         startBlock    = tr_pieceStartBlock( i );
    694         countBlocks   = tr_pieceCountBlocks( i );
    695         endBlock      = startBlock + countBlocks;
    696         missingBlocks = countBlocks;
    697         for( j = startBlock; j < endBlock; j++ )
    698         {
    699             /* TODO: optimize */
    700             if( tor->blockHave[j] )
    701             {
    702                 missingBlocks--;
    703             }
    704             if( missingBlocks > minMissing )
    705             {
    706                 break;
    707             }
    708         }
    709 
    710         if( missingBlocks < 1 )
    711         {
    712             /* We are already downloading all blocks */
    713286            continue;
    714287        }
     
    776349
    777350        /* Pick a block in this piece */
    778         startBlock = tr_pieceStartBlock( piece );
    779         endBlock   = startBlock + tr_pieceCountBlocks( piece );
    780         for( i = startBlock; i < endBlock; i++ )
    781         {
    782             if( !tor->blockHave[i] )
    783             {
    784                 block = i;
    785                 goto check;
    786             }
    787         }
    788 
     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    {
    789384        /* Shouldn't happen */
    790385        return -1;
    791386    }
    792387
    793     free( pool );
    794 
    795     /* "End game" mode */
    796     block          = -1;
    797     minDownloading = TR_MAX_PEER_COUNT + 1;
    798     for( i = 0; i < tor->blockCount; i++ )
    799     {
    800         /* TODO: optimize */
    801         if( tor->blockHave[i] > 0 && tor->blockHave[i] < minDownloading )
    802         {
    803             block          = i;
    804             minDownloading = tor->blockHave[i];
    805         }
    806     }
    807 
    808     if( block < 0 )
    809     {
    810         /* Shouldn't happen */
    811         return -1;
    812     }
    813 
    814 check:
    815388    for( i = 0; i < peer->inRequestCount; i++ )
    816389    {
  • /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

    r1 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 );
     
    6067
    6168    h->bindPort = 9090;
     69
     70    snprintf( h->prefsDirectory, sizeof( h->prefsDirectory ),
     71              "%s/.transmission", getenv( "HOME" ) );
     72    mkdir( h->prefsDirectory, 0755 );
    6273   
    6374    return h;
     75}
     76
     77/***********************************************************************
     78 * tr_getPrefsDirectory
     79 ***********************************************************************
     80 *
     81 **********************************************************************/
     82char * tr_getPrefsDirectory( tr_handle_t * h )
     83{
     84    return (char *) h->prefsDirectory;
    6485}
    6586
     
    151172    tor->status = TR_STATUS_PAUSE;
    152173    tor->id     = h->id;
     174    tor->key    = h->key;
    153175
    154176    /* Guess scrape URL */
     
    178200    tor->blockCount = ( inf->totalSize + tor->blockSize - 1 ) /
    179201                        tor->blockSize;
    180     tor->blockHave  = calloc( tor->blockCount, 1 );
    181     tor->bitfield   = calloc( ( inf->pieceCount + 7 ) / 8, 1 );
     202    tor->completion = tr_cpInit( tor );
    182203
    183204    tr_lockInit( &tor->lock );
     
    185206    tor->upload  = h->upload;
    186207    tor->fdlimit = h->fdlimit;
     208    tor->prefsDirectory = (char *) h->prefsDirectory;
    187209 
    188210    /* We have a new torrent */
     
    253275    tr_trackerStopped( tor->tracker );
    254276    tor->status = TR_STATUS_STOPPING;
     277    tor->stopDate = tr_date();
    255278    tr_lockUnlock( tor->lock );
    256279}
     
    315338        inf = &tor->info;
    316339
    317         tr_lockLock( tor->lock );
    318 
    319         if( tor->status & TR_STATUS_STOPPED )
     340        if( ( tor->status & TR_STATUS_STOPPED ) ||
     341            ( ( tor->status & TR_STATUS_STOPPING ) &&
     342              tr_date() > tor->stopDate + 60000 ) )
    320343        {
    321344            torrentReallyStop( h, i );
    322345            tor->status = TR_STATUS_PAUSE;
    323346        }
     347
     348        tr_lockLock( tor->lock );
    324349
    325350        memcpy( &s[i].info, &tor->info, sizeof( tr_info_t ) );
     
    347372        }
    348373
    349         s[i].progress = (float) tor->blockHaveCount / (float) tor->blockCount;
    350 
     374        s[i].progress     = tr_cpCompletionAsFloat( tor->completion );
    351375        s[i].rateDownload = rateDownload( tor );
    352376        s[i].rateUpload   = rateUpload( tor );
     
    358382        else
    359383        {
    360             s[i].eta = (float) (tor->blockCount - tor->blockHaveCount ) *
    361                 (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;
    362386            if( s[i].eta > 99 * 3600 + 59 * 60 + 59 )
    363387            {
     
    370394            piece = j * inf->pieceCount / 120;
    371395
    372             if( tr_bitfieldHas( tor->bitfield, piece ) )
     396            if( tr_cpPieceIsComplete( tor->completion, piece ) )
    373397            {
    374398                s[i].pieces[j] = -1;
     
    413437    {
    414438        /* Join the thread first */
    415         tr_lockLock( tor->lock );
    416439        torrentReallyStop( h, t );
    417         tr_lockUnlock( tor->lock );
    418440    }
    419441
     
    421443
    422444    tr_lockClose( tor->lock );
     445    tr_cpClose( tor->completion );
    423446
    424447    if( tor->destination )
     
    428451    free( inf->pieces );
    429452    free( inf->files );
    430     free( tor->blockHave );
    431     free( tor->bitfield );
    432453    free( tor );
    433454
     
    459480#endif
    460481
     482    tr_lockLock( tor->lock );
     483
    461484    tor->io     = tr_ioInit( tor );
    462     tor->status = ( tor->blockHaveCount < tor->blockCount ) ?
    463                       TR_STATUS_DOWNLOAD : TR_STATUS_SEED;
    464    
     485    tor->status = tr_cpIsSeeding( tor->completion ) ?
     486                      TR_STATUS_SEED : TR_STATUS_DOWNLOAD;
     487
    465488    while( !tor->die )
    466489    {
    467490        date1 = tr_date();
    468 
    469         tr_lockLock( tor->lock );
    470491
    471492        /* Are we finished ? */
    472493        if( ( tor->status & TR_STATUS_DOWNLOAD ) &&
    473             tor->blockHaveCount >= tor->blockCount )
     494            tr_cpIsSeeding( tor->completion ) )
    474495        {
    475496            /* Done */
     
    479500
    480501        /* Receive/send messages */
    481         if( !( tor->status & TR_STATUS_STOPPING ) )
    482         {
    483             tr_peerPulse( tor );
    484         }
     502        tr_peerPulse( tor );
    485503
    486504        /* Try to get new peers or to send a message to the tracker */
    487505        tr_trackerPulse( tor->tracker );
    488 
    489         tr_lockUnlock( tor->lock );
    490506
    491507        if( tor->status & TR_STATUS_STOPPED )
     
    498514        if( date2 < date1 + 20 )
    499515        {
     516            tr_lockUnlock( tor->lock );
    500517            tr_wait( date1 + 20 - date2 );
    501         }
    502     }
     518            tr_lockLock( tor->lock );
     519        }
     520    }
     521
     522    tr_lockUnlock( tor->lock );
    503523
    504524    tr_ioClose( tor->io );
  • /trunk/libtransmission/transmission.h

    r1 r3  
    4141
    4242/***********************************************************************
     43 * tr_getPrefsDirectory
     44 ***********************************************************************
     45 * Returns the full path to the directory used by libtransmission to
     46 * store the resume files. The string belongs to libtransmission, do
     47 * not free it.
     48 **********************************************************************/
     49char * tr_getPrefsDirectory( tr_handle_t * );
     50
     51/***********************************************************************
    4352 * tr_setBindPort
    4453 ***********************************************************************
  • /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

    r1 r3  
    2121 *****************************************************************************/
    2222
     23#ifndef CONTROLLER_H
     24#define CONTROLLER_H
     25
    2326#include <Cocoa/Cocoa.h>
    2427#include <transmission.h>
    2528#include "PrefsController.h"
     29
     30@class TorrentTableView;
    2631
    2732@interface Controller : NSObject
     
    3136    tr_stat_t                    * fStat;
    3237    int                            fResumeOnWake[TR_MAX_TORRENT_COUNT];
    33    
     38
    3439    NSToolbar                    * fToolbar;
    3540
     
    3944
    4045    IBOutlet NSWindow            * fWindow;
    41     IBOutlet NSTableView         * fTableView;
     46    IBOutlet TorrentTableView    * fTableView;
    4247    IBOutlet NSTextField         * fTotalDLField;
    4348    IBOutlet NSTextField         * fTotalULField;
     49    IBOutlet NSMenu              * fContextMenu;
    4450
    4551    IBOutlet NSPanel             * fInfoPanel;
     
    6470                            contextInfo: (void *) info;
    6571- (void) stopTorrent:     (id) sender;
     72- (void) stopTorrentWithIndex: (int) index;
    6673- (void) resumeTorrent:   (id) sender;
     74- (void) resumeTorrentWithIndex: (int) index;
    6775- (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;
    6882- (void) showInfo:        (id) sender;
    6983
     
    7286                            (void *) messageArgument;
    7387
     88- (NSMenu *) menuForIndex: (int) idx;
     89
    7490- (void) showMainWindow:  (id) sender;
    7591- (void) linkHomepage:    (id) sender;
     
    7793
    7894@end
     95
     96#endif
  • /trunk/macosx/Controller.m

    r1 r3  
    2323#include <IOKit/IOMessage.h>
    2424
    25 #include "Controller.h"
    2625#include "NameCell.h"
    2726#include "ProgressCell.h"
    2827#include "Utils.h"
     28#include "TorrentTableView.h"
    2929
    3030#define TOOLBAR_OPEN   @"Toolbar Open"
    31 #define TOOLBAR_RESUME @"Toolbar Resume"
    32 #define TOOLBAR_STOP   @"Toolbar Stop"
    3331#define TOOLBAR_REMOVE @"Toolbar Remove"
     32#define TOOLBAR_PREFS  @"Toolbar Preferences"
    3433#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                                             
    3541
    3642static void sleepCallBack( void * controller, io_service_t y,
     
    4349@implementation Controller
    4450
    45 - (void) enableToolbarItem: (NSString *) ident flag: (BOOL) e
    46 {
    47     NSArray * array = [fToolbar items];
     51- (void) updateToolbar
     52{
     53    NSArray * items;
    4854    NSToolbarItem * item;
    49 
    50     if( [ident isEqualToString: TOOLBAR_OPEN] )
    51     {
    52         item = [array objectAtIndex: 0];
    53         [item setAction: e ? @selector( openShowSheet: ) : NULL];
    54     }
    55     else if( [ident isEqualToString: TOOLBAR_RESUME] )
    56     {
    57         item = [array objectAtIndex: 1];
    58         [item setAction: e ? @selector( resumeTorrent: ) : NULL];
    59     }
    60     else if( [ident isEqualToString: TOOLBAR_STOP] )
    61     {
    62         item = [array objectAtIndex: 2];
    63         [item setAction: e ? @selector( stopTorrent: ) : NULL];
    64     }
    65     else if( [ident isEqualToString: TOOLBAR_REMOVE] )
    66     {
    67         item = [array objectAtIndex: 3];
    68         [item setAction: e ? @selector( removeTorrent: ) : NULL];
    69     }
    70     else if( [ident isEqualToString: TOOLBAR_INFO] )
    71     {
    72         item = [array objectAtIndex: 5];
    73         [item setAction: e ? @selector( showInfo: ) : NULL];
    74     }
    75 }
    76 
    77 - (void) updateToolbar
    78 {
    79     int row = [fTableView selectedRow];
    80 
    81     [self enableToolbarItem: TOOLBAR_RESUME flag: NO];
    82     [self enableToolbarItem: TOOLBAR_STOP   flag: NO];
    83     [self enableToolbarItem: TOOLBAR_REMOVE flag: NO];
    84 
    85     if( row < 0 )
    86     {
    87         return;
    88     }
    89 
    90     if( fStat[row].status &
    91         ( TR_STATUS_CHECK | TR_STATUS_DOWNLOAD | TR_STATUS_SEED ) )
    92     {
    93         [self enableToolbarItem: TOOLBAR_STOP   flag: YES];
    94     }
    95     else
    96     {
    97         [self enableToolbarItem: TOOLBAR_RESUME flag: YES];
    98         [self enableToolbarItem: TOOLBAR_REMOVE flag: YES];
     55    BOOL enable;
     56    int row;
     57    unsigned i;
     58
     59    row    = [fTableView selectedRow];
     60    enable = ( row >= 0 ) && ( fStat[row].status &
     61                ( TR_STATUS_STOPPING | TR_STATUS_PAUSE ) );
     62
     63    items = [fToolbar items];
     64    for( i = 0; i < [items count]; i++ )
     65    {
     66        item = [items objectAtIndex: i];
     67        if( [[item itemIdentifier] isEqualToString: TOOLBAR_REMOVE] )
     68        {
     69            [item setAction: enable ? @selector( removeTorrent: ) : NULL];
     70        }
    9971    }
    10072}
     
    11991    [fWindow  setDelegate: self];
    12092
    121     [self enableToolbarItem: TOOLBAR_OPEN   flag: YES];
    122     [self enableToolbarItem: TOOLBAR_RESUME flag: NO];
    123     [self enableToolbarItem: TOOLBAR_STOP   flag: NO];
    124     [self enableToolbarItem: TOOLBAR_REMOVE flag: NO];
    125     [self enableToolbarItem: TOOLBAR_INFO   flag: YES];
    126 
    127     [fTableView setDataSource: self];
    128     [fTableView setDelegate:   self];
    129 
    13093    NSTableColumn * tableColumn;
    13194    NameCell      * nameCell;
     
    144107    [tableColumn setMaxWidth: 134.0];
    145108
    146     [fTableView  sizeToFit];
     109    [fTableView setAutosaveTableColumns: YES];
     110    [fTableView sizeToFit];
    147111
    148112    [fTableView registerForDraggedTypes: [NSArray arrayWithObjects:
     
    209173        selector: @selector( updateUI: ) userInfo: NULL repeats: YES];
    210174    [[NSRunLoop currentRunLoop] addTimer: fTimer
    211         forMode: NSModalPanelRunLoopMode];
     175        forMode: NSEventTrackingRunLoopMode];
     176}
     177
     178- (void) windowDidResize: (NSNotification *) n
     179{
     180    [fTableView sizeToFit];
    212181}
    213182
     
    412381- (void) resumeTorrent: (id) sender
    413382{
    414     tr_torrentStart( fHandle, [fTableView selectedRow] );
    415     [self updateToolbar];
     383    [self resumeTorrentWithIndex: [fTableView selectedRow]];
     384}
     385
     386- (void) resumeTorrentWithIndex: (int) idx
     387{
     388    tr_torrentStart( fHandle, idx );
     389    [self updateUI: NULL];
    416390}
    417391
    418392- (void) stopTorrent: (id) sender
    419393{
    420     tr_torrentStop( fHandle, [fTableView selectedRow] );
    421     [self updateToolbar];
     394    [self stopTorrentWithIndex: [fTableView selectedRow]];
     395}
     396
     397- (void) stopTorrentWithIndex: (int) idx
     398{
     399    tr_torrentStop( fHandle, idx );
     400    [self updateUI: NULL];
     401}
     402
     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];
    422480}
    423481
    424482- (void) removeTorrent: (id) sender
    425483{
    426     tr_torrentClose( fHandle, [fTableView selectedRow] );
    427     [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];
    428500}
    429501
     
    451523    }
    452524    fCount = tr_torrentStat( fHandle, &fStat );
    453     [fTableView reloadData];
     525    [fTableView updateUI: fStat];
    454526
    455527    /* Update the global DL/UL rates */
     
    472544    /* Must we do this? Can't remember */
    473545    [self updateToolbar];
     546}
     547
     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;
    474583}
    475584
     
    544653
    545654    /* Update info window */
    546     [fInfoTitle setStringValue: [NSString stringWithCString:
     655    [fInfoTitle setStringValue: [NSString stringWithUTF8String:
    547656        fStat[row].info.name]];
    548657    [fInfoTracker setStringValue: [NSString stringWithFormat:
     
    566675    item = [[NSToolbarItem alloc] initWithItemIdentifier: ident];
    567676
    568     [item setTarget: self];
    569 
    570677    if( [ident isEqualToString: TOOLBAR_OPEN] )
    571678    {
    572679        [item setLabel: @"Open"];
    573680        [item setToolTip: @"Open a torrent"];
    574         [item setImage: [NSImage imageNamed: @"Open.tiff"]];
    575     }
    576     else if( [ident isEqualToString: TOOLBAR_RESUME] )
    577     {
    578         [item setLabel: @"Resume"];
    579         [item setToolTip: @"Resume download"];
    580         [item setImage: [NSImage imageNamed: @"Resume.tiff"]];
    581     }
    582     else if( [ident isEqualToString: TOOLBAR_STOP] )
    583     {
    584         [item setLabel: @"Stop"];
    585         [item setToolTip: @"Stop download"];
    586         [item setImage: [NSImage imageNamed: @"Stop.tiff"]];
     681        [item setImage: [NSImage imageNamed: @"Open.png"]];
     682        [item setTarget: self];
     683        [item setAction: @selector( openShowSheet: )];
    587684    }
    588685    else if( [ident isEqualToString: TOOLBAR_REMOVE] )
     
    590687        [item setLabel: @"Remove"];
    591688        [item setToolTip: @"Remove torrent from list"];
    592         [item setImage: [NSImage imageNamed: @"Remove.tiff"]];
     689        [item setImage: [NSImage imageNamed: @"Remove.png"]];
     690        [item setTarget: self];
     691        /* We set the selector in updateToolbar: */
     692    }
     693    else if( [ident isEqualToString: TOOLBAR_PREFS] )
     694    {
     695        [item setLabel: @"Preferences"];
     696        [item setToolTip: @"Show the Preferences panel"];
     697        [item setImage: [NSImage imageNamed: @"Preferences.png"]];
     698        [item setTarget: fPrefsController];
     699        [item setAction: @selector( show: )];
    593700    }
    594701    else if( [ident isEqualToString: TOOLBAR_INFO] )
     
    596703        [item setLabel: @"Info"];
    597704        [item setToolTip: @"Information"];
    598         [item setImage: [NSImage imageNamed: @"Info.tiff"]];
     705        [item setImage: [NSImage imageNamed: @"Info.png"]];
     706        [item setTarget: self];
     707        [item setAction: @selector( showInfo: )];
    599708    }
    600709    else
     
    609718- (NSArray *) toolbarAllowedItemIdentifiers: (NSToolbar *) t
    610719{
    611     return [NSArray arrayWithObjects:
    612             TOOLBAR_OPEN, TOOLBAR_RESUME, TOOLBAR_STOP, TOOLBAR_REMOVE,
    613             NSToolbarFlexibleSpaceItemIdentifier, TOOLBAR_INFO, NULL];
     720    return [NSArray arrayWithObjects: TOOLBAR_OPEN, TOOLBAR_REMOVE,
     721            NSToolbarFlexibleSpaceItemIdentifier, TOOLBAR_PREFS,
     722            TOOLBAR_INFO, NULL];
    614723}
    615724
  • /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

    r1 r3  
    2121 *****************************************************************************/
    2222
     23#ifndef NAMECELL_H
     24#define NAMECELL_H
     25
    2326#include <Cocoa/Cocoa.h>
    2427#include <transmission.h>
     28#include "Controller.h"
    2529
    2630@interface NameCell : NSCell
    2731{
    28     tr_stat_t * fStat;
    29     NSRect      fRevealRect;
    30     NSPoint     fClickPoint;
     32    NSString * fNameString;
     33    NSString * fSizeString;
     34    NSString * fTimeString;
     35    NSString * fPeersString;
    3136}
    3237- (void) setStat: (tr_stat_t *) stat;
    3338@end
     39
     40#endif
  • /trunk/macosx/NameCell.m

    r1 r3  
    2626@implementation NameCell
    2727
    28 - (void) setStat: (tr_stat_t *) stat;
     28- (void) setStat: (tr_stat_t *) stat
    2929{
    30     fStat = stat;
     30    fNameString  = [NSString stringWithUTF8String: stat->info.name];
     31    fSizeString  = [NSString stringWithFormat: @" (%@)",
     32                    stringForFileSize( stat->info.totalSize )];
     33    fTimeString  = @"";
     34    fPeersString = @"";
     35
     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    }
    3183}
    3284
    3385- (void) drawWithFrame: (NSRect) cellFrame inView: (NSView *) view
    3486{
     87    NSString * string;
     88    NSPoint pen;
     89    NSMutableDictionary * attributes;
     90
    3591    if( ![view lockFocusIfCanDraw] )
    3692    {
     
    3894    }
    3995
    40     NSString * nameString = NULL, * timeString = @"", * peersString = @"";
    41     NSMutableDictionary * attributes;
     96    pen = cellFrame.origin;
     97
    4298    attributes = [NSMutableDictionary dictionaryWithCapacity: 1];
    43     NSPoint pen = cellFrame.origin;
    44    
    45     NSString * sizeString = [NSString stringWithFormat: @" (%@)",
    46         stringForFileSize( fStat->info.totalSize )];
    47 
    48     nameString = [NSString stringWithFormat: @"%@%@",
    49         stringFittingInWidth( fStat->info.name, cellFrame.size.width -
    50                 10 - widthForString( sizeString, 12 ), 12 ),
    51         sizeString];
    52 
    53     if( fStat->status & TR_STATUS_PAUSE )
    54     {
    55         timeString = [NSString stringWithFormat:
    56             @"Paused (%.2f %%)", 100 * fStat->progress];
    57         peersString = @"";
    58     }
    59     else if( fStat->status & TR_STATUS_CHECK )
    60     {
    61         timeString = [NSString stringWithFormat:
    62             @"Checking existing files (%.2f %%)", 100 * fStat->progress];
    63         peersString = @"";
    64     }
    65     else if( fStat->status & TR_STATUS_DOWNLOAD )
    66     {
    67         if( fStat->eta < 0 )
    68         {
    69             timeString = [NSString stringWithFormat:
    70                 @"Finishing in --:--:-- (%.2f %%)", 100 * fStat->progress];
    71         }
    72         else
    73         {
    74             timeString = [NSString stringWithFormat:
    75                 @"Finishing in %02d:%02d:%02d (%.2f %%)",
    76                 fStat->eta / 3600, ( fStat->eta / 60 ) % 60,
    77                 fStat->eta % 60, 100 * fStat->progress];
    78         }
    79         peersString = [NSString stringWithFormat:
    80             @"Downloading from %d of %d peer%s",
    81             fStat->peersUploading, fStat->peersTotal,
    82             ( fStat->peersTotal == 1 ) ? "" : "s"];
    83     }
    84     else if( fStat->status & TR_STATUS_SEED )
    85     {
    86         timeString  = [NSString stringWithFormat:
    87             @"Seeding, uploading to %d of %d peer%s",
    88             fStat->peersDownloading, fStat->peersTotal,
    89             ( fStat->peersTotal == 1 ) ? "" : "s"];
    90         peersString = @"";
    91     }
    92     else if( fStat->status & TR_STATUS_STOPPING )
    93     {
    94         timeString  = @"Stopping...";
    95         peersString = @"";
    96     }
    97 
    98     if( ( fStat->status & ( TR_STATUS_DOWNLOAD | TR_STATUS_SEED ) ) &&
    99         ( fStat->status & TR_TRACKER_ERROR ) )
    100     {
    101         peersString = [NSString stringWithFormat: @"%@%@",
    102         @"Error: ", stringFittingInWidth( fStat->error,
    103             cellFrame.size.width - 15 -
    104             widthForString( @"Error: ", 10 ), 10 )];
    105     }
    106 
    10799    [attributes setObject: [NSFont messageFontOfSize:12.0]
    108100        forKey: NSFontAttributeName];
    109101
    110102    pen.x += 5; pen.y += 5;
    111     [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];
    112107
    113108    [attributes setObject: [NSFont messageFontOfSize:10.0]
     
    115110
    116111    pen.x += 5; pen.y += 20;
    117     [timeString drawAtPoint: pen withAttributes: attributes];
     112    [fTimeString drawAtPoint: pen withAttributes: attributes];
    118113
    119114    pen.x += 0; pen.y += 15;
    120     [peersString drawAtPoint: pen withAttributes: attributes];
    121 
    122     /* "Reveal in Finder" button */
    123     fRevealRect = NSMakeRect( cellFrame.origin.x + cellFrame.size.width - 19,
    124                               cellFrame.origin.y + cellFrame.size.height - 19,
    125                               14, 14 );
    126     NSImage * revealImage;
    127     if( NSPointInRect( fClickPoint, fRevealRect ) )
    128     {
    129         revealImage = [NSImage imageNamed: @"RevealOn.tiff"];
    130     }
    131     else
    132     {
    133         revealImage = [NSImage imageNamed: @"RevealOff.tiff"];
    134     }
    135     pen.x = fRevealRect.origin.x;
    136     pen.y = fRevealRect.origin.y + 14;
    137     [revealImage compositeToPoint: pen operation: NSCompositeSourceOver];
     115    string = stringFittingInWidth( fPeersString,
     116                cellFrame.size.width - 40, 10 );
     117    [string drawAtPoint: pen withAttributes: attributes];
    138118
    139119    [view unlockFocus];
    140120}
    141121
    142 /* Track mouse as long as button is down */
    143 - (BOOL) startTrackingAt: (NSPoint) start inView: (NSView *) v
    144 {
    145     fClickPoint = start;
    146     return YES;
    147 }
    148 - (BOOL) continueTracking: (NSPoint) last at: (NSPoint) current
    149     inView: (NSView *) v
    150 {
    151     fClickPoint = current;
    152     return YES;
    153 }
    154 
    155 - (void) stopTracking: (NSPoint) last at:(NSPoint) stop
    156     inView: (NSView *) v mouseIsUp: (BOOL) flag
    157 {
    158     if( flag && NSPointInRect( stop, fRevealRect ) )
    159     {
    160         /* Reveal in Finder */
    161         [[NSWorkspace sharedWorkspace] openFile:
    162             [NSString stringWithUTF8String: fStat->folder]];
    163     }
    164     fClickPoint = NSMakePoint(0,0);
    165 }
    166 
    167122@end
  • /trunk/macosx/ProgressCell.h

    r1 r3  
    2121 *****************************************************************************/
    2222
     23#ifndef PROGRESSCELL_H
     24#define PROGRESSCELL_H
     25
    2326#include <Cocoa/Cocoa.h>
    2427#include <transmission.h>
     
    4245- (void) drawWithFrame: (NSRect) cellFrame inView: (NSView *) view;
    4346@end
     47
     48#endif
  • /trunk/macosx/ProgressCell.m

    r1 r3  
    8282
    8383    /* Have a NSBitmapImageRep ready to draw the progression bar */
    84     bgImg  = [NSImage imageNamed: @"Progress.tiff"];
     84    bgImg  = [NSImage imageNamed: @"Progress.png"];
    8585    fBgBmp = [[bgImg representations] objectAtIndex: 0];
    8686    size   = [bgImg size];
  • /trunk/macosx/Transmission.xcodeproj/project.pbxproj

    r1 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 */; };
    16                 4D6DAAC6090CE00500F43C22 /* RevealOff.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 4D6DAAC4090CE00500F43C22 /* RevealOff.tiff */; };
    17                 4D6DAAC7090CE00500F43C22 /* RevealOn.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 4D6DAAC5090CE00500F43C22 /* RevealOn.tiff */; };
    18                 4D813EB508AA43AC00191DB4 /* Progress.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 4D813EB408AA43AC00191DB4 /* Progress.tiff */; };
     17                4D6DAAC6090CE00500F43C22 /* RevealOff.png in Resources */ = {isa = PBXBuildFile; fileRef = 4D6DAAC4090CE00500F43C22 /* RevealOff.png */; };
     18                4D6DAAC7090CE00500F43C22 /* RevealOn.png in Resources */ = {isa = PBXBuildFile; fileRef = 4D6DAAC5090CE00500F43C22 /* RevealOn.png */; };
     19                4D752E930913C949008EAAD4 /* Preferences.png in Resources */ = {isa = PBXBuildFile; fileRef = 4D752E920913C949008EAAD4 /* Preferences.png */; };
     20                4D813EB508AA43AC00191DB4 /* Progress.png in Resources */ = {isa = PBXBuildFile; fileRef = 4D813EB408AA43AC00191DB4 /* Progress.png */; };
     21                4DA6FDBA0911233800450CB1 /* PauseOn.png in Resources */ = {isa = PBXBuildFile; fileRef = 4DA6FDB80911233800450CB1 /* PauseOn.png */; };
     22                4DA6FDBB0911233800450CB1 /* PauseOff.png in Resources */ = {isa = PBXBuildFile; fileRef = 4DA6FDB90911233800450CB1 /* PauseOff.png */; };
     23                4DA6FDC5091141AD00450CB1 /* ResumeOff.png in Resources */ = {isa = PBXBuildFile; fileRef = 4DA6FDC3091141AD00450CB1 /* ResumeOff.png */; };
     24                4DA6FDC6091141AD00450CB1 /* ResumeOn.png in Resources */ = {isa = PBXBuildFile; fileRef = 4DA6FDC4091141AD00450CB1 /* ResumeOn.png */; };
    1925                4DF0C5AB0899190500DD8943 /* Controller.m in Sources */ = {isa = PBXBuildFile; fileRef = 4DF0C5A90899190500DD8943 /* Controller.m */; };
    2026                4DF0C5AE08991C1600DD8943 /* libtransmission.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4DF0C5AD08991C1600DD8943 /* libtransmission.a */; };
    21                 4DF7500C08A103AD007B0D70 /* Open.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 4DF7500708A103AD007B0D70 /* Open.tiff */; };
    22                 4DF7500D08A103AD007B0D70 /* Info.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 4DF7500808A103AD007B0D70 /* Info.tiff */; };
    23                 4DF7500E08A103AD007B0D70 /* Remove.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 4DF7500908A103AD007B0D70 /* Remove.tiff */; };
    24                 4DF7500F08A103AD007B0D70 /* Resume.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 4DF7500A08A103AD007B0D70 /* Resume.tiff */; };
    25                 4DF7501008A103AD007B0D70 /* Stop.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 4DF7500B08A103AD007B0D70 /* Stop.tiff */; };
     27                4DF7500C08A103AD007B0D70 /* Open.png in Resources */ = {isa = PBXBuildFile; fileRef = 4DF7500708A103AD007B0D70 /* Open.png */; };
     28                4DF7500D08A103AD007B0D70 /* Info.png in Resources */ = {isa = PBXBuildFile; fileRef = 4DF7500808A103AD007B0D70 /* Info.png */; };
     29                4DF7500E08A103AD007B0D70 /* Remove.png in Resources */ = {isa = PBXBuildFile; fileRef = 4DF7500908A103AD007B0D70 /* Remove.png */; };
    2630                8D11072A0486CEB800E47090 /* MainMenu.nib in Resources */ = {isa = PBXBuildFile; fileRef = 29B97318FDCFA39411CA2CEA /* MainMenu.nib */; };
    2731                8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */; };
     
    7478                4D118E1908CB46B20033958F /* PrefsController.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = PrefsController.m; sourceTree = "<group>"; };
    7579                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>"; };
    7682                4D3EA0A908AE13C600EA10C2 /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = /System/Library/Frameworks/IOKit.framework; sourceTree = "<absolute>"; };
    77                 4D6DAAC4090CE00500F43C22 /* RevealOff.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; name = RevealOff.tiff; path = Images/RevealOff.tiff; sourceTree = "<group>"; };
    78                 4D6DAAC5090CE00500F43C22 /* RevealOn.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; name = RevealOn.tiff; path = Images/RevealOn.tiff; sourceTree = "<group>"; };
    79                 4D813EB408AA43AC00191DB4 /* Progress.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; name = Progress.tiff; path = Images/Progress.tiff; sourceTree = "<group>"; };
     83                4D6DAAC4090CE00500F43C22 /* RevealOff.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = RevealOff.png; path = Images/RevealOff.png; sourceTree = "<group>"; };
     84                4D6DAAC5090CE00500F43C22 /* RevealOn.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = RevealOn.png; path = Images/RevealOn.png; sourceTree = "<group>"; };
     85                4D752E920913C949008EAAD4 /* Preferences.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Preferences.png; path = Images/Preferences.png; sourceTree = "<group>"; };
     86                4D813EB408AA43AC00191DB4 /* Progress.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Progress.png; path = Images/Progress.png; sourceTree = "<group>"; };
     87                4DA6FDB80911233800450CB1 /* PauseOn.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = PauseOn.png; path = Images/PauseOn.png; sourceTree = "<group>"; };
     88                4DA6FDB90911233800450CB1 /* PauseOff.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = PauseOff.png; path = Images/PauseOff.png; sourceTree = "<group>"; };
     89                4DA6FDC3091141AD00450CB1 /* ResumeOff.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = ResumeOff.png; path = Images/ResumeOff.png; sourceTree = "<group>"; };
     90                4DA6FDC4091141AD00450CB1 /* ResumeOn.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = ResumeOn.png; path = Images/ResumeOn.png; sourceTree = "<group>"; };
    8091                4DF0C5A90899190500DD8943 /* Controller.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = Controller.m; sourceTree = "<group>"; };
    8192                4DF0C5AA0899190500DD8943 /* Controller.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = Controller.h; sourceTree = "<group>"; };
    8293                4DF0C5AD08991C1600DD8943 /* libtransmission.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libtransmission.a; path = ../libtransmission/libtransmission.a; sourceTree = SOURCE_ROOT; };
    83                 4DF7500708A103AD007B0D70 /* Open.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; name = Open.tiff; path = Images/Open.tiff; sourceTree = "<group>"; };
    84                 4DF7500808A103AD007B0D70 /* Info.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; name = Info.tiff; path = Images/Info.tiff; sourceTree = "<group>"; };
    85                 4DF7500908A103AD007B0D70 /* Remove.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; name = Remove.tiff; path = Images/Remove.tiff; sourceTree = "<group>"; };
    86                 4DF7500A08A103AD007B0D70 /* Resume.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; name = Resume.tiff; path = Images/Resume.tiff; sourceTree = "<group>"; };
    87                 4DF7500B08A103AD007B0D70 /* Stop.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; name = Stop.tiff; path = Images/Stop.tiff; sourceTree = "<group>"; };
     94                4DF7500708A103AD007B0D70 /* Open.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Open.png; path = Images/Open.png; sourceTree = "<group>"; };
     95                4DF7500808A103AD007B0D70 /* Info.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Info.png; path = Images/Info.png; sourceTree = "<group>"; };
     96                4DF7500908A103AD007B0D70 /* Remove.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Remove.png; path = Images/Remove.png; sourceTree = "<group>"; };
    8897                8D1107310486CEB800E47090 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
    8998                8D1107320486CEB800E47090 /* Transmission.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Transmission.app; sourceTree = BUILT_PRODUCTS_DIR; };
     
    115124                                4D118E1808CB46B20033958F /* PrefsController.h */,
    116125                                4D118E1908CB46B20033958F /* PrefsController.m */,
     126                                4D364D9E091FBB2C00377D12 /* TorrentTableView.h */,
     127                                4D364D9F091FBB2C00377D12 /* TorrentTableView.m */,
    117128                        );
    118129                        name = Classes;
     
    173184                                4D2784360905709500687951 /* Transmission.icns */,
    174185                                4D043A7E090AE979009FEDA8 /* TransmissionDocument.icns */,
    175                                 4DF7500808A103AD007B0D70 /* Info.tiff */,
    176                                 4D813EB408AA43AC00191DB4 /* Progress.tiff */,
    177                                 4DF7500708A103AD007B0D70 /* Open.tiff */,
    178                                 4DF7500908A103AD007B0D70 /* Remove.tiff */,
    179                                 4DF7500A08A103AD007B0D70 /* Resume.tiff */,
    180                                 4DF7500B08A103AD007B0D70 /* Stop.tiff */,
    181                                 4D6DAAC4090CE00500F43C22 /* RevealOff.tiff */,
    182                                 4D6DAAC5090CE00500F43C22 /* RevealOn.tiff */,
     186                                4DF7500808A103AD007B0D70 /* Info.png */,
     187                                4D813EB408AA43AC00191DB4 /* Progress.png */,
     188                                4DF7500708A103AD007B0D70 /* Open.png */,
     189                                4DF7500908A103AD007B0D70 /* Remove.png */,
     190                                4D6DAAC4090CE00500F43C22 /* RevealOff.png */,
     191                                4D6DAAC5090CE00500F43C22 /* RevealOn.png */,
     192                                4DA6FDB80911233800450CB1 /* PauseOn.png */,
     193                                4DA6FDB90911233800450CB1 /* PauseOff.png */,
     194                                4DA6FDC3091141AD00450CB1 /* ResumeOff.png */,
     195                                4DA6FDC4091141AD00450CB1 /* ResumeOn.png */,
     196                                4D752E920913C949008EAAD4 /* Preferences.png */,
    183197                                8D1107310486CEB800E47090 /* Info.plist */,
    184198                                089C165CFE840E0CC02AAC07 /* InfoPlist.strings */,
     
    257271                                8D11072A0486CEB800E47090 /* MainMenu.nib in Resources */,
    258272                                8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */,
    259                                 4DF7500C08A103AD007B0D70 /* Open.tiff in Resources */,
    260                                 4DF7500D08A103AD007B0D70 /* Info.tiff in Resources */,
    261                                 4DF7500E08A103AD007B0D70 /* Remove.tiff in Resources */,
    262                                 4DF7500F08A103AD007B0D70 /* Resume.tiff in Resources */,
    263                                 4DF7501008A103AD007B0D70 /* Stop.tiff in Resources */,
    264                                 4D813EB508AA43AC00191DB4 /* Progress.tiff in Resources */,
     273                                4DF7500C08A103AD007B0D70 /* Open.png in Resources */,
     274                                4DF7500D08A103AD007B0D70 /* Info.png in Resources */,
     275                                4DF7500E08A103AD007B0D70 /* Remove.png in Resources */,
     276                                4D813EB508AA43AC00191DB4 /* Progress.png in Resources */,
    265277                                4D2784370905709500687951 /* Transmission.icns in Resources */,
    266278                                4D043A7F090AE979009FEDA8 /* TransmissionDocument.icns in Resources */,
    267                                 4D6DAAC6090CE00500F43C22 /* RevealOff.tiff in Resources */,
    268                                 4D6DAAC7090CE00500F43C22 /* RevealOn.tiff in Resources */,
     279                                4D6DAAC6090CE00500F43C22 /* RevealOff.png in Resources */,
     280                                4D6DAAC7090CE00500F43C22 /* RevealOn.png in Resources */,
     281                                4DA6FDBA0911233800450CB1 /* PauseOn.png in Resources */,
     282                                4DA6FDBB0911233800450CB1 /* PauseOff.png in Resources */,
     283                                4DA6FDC5091141AD00450CB1 /* ResumeOff.png in Resources */,
     284                                4DA6FDC6091141AD00450CB1 /* ResumeOn.png in Resources */,
     285                                4D752E930913C949008EAAD4 /* Preferences.png in Resources */,
    269286                        );
    270287                        runOnlyForDeploymentPostprocessing = 0;
     
    282299                                4D096C13089FB4E20091B166 /* ProgressCell.m in Sources */,
    283300                                4D118E1A08CB46B20033958F /* PrefsController.m in Sources */,
     301                                4D364DA0091FBB2C00377D12 /* TorrentTableView.m in Sources */,
    284302                        );
    285303                        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.