From 8adc2d7338a9f4be9df536c27512162d60e899c0 Mon Sep 17 00:00:00 2001 From: Eric Petit Date: Thu, 12 Jan 2006 18:20:48 +0000 Subject: [PATCH] Update 2005-11-01 --- Jamfile | 16 +- Jamrules | 7 +- configure | 111 +++- libtransmission/fastresume.h | 25 +- libtransmission/internal.h | 4 + libtransmission/peer.c | 8 +- libtransmission/peermessages.h | 4 + libtransmission/peerparse.h | 529 ++++++++++++++++++ libtransmission/peerutils.h | 453 +-------------- libtransmission/transmission.c | 25 +- libtransmission/transmission.h | 9 + macosx/Controller.h | 7 + macosx/Controller.m | 145 ++--- .../MainMenu.nib/keyedobjects.nib | Bin 35882 -> 35917 bytes macosx/Images/Info.png | Bin 0 -> 1881 bytes macosx/Images/Open.png | Bin 0 -> 1664 bytes macosx/Images/PauseOff.png | Bin 0 -> 651 bytes macosx/Images/PauseOn.png | Bin 0 -> 677 bytes macosx/Images/Preferences.png | Bin 0 -> 1374 bytes macosx/Images/Progress.png | Bin 0 -> 3579 bytes macosx/Images/Remove.png | Bin 0 -> 1959 bytes macosx/Images/ResumeOff.png | Bin 0 -> 3218 bytes macosx/Images/ResumeOn.png | Bin 0 -> 3237 bytes macosx/Images/RevealOff.png | Bin 0 -> 553 bytes macosx/Images/RevealOn.png | Bin 0 -> 492 bytes macosx/NameCell.h | 18 +- macosx/NameCell.m | 86 ++- macosx/ProgressCell.h | 5 + macosx/ProgressCell.m | 2 +- macosx/Transmission.xcodeproj/project.pbxproj | 76 +-- 30 files changed, 896 insertions(+), 634 deletions(-) create mode 100644 libtransmission/peerparse.h create mode 100644 macosx/Images/Info.png create mode 100644 macosx/Images/Open.png create mode 100644 macosx/Images/PauseOff.png create mode 100644 macosx/Images/PauseOn.png create mode 100644 macosx/Images/Preferences.png create mode 100644 macosx/Images/Progress.png create mode 100644 macosx/Images/Remove.png create mode 100644 macosx/Images/ResumeOff.png create mode 100644 macosx/Images/ResumeOn.png create mode 100644 macosx/Images/RevealOff.png create mode 100644 macosx/Images/RevealOn.png diff --git a/Jamfile b/Jamfile index 85a3e37f0..5269f698b 100644 --- a/Jamfile +++ b/Jamfile @@ -15,14 +15,14 @@ if $(OS) = MACOSX macosx/English.lproj/MainMenu.nib/classes.nib macosx/English.lproj/MainMenu.nib/info.nib macosx/English.lproj/MainMenu.nib/keyedobjects.nib - macosx/Images/Info.tiff - macosx/Images/Open.tiff - macosx/Images/Progress.tiff - macosx/Images/Remove.tiff - macosx/Images/Resume.tiff - macosx/Images/RevealOff.tiff - macosx/Images/RevealOn.tiff - macosx/Images/Stop.tiff + macosx/Images/Info.png + macosx/Images/Open.png + macosx/Images/PauseOff.png + macosx/Images/PauseOn.png + macosx/Images/Progress.png + macosx/Images/Remove.png + macosx/Images/RevealOff.png + macosx/Images/RevealOn.png macosx/Images/Transmission.icns macosx/Images/TransmissionDocument.icns macosx/Info.plist diff --git a/Jamrules b/Jamrules index 1633988a5..ed0fe6aab 100644 --- a/Jamrules +++ b/Jamrules @@ -7,12 +7,12 @@ if ! $(DEFINES) VERSION_MAJOR = 0 ; VERSION_MINOR = 3 ; -VERSION_STRING = $(VERSION_MAJOR).$(VERSION_MINOR) ; +# VERSION_STRING = $(VERSION_MAJOR).$(VERSION_MINOR) ; +VERSION_STRING = 0.4-cvs ; DEFINES += VERSION_MAJOR=$(VERSION_MAJOR) VERSION_MINOR=$(VERSION_MINOR) VERSION_STRING=\\\"$(VERSION_STRING)\\\" ; -CCFLAGS = -g -Wall -W ; OPTIM = -O3 ; RM = rm -Rf ; @@ -27,6 +27,7 @@ if $(OS) = MACOSX # Use libtool to build static libraries (ar does not handle # universal binaries) RANLIB = ; + NOARSCAN = 1 ; NOARUPDATE = 1 ; actions Archive { @@ -36,10 +37,12 @@ if $(OS) = MACOSX rule OSXInfoPlist { Depends $(1) : $(2) ; + Depends $(1) : Jamrules ; Clean clean : $(1) ; } actions OSXInfoPlist { + $(RM) $(1) sed "s/%%VERSION%%/$(VERSION_STRING)/" < $(2) > $(1) } diff --git a/configure b/configure index 82b8a42fc..4c22fafff 100755 --- a/configure +++ b/configure @@ -1,12 +1,82 @@ #! /bin/sh +# +# Functions +# +usage() +{ + cat << EOF + + OpenSSL options: + --disable-openssl Disable OpenSSL, use built-in SHA1 implementation + --openssl-prefix=PATH Location of OpenSSL headers and library + + Some influential environment variables: + CC C compiler command (default "cc") + CFLAGS C compiler flags (default "-g -Wall -W") + +EOF +} + +openssl_test() +{ + cat > testconf.c << EOF + #include + #include + int main() + { + SHA1( 0, 0, 0 ); + } +EOF + if [ -n "$PREFIX" ]; then + TMPFLAGS="-I$PREFIX/include -L$PREFIX/lib" + fi + if $CC $TMPFLAGS -o testconf testconf.c -lcrypto > /dev/null 2>&1 + then + echo "OpenSSL: yes" + DEFINES="$DEFINES HAVE_OPENSSL" + LINKLIBS="$LINKLIBS -lcrypto" + else + echo "OpenSSL: no, using built-in SHA1 implementation" + fi + rm -f testconf.c testconf +} + +# +# Defaults settings +# +CC="${CC-cc}" +CFLAGS="${CFLAGS--g -Wall -W}" # For > 2 GB files DEFINES="_FILE_OFFSET_BITS=64 _LARGEFILE_SOURCE" - # For asprintf DEFINES="$DEFINES _GNU_SOURCE" +openssl_disable=0 +# +# Parse options +# +while [ $# -ne 0 ]; do + param=`expr "opt$1" : 'opt[^=]*=\(.*\)'` + + case "x$1" in + x--disable-openssl) + openssl_disable="1"; + ;; + x--openssl-prefix=*) + OPENSSL_PREFIX="$param"; + ;; + x--help) + usage + exit 0 + ;; + esac + shift +done + +# # System-specific flags +# SYSTEM=`uname -s` case $SYSTEM in BeOS) @@ -55,31 +125,32 @@ case $SYSTEM in esac echo "System: $SYSTEM" -# Check for OpenSSL -cat > testconf.c << EOF -#include -#include -int main() -{ - SHA1( 0, 0, 0 ); -} -EOF -if cc -o testconf testconf.c -lcrypto > /dev/null 2>&1 -then - echo "OpenSSL: yes" - DEFINES="$DEFINES HAVE_OPENSSL" - LINKLIBS="$LINKLIBS -lcrypto" -else +# +# OpenSSL settings +# +if [ ${openssl_disable} = 1 ]; then echo "OpenSSL: no, using built-in SHA1 implementation" +else + openssl_test fi -rm -f testconf.c testconf +# # Generate config.jam +# rm -f config.jam -cat << EOF > config.jam -DEFINES = $DEFINES ; -LINKLIBS = $LINKLIBS ; +cat > config.jam << EOF +CC = $CC ; +LINK = $CC ; +CCFLAGS = $CFLAGS ; +DEFINES = $DEFINES ; +LINKLIBS = $LINKLIBS ; EOF +if [ -n "$OPENSSL_PREFIX" ]; then +cat >> config.jam << EOF +HDRS += $OPENSSL_PREFIX/include ; +LINKFLAGS += -L$OPENSSL_PREFIX/lib ; +EOF +fi echo echo "To build Transmission, run 'jam'." diff --git a/libtransmission/fastresume.h b/libtransmission/fastresume.h index 8b869fa3f..4c240b4a2 100644 --- a/libtransmission/fastresume.h +++ b/libtransmission/fastresume.h @@ -30,8 +30,7 @@ * - 4 bytes * number of pieces (byte aligned): the pieces that have * been completed or started in each slot * - * The resume file is located in ~/.transmission/. Its name is - * "resume.". + * The name of the resume file is "resume.". * * All values are stored in the native endianness. Moving a * libtransmission resume file from an architecture to another will not @@ -39,21 +38,13 @@ * so the files will be scanned). **********************************************************************/ -static char * fastResumeFolderName() -{ - char * ret; - asprintf( &ret, "%s/.transmission", getenv( "HOME" ) ); - return ret; -} - static char * fastResumeFileName( tr_io_t * io ) { + tr_torrent_t * tor = io->tor; char * ret, * p; int i; - p = fastResumeFolderName(); - asprintf( &ret, "%s/resume.%40d", p, 0 ); - free( p ); + asprintf( &ret, "%s/resume.%40d", tor->prefsDirectory, 0 ); p = &ret[ strlen( ret ) - 2 * SHA_DIGEST_LENGTH ]; for( i = 0; i < SHA_DIGEST_LENGTH; i++ ) @@ -121,11 +112,6 @@ static void fastResumeSave( tr_io_t * io ) return; } - /* Create folder if missing */ - path = fastResumeFolderName(); - mkdir( path, 0755 ); - free( path ); - /* Create/overwrite the resume file */ path = fastResumeFileName( io ); if( !( file = fopen( path, "w" ) ) ) @@ -135,7 +121,6 @@ static void fastResumeSave( tr_io_t * io ) free( path ); return; } - free( path ); /* Write format version */ fwrite( &version, 4, 1, file ); @@ -160,6 +145,9 @@ static void fastResumeSave( tr_io_t * io ) fwrite( io->slotPiece, 4, inf->pieceCount, file ); fclose( file ); + + tr_dbg( "Resume file '%s' written", path ); + free( path ); } static int fastResumeLoad( tr_io_t * io ) @@ -184,6 +172,7 @@ static int fastResumeLoad( tr_io_t * io ) free( path ); return 1; } + tr_dbg( "Resume file '%s' loaded", path ); free( path ); /* Check the size */ diff --git a/libtransmission/internal.h b/libtransmission/internal.h index 7c63b45e7..86e265d24 100644 --- a/libtransmission/internal.h +++ b/libtransmission/internal.h @@ -155,6 +155,7 @@ struct tr_torrent_s tr_tracker_t * tracker; tr_io_t * io; + uint64_t stopDate; int bindSocket; int bindPort; @@ -164,6 +165,8 @@ struct tr_torrent_s uint64_t dates[10]; uint64_t downloaded[10]; uint64_t uploaded[10]; + + char * prefsDirectory; }; #include "utils.h" @@ -179,6 +182,7 @@ struct tr_handle_s int bindPort; char id[21]; + char prefsDirectory[256]; }; #endif diff --git a/libtransmission/peer.c b/libtransmission/peer.c index 95760f911..87c4ab31b 100644 --- a/libtransmission/peer.c +++ b/libtransmission/peer.c @@ -100,6 +100,7 @@ static void __peer_dbg( tr_peer_t * peer, char * msg, ... ) #include "peermessages.h" #include "peerutils.h" +#include "peerparse.h" /*********************************************************************** * tr_peerAddOld @@ -232,6 +233,11 @@ void tr_peerPulse( tr_torrent_t * tor ) } } + if( tor->status & TR_STATUS_STOPPING ) + { + return; + } + /* Check for incoming connections */ if( tor->bindSocket > -1 && tor->peerCount < TR_MAX_PEER_COUNT && @@ -331,7 +337,7 @@ void tr_peerPulse( tr_torrent_t * tor ) } peer->date = tr_date(); peer->pos += ret; - if( parseMessage( tor, peer, ret ) ) + if( parseBuf( tor, peer, ret ) ) { goto dropPeer; } diff --git a/libtransmission/peermessages.h b/libtransmission/peermessages.h index d740a36c0..d0710aacd 100644 --- a/libtransmission/peermessages.h +++ b/libtransmission/peermessages.h @@ -20,6 +20,10 @@ * DEALINGS IN THE SOFTWARE. *****************************************************************************/ +/*********************************************************************** + * This file handles all outgoing messages + **********************************************************************/ + static uint8_t * messagesPending( tr_peer_t * peer, int * size ) { if( peer->outBlockSending || peer->outMessagesPos < 1 ) diff --git a/libtransmission/peerparse.h b/libtransmission/peerparse.h new file mode 100644 index 000000000..76085aca1 --- /dev/null +++ b/libtransmission/peerparse.h @@ -0,0 +1,529 @@ +/****************************************************************************** + * Copyright (c) 2005 Eric Petit + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + *****************************************************************************/ + +/*********************************************************************** + * This file handles all incoming messages + **********************************************************************/ + +/*********************************************************************** + * parseChoke + *********************************************************************** + * + **********************************************************************/ +static inline int parseChoke( tr_torrent_t * tor, tr_peer_t * peer, + int len, int choking ) +{ + tr_request_t * r; + int i; + + if( len != 1 ) + { + peer_dbg( "GET %schoke, invalid", choking ? "" : "un" ); + return 1; + } + + peer_dbg( "GET %schoke", choking ? "" : "un" ); + + peer->peerChoking = choking; + + if( choking ) + { + /* Discard all pending requests */ + for( i = 0; i < peer->inRequestCount; i++ ) + { + r = &peer->inRequests[i]; + if( tor->blockHave[tr_block(r->index,r->begin)] > 0 ) + { + tor->blockHave[tr_block(r->index,r->begin)]--; + } + } + peer->inRequestCount = 0; + } + + return 0; +} + +/*********************************************************************** + * parseInterested + *********************************************************************** + * + **********************************************************************/ +static inline int parseInterested( tr_peer_t * peer, int len, + int interested ) +{ + if( len != 1 ) + { + peer_dbg( "GET %sinterested, invalid", interested ? "" : "un" ); + return 1; + } + + peer_dbg( "GET %sinterested", interested ? "" : "un" ); + + peer->peerInterested = interested; + + return 0; +} + +/*********************************************************************** + * parseHave + *********************************************************************** + * + **********************************************************************/ +static inline int parseHave( tr_torrent_t * tor, tr_peer_t * peer, + uint8_t * p, int len ) +{ + uint32_t piece; + + if( len != 5 ) + { + peer_dbg( "GET have, invalid" ); + return 1; + } + + TR_NTOHL( p, piece ); + + peer_dbg( "GET have %d", piece ); + + if( !peer->bitfield ) + { + peer->bitfield = calloc( ( tor->info.pieceCount + 7 ) / 8, 1 ); + } + tr_bitfieldAdd( peer->bitfield, piece ); + updateInterest( tor, peer ); + + return 0; +} + +static inline int parseBitfield( tr_torrent_t * tor, tr_peer_t * peer, + uint8_t * p, int len ) +{ + tr_info_t * inf = &tor->info; + int bitfieldSize; + + bitfieldSize = ( inf->pieceCount + 7 ) / 8; + + if( len != 1 + bitfieldSize ) + { + peer_dbg( "GET bitfield, wrong size" ); + return 1; + } + + /* Make sure the spare bits are unset */ + if( ( inf->pieceCount & 0x7 ) ) + { + uint8_t lastByte; + + lastByte = p[bitfieldSize-1]; + lastByte <<= inf->pieceCount & 0x7; + lastByte &= 0xFF; + + if( lastByte ) + { + peer_dbg( "GET bitfield, spare bits set" ); + return 1; + } + } + + peer_dbg( "GET bitfield, ok" ); + + if( !peer->bitfield ) + { + peer->bitfield = malloc( bitfieldSize ); + } + memcpy( peer->bitfield, p, bitfieldSize ); + updateInterest( tor, peer ); + + return 0; +} + +static inline int parseRequest( tr_peer_t * peer, uint8_t * p, int len ) +{ + int index, begin, length; + tr_request_t * r; + + if( len != 13 ) + { + peer_dbg( "GET request, invalid" ); + return 1; + } + + if( peer->amChoking ) + { + /* Didn't he get it? */ + sendChoke( peer, 1 ); + return 0; + } + + TR_NTOHL( p, index ); + TR_NTOHL( &p[4], begin ); + TR_NTOHL( &p[8], length ); + + peer_dbg( "GET request %d/%d (%d bytes)", + index, begin, length ); + + /* TODO sanity checks (do we have the piece, etc) */ + + if( length > 16384 ) + { + /* Sorry mate */ + return 1; + } + + if( peer->outRequestCount >= MAX_REQUEST_COUNT ) + { + tr_err( "Too many requests" ); + return 1; + } + + r = &peer->outRequests[peer->outRequestCount]; + r->index = index; + r->begin = begin; + r->length = length; + + (peer->outRequestCount)++; + + return 0; +} + +static inline int parsePiece( tr_torrent_t * tor, tr_peer_t * peer, + uint8_t * p, int len ) +{ + int index, begin, block, i, j; + tr_request_t * r; + + TR_NTOHL( p, index ); + TR_NTOHL( &p[4], begin ); + + peer_dbg( "GET piece %d/%d (%d bytes)", + index, begin, len - 9 ); + + if( peer->inRequestCount < 1 ) + { + /* Our "cancel" was probably late */ + peer_dbg( "not expecting a block" ); + return 0; + } + + r = &peer->inRequests[0]; + if( index != r->index || begin != r->begin ) + { + int suckyClient; + + /* Either our "cancel" was late, or this is a sucky + client that cannot deal with multiple requests */ + suckyClient = 0; + for( i = 0; i < peer->inRequestCount; i++ ) + { + r = &peer->inRequests[i]; + + if( index != r->index || begin != r->begin ) + { + continue; + } + + /* Sucky client, he dropped the previous requests */ + peer_dbg( "block was expected later" ); + for( j = 0; j < i; j++ ) + { + r = &peer->inRequests[j]; + if( tor->blockHave[tr_block(r->index,r->begin)] > 0 ) + { + tor->blockHave[tr_block(r->index,r->begin)]--; + } + } + suckyClient = 1; + peer->inRequestCount -= i; + memmove( &peer->inRequests[0], &peer->inRequests[i], + peer->inRequestCount * sizeof( tr_request_t ) ); + r = &peer->inRequests[0]; + break; + } + + if( !suckyClient ) + { + r = &peer->inRequests[0]; + peer_dbg( "wrong block (expecting %d/%d)", + r->index, r->begin ); + return 0; + } + } + + if( len - 9 != r->length ) + { + peer_dbg( "wrong size (expecting %d)", r->length ); + return 1; + } + + block = tr_block( r->index, r->begin ); + if( tor->blockHave[block] < 0 ) + { + peer_dbg( "have this block already" ); + (peer->inRequestCount)--; + memmove( &peer->inRequests[0], &peer->inRequests[1], + peer->inRequestCount * sizeof( tr_request_t ) ); + return 0; + } + + tor->blockHave[block] = -1; + tor->blockHaveCount += 1; + tr_ioWrite( tor->io, index, begin, len - 9, &p[8] ); + + sendCancel( tor, block ); + + if( tr_bitfieldHas( tor->bitfield, index ) ) + { + tr_peer_t * otherPeer; + + for( i = 0; i < tor->peerCount; i++ ) + { + otherPeer = tor->peers[i]; + + if( otherPeer->status < PEER_STATUS_CONNECTED ) + { + continue; + } + + sendHave( otherPeer, index ); + updateInterest( tor, otherPeer ); + } + } + + (peer->inRequestCount)--; + memmove( &peer->inRequests[0], &peer->inRequests[1], + peer->inRequestCount * sizeof( tr_request_t ) ); + + return 0; +} + +static inline int parseCancel( tr_peer_t * peer, uint8_t * p, int len ) +{ + int index, begin, length; + int i; + tr_request_t * r; + + if( len != 13 ) + { + peer_dbg( "GET cancel, invalid" ); + return 1; + } + + TR_NTOHL( p, index ); + TR_NTOHL( &p[4], begin ); + TR_NTOHL( &p[8], length ); + + peer_dbg( "GET cancel %d/%d (%d bytes)", + index, begin, length ); + + for( i = 0; i < peer->outRequestCount; i++ ) + { + r = &peer->outRequests[i]; + if( r->index == index && r->begin == begin && + r->length == length ) + { + (peer->outRequestCount)--; + memmove( &r[0], &r[1], sizeof( tr_request_t ) * + ( peer->outRequestCount - i ) ); + break; + } + } + + return 0; +} + +static inline int parsePort( tr_peer_t * peer, uint8_t * p, int len ) +{ + in_port_t port; + + if( len != 3 ) + { + peer_dbg( "GET port, invalid" ); + return 1; + } + + port = *( (in_port_t *) p ); + peer_dbg( "GET port %d", ntohs( port ) ); + + return 0; +} + +static inline int parseMessage( tr_torrent_t * tor, tr_peer_t * peer, + uint8_t * p, int len ) +{ + char id; + + /* Type of the message */ + id = *(p++); + + switch( id ) + { + case 0: + return parseChoke( tor, peer, len, 1 ); + case 1: + return parseChoke( tor, peer, len, 0 ); + case 2: + return parseInterested( peer, len, 1 ); + case 3: + return parseInterested( peer, len, 0 ); + case 4: + return parseHave( tor, peer, p, len ); + case 5: + return parseBitfield( tor, peer, p, len ); + case 6: + return parseRequest( peer, p, len ); + case 7: + return parsePiece( tor, peer, p, len ); + case 8: + return parseCancel( peer, p, len ); + case 9: + return parsePort( peer, p, len ); + } + + peer_dbg( "Unknown message '%d'", id ); + return 1; +} + +static inline int parseBuf( tr_torrent_t * tor, tr_peer_t * peer, + int newBytes ) +{ + tr_info_t * inf = &tor->info; + + int i; + int len; + uint8_t * p = peer->buf; + uint8_t * end = &p[peer->pos]; + + while( peer->pos >= 4 ) + { + if( peer->status & PEER_STATUS_HANDSHAKE ) + { + char * client; + + if( p[0] != 19 || memcmp( &p[1], "Bit", 3 ) ) + { + /* Don't wait until we get 68 bytes, this is wrong + already */ + peer_dbg( "GET handshake, invalid" ); + tr_netSend( peer->socket, (uint8_t *) "Nice try...\r\n", 13 ); + return 1; + } + + if( peer->pos < 68 ) + { + break; + } + + if( memcmp( &p[4], "Torrent protocol", 16 ) ) + { + peer_dbg( "GET handshake, invalid" ); + return 1; + } + + if( memcmp( &p[28], inf->hash, 20 ) ) + { + peer_dbg( "GET handshake, wrong torrent hash" ); + return 1; + } + + if( !memcmp( &p[48], tor->id, 20 ) ) + { + /* We are connected to ourselves... */ + peer_dbg( "GET handshake, that is us" ); + return 1; + } + + peer->status = PEER_STATUS_CONNECTED; + memcpy( peer->id, &p[48], 20 ); + p += 68; + peer->pos -= 68; + + for( i = 0; i < tor->peerCount; i++ ) + { + if( tor->peers[i] == peer ) + { + continue; + } + if( !peerCmp( peer, tor->peers[i] ) ) + { + peer_dbg( "GET handshake, duplicate" ); + return 1; + } + } + + client = tr_clientForId( (uint8_t *) peer->id ); + peer_dbg( "GET handshake, ok (%s)", client ); + free( client ); + + sendBitfield( tor, peer ); + + continue; + } + + /* Get payload size */ + TR_NTOHL( p, len ); + p += 4; + + if( len > 9 + tor->blockSize ) + { + /* This should never happen. Drop that peer */ + peer_dbg( "message too large (%d bytes)", len ); + return 1; + } + + if( !len ) + { + /* keep-alive */ + peer_dbg( "GET keep-alive" ); + peer->pos -= 4; + continue; + } + + /* That's a piece coming */ + if( p < end && *p == 7 ) + { + /* XXX */ + tor->downloaded[9] += newBytes; + peer->inTotal += newBytes; + newBytes = 0; + } + + if( &p[len] > end ) + { + /* We do not have the entire message */ + p -= 4; + break; + } + + /* Remaining data after this message */ + peer->pos -= 4 + len; + + if( parseMessage( tor, peer, p, len ) ) + { + return 1; + } + + p += len; + } + + memmove( peer->buf, p, peer->pos ); + + return 0; +} diff --git a/libtransmission/peerutils.h b/libtransmission/peerutils.h index d7319214c..e1d2c0d1f 100644 --- a/libtransmission/peerutils.h +++ b/libtransmission/peerutils.h @@ -122,6 +122,8 @@ static int checkPeer( tr_torrent_t * tor, int i ) return 1; } + /* TODO: check for bad downloaders */ + #if 0 /* Choke unchoked peers we are not sending anything to */ if( !peer->amChoking && tr_date() > peer->outDate + 10000 ) @@ -164,452 +166,6 @@ static int checkPeer( tr_torrent_t * tor, int i ) return 0; } -static int parseMessage( tr_torrent_t * tor, tr_peer_t * peer, - int newBytes ) -{ - tr_info_t * inf = &tor->info; - - int i, j; - int len; - char id; - uint8_t * p = peer->buf; - uint8_t * end = &p[peer->pos]; - - for( ;; ) - { - if( peer->pos < 4 ) - { - break; - } - - if( peer->status & PEER_STATUS_HANDSHAKE ) - { - char * client; - - if( p[0] != 19 || memcmp( &p[1], "Bit", 3 ) ) - { - /* Don't wait until we get 68 bytes, this is wrong - already */ - peer_dbg( "GET handshake, invalid" ); - tr_netSend( peer->socket, (uint8_t *) "Nice try...\r\n", 13 ); - return 1; - } - - if( peer->pos < 68 ) - { - break; - } - - if( memcmp( &p[4], "Torrent protocol", 16 ) ) - { - peer_dbg( "GET handshake, invalid" ); - return 1; - } - - if( memcmp( &p[28], inf->hash, 20 ) ) - { - peer_dbg( "GET handshake, wrong torrent hash" ); - return 1; - } - - if( !memcmp( &p[48], tor->id, 20 ) ) - { - /* We are connected to ourselves... */ - peer_dbg( "GET handshake, that is us" ); - return 1; - } - - peer->status = PEER_STATUS_CONNECTED; - memcpy( peer->id, &p[48], 20 ); - p += 68; - peer->pos -= 68; - - for( i = 0; i < tor->peerCount; i++ ) - { - if( tor->peers[i] == peer ) - { - continue; - } - if( !peerCmp( peer, tor->peers[i] ) ) - { - peer_dbg( "GET handshake, duplicate" ); - return 1; - } - } - - client = tr_clientForId( (uint8_t *) peer->id ); - peer_dbg( "GET handshake, ok (%s)", client ); - free( client ); - - sendBitfield( tor, peer ); - - continue; - } - - /* Get payload size */ - TR_NTOHL( p, len ); - p += 4; - - if( len > 9 + tor->blockSize ) - { - /* This shouldn't happen. Forget about that peer */ - peer_dbg( "message too large" ); - return 1; - } - - if( !len ) - { - /* keep-alive */ - peer_dbg( "GET keep-alive" ); - peer->pos -= 4; - continue; - } - - /* That's a piece coming */ - if( p < end && *p == 7 ) - { - /* XXX */ - tor->downloaded[9] += newBytes; - peer->inTotal += newBytes; - newBytes = 0; - } - - if( &p[len] > end ) - { - /* We do not have the entire message */ - p -= 4; - break; - } - - /* Remaining data after this message */ - peer->pos -= 4 + len; - - /* Type of the message */ - id = *(p++); - - switch( id ) - { - case 0: /* choke */ - { - tr_request_t * r; - - if( len != 1 ) - { - peer_dbg( "GET choke, invalid" ); - return 1; - } - - peer_dbg( "GET choke" ); - peer->peerChoking = 1; - - for( i = 0; i < peer->inRequestCount; i++ ) - { - r = &peer->inRequests[i]; - if( tor->blockHave[tr_block(r->index,r->begin)] > 0 ) - { - tor->blockHave[tr_block(r->index,r->begin)]--; - } - } - peer->inRequestCount = 0; - - break; - } - case 1: /* unchoke */ - if( len != 1 ) - { - peer_dbg( "GET unchoke, invalid" ); - return 1; - } - peer_dbg( "GET unchoke" ); - peer->peerChoking = 0; - break; - case 2: /* interested */ - if( len != 1 ) - { - peer_dbg( "GET interested, invalid" ); - return 1; - } - peer_dbg( "GET interested" ); - peer->peerInterested = 1; - break; - case 3: /* uninterested */ - if( len != 1 ) - { - peer_dbg( "GET uninterested, invalid" ); - return 1; - } - peer_dbg( "GET uninterested" ); - peer->peerInterested = 0; - break; - case 4: /* have */ - { - uint32_t piece; - if( len != 5 ) - { - peer_dbg( "GET have, invalid" ); - return 1; - } - TR_NTOHL( p, piece ); - if( !peer->bitfield ) - { - peer->bitfield = calloc( ( inf->pieceCount + 7 ) / 8, 1 ); - } - tr_bitfieldAdd( peer->bitfield, piece ); - - updateInterest( tor, peer ); - - peer_dbg( "GET have %d", piece ); - break; - } - case 5: /* bitfield */ - { - int bitfieldSize; - - bitfieldSize = ( inf->pieceCount + 7 ) / 8; - - if( len != 1 + bitfieldSize ) - { - peer_dbg( "GET bitfield, wrong size" ); - return 1; - } - - /* Make sure the spare bits are unset */ - if( ( inf->pieceCount & 0x7 ) ) - { - uint8_t lastByte; - - lastByte = p[bitfieldSize-1]; - lastByte <<= inf->pieceCount & 0x7; - lastByte &= 0xFF; - - if( lastByte ) - { - peer_dbg( "GET bitfield, spare bits set" ); - return 1; - } - } - - if( !peer->bitfield ) - { - peer->bitfield = malloc( bitfieldSize ); - } - memcpy( peer->bitfield, p, bitfieldSize ); - - updateInterest( tor, peer ); - - peer_dbg( "GET bitfield, ok" ); - break; - } - case 6: /* request */ - { - int index, begin, length; - - if( peer->amChoking ) - { - /* Didn't he get it? */ - sendChoke( peer, 1 ); - break; - } - - TR_NTOHL( p, index ); - TR_NTOHL( &p[4], begin ); - TR_NTOHL( &p[8], length ); - - peer_dbg( "GET request %d/%d (%d bytes)", - index, begin, length ); - - /* TODO sanity checks (do we have the piece, etc) */ - - if( length > 16384 ) - { - /* Sorry mate */ - return 1; - } - - if( peer->outRequestCount < MAX_REQUEST_COUNT ) - { - tr_request_t * r; - - r = &peer->outRequests[peer->outRequestCount]; - r->index = index; - r->begin = begin; - r->length = length; - - (peer->outRequestCount)++; - } - else - { - tr_err( "Too many requests" ); - return 1; - } - break; - } - case 7: /* piece */ - { - int index, begin; - int block; - tr_request_t * r; - - TR_NTOHL( p, index ); - TR_NTOHL( &p[4], begin ); - - peer_dbg( "GET piece %d/%d (%d bytes)", - index, begin, len - 9 ); - - if( peer->inRequestCount < 1 ) - { - /* Our "cancel" was probably late */ - peer_dbg( "not expecting a block" ); - break; - } - - r = &peer->inRequests[0]; - if( index != r->index || begin != r->begin ) - { - int suckyClient; - - /* Either our "cancel" was late, or this is a sucky - client that cannot deal with multiple requests */ - suckyClient = 0; - for( i = 0; i < peer->inRequestCount; i++ ) - { - r = &peer->inRequests[i]; - - if( index != r->index || begin != r->begin ) - { - continue; - } - - /* Sucky client, he dropped the previous requests */ - peer_dbg( "block was expected later" ); - for( j = 0; j < i; j++ ) - { - r = &peer->inRequests[j]; - if( tor->blockHave[tr_block(r->index,r->begin)] > 0 ) - { - tor->blockHave[tr_block(r->index,r->begin)]--; - } - } - suckyClient = 1; - peer->inRequestCount -= i; - memmove( &peer->inRequests[0], &peer->inRequests[i], - peer->inRequestCount * sizeof( tr_request_t ) ); - r = &peer->inRequests[0]; - break; - } - - if( !suckyClient ) - { - r = &peer->inRequests[0]; - peer_dbg( "wrong block (expecting %d/%d)", - r->index, r->begin ); - break; - } - } - - if( len - 9 != r->length ) - { - peer_dbg( "wrong size (expecting %d)", r->length ); - return 1; - } - - block = tr_block( r->index, r->begin ); - if( tor->blockHave[block] < 0 ) - { - peer_dbg( "have this block already" ); - (peer->inRequestCount)--; - memmove( &peer->inRequests[0], &peer->inRequests[1], - peer->inRequestCount * sizeof( tr_request_t ) ); - break; - } - - tor->blockHave[block] = -1; - tor->blockHaveCount += 1; - tr_ioWrite( tor->io, index, begin, len - 9, &p[8] ); - - sendCancel( tor, block ); - - if( tr_bitfieldHas( tor->bitfield, index ) ) - { - tr_peer_t * otherPeer; - - for( i = 0; i < tor->peerCount; i++ ) - { - otherPeer = tor->peers[i]; - - if( otherPeer->status < PEER_STATUS_CONNECTED ) - { - continue; - } - - sendHave( otherPeer, index ); - updateInterest( tor, otherPeer ); - } - } - - (peer->inRequestCount)--; - memmove( &peer->inRequests[0], &peer->inRequests[1], - peer->inRequestCount * sizeof( tr_request_t ) ); - break; - } - case 8: /* cancel */ - { - int index, begin, length; - int i; - tr_request_t * r; - - TR_NTOHL( p, index ); - TR_NTOHL( &p[4], begin ); - TR_NTOHL( &p[8], length ); - - peer_dbg( "GET cancel %d/%d (%d bytes)", - index, begin, length ); - - for( i = 0; i < peer->outRequestCount; i++ ) - { - r = &peer->outRequests[i]; - if( r->index == index && r->begin == begin && - r->length == length ) - { - (peer->outRequestCount)--; - memmove( &r[0], &r[1], sizeof( tr_request_t ) * - ( peer->outRequestCount - i ) ); - break; - } - } - - break; - } - case 9: - { - in_port_t port; - - if( len != 3 ) - { - peer_dbg( "GET port, invalid" ); - return 1; - } - - port = *( (in_port_t *) p ); - peer_dbg( "GET port %d", ntohs( port ) ); - - break; - } - default: - { - peer_dbg( "Unknown message '%d'", id ); - return 1; - } - } - - p += len - 1; - } - - memmove( peer->buf, p, peer->pos ); - - return 0; -} - /*********************************************************************** * isInteresting *********************************************************************** @@ -662,7 +218,7 @@ static void updateInterest( tr_torrent_t * tor, tr_peer_t * peer ) * Our main goal is to complete pieces, so we look the pieces which are * missing less blocks. **********************************************************************/ -static int chooseBlock( tr_torrent_t * tor, tr_peer_t * peer ) +static inline int chooseBlock( tr_torrent_t * tor, tr_peer_t * peer ) { tr_info_t * inf = &tor->info; @@ -798,7 +354,8 @@ static int chooseBlock( tr_torrent_t * tor, tr_peer_t * peer ) for( i = 0; i < tor->blockCount; i++ ) { /* TODO: optimize */ - if( tor->blockHave[i] > 0 && tor->blockHave[i] < minDownloading ) + if( tr_bitfieldHas( peer->bitfield, tr_blockPiece( i ) ) && + tor->blockHave[i] >= 0 && tor->blockHave[i] < minDownloading ) { block = i; minDownloading = tor->blockHave[i]; diff --git a/libtransmission/transmission.c b/libtransmission/transmission.c index 2404d4fd7..caaa59a92 100644 --- a/libtransmission/transmission.c +++ b/libtransmission/transmission.c @@ -59,10 +59,24 @@ tr_handle_t * tr_init() h->fdlimit = tr_fdInit(); h->bindPort = 9090; + + snprintf( h->prefsDirectory, sizeof( h->prefsDirectory ), + "%s/.transmission", getenv( "HOME" ) ); + mkdir( h->prefsDirectory, 0755 ); return h; } +/*********************************************************************** + * tr_getPrefsDirectory + *********************************************************************** + * + **********************************************************************/ +char * tr_getPrefsDirectory( tr_handle_t * h ) +{ + return (char *) h->prefsDirectory; +} + /*********************************************************************** * tr_setBindPort *********************************************************************** @@ -184,6 +198,7 @@ int tr_torrentInit( tr_handle_t * h, const char * path ) tor->upload = h->upload; tor->fdlimit = h->fdlimit; + tor->prefsDirectory = (char *) h->prefsDirectory; /* We have a new torrent */ h->torrents[h->torrentCount] = tor; @@ -252,6 +267,7 @@ void tr_torrentStop( tr_handle_t * h, int t ) tr_lockLock( tor->lock ); tr_trackerStopped( tor->tracker ); tor->status = TR_STATUS_STOPPING; + tor->stopDate = tr_date(); tr_lockUnlock( tor->lock ); } @@ -316,7 +332,9 @@ int tr_torrentStat( tr_handle_t * h, tr_stat_t ** stat ) tr_lockLock( tor->lock ); - if( tor->status & TR_STATUS_STOPPED ) + if( ( tor->status & TR_STATUS_STOPPED ) || + ( ( tor->status & TR_STATUS_STOPPING ) && + tr_date() > tor->stopDate + 60000 ) ) { torrentReallyStop( h, i ); tor->status = TR_STATUS_PAUSE; @@ -478,10 +496,7 @@ static void downloadLoop( void * _tor ) } /* Receive/send messages */ - if( !( tor->status & TR_STATUS_STOPPING ) ) - { - tr_peerPulse( tor ); - } + tr_peerPulse( tor ); /* Try to get new peers or to send a message to the tracker */ tr_trackerPulse( tor->tracker ); diff --git a/libtransmission/transmission.h b/libtransmission/transmission.h index cd246a7fb..8ac1ce0c8 100644 --- a/libtransmission/transmission.h +++ b/libtransmission/transmission.h @@ -39,6 +39,15 @@ typedef struct tr_handle_s tr_handle_t; tr_handle_t * tr_init(); +/*********************************************************************** + * tr_getPrefsDirectory + *********************************************************************** + * Returns the full path to the directory used by libtransmission to + * store the resume files. The string belongs to libtransmission, do + * not free it. + **********************************************************************/ +char * tr_getPrefsDirectory( tr_handle_t * ); + /*********************************************************************** * tr_setBindPort *********************************************************************** diff --git a/macosx/Controller.h b/macosx/Controller.h index 8f675fbbb..43f1718ed 100644 --- a/macosx/Controller.h +++ b/macosx/Controller.h @@ -20,6 +20,9 @@ * DEALINGS IN THE SOFTWARE. *****************************************************************************/ +#ifndef CONTROLLER_H +#define CONTROLLER_H + #include #include #include "PrefsController.h" @@ -63,7 +66,9 @@ - (void) openSheetClosed: (NSOpenPanel *) s returnCode: (int) code contextInfo: (void *) info; - (void) stopTorrent: (id) sender; +- (void) stopTorrentWithIndex: (int) index; - (void) resumeTorrent: (id) sender; +- (void) resumeTorrentWithIndex: (int) index; - (void) removeTorrent: (id) sender; - (void) showInfo: (id) sender; @@ -76,3 +81,5 @@ - (void) linkForums: (id) sender; @end + +#endif diff --git a/macosx/Controller.m b/macosx/Controller.m index 762b7510e..06975d0b3 100644 --- a/macosx/Controller.m +++ b/macosx/Controller.m @@ -22,15 +22,13 @@ #include -#include "Controller.h" #include "NameCell.h" #include "ProgressCell.h" #include "Utils.h" #define TOOLBAR_OPEN @"Toolbar Open" -#define TOOLBAR_RESUME @"Toolbar Resume" -#define TOOLBAR_STOP @"Toolbar Stop" #define TOOLBAR_REMOVE @"Toolbar Remove" +#define TOOLBAR_PREFS @"Toolbar Preferences" #define TOOLBAR_INFO @"Toolbar Info" static void sleepCallBack( void * controller, io_service_t y, @@ -42,60 +40,26 @@ static void sleepCallBack( void * controller, io_service_t y, @implementation Controller -- (void) enableToolbarItem: (NSString *) ident flag: (BOOL) e -{ - NSArray * array = [fToolbar items]; - NSToolbarItem * item; - - if( [ident isEqualToString: TOOLBAR_OPEN] ) - { - item = [array objectAtIndex: 0]; - [item setAction: e ? @selector( openShowSheet: ) : NULL]; - } - else if( [ident isEqualToString: TOOLBAR_RESUME] ) - { - item = [array objectAtIndex: 1]; - [item setAction: e ? @selector( resumeTorrent: ) : NULL]; - } - else if( [ident isEqualToString: TOOLBAR_STOP] ) - { - item = [array objectAtIndex: 2]; - [item setAction: e ? @selector( stopTorrent: ) : NULL]; - } - else if( [ident isEqualToString: TOOLBAR_REMOVE] ) - { - item = [array objectAtIndex: 3]; - [item setAction: e ? @selector( removeTorrent: ) : NULL]; - } - else if( [ident isEqualToString: TOOLBAR_INFO] ) - { - item = [array objectAtIndex: 5]; - [item setAction: e ? @selector( showInfo: ) : NULL]; - } -} - - (void) updateToolbar { - int row = [fTableView selectedRow]; + NSArray * items; + NSToolbarItem * item; + BOOL enable; + int row; + unsigned i; - [self enableToolbarItem: TOOLBAR_RESUME flag: NO]; - [self enableToolbarItem: TOOLBAR_STOP flag: NO]; - [self enableToolbarItem: TOOLBAR_REMOVE flag: NO]; + row = [fTableView selectedRow]; + enable = ( row >= 0 ) && ( fStat[row].status & + ( TR_STATUS_STOPPING | TR_STATUS_PAUSE ) ); - if( row < 0 ) + items = [fToolbar items]; + for( i = 0; i < [items count]; i++ ) { - return; - } - - if( fStat[row].status & - ( TR_STATUS_CHECK | TR_STATUS_DOWNLOAD | TR_STATUS_SEED ) ) - { - [self enableToolbarItem: TOOLBAR_STOP flag: YES]; - } - else - { - [self enableToolbarItem: TOOLBAR_RESUME flag: YES]; - [self enableToolbarItem: TOOLBAR_REMOVE flag: YES]; + item = [items objectAtIndex: i]; + if( [[item itemIdentifier] isEqualToString: TOOLBAR_REMOVE] ) + { + [item setAction: enable ? @selector( removeTorrent: ) : NULL]; + } } } @@ -118,12 +82,6 @@ static void sleepCallBack( void * controller, io_service_t y, [fWindow setToolbar: fToolbar]; [fWindow setDelegate: self]; - [self enableToolbarItem: TOOLBAR_OPEN flag: YES]; - [self enableToolbarItem: TOOLBAR_RESUME flag: NO]; - [self enableToolbarItem: TOOLBAR_STOP flag: NO]; - [self enableToolbarItem: TOOLBAR_REMOVE flag: NO]; - [self enableToolbarItem: TOOLBAR_INFO flag: YES]; - [fTableView setDataSource: self]; [fTableView setDelegate: self]; @@ -134,6 +92,7 @@ static void sleepCallBack( void * controller, io_service_t y, nameCell = [[NameCell alloc] init]; progressCell = [[ProgressCell alloc] init]; tableColumn = [fTableView tableColumnWithIdentifier: @"Name"]; + [nameCell setController: self]; [tableColumn setDataCell: nameCell]; [tableColumn setMinWidth: 10.0]; [tableColumn setMaxWidth: 3000.0]; @@ -143,7 +102,8 @@ static void sleepCallBack( void * controller, io_service_t y, [tableColumn setMinWidth: 134.0]; [tableColumn setMaxWidth: 134.0]; - [fTableView sizeToFit]; + [fTableView setAutosaveTableColumns: YES]; + [fTableView sizeToFit]; [fTableView registerForDraggedTypes: [NSArray arrayWithObjects: NSFilenamesPboardType, NULL]]; @@ -208,7 +168,12 @@ static void sleepCallBack( void * controller, io_service_t y, fTimer = [NSTimer scheduledTimerWithTimeInterval: 0.5 target: self selector: @selector( updateUI: ) userInfo: NULL repeats: YES]; [[NSRunLoop currentRunLoop] addTimer: fTimer - forMode: NSModalPanelRunLoopMode]; + forMode: NSEventTrackingRunLoopMode]; +} + +- (void) windowDidResize: (NSNotification *) n +{ + [fTableView sizeToFit]; } - (BOOL) windowShouldClose: (id) sender @@ -411,14 +376,24 @@ static void sleepCallBack( void * controller, io_service_t y, - (void) resumeTorrent: (id) sender { - tr_torrentStart( fHandle, [fTableView selectedRow] ); - [self updateToolbar]; + [self resumeTorrentWithIndex: [fTableView selectedRow]]; +} + +- (void) resumeTorrentWithIndex: (int) idx +{ + tr_torrentStart( fHandle, idx ); + [self updateUI: NULL]; } - (void) stopTorrent: (id) sender { - tr_torrentStop( fHandle, [fTableView selectedRow] ); - [self updateToolbar]; + [self stopTorrentWithIndex: [fTableView selectedRow]]; +} + +- (void) stopTorrentWithIndex: (int) idx +{ + tr_torrentStop( fHandle, idx ); + [self updateUI: NULL]; } - (void) removeTorrent: (id) sender @@ -489,7 +464,7 @@ static void sleepCallBack( void * controller, io_service_t y, { if( [[tableColumn identifier] isEqualToString: @"Name"] ) { - [(NameCell *) cell setStat: &fStat[rowIndex]]; + [(NameCell *) cell setStat: &fStat[rowIndex] index: rowIndex]; } else if( [[tableColumn identifier] isEqualToString: @"Progress"] ) { @@ -565,37 +540,37 @@ static void sleepCallBack( void * controller, io_service_t y, NSToolbarItem * item; item = [[NSToolbarItem alloc] initWithItemIdentifier: ident]; - [item setTarget: self]; - if( [ident isEqualToString: TOOLBAR_OPEN] ) { [item setLabel: @"Open"]; [item setToolTip: @"Open a torrent"]; - [item setImage: [NSImage imageNamed: @"Open.tiff"]]; - } - else if( [ident isEqualToString: TOOLBAR_RESUME] ) - { - [item setLabel: @"Resume"]; - [item setToolTip: @"Resume download"]; - [item setImage: [NSImage imageNamed: @"Resume.tiff"]]; - } - else if( [ident isEqualToString: TOOLBAR_STOP] ) - { - [item setLabel: @"Stop"]; - [item setToolTip: @"Stop download"]; - [item setImage: [NSImage imageNamed: @"Stop.tiff"]]; + [item setImage: [NSImage imageNamed: @"Open.png"]]; + [item setTarget: self]; + [item setAction: @selector( openShowSheet: )]; } else if( [ident isEqualToString: TOOLBAR_REMOVE] ) { [item setLabel: @"Remove"]; [item setToolTip: @"Remove torrent from list"]; - [item setImage: [NSImage imageNamed: @"Remove.tiff"]]; + [item setImage: [NSImage imageNamed: @"Remove.png"]]; + [item setTarget: self]; + /* We set the selector in updateToolbar: */ + } + else if( [ident isEqualToString: TOOLBAR_PREFS] ) + { + [item setLabel: @"Preferences"]; + [item setToolTip: @"Show the Preferences panel"]; + [item setImage: [NSImage imageNamed: @"Preferences.png"]]; + [item setTarget: fPrefsController]; + [item setAction: @selector( show: )]; } else if( [ident isEqualToString: TOOLBAR_INFO] ) { [item setLabel: @"Info"]; [item setToolTip: @"Information"]; - [item setImage: [NSImage imageNamed: @"Info.tiff"]]; + [item setImage: [NSImage imageNamed: @"Info.png"]]; + [item setTarget: self]; + [item setAction: @selector( showInfo: )]; } else { @@ -608,9 +583,9 @@ static void sleepCallBack( void * controller, io_service_t y, - (NSArray *) toolbarAllowedItemIdentifiers: (NSToolbar *) t { - return [NSArray arrayWithObjects: - TOOLBAR_OPEN, TOOLBAR_RESUME, TOOLBAR_STOP, TOOLBAR_REMOVE, - NSToolbarFlexibleSpaceItemIdentifier, TOOLBAR_INFO, NULL]; + return [NSArray arrayWithObjects: TOOLBAR_OPEN, TOOLBAR_REMOVE, + NSToolbarFlexibleSpaceItemIdentifier, TOOLBAR_PREFS, + TOOLBAR_INFO, NULL]; } - (NSArray *) toolbarDefaultItemIdentifiers: (NSToolbar *) t diff --git a/macosx/English.lproj/MainMenu.nib/keyedobjects.nib b/macosx/English.lproj/MainMenu.nib/keyedobjects.nib index 8b31fb0bc386a077cdc4d5bfe0cee29302369554..232682329afb7eba5255e4764528dae0ef359a86 100644 GIT binary patch literal 35917 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 literal 35882 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 diff --git a/macosx/Images/Info.png b/macosx/Images/Info.png new file mode 100644 index 0000000000000000000000000000000000000000..e5c567f17ad1b099530b2ceee181f76937c95a95 GIT binary patch literal 1881 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 KMNUMnLSTZ5p8Hz> literal 0 HcmV?d00001 diff --git a/macosx/Images/PauseOff.png b/macosx/Images/PauseOff.png new file mode 100644 index 0000000000000000000000000000000000000000..590e7b6c9be54d320c3fc2b1b75cfd52f3877228 GIT binary patch literal 651 zcmV;60(AX}P)4Tx0C=30kxfe*Q51%s8xcV?n@(%72zQlck(6L4 zC`cihg-W`JK`_Z~GBZsA>2(g~-ofoCO@Dt%)7#H5a{(u@Khy_*?;^x0+?U(k zY;J!5w(s3+_XnT^M_aHr54E@O{t}*kg$LiEz6iVPh*R?~?5zM;cD< zKjjMm005&&L_t(2k)@Km3c^4TMb8X&ArMGsIxGMG2l5+Mc1b4$!ghgcu@6LuX7zNt z%z=4alSIbMTA&9yph@2WSb&G5wbW+O%+A2@BD@oTNzz=zBwc{hj+EJeyQJAud&2>V zR5O@a>uILao}^LIxc5%Ay%QHvOXut_;}eblAOYaSHP;fHc-&eR16tt9@mT|K^Aw|r lc-(Jn#PqEHMJ`hl@+TaRP(dLuEfxR(002ovPDHLkV1j~@9y$O3 literal 0 HcmV?d00001 diff --git a/macosx/Images/PauseOn.png b/macosx/Images/PauseOn.png new file mode 100644 index 0000000000000000000000000000000000000000..b0e492b001077be5042164f3b5faba2ef6066f85 GIT binary patch literal 677 zcmV;W0$TlvP)4Tx0C=30kxfe*Q51%s8!4jLY&vRbDcqIKB9@RK zD3pSkg)MfG2Ein|$;>nfjFZcqo3#A_-3#u-g{|O9q^r`UKOh?sT}h$;KnS=gVY;|2 z*(jke9C*&lf%65Kfu~fI0d``es~crmt2gBIC9^!mA;*m8Cu+S?dCR@Jy+Qz7EPF~t z_t)L2IbEwaKsbQYvYZ2)rsbD_^T8+sq5?R6+Y3MhKwfWET@brKwq^Mc$Tls11G0U; zWk8$(g*fQMAZ~!wAWVG7%4aQz0fs6drwt%0t*%L!6s`VQ&S%6W5D6P3@U5kkyT{ zbh}YkC(84~|9bmmned|laJ%Z0?zGxQt}7LV((Uf3o(XkczKQ)8d08wJRsjA0tL$_5 zG9?t10002INkl0YwU9 zCB#s5u+!40frfvxeUI+-J*V=UN32j|S?)SCct_h8Ru`f*p77vrj(9_Ycp-Ymd_)AZ zGRX;TEaPo%h|APBp|qb$9sF!>3d?YZ2n5-usdPa$cPdRK z+wo%D_#sEUMttB?-fp}q;fx*jePJsnVj(AFtkX`3Ts3(7cgVj0Gu$dCeq7BJ00000 LNkvXXu0mjf`)@MF literal 0 HcmV?d00001 diff --git a/macosx/Images/Preferences.png b/macosx/Images/Preferences.png new file mode 100644 index 0000000000000000000000000000000000000000..36239029a648c598044f00af134f75b31f8176e1 GIT binary patch literal 1374 zcmV-k1)=(hP)B1~@p>EQhn=~OPf)Me!RjHa51g$Yqthx}aj~J=3)M9<%_&_Ytah!+F z%XvBDs3ShUe*ZJ~T%A`dH8DN#=bV{)&j0(}^PO|P7ZBCe z)u8}~hlixqY6aa7KR&1Bm6i7f0N=r(LE#Nw07T9Jcih<6pvO-hQ`R@xG&?&Z{@dT* z|IdLj7J~10j~>yNUwO+N@Z7I}UIh249v7Z_(E&XC!0qvnhvin}Q6%9|(EjYs9ZE<@pv1&P zN=izi)losd(hW!tIJW1zucpscJcIz2rVc}Hn!X`zym z646E=kMibdOAqPiV4a=wr8k(4xpylOdajaHV zRjD)JQQjOurN@P*L%=KjuLdCLL2fdcC?_X}`uqDu3I+xSsI;_H9xTBbQ{Mct$F%^^ zmH<0DJCvE3DfV=4Z!e9FjZtxNF_o8>t1|!qIqu2;=w#oGE#wXZjYcD-r>BeUXR%mB z(B{TQDk>^c0U$AkgGGAT>Ez@j62NZXC7az&Hk&P2%eL}+mj`1Y0MgRZ#NJ0bP`+n? zyu3Wo&!wcm&TRy|>=A6+>G=3K62Qt2tyXJH!4Lr3+uM|ynko(>y8X6iX=fMM- zotq<_PDgI1TMc^I!-AUs4+H{KSydSdV6j-q%u^am060AfrXVFHCFC35#+QwI_wUpA z+qbl+StN(cp}tV4k6Az*9vxC;Wf%Y+?wEIC%4)OH@yRi1by|v#k5~6RN?lzYWn^T~ zATP|x-+v>k-O6uo6G6j$@E_6_0kF-h8`B$0X0w^xu3&DvySv5Js-vTW`uh6ld2=(B zS5#1EXD7XW^MCz)WtgYk*RZphBg76(#pZZ4HSdqzFH zC4XY5AwHV_;+qeq9|5EJc=lOQ+Jmfj#%J!*r|q`u)2U$K52AE#gVL>%GZ zfAGa)eSKYwMKFvB_r=<&;FPHVjl7T1QKMO)`T2SAo&R%cii7D%r{Ch7>P1_vS$Y65 z^g3U+7Wlg`Jv~kHbMv&qLGXXDZ?KlWDDcHW6$xO_)AYR>RxBgB|!*vzzi?1aB5E}ru>-jb_ z#ZxoKCmDqHhtTZItVq?;(h`-Gl>y*30KUM!F68OS&(FURVf*6nLTmth%1UK@e#1&t gVv9U{@{x4wZ&r4hy`*6J>i_@%07*qoM6N<$g1?EG$N&HU literal 0 HcmV?d00001 diff --git a/macosx/Images/Progress.png b/macosx/Images/Progress.png new file mode 100644 index 0000000000000000000000000000000000000000..b385967dbf1c1bfeb4f48638307568b2a53d8da8 GIT binary patch literal 3579 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 B^hf{z literal 0 HcmV?d00001 diff --git a/macosx/Images/Remove.png b/macosx/Images/Remove.png new file mode 100644 index 0000000000000000000000000000000000000000..cb1865191fb4feb8536eed2aaf8e56942776ba0d GIT binary patch literal 1959 zcmV;Y2Uz%tP)ZNNB!C{8R)Nl7U` zkf72C(nd0&v`MSJWF@qJ0DnNGR;X=4D=MhuwD3byB9u0yAto*XlGuQ-vGMZ81{>pj z+55Q9=?`nxyS7uHKQ!m+YIbMNobUIXnR8~YkW%thit?4B1KQ&#cduyMWJbrIcTG)f z5T>zElU5mkCA5fix$l`p<=w8*ijOy3{`8|qwJ9F91pTYO;~tA$e$6|6{pa35Xqk7@ z22W|*I>oc35|2LswvC>gLXXCX4h=e)p7VO5|{p_?P^tA^Z>heJ?n79u^Vdx)%zPp7JRG=yd-E(nz} znNBho{(RWlwDpB`?{{9zS1U-vuDyFT=j8FXYHC*06iu3N>?EAHme)~rBox!k^L|=}V({Ia(0LH*mSy{%S_^+Ygr)0<`UYe?jx;T-zCMhd-^V+W zK|@2JAW*VPVLOz&io`@>s&!5TG|Q>+dwgJ8%oP=GrJ;Q{&fsP2R2nND$6QbWRn?Hm zpg0ox`?0_GI{py@4=xl0BHxT@;a6R#rd?G)Al%X65sK>5P{4L(qvJR>b|#H}@g|A3 zjTqhCD2_zaG&D^^RaLB>9`u*n@!m?~LY>EnYy=c3P!+`!-jdyjGdZaU2zVi`0}?46 z93zcPPa#wKv^Nsfp9NHem}6gNXEI-!N+6f{A+W&!<4hQBt}Ta#M?fV9R) zG!aL?a~m0-M76}U&k_TZRILwh=b-{{qqhg6wl_Um3ng%L882Sg4mmT zuoH38A09{FwhJ}y7*SEsx9uYR;c@IloYwF&KPKN=cJVTR)%v9PdOSK~T+30Gc%S=jLOQw$|i z#zesP#+GwkZ_SASfWL@?F+0;6@pz;B7Z{V%ABc=EIqnfDl3f+y~b@yRJAI z`Axao-PF+7sFf_Nz?KfiWE@-9vF&U$RaHZEyK(!AQN3Q`Q%S;ugH|M#xReN$ys`Vj zXQu%}N;&7CnL|Jb5dcC!z}rYgW1wVFyRm5d(s<^ZL6^HMxNwoWXnCFRFQ~vDECxj( zl}?k2Cm0(ZmWh!OI}sU=+zxvGb=ZCXZ+*$gXl96UAPK~zl*z{;pcGLm+ysS6@sBgh zTNh8srm~D#>$0TZkMIDbkh-af)P1k(=Di{@@V58rS!pvV4HC{2(Z?c?zXbEVF0v=6 ti|nUJ{x-15UIeY_)|mZ1(h~f?4Tx0C=38Q)g6D=@vcr-t0tDdAZ9o73ZrB8d;W+$%P#4PN3IHSw0H%w?*+KxM5CAfA6*2_?G713f zq;#1A03`qbTOJ!14}fX`06S%pPXhpU(j@l+0JbPoDgr>G06-?n6o~-P4ggq}qZEq( zuwVcv%8`h30kE3@V3MKClmKAw0Dzq-7Gwhu7yw|R5DQZQ2=)NLkVi%E00=VyD2$Y^ zc+yu~A!AINCaVwW$9Z{ELWe@Q z#JLh_3eL-tiX;k2mK2vr|C5P-v+NI;ylVhp)qV!{LR(O~$_DJ0E+DYzfFK@c*+L|BM6Vt|+;)`%m*MP?y>NH7wC#36}D3L-_6$WmlAQi7Bt zRmfhX9{C7|KA|s2*yD+M!(38x2CE(D`Tzx(Llj3(*Z| zC0c_XLYvXE=oR!9+KWC%$1uR?7zZ=NY%ngy!$PrmEE$ty%dujt0;|Cqu{Nv|yMgs$ zBiMTaiNGS@1RH`oA&@YaAS5gzEGLu_st5-N&4doZ4MHDbl<=9zBT%>LK-$#>ix{Hrbl& zK@KGg$O>{1c{}+K`84@Dd4T+mLZ=u|94Y>kc#4!#K&hY{q@1E$rwmfYscKYHsv9+w zDxxl?mQib|ZPcsO0qQtSon}t+pheNrXsc+Iv_{%_+C$naI-PDrccq8ZCG?f_O8Q~? zMS36oJwtCX;E#^kT*{70eCHdgeLiW9B;*mWr(kUqz&{ zLZw=zS>?LQ3stJBsj9c?0@Zxg3e}^kS5-&UsA^_vJT-yZah$}`!vpJ^s_LQF^k6%vR1QdS?5^;Y!cg?&1a{vOW5DB zyV;|f8k#dSqc!t1t2Iw*_HrV>*%V3s)#9)iTX@h6DChm=A z;FWj>K5D3I7-*<8+-ulvIBsNaG}oxWsLAM&G0m83oNT02{%vlz3rW-Vqz=33@_^L+CL^M@8p3r~wh7Bv<(ElHMK%XG_~me;IM zD<`W|t7@yO)~L0!wZwX-b`d(9 z?KatU+I_Znvd^%uwSVBC;V|2wz@g3IH%BW+p<}h<&FReP{?k`XZ=L>fhV=~bj9oMC zI5S6S0C3Et|wgIyE(fl+>W^Y z>Tctn?tajHXr}p0@yyzp{T@ah0*}2Oy|eIHiL>_1>h(1A6nNHnKJha45_#2m4SHL8 zr+FXpe&J*9BlkJx^OonvTgE%%i}?ol7W;PlY52|ctMYs7Z{(li-{3zQ;1sYV;7lMf zFeq?S;2pjmU&ycLkIr_Py=-?QME!`u_BZjF^n3OiE^A=5eWpG+o+}rJt3T)g!Z( z70G%Rxh>kdXjC2~uggZV=V!Mn*b14VOKG88qwLF>l~bMbE;l;&Xr6kWG_Py1)#CMw z2lM^&zg?sbH9l6Bqdr?20>{&TUY_;QJTNkz%WQeo-k zZydg<{AOZ9@`mm*=dxWJNgJgbZ*TJ4bf8?lJiokubI9iAE%=t=Ew8sO++@f7m10b9ZmR z-sT#!nu>j-eL4G{)<)NMe#`x~zD}pEr0&yx>HgjW5eGWzUFz!(>K-g>KpT_|!-o^R0cR@{sWv6JT3QtX(&ObeNMs{Z8Y|7dG_J!>a&c&X) z*%98+eLm>?#S8uy&UJcsp1$aD@x&#!OUEyBFSlHAzS7*~)OGA9r=N~>J9jrbX7l_T4)PcY5!N?hf6{xcBmY&i(NRD<2Xc z7C%yZRPk8%am~+`KR5L{_nz$Y?dy6H`Q$;ru>bi#vj6dMpzta4X~m$yVEvHe(1~IH z;cL(4JsWr~dp44_~BQtU%N(QM+aWYUrzj1`bzuN{?{{Jw~vL5J${q< zX8djOJMDJ|-gDn~eu()nIG+2F{IT+r<)^mKL7yK^WKK-{69bs@`~aU|`v3p{AY({U zO#lFTB>(_=B>(^b0001bB>(^b0001bB>(^b0001bC2}$zU;qFB32;bRa{vGf6951U z69E94oEQKA0k=s+K~yM_g_5z08c`63zg^vMxnzx;!r2{xnAPOL86n*Yf^}GyY@Na) zkSc$Hq_DRVn+3ac2C0xn ziio3JE@y#5AO}2>=_}v@_~HBh&$w1iKu16Ycq+lYZns;L50!xzvgR)+6VHGQkOI;G zjYi}Ba=CnK7{&u23p|furBo_qX%*lhun$~yI-L)$>z)B?;8c}T z^cZp+$KGrgJ!e&$@9GHdc8igZTn*o z1RG!n#FNaWr9=QUO{+|&)1K#f*TAk?t-A6mNo~LBZck0qJQhOS zm(xuNA@&!G#hcY?^)3Og)7&gK2SlUM=(FQEcE8_$J)h6N0GOtEtn2#GU@#aaz>8Eq zpWguoz`faQc3vnHE`<Lg1ScMN!fKnv}2H>NAqi z7WnG>exSr&mGEP3_H4Tx0C=38Q)g6D=@vcr-t0tDdAZ9o73ZrB8d;W+$%P#4PN3IHSw0H%w?*+KxM5CAfA6*2_?G713f zq;#1A03`qbTOJ!14}fX`06S%pPXhpU(j@l+0JbPoDgr>G06-?n6o~-P4ggq}qZEq( zuwVcv%8`h30kE3@V3MKClmKAw0Dzq-7Gwhu7yw|R5DQZQ2=)NLkVi%E00=VyD2$Y^ zc+yu~A!AINCaVwW$9Z{ELWe@Q z#JLh_3eL-tiX;k2mK2vr|C5P-v+NI;ylVhp)qV!{LR(O~$_DJ0E+DYzfFK@c*+L|BM6Vt|+;)`%m*MP?y>NH7wC#36}D3L-_6$WmlAQi7Bt zRmfhX9{C7|KA|s2*yD+M!(38x2CE(D`Tzx(Llj3(*Z| zC0c_XLYvXE=oR!9+KWC%$1uR?7zZ=NY%ngy!$PrmEE$ty%dujt0;|Cqu{Nv|yMgs$ zBiMTaiNGS@1RH`oA&@YaAS5gzEGLu_st5-N&4doZ4MHDbl<=9zBT%>LK-$#>ix{Hrbl& zK@KGg$O>{1c{}+K`84@Dd4T+mLZ=u|94Y>kc#4!#K&hY{q@1E$rwmfYscKYHsv9+w zDxxl?mQib|ZPcsO0qQtSon}t+pheNrXsc+Iv_{%_+C$naI-PDrccq8ZCG?f_O8Q~? zMS36oJwtCX;E#^kT*{70eCHdgeLiW9B;*mWr(kUqz&{ zLZw=zS>?LQ3stJBsj9c?0@Zxg3e}^kS5-&UsA^_vJT-yZah$}`!vpJ^s_LQF^k6%vR1QdS?5^;Y!cg?&1a{vOW5DB zyV;|f8k#dSqc!t1t2Iw*_HrV>*%V3s)#9)iTX@h6DChm=A z;FWj>K5D3I7-*<8+-ulvIBsNaG}oxWsLAM&G0m83oNT02{%vlz3rW-Vqz=33@_^L+CL^M@8p3r~wh7Bv<(ElHMK%XG_~me;IM zD<`W|t7@yO)~L0!wZwX-b`d(9 z?KatU+I_Znvd^%uwSVBC;V|2wz@g3IH%BW+p<}h<&FReP{?k`XZ=L>fhV=~bj9oMC zI5S6S0C3Et|wgIyE(fl+>W^Y z>Tctn?tajHXr}p0@yyzp{T@ah0*}2Oy|eIHiL>_1>h(1A6nNHnKJha45_#2m4SHL8 zr+FXpe&J*9BlkJx^OonvTgE%%i}?ol7W;PlY52|ctMYs7Z{(li-{3zQ;1sYV;7lMf zFeq?S;2pjmU&ycLkIr_Py=-?QME!`u_BZjF^n3OiE^A=5eWpG+o+}rJt3T)g!Z( z70G%Rxh>kdXjC2~uggZV=V!Mn*b14VOKG88qwLF>l~bMbE;l;&Xr6kWG_Py1)#CMw z2lM^&zg?sbH9l6Bqdr?20>{&TUY_;QJTNkz%WQeo-k zZydg<{AOZ9@`mm*=dxWJNgJgbZ*TJ4bf8?lJiokubI9iAE%=t=Ew8sO++@f7m10b9ZmR z-sT#!nu>j-eL4G{)<)NMe#`x~zD}pEr0&yx>HgjW5eGWzUFz!(>K-g>KpT_|!-o^R0cR@{sWv6JT3QtX(&ObeNMs{Z8Y|7dG_J!>a&c&X) z*%98+eLm>?#S8uy&UJcsp1$aD@x&#!OUEyBFSlHAzS7*~)OGA9r=N~>J9jrbX7l_T4)PcY5!N?hf6{xcBmY&i(NRD<2Xc z7C%yZRPk8%am~+`KR5L{_nz$Y?dy6H`Q$;ru>bi#vj6dMpzta4X~m$yVEvHe(1~IH z;cL(4JsWr~dp44_~BQtU%N(QM+aWYUrzj1`bzuN{?{{Jw~vL5J${q< zX8djOJMDJ|-gDn~eu()nIG+2F{IT+r<)^mKL7yK^WKK-{69bs@`~aU|`v3p{AY({U zO#lFTB>(_=B>(^b0001bB>(^b0001bB>(^b0001bC2}$zU;qFB32;bRa{vGf6951U z69E94oEQKA0m?~4K~yM_g_6CG5>XVze{VoZHYz5|hwP4K-Qb2nSS(BoofHloiPW_G z4WI^Z(Kr*-tOL<^WHt@9-)*XD9f@A zfD_;dxTD82-~;g9b={8v*4QFF2A%^?HF2+6tsVpSff7*Fm@A?lJOko@073xudi}KB zZa<7hqt}20UN#n!+*V9)cs3n}IIbUGa^l}g9CTrRO(F26aBGaU|x z3$1ejyc9~Q?Eplyj$Q46tCzqF*LBZDkX227!tXo%trozU>$>Lv;vXRo&NKt2?1cOm XcwwlG*4^JU00000NkvXXu0mjfP4_8V literal 0 HcmV?d00001 diff --git a/macosx/Images/RevealOff.png b/macosx/Images/RevealOff.png new file mode 100644 index 0000000000000000000000000000000000000000..1b36f8abad729985b479d432c023744eb32da2b8 GIT binary patch literal 553 zcmV+^0@nSBP)B=lVuL*xL6#Ow z!1hqAwE}~e4h4bp0D1s#S??3*0W`(XrI|cNrMJ-7OW{hQHBtGZa5oTEy6A@)u77HOl*L59T*DnBc0JH&|Y#-Bx!{G-Z zM3^Lr;GADsmi3--vT3 zy3W3S}kUprncQ95h;_&WKt@X-bg9$dcEG4 zFbpMt9{`|jEbbgC+qN&`IF8oq^}}>J{k&a=LPSs$2kI=Gu~Yjl!FSHX_xB2=6nhCF zPJs*H4A7GF8u$$ON~!Sv?;aFFyZ~xIp2NBY-Ycc9@4*Cmh*dbQt$?Nqm;TnO4_SAj&o@kM$z-UMXS|%?Rnn9FpQ$(I4@IR7N`j!PLGqB@}_B) zJkMJUhr^Em``B)`D?qwx9=JG47J-yfb6xi)J(TOZH&RNCzjb!B-(C;|YumPq=~vsf zi$M^q4}e+*r~}94aye^RR;f@Z2;cXAbi3VWmSvsCal9FgM$0IQb}8^@1~>Hk;2ooz9h%@=+Yeo9%YHtJmxK`F#E@1qTAS0?Y&LpzC^eFc=uRuAhWq_`Ton zzgw+V8(^B@?lw<>hkt@m6zyiSS=eYav|KKC;`{#Xa=H8hy#37p+}-J5 #include +#include "Controller.h" @interface NameCell : NSCell { - tr_stat_t * fStat; - NSRect fRevealRect; - NSPoint fClickPoint; + Controller * fController; + tr_stat_t * fStat; + int fIndex; + NSRect fPauseRect; + NSRect fRevealRect; + NSPoint fClickPoint; } -- (void) setStat: (tr_stat_t *) stat; +- (void) setController: (Controller *) controller; +- (void) setStat: (tr_stat_t *) stat index: (int) index; @end + +#endif diff --git a/macosx/NameCell.m b/macosx/NameCell.m index c22d16d3a..1ef9f8989 100644 --- a/macosx/NameCell.m +++ b/macosx/NameCell.m @@ -25,9 +25,15 @@ @implementation NameCell -- (void) setStat: (tr_stat_t *) stat; +- (void) setController: (Controller *) controller { - fStat = stat; + fController = controller; +} + +- (void) setStat: (tr_stat_t *) stat index: (int) idx +{ + fStat = stat; + fIndex = idx; } - (void) drawWithFrame: (NSRect) cellFrame inView: (NSView *) view @@ -47,7 +53,7 @@ nameString = [NSString stringWithFormat: @"%@%@", stringFittingInWidth( fStat->info.name, cellFrame.size.width - - 10 - widthForString( sizeString, 12 ), 12 ), + 35 - widthForString( sizeString, 12 ), 12 ), sizeString]; if( fStat->status & TR_STATUS_PAUSE ) @@ -100,7 +106,7 @@ { peersString = [NSString stringWithFormat: @"%@%@", @"Error: ", stringFittingInWidth( fStat->error, - cellFrame.size.width - 15 - + cellFrame.size.width - 40 - widthForString( @"Error: ", 10 ), 10 )]; } @@ -119,6 +125,41 @@ pen.x += 0; pen.y += 15; [peersString drawAtPoint: pen withAttributes: attributes]; + /* "Pause" button */ + fPauseRect = NSMakeRect( cellFrame.origin.x + cellFrame.size.width - 19, + cellFrame.origin.y + cellFrame.size.height - 38, + 14, 14 ); + NSImage * pauseImage = NULL; + if( fStat->status & TR_STATUS_PAUSE ) + { + if( NSPointInRect( fClickPoint, fPauseRect ) ) + { + pauseImage = [NSImage imageNamed: @"ResumeOn.png"]; + } + else + { + pauseImage = [NSImage imageNamed: @"ResumeOff.png"]; + } + } + else if( fStat->status & + ( TR_STATUS_CHECK | TR_STATUS_DOWNLOAD | TR_STATUS_SEED ) ) + { + if( NSPointInRect( fClickPoint, fPauseRect ) ) + { + pauseImage = [NSImage imageNamed: @"PauseOn.png"]; + } + else + { + pauseImage = [NSImage imageNamed: @"PauseOff.png"]; + } + } + if( pauseImage ) + { + pen.x = fPauseRect.origin.x; + pen.y = fPauseRect.origin.y + 14; + [pauseImage compositeToPoint: pen operation: NSCompositeSourceOver]; + } + /* "Reveal in Finder" button */ fRevealRect = NSMakeRect( cellFrame.origin.x + cellFrame.size.width - 19, cellFrame.origin.y + cellFrame.size.height - 19, @@ -126,11 +167,11 @@ NSImage * revealImage; if( NSPointInRect( fClickPoint, fRevealRect ) ) { - revealImage = [NSImage imageNamed: @"RevealOn.tiff"]; + revealImage = [NSImage imageNamed: @"RevealOn.png"]; } else { - revealImage = [NSImage imageNamed: @"RevealOff.tiff"]; + revealImage = [NSImage imageNamed: @"RevealOff.png"]; } pen.x = fRevealRect.origin.x; pen.y = fRevealRect.origin.y + 14; @@ -155,11 +196,36 @@ - (void) stopTracking: (NSPoint) last at:(NSPoint) stop inView: (NSView *) v mouseIsUp: (BOOL) flag { - if( flag && NSPointInRect( stop, fRevealRect ) ) + if( flag ) { - /* Reveal in Finder */ - [[NSWorkspace sharedWorkspace] openFile: - [NSString stringWithUTF8String: fStat->folder]]; + if( NSPointInRect( stop, fRevealRect ) ) + { + /* Reveal in Finder */ + NSString * string = [NSString stringWithFormat: + @"tell application \"Finder\"\nactivate\nreveal (POSIX file \"%s/%s\")\nend tell", + fStat->folder, fStat->info.name]; + NSAppleScript * appleScript; + appleScript = [[NSAppleScript alloc] initWithSource: string]; + NSDictionary * error; + if( ![appleScript executeAndReturnError: &error] ) + { + printf( "Reveal in Finder: AppleScript failed\n" ); + } + [appleScript release]; + } + else if( NSPointInRect( stop, fPauseRect ) ) + { + /* Pause, resume */ + if( fStat->status & TR_STATUS_PAUSE ) + { + [fController resumeTorrentWithIndex: fIndex]; + } + else if( fStat->status & ( TR_STATUS_CHECK | + TR_STATUS_DOWNLOAD | TR_STATUS_SEED ) ) + { + [fController stopTorrentWithIndex: fIndex]; + } + } } fClickPoint = NSMakePoint(0,0); } diff --git a/macosx/ProgressCell.h b/macosx/ProgressCell.h index bd25b19a9..6b0c25ac2 100644 --- a/macosx/ProgressCell.h +++ b/macosx/ProgressCell.h @@ -20,6 +20,9 @@ * DEALINGS IN THE SOFTWARE. *****************************************************************************/ +#ifndef PROGRESSCELL_H +#define PROGRESSCELL_H + #include #include @@ -41,3 +44,5 @@ - (void) buildAdvancedBar; - (void) drawWithFrame: (NSRect) cellFrame inView: (NSView *) view; @end + +#endif diff --git a/macosx/ProgressCell.m b/macosx/ProgressCell.m index 182ff43c8..abd31d027 100644 --- a/macosx/ProgressCell.m +++ b/macosx/ProgressCell.m @@ -81,7 +81,7 @@ static uint32_t kGreen[] = self = [super init]; /* Have a NSBitmapImageRep ready to draw the progression bar */ - bgImg = [NSImage imageNamed: @"Progress.tiff"]; + bgImg = [NSImage imageNamed: @"Progress.png"]; fBgBmp = [[bgImg representations] objectAtIndex: 0]; size = [bgImg size]; fImg = [[NSImage alloc] initWithSize: size]; diff --git a/macosx/Transmission.xcodeproj/project.pbxproj b/macosx/Transmission.xcodeproj/project.pbxproj index cf0abcbfb..61535f1d6 100644 --- a/macosx/Transmission.xcodeproj/project.pbxproj +++ b/macosx/Transmission.xcodeproj/project.pbxproj @@ -13,16 +13,19 @@ 4D118E1A08CB46B20033958F /* PrefsController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4D118E1908CB46B20033958F /* PrefsController.m */; }; 4D2784370905709500687951 /* Transmission.icns in Resources */ = {isa = PBXBuildFile; fileRef = 4D2784360905709500687951 /* Transmission.icns */; }; 4D3EA0AA08AE13C600EA10C2 /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4D3EA0A908AE13C600EA10C2 /* IOKit.framework */; }; - 4D6DAAC6090CE00500F43C22 /* RevealOff.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 4D6DAAC4090CE00500F43C22 /* RevealOff.tiff */; }; - 4D6DAAC7090CE00500F43C22 /* RevealOn.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 4D6DAAC5090CE00500F43C22 /* RevealOn.tiff */; }; - 4D813EB508AA43AC00191DB4 /* Progress.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 4D813EB408AA43AC00191DB4 /* Progress.tiff */; }; + 4D6DAAC6090CE00500F43C22 /* RevealOff.png in Resources */ = {isa = PBXBuildFile; fileRef = 4D6DAAC4090CE00500F43C22 /* RevealOff.png */; }; + 4D6DAAC7090CE00500F43C22 /* RevealOn.png in Resources */ = {isa = PBXBuildFile; fileRef = 4D6DAAC5090CE00500F43C22 /* RevealOn.png */; }; + 4D752E930913C949008EAAD4 /* Preferences.png in Resources */ = {isa = PBXBuildFile; fileRef = 4D752E920913C949008EAAD4 /* Preferences.png */; }; + 4D813EB508AA43AC00191DB4 /* Progress.png in Resources */ = {isa = PBXBuildFile; fileRef = 4D813EB408AA43AC00191DB4 /* Progress.png */; }; + 4DA6FDBA0911233800450CB1 /* PauseOn.png in Resources */ = {isa = PBXBuildFile; fileRef = 4DA6FDB80911233800450CB1 /* PauseOn.png */; }; + 4DA6FDBB0911233800450CB1 /* PauseOff.png in Resources */ = {isa = PBXBuildFile; fileRef = 4DA6FDB90911233800450CB1 /* PauseOff.png */; }; + 4DA6FDC5091141AD00450CB1 /* ResumeOff.png in Resources */ = {isa = PBXBuildFile; fileRef = 4DA6FDC3091141AD00450CB1 /* ResumeOff.png */; }; + 4DA6FDC6091141AD00450CB1 /* ResumeOn.png in Resources */ = {isa = PBXBuildFile; fileRef = 4DA6FDC4091141AD00450CB1 /* ResumeOn.png */; }; 4DF0C5AB0899190500DD8943 /* Controller.m in Sources */ = {isa = PBXBuildFile; fileRef = 4DF0C5A90899190500DD8943 /* Controller.m */; }; 4DF0C5AE08991C1600DD8943 /* libtransmission.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4DF0C5AD08991C1600DD8943 /* libtransmission.a */; }; - 4DF7500C08A103AD007B0D70 /* Open.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 4DF7500708A103AD007B0D70 /* Open.tiff */; }; - 4DF7500D08A103AD007B0D70 /* Info.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 4DF7500808A103AD007B0D70 /* Info.tiff */; }; - 4DF7500E08A103AD007B0D70 /* Remove.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 4DF7500908A103AD007B0D70 /* Remove.tiff */; }; - 4DF7500F08A103AD007B0D70 /* Resume.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 4DF7500A08A103AD007B0D70 /* Resume.tiff */; }; - 4DF7501008A103AD007B0D70 /* Stop.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 4DF7500B08A103AD007B0D70 /* Stop.tiff */; }; + 4DF7500C08A103AD007B0D70 /* Open.png in Resources */ = {isa = PBXBuildFile; fileRef = 4DF7500708A103AD007B0D70 /* Open.png */; }; + 4DF7500D08A103AD007B0D70 /* Info.png in Resources */ = {isa = PBXBuildFile; fileRef = 4DF7500808A103AD007B0D70 /* Info.png */; }; + 4DF7500E08A103AD007B0D70 /* Remove.png in Resources */ = {isa = PBXBuildFile; fileRef = 4DF7500908A103AD007B0D70 /* Remove.png */; }; 8D11072A0486CEB800E47090 /* MainMenu.nib in Resources */ = {isa = PBXBuildFile; fileRef = 29B97318FDCFA39411CA2CEA /* MainMenu.nib */; }; 8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */; }; 8D11072D0486CEB800E47090 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; settings = {ATTRIBUTES = (); }; }; @@ -74,17 +77,20 @@ 4D118E1908CB46B20033958F /* PrefsController.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = PrefsController.m; sourceTree = ""; }; 4D2784360905709500687951 /* Transmission.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; name = Transmission.icns; path = Images/Transmission.icns; sourceTree = ""; }; 4D3EA0A908AE13C600EA10C2 /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = /System/Library/Frameworks/IOKit.framework; sourceTree = ""; }; - 4D6DAAC4090CE00500F43C22 /* RevealOff.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; name = RevealOff.tiff; path = Images/RevealOff.tiff; sourceTree = ""; }; - 4D6DAAC5090CE00500F43C22 /* RevealOn.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; name = RevealOn.tiff; path = Images/RevealOn.tiff; sourceTree = ""; }; - 4D813EB408AA43AC00191DB4 /* Progress.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; name = Progress.tiff; path = Images/Progress.tiff; sourceTree = ""; }; + 4D6DAAC4090CE00500F43C22 /* RevealOff.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = RevealOff.png; path = Images/RevealOff.png; sourceTree = ""; }; + 4D6DAAC5090CE00500F43C22 /* RevealOn.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = RevealOn.png; path = Images/RevealOn.png; sourceTree = ""; }; + 4D752E920913C949008EAAD4 /* Preferences.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Preferences.png; path = Images/Preferences.png; sourceTree = ""; }; + 4D813EB408AA43AC00191DB4 /* Progress.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Progress.png; path = Images/Progress.png; sourceTree = ""; }; + 4DA6FDB80911233800450CB1 /* PauseOn.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = PauseOn.png; path = Images/PauseOn.png; sourceTree = ""; }; + 4DA6FDB90911233800450CB1 /* PauseOff.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = PauseOff.png; path = Images/PauseOff.png; sourceTree = ""; }; + 4DA6FDC3091141AD00450CB1 /* ResumeOff.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = ResumeOff.png; path = Images/ResumeOff.png; sourceTree = ""; }; + 4DA6FDC4091141AD00450CB1 /* ResumeOn.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = ResumeOn.png; path = Images/ResumeOn.png; sourceTree = ""; }; 4DF0C5A90899190500DD8943 /* Controller.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = Controller.m; sourceTree = ""; }; 4DF0C5AA0899190500DD8943 /* Controller.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = Controller.h; sourceTree = ""; }; 4DF0C5AD08991C1600DD8943 /* libtransmission.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libtransmission.a; path = ../libtransmission/libtransmission.a; sourceTree = SOURCE_ROOT; }; - 4DF7500708A103AD007B0D70 /* Open.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; name = Open.tiff; path = Images/Open.tiff; sourceTree = ""; }; - 4DF7500808A103AD007B0D70 /* Info.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; name = Info.tiff; path = Images/Info.tiff; sourceTree = ""; }; - 4DF7500908A103AD007B0D70 /* Remove.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; name = Remove.tiff; path = Images/Remove.tiff; sourceTree = ""; }; - 4DF7500A08A103AD007B0D70 /* Resume.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; name = Resume.tiff; path = Images/Resume.tiff; sourceTree = ""; }; - 4DF7500B08A103AD007B0D70 /* Stop.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; name = Stop.tiff; path = Images/Stop.tiff; sourceTree = ""; }; + 4DF7500708A103AD007B0D70 /* Open.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Open.png; path = Images/Open.png; sourceTree = ""; }; + 4DF7500808A103AD007B0D70 /* Info.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Info.png; path = Images/Info.png; sourceTree = ""; }; + 4DF7500908A103AD007B0D70 /* Remove.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Remove.png; path = Images/Remove.png; sourceTree = ""; }; 8D1107310486CEB800E47090 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; 8D1107320486CEB800E47090 /* Transmission.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Transmission.app; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ @@ -172,14 +178,17 @@ children = ( 4D2784360905709500687951 /* Transmission.icns */, 4D043A7E090AE979009FEDA8 /* TransmissionDocument.icns */, - 4DF7500808A103AD007B0D70 /* Info.tiff */, - 4D813EB408AA43AC00191DB4 /* Progress.tiff */, - 4DF7500708A103AD007B0D70 /* Open.tiff */, - 4DF7500908A103AD007B0D70 /* Remove.tiff */, - 4DF7500A08A103AD007B0D70 /* Resume.tiff */, - 4DF7500B08A103AD007B0D70 /* Stop.tiff */, - 4D6DAAC4090CE00500F43C22 /* RevealOff.tiff */, - 4D6DAAC5090CE00500F43C22 /* RevealOn.tiff */, + 4DF7500808A103AD007B0D70 /* Info.png */, + 4D813EB408AA43AC00191DB4 /* Progress.png */, + 4DF7500708A103AD007B0D70 /* Open.png */, + 4DF7500908A103AD007B0D70 /* Remove.png */, + 4D6DAAC4090CE00500F43C22 /* RevealOff.png */, + 4D6DAAC5090CE00500F43C22 /* RevealOn.png */, + 4DA6FDB80911233800450CB1 /* PauseOn.png */, + 4DA6FDB90911233800450CB1 /* PauseOff.png */, + 4DA6FDC3091141AD00450CB1 /* ResumeOff.png */, + 4DA6FDC4091141AD00450CB1 /* ResumeOn.png */, + 4D752E920913C949008EAAD4 /* Preferences.png */, 8D1107310486CEB800E47090 /* Info.plist */, 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */, 29B97318FDCFA39411CA2CEA /* MainMenu.nib */, @@ -256,16 +265,19 @@ files = ( 8D11072A0486CEB800E47090 /* MainMenu.nib in Resources */, 8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */, - 4DF7500C08A103AD007B0D70 /* Open.tiff in Resources */, - 4DF7500D08A103AD007B0D70 /* Info.tiff in Resources */, - 4DF7500E08A103AD007B0D70 /* Remove.tiff in Resources */, - 4DF7500F08A103AD007B0D70 /* Resume.tiff in Resources */, - 4DF7501008A103AD007B0D70 /* Stop.tiff in Resources */, - 4D813EB508AA43AC00191DB4 /* Progress.tiff in Resources */, + 4DF7500C08A103AD007B0D70 /* Open.png in Resources */, + 4DF7500D08A103AD007B0D70 /* Info.png in Resources */, + 4DF7500E08A103AD007B0D70 /* Remove.png in Resources */, + 4D813EB508AA43AC00191DB4 /* Progress.png in Resources */, 4D2784370905709500687951 /* Transmission.icns in Resources */, 4D043A7F090AE979009FEDA8 /* TransmissionDocument.icns in Resources */, - 4D6DAAC6090CE00500F43C22 /* RevealOff.tiff in Resources */, - 4D6DAAC7090CE00500F43C22 /* RevealOn.tiff in Resources */, + 4D6DAAC6090CE00500F43C22 /* RevealOff.png in Resources */, + 4D6DAAC7090CE00500F43C22 /* RevealOn.png in Resources */, + 4DA6FDBA0911233800450CB1 /* PauseOn.png in Resources */, + 4DA6FDBB0911233800450CB1 /* PauseOff.png in Resources */, + 4DA6FDC5091141AD00450CB1 /* ResumeOff.png in Resources */, + 4DA6FDC6091141AD00450CB1 /* ResumeOn.png in Resources */, + 4D752E930913C949008EAAD4 /* Preferences.png in Resources */, ); runOnlyForDeploymentPostprocessing = 0; };