From f5deceaa64aac61974edc1cc96dff9810a6b0a49 Mon Sep 17 00:00:00 2001 From: Eric Petit Date: Thu, 12 Jan 2006 18:57:23 +0000 Subject: [PATCH] Update 2005-12-21 --- Jamrules | 6 +- macosx/Controller.h | 135 +++-- macosx/Controller.m | 526 +++++++++--------- macosx/English.lproj/MainMenu.nib/classes.nib | 24 +- macosx/English.lproj/MainMenu.nib/info.nib | 19 +- .../MainMenu.nib/keyedobjects.nib | Bin 35040 -> 36554 bytes macosx/Images/Network.png | Bin 0 -> 7412 bytes macosx/Info.plist.in | 2 + macosx/PrefsController.h | 42 +- macosx/PrefsController.m | 510 ++++++++--------- macosx/TorrentTableView.h | 3 + macosx/TorrentTableView.m | 12 +- macosx/Transmission.xcodeproj/project.pbxproj | 8 +- 13 files changed, 683 insertions(+), 604 deletions(-) create mode 100755 macosx/Images/Network.png diff --git a/Jamrules b/Jamrules index 002b1a0c7..e638aef91 100644 --- a/Jamrules +++ b/Jamrules @@ -8,7 +8,7 @@ if ! $(DEFINES) VERSION_MAJOR = 0 ; VERSION_MINOR = 4 ; # VERSION_STRING = $(VERSION_MAJOR).$(VERSION_MINOR) ; -VERSION_STRING = CVS-20051213 ; +VERSION_STRING = CVS-20051221 ; DEFINES += VERSION_MAJOR=$(VERSION_MAJOR) VERSION_MINOR=$(VERSION_MINOR) @@ -49,7 +49,7 @@ if $(OS) = MACOSX actions OSXInfoPlist { $(RM) $(1) - sed "s/%%VERSION%%/$(VERSION_STRING)/" < $(2) > $(1) + sed "s/%%VERSION%%/$(VERSION_STRING)/g" < $(2) > $(1) } rule OSXBundle @@ -107,4 +107,4 @@ if $(OS) = BEOS $(RM) $(1) && ( cd beos && make ) && \ mv beos/obj.$(CPU)/Transmission $(1) } -} \ No newline at end of file +} diff --git a/macosx/Controller.h b/macosx/Controller.h index 39c57b414..99b7c0079 100644 --- a/macosx/Controller.h +++ b/macosx/Controller.h @@ -31,83 +31,100 @@ @interface Controller : NSObject { - tr_handle_t * fHandle; - int fCount; - tr_stat_t * fStat; - int fResumeOnWake[TR_MAX_TORRENT_COUNT]; + tr_handle_t * fHandle; + int fCount; + tr_stat_t * fStat; + int fResumeOnWake[TR_MAX_TORRENT_COUNT]; - NSToolbar * fToolbar; + NSToolbar * fToolbar; - IBOutlet PrefsController * fPrefsController; + IBOutlet NSMenuItem * fAdvancedBarItem; + IBOutlet NSMenuItem * fPauseResumeItem; + IBOutlet NSMenuItem * fRemoveItem; + IBOutlet NSMenuItem * fRemoveTorrentItem; + IBOutlet NSMenuItem * fRemoveDataItem; + IBOutlet NSMenuItem * fRemoveBothItem; + IBOutlet NSMenuItem * fRevealItem; + IBOutlet NSMenuItem * fShowHideToolbar; - IBOutlet NSMenuItem * fAdvancedBarItem; - IBOutlet NSMenuItem * fPauseResumeItem; - IBOutlet NSMenuItem * fRemoveItem; - IBOutlet NSMenuItem * fRemoveTorrentItem; - IBOutlet NSMenuItem * fRemoveDataItem; - IBOutlet NSMenuItem * fRemoveBothItem; - IBOutlet NSMenuItem * fRevealItem; - IBOutlet NSMenuItem * fShowHideToolbar; + IBOutlet NSWindow * fWindow; + IBOutlet TorrentTableView * fTableView; + IBOutlet NSTextField * fTotalDLField; + IBOutlet NSTextField * fTotalULField; - IBOutlet NSWindow * fWindow; - IBOutlet TorrentTableView * fTableView; - IBOutlet NSTextField * fTotalDLField; - IBOutlet NSTextField * fTotalULField; - IBOutlet NSMenu * fContextMenu; + IBOutlet NSPanel * fInfoPanel; + IBOutlet NSTextField * fInfoTitle; + IBOutlet NSTextField * fInfoTracker; + IBOutlet NSTextField * fInfoAnnounce; + IBOutlet NSTextField * fInfoSize; + IBOutlet NSTextField * fInfoPieces; + IBOutlet NSTextField * fInfoPieceSize; + IBOutlet NSTextField * fInfoSeeders; + IBOutlet NSTextField * fInfoLeechers; + IBOutlet NSTextField * fInfoFolder; + IBOutlet NSTextField * fInfoDownloaded; + IBOutlet NSTextField * fInfoUploaded; - IBOutlet NSPanel * fInfoPanel; - IBOutlet NSTextField * fInfoTitle; - IBOutlet NSTextField * fInfoTracker; - IBOutlet NSTextField * fInfoAnnounce; - IBOutlet NSTextField * fInfoSize; - IBOutlet NSTextField * fInfoPieces; - IBOutlet NSTextField * fInfoPieceSize; - IBOutlet NSTextField * fInfoSeeders; - IBOutlet NSTextField * fInfoLeechers; - IBOutlet NSTextField * fInfoFolder; - IBOutlet NSTextField * fInfoDownloaded; - IBOutlet NSTextField * fInfoUploaded; - - io_connect_t fRootPort; - NSArray * fFilenames; - NSTimer * fTimer; + io_connect_t fRootPort; + NSArray * fFilenames; + NSTimer * fTimer; + + IBOutlet NSPanel * fPrefsWindow; + IBOutlet PrefsController * fPrefsController; + NSUserDefaults * fDefaults; + + BOOL fHasGrowl; } - (void) advancedChanged: (id) sender; - (void) openShowSheet: (id) sender; - (void) openSheetClosed: (NSOpenPanel *) s returnCode: (int) code - contextInfo: (void *) info; -- (void) stopTorrent: (id) sender; -- (void) stopAllTorrents: (id) sender; -- (void) stopTorrentWithIndex: (int) index; -- (void) resumeTorrent: (id) sender; -- (void) resumeAllTorrents: (id) sender; -- (void) resumeTorrentWithIndex: (int) index; -- (void) removeTorrent: (id) sender; -- (void) removeTorrentDeleteFile: (id) sender; -- (void) removeTorrentDeleteData: (id) sender; -- (void) removeTorrentDeleteBoth: (id) sender; -- (void) removeTorrentWithIndex: (int) idx - deleteTorrent: (BOOL) deleteTorrent - deleteData: (BOOL) deleteData; + contextInfo: (void *) info; + +- (void) quitSheetDidEnd: (NSWindow *)sheet returnCode:(int)returnCode + contextInfo:(void *)contextInfo; +- (void) quitProcedure; + +- (void) stopTorrent: (id) sender; +- (void) stopAllTorrents: (id) sender; +- (void) stopTorrentWithIndex: (int) index; +- (void) resumeTorrent: (id) sender; +- (void) resumeAllTorrents: (id) sender; +- (void) resumeTorrentWithIndex: (int) index; + +- (void) removeTorrent: (id) sender; +- (void) removeTorrentDeleteFile: (id) sender; +- (void) removeTorrentDeleteData: (id) sender; +- (void) removeTorrentDeleteBoth: (id) sender; +- (void) removeTorrentWithIndex: (int) idx + deleteTorrent: (BOOL) deleteTorrent + deleteData: (BOOL) deleteData; + +- (void) removeSheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode + contextInfo:(NSDictionary *)dict; +- (void) confirmRemoveTorrentWithIndex: (int) idx + deleteTorrent: (BOOL) deleteTorrent + deleteData: (BOOL) deleteData; + - (void) showInfo: (id) sender; - (void) updateUI: (NSTimer *) timer; - (void) sleepCallBack: (natural_t) messageType argument: - (void *) messageArgument; - -- (NSMenu *) menuForIndex: (int) idx; + (void *) messageArgument; - (void) runCustomizationPalette: (id) sender; - (void) showHideToolbar: (id) sender; -- (void) showMainWindow: (id) sender; -- (void) linkHomepage: (id) sender; -- (void) linkForums: (id) sender; -- (void) notifyGrowl: (NSString *) file; -- (void) finderReveal: (NSString *) path; -- (void) finderTrash: (NSString *) path; -- (void) growlRegister: (id) sender; +- (void) showPreferenceWindow: (id) sender; + +- (void) showMainWindow: (id) sender; +- (void) linkHomepage: (id) sender; +- (void) linkForums: (id) sender; +- (void) notifyGrowl: (NSString *) file; +- (void) revealFromMenu: (id) sender; +- (void) finderReveal: (NSString *) path; +- (void) finderTrash: (NSString *) path; +- (void) growlRegister: (id) sender; @end diff --git a/macosx/Controller.m b/macosx/Controller.m index 0d89a3f09..9426412bf 100644 --- a/macosx/Controller.m +++ b/macosx/Controller.m @@ -27,19 +27,18 @@ #include "Utils.h" #include "TorrentTableView.h" +#import "PrefsController.h" + #define TOOLBAR_OPEN @"Toolbar Open" #define TOOLBAR_REMOVE @"Toolbar Remove" -#define TOOLBAR_PREFS @"Toolbar Preferences" #define TOOLBAR_INFO @"Toolbar Info" #define TOOLBAR_PAUSE_ALL @"Toolbar Pause All" #define TOOLBAR_RESUME_ALL @"Toolbar Resume All" -#define CONTEXT_PAUSE 1 -#define CONTEXT_REMOVE 2 -#define CONTEXT_REMOVE_TORRENT 3 -#define CONTEXT_REMOVE_DATA 4 -#define CONTEXT_REMOVE_BOTH 5 -#define CONTEXT_INFO 6 +#define WEBSITE_URL @"http://transmission.m0k.org/" +#define FORUM_URL @"http://transmission.m0k.org/forum/" + +#define GROWL_PATH @"/Library/PreferencePanes/Growl.prefPane/Contents/Resources/GrowlHelperApp.app" static void sleepCallBack( void * controller, io_service_t y, natural_t messageType, void * messageArgument ) @@ -52,103 +51,88 @@ static void sleepCallBack( void * controller, io_service_t y, - (void) awakeFromNib { + [fWindow setContentMinSize: NSMakeSize( 400, 120 )]; + fHandle = tr_init(); - [fPrefsController setHandle: fHandle]; + [fPrefsController setPrefsWindow: fHandle]; + fDefaults = [NSUserDefaults standardUserDefaults]; + + [fInfoPanel setFrameAutosaveName:@"InfoPanel"]; - [fWindow setContentMinSize: NSMakeSize( 400, 120 )]; - - /* Check or uncheck menu item in respect to current preferences */ - [fAdvancedBarItem setState: [[NSUserDefaults standardUserDefaults] + //check advanced bar menu item + [fAdvancedBarItem setState: [fDefaults boolForKey:@"UseAdvancedBar"] ? NSOnState : NSOffState]; fToolbar = [[NSToolbar alloc] initWithIdentifier: @"Transmission Toolbar"]; [fToolbar setDelegate: self]; [fToolbar setAllowsUserCustomization: YES]; [fToolbar setAutosavesConfiguration: YES]; - [fWindow setToolbar: fToolbar]; - [fWindow setDelegate: self]; + [fWindow setToolbar: fToolbar]; + [fWindow setDelegate: self]; NSTableColumn * tableColumn; - NameCell * nameCell; - ProgressCell * progressCell; + NameCell * nameCell = [[NameCell alloc] init]; + ProgressCell * progressCell = [[ProgressCell alloc] init]; - nameCell = [[NameCell alloc] init]; - progressCell = [[ProgressCell alloc] init]; tableColumn = [fTableView tableColumnWithIdentifier: @"Name"]; [tableColumn setDataCell: nameCell]; - [tableColumn setMinWidth: 10.0]; - [tableColumn setMaxWidth: 3000.0]; tableColumn = [fTableView tableColumnWithIdentifier: @"Progress"]; [tableColumn setDataCell: progressCell]; - [tableColumn setMinWidth: 134.0]; - [tableColumn setMaxWidth: 134.0]; [fTableView setAutosaveTableColumns: YES]; - [fTableView sizeToFit]; + //[fTableView sizeToFit]; [fTableView registerForDraggedTypes: [NSArray arrayWithObjects: NSFilenamesPboardType, NULL]]; + //Register for sleep notifications IONotificationPortRef notify; io_object_t anIterator; - /* Register for sleep notifications */ - fRootPort = IORegisterForSystemPower( self, ¬ify, sleepCallBack, - &anIterator); - if( !fRootPort ) - { - printf( "Could not IORegisterForSystemPower\n" ); - } - else + fRootPort = IORegisterForSystemPower( self, & notify, sleepCallBack, + & anIterator); + if (fRootPort) { CFRunLoopAddSource( CFRunLoopGetCurrent(), IONotificationPortGetRunLoopSource( notify ), kCFRunLoopCommonModes ); } + else + printf( "Could not IORegisterForSystemPower\n" ); - NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults]; + NSString * torrentPath, * downloadFolder, * paused; + NSDictionary * dic; - NSArray * history = [defaults arrayForKey: @"History"]; - if( history ) + NSEnumerator * enumerator = [[fDefaults arrayForKey: @"History"] objectEnumerator]; + while ((dic = [enumerator nextObject])) { - unsigned i; - NSDictionary * dic; - NSString * torrentPath, * downloadFolder, * paused; + torrentPath = [dic objectForKey: @"TorrentPath"]; + downloadFolder = [dic objectForKey: @"DownloadFolder"]; + paused = [dic objectForKey: @"Paused"]; + + if (!torrentPath || !downloadFolder || !paused) + continue; - for( i = 0; i < [history count]; i++ ) - { - dic = [history objectAtIndex: i]; + if (tr_torrentInit(fHandle, [torrentPath UTF8String])) + continue; - torrentPath = [dic objectForKey: @"TorrentPath"]; - downloadFolder = [dic objectForKey: @"DownloadFolder"]; - paused = [dic objectForKey: @"Paused"]; + tr_torrentSetFolder( fHandle, tr_torrentCount( fHandle ) - 1, + [downloadFolder UTF8String] ); - if( !torrentPath || !downloadFolder || !paused ) - { - continue; - } - - if( tr_torrentInit( fHandle, [torrentPath UTF8String] ) ) - { - continue; - } - - tr_torrentSetFolder( fHandle, tr_torrentCount( fHandle ) - 1, - [downloadFolder UTF8String] ); - - if( [paused isEqualToString: @"NO"] ) - { - tr_torrentStart( fHandle, tr_torrentCount( fHandle ) - 1 ); - } - } + if ([paused isEqualToString: @"NO"]) + tr_torrentStart( fHandle, tr_torrentCount( fHandle ) - 1 ); } - - /* Register with the growl system */ + + //check and register Growl if it is installed for this user or all users + NSFileManager * manager = [NSFileManager defaultManager]; + fHasGrowl = [manager fileExistsAtPath: GROWL_PATH] + || [manager fileExistsAtPath: [[NSString stringWithFormat: @"~%@", + GROWL_PATH] stringByExpandingTildeInPath]]; [self growlRegister: self]; - /* Update the interface every 500 ms */ + //update the interface every 500 ms fCount = 0; fStat = NULL; fTimer = [NSTimer scheduledTimerWithTimeInterval: 0.5 target: self @@ -175,17 +159,50 @@ static void sleepCallBack( void * controller, io_service_t y, return NO; } -- (NSApplicationTerminateReply) applicationShouldTerminate: - (NSApplication *) app +- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender { + if ([[fDefaults stringForKey: @"CheckQuit"] isEqualToString:@"YES"]) + { + int i; + for( i = 0; i < fCount; i++ ) + { + if( fStat[i].status & ( TR_STATUS_CHECK | + TR_STATUS_DOWNLOAD ) ) + { + NSBeginAlertSheet(@"Confirm Quit", + @"Quit", @"Cancel", nil, + fWindow, self, + @selector(quitSheetDidEnd:returnCode:contextInfo:), + NULL, NULL, @"There are active torrents. Do you really want to quit?"); + return NSTerminateLater; + } + } + } + + [self quitProcedure]; + return NSTerminateNow; +} + +- (void) quitSheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode + contextInfo:(void *)contextInfo +{ + if (returnCode == NSAlertDefaultReturn) + [self quitProcedure]; + + [NSApp stopModal]; + [NSApp replyToApplicationShouldTerminate: (returnCode == NSAlertDefaultReturn)]; +} + +- (void) quitProcedure +{ + int i; NSMutableArray * history = [NSMutableArray arrayWithCapacity: TR_MAX_TORRENT_COUNT]; - int i; - - /* Stop updating the interface */ + + // Stop updating the interface [fTimer invalidate]; - /* Save history and stop running torrents */ + // Save history and stop running torrents for( i = 0; i < fCount; i++ ) { [history addObject: [NSDictionary dictionaryWithObjectsAndKeys: @@ -205,7 +222,7 @@ static void sleepCallBack( void * controller, io_service_t y, } } - /* Wait for torrents to stop (5 seconds timeout) */ + // Wait for torrents to stop (5 seconds timeout) NSDate * start = [NSDate date]; while( fCount > 0 ) { @@ -224,48 +241,73 @@ static void sleepCallBack( void * controller, io_service_t y, tr_close( fHandle ); - [[NSUserDefaults standardUserDefaults] - setObject: history forKey: @"History"]; + [fDefaults setObject: history forKey: @"History"]; +} - return NSTerminateNow; +- (void) showPreferenceWindow: (id) sender +{ + NSRect mainFrame; + NSRect prefsFrame; + NSRect screenRect; + NSPoint point; + + /* Place the window */ + mainFrame = [fWindow frame]; + prefsFrame = [fPrefsWindow frame]; + screenRect = [[NSScreen mainScreen] visibleFrame]; + point.x = mainFrame.origin.x + mainFrame.size.width / 2 - + prefsFrame.size.width / 2; + point.y = mainFrame.origin.y + mainFrame.size.height - 30; + + /* Make sure it is in the screen */ + if( point.x < screenRect.origin.x ) + { + point.x = screenRect.origin.x; + } + if( point.x + prefsFrame.size.width > + screenRect.origin.x + screenRect.size.width ) + { + point.x = screenRect.origin.x + + screenRect.size.width - prefsFrame.size.width; + } + if( point.y - prefsFrame.size.height < screenRect.origin.y ) + { + point.y = screenRect.origin.y + prefsFrame.size.height; + } + + [fPrefsWindow setFrameTopLeftPoint: point]; + [fPrefsWindow makeKeyAndOrderFront:NULL]; } - (void) folderChoiceClosed: (NSOpenPanel *) s returnCode: (int) code contextInfo: (void *) info { - if( code != NSOKButton ) + if (code == NSOKButton) + { + tr_torrentSetFolder( fHandle, tr_torrentCount( fHandle ) - 1, + [[[s filenames] objectAtIndex: 0] UTF8String] ); + tr_torrentStart( fHandle, tr_torrentCount( fHandle ) - 1 ); + } + else { tr_torrentClose( fHandle, tr_torrentCount( fHandle ) - 1 ); - [NSApp stopModal]; - return; } - - tr_torrentSetFolder( fHandle, tr_torrentCount( fHandle ) - 1, - [[[s filenames] objectAtIndex: 0] UTF8String] ); - tr_torrentStart( fHandle, tr_torrentCount( fHandle ) - 1 ); [NSApp stopModal]; } - - (void) application: (NSApplication *) sender openFiles: (NSArray *) filenames { - unsigned i; - NSUserDefaults * defaults; NSString * downloadChoice, * downloadFolder, * torrentPath; - defaults = [NSUserDefaults standardUserDefaults]; - downloadChoice = [defaults stringForKey: @"DownloadChoice"]; - downloadFolder = [defaults stringForKey: @"DownloadFolder"]; + downloadChoice = [fDefaults stringForKey: @"DownloadChoice"]; + downloadFolder = [fDefaults stringForKey: @"DownloadFolder"]; - for( i = 0; i < [filenames count]; i++ ) + NSEnumerator * enumerator = [filenames objectEnumerator]; + while ((torrentPath = [enumerator nextObject])) { - torrentPath = [filenames objectAtIndex: i]; - if( tr_torrentInit( fHandle, [torrentPath UTF8String] ) ) - { continue; - } if( [downloadChoice isEqualToString: @"Constant"] ) { @@ -283,19 +325,15 @@ static void sleepCallBack( void * controller, io_service_t y, continue; } - NSOpenPanel * panel; - NSString * message; - - panel = [NSOpenPanel openPanel]; - message = [NSString stringWithFormat: - @"Select the download folder for %@", - [torrentPath lastPathComponent]]; - - [panel setPrompt: @"Select"]; - [panel setMessage: message]; - [panel setAllowsMultipleSelection: NO]; - [panel setCanChooseFiles: NO]; - [panel setCanChooseDirectories: YES]; + NSOpenPanel * panel = [NSOpenPanel openPanel]; + + [panel setPrompt: @"Select Download Folder"]; + [panel setMessage: [NSString stringWithFormat: + @"Select the download folder for %@", + [torrentPath lastPathComponent]]]; + [panel setAllowsMultipleSelection: NO]; + [panel setCanChooseFiles: NO]; + [panel setCanChooseDirectories: YES]; [panel beginSheetForDirectory: NULL file: NULL types: NULL modalForWindow: fWindow modalDelegate: self didEndSelector: @@ -309,21 +347,14 @@ static void sleepCallBack( void * controller, io_service_t y, - (void) advancedChanged: (id) sender { - NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults]; - if( [fAdvancedBarItem state] == NSOnState ) - { - [fAdvancedBarItem setState: NSOffState]; - [defaults setObject:@"NO" forKey:@"UseAdvancedBar"]; - } - else - { - [fAdvancedBarItem setState: NSOnState]; - [defaults setObject:@"YES" forKey:@"UseAdvancedBar"]; - } + [fAdvancedBarItem setState: ![fAdvancedBarItem state]]; + [fDefaults setObject: [fAdvancedBarItem state] == NSOffState ? @"NO" : @"YES" + forKey:@"UseAdvancedBar"]; + [fTableView display]; } -/* called on by applescript */ +//called on by applescript - (void) open: (NSArray *) files { fFilenames = [files retain]; @@ -417,10 +448,62 @@ static void sleepCallBack( void * controller, io_service_t y, [self updateUI: NULL]; } - - (void) removeTorrentWithIndex: (int) idx deleteTorrent: (BOOL) deleteTorrent deleteData: (BOOL) deleteData +{ + if ( fStat[idx].status & ( TR_STATUS_CHECK + | TR_STATUS_DOWNLOAD) ) + { + if ([[fDefaults stringForKey: @"CheckRemove"] isEqualToString:@"YES"]) + { + NSDictionary * dict = [NSDictionary dictionaryWithObjectsAndKeys: + [NSString stringWithFormat: @"%d", idx], @"Index", + [NSString stringWithFormat: @"%d", deleteTorrent], @"DeleteTorrent", + [NSString stringWithFormat: @"%d", deleteData], @"DeleteData", + nil]; + [dict retain]; + + NSBeginAlertSheet(@"Confirm Remove", + @"Remove", @"Cancel", nil, + fWindow, self, + @selector(removeSheetDidEnd:returnCode:contextInfo:), + NULL, dict, @"This torrent is active. Do you really want to remove it?"); + return; + } + //stop if not stopped + else + [self stopTorrentWithIndex:idx]; + } + + [self confirmRemoveTorrentWithIndex: idx + deleteTorrent: deleteTorrent + deleteData: deleteData]; +} + +- (void) removeSheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode + contextInfo:(NSDictionary *)dict +{ + [NSApp stopModal]; + if (returnCode != NSAlertDefaultReturn) + { + [dict release]; + return; + } + + int idx = [[dict objectForKey:@"Index"] intValue]; + + [self stopTorrentWithIndex:idx]; + + [self confirmRemoveTorrentWithIndex: idx + deleteTorrent: [[dict objectForKey:@"DeleteTorrent"] intValue] + deleteData: [[dict objectForKey:@"DeleteData"] intValue]]; + [dict release]; +} + +- (void) confirmRemoveTorrentWithIndex: (int) idx + deleteTorrent: (BOOL) deleteTorrent + deleteData: (BOOL) deleteData { if( deleteData ) { @@ -475,24 +558,23 @@ static void sleepCallBack( void * controller, io_service_t y, float dl, ul; int row, i; - /* Update the NSTableView */ - if( fStat ) - { - free( fStat ); - } + //Update the NSTableView + if (fStat) + free(fStat); + fCount = tr_torrentStat( fHandle, &fStat ); [fTableView updateUI: fStat]; - /* Update the global DL/UL rates */ + //Update the global DL/UL rates tr_torrentRates( fHandle, &dl, &ul ); [fTotalDLField setStringValue: [NSString stringWithFormat: @"Total DL: %.2f KB/s", dl]]; [fTotalULField setStringValue: [NSString stringWithFormat: @"Total UL: %.2f KB/s", ul]]; - /* Update DL/UL totals in the Info panel */ + //Update DL/UL totals in the Info panel row = [fTableView selectedRow]; - if( row > -1 ) + if( row >= 0 ) { [fInfoDownloaded setStringValue: stringForFileSize( fStat[row].downloaded )]; @@ -500,64 +582,18 @@ static void sleepCallBack( void * controller, io_service_t y, stringForFileSize( fStat[row].uploaded )]; } - /* check if torrents have recently ended. */ + //check if torrents have recently ended. for (i = 0; i < fCount; i++) { if( !tr_getFinished( fHandle, i ) ) - { continue; - } + [self notifyGrowl: [NSString stringWithUTF8String: fStat[i].info.name]]; tr_setFinished( fHandle, i, 0 ); } } - -- (NSMenu *) menuForIndex: (int) idx -{ - if ( idx < 0 || idx >= fCount ) - { - return nil; - } - - int status = fStat[idx].status; - - NSMenuItem *pauseItem = [fContextMenu itemWithTag: CONTEXT_PAUSE]; - NSMenuItem *removeItem = [fContextMenu itemAtIndex: 1]; - NSMenuItem *infoItem = [fContextMenu itemAtIndex: 2]; - - [pauseItem setTarget: self]; - - if ( status & TR_STATUS_CHECK || - status & TR_STATUS_DOWNLOAD || - status & TR_STATUS_SEED ) - { - /* we can stop */ - [removeItem setEnabled: NO]; - [pauseItem setTitle: @"Pause"]; - [pauseItem setAction: @selector(stopTorrent:)]; - [pauseItem setEnabled: YES]; - } else { - /* we are stopped */ - [removeItem setEnabled: YES]; - [pauseItem setTitle: @"Resume"]; - [pauseItem setAction: @selector(resumeTorrent:)]; - /* don't allow resuming if we aren't in PAUSE */ - if ( !(status & TR_STATUS_PAUSE) ) - [pauseItem setEnabled: NO]; - } - - if( [fInfoPanel isVisible] ) - { - [infoItem setTitle: @"Hide Info"]; - } else { - [infoItem setTitle: @"Show Info"]; - } - - return fContextMenu; -} - - (int) numberOfRowsInTableView: (NSTableView *) t { return fCount; @@ -663,8 +699,7 @@ static void sleepCallBack( void * controller, io_service_t y, - (NSToolbarItem *) toolbar: (NSToolbar *) t itemForItemIdentifier: (NSString *) ident willBeInsertedIntoToolbar: (BOOL) flag { - NSToolbarItem * item; - item = [[NSToolbarItem alloc] initWithItemIdentifier: ident]; + NSToolbarItem * item = [[NSToolbarItem alloc] initWithItemIdentifier: ident]; if( [ident isEqualToString: TOOLBAR_OPEN] ) { @@ -684,15 +719,6 @@ static void sleepCallBack( void * controller, io_service_t y, [item setTarget: self]; [item setAction: @selector( removeTorrent: )]; } - else if( [ident isEqualToString: TOOLBAR_PREFS] ) - { - [item setLabel: @"Preferences"]; - [item setPaletteLabel: [item label]]; - [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"]; @@ -733,8 +759,8 @@ static void sleepCallBack( void * controller, io_service_t y, { return [NSArray arrayWithObjects: TOOLBAR_OPEN, TOOLBAR_REMOVE, - /* TOOLBAR_RESUME_ALL, TOOLBAR_PAUSE_ALL, */ - TOOLBAR_PREFS, TOOLBAR_INFO, + TOOLBAR_RESUME_ALL, TOOLBAR_PAUSE_ALL, + TOOLBAR_INFO, NSToolbarSeparatorItemIdentifier, NSToolbarSpaceItemIdentifier, NSToolbarFlexibleSpaceItemIdentifier, @@ -746,26 +772,13 @@ static void sleepCallBack( void * controller, io_service_t y, { return [NSArray arrayWithObjects: TOOLBAR_OPEN, TOOLBAR_REMOVE, - /* NSToolbarSeparatorItemIdentifier, - TOOLBAR_RESUME_ALL, TOOLBAR_PAUSE_ALL, */ + NSToolbarSeparatorItemIdentifier, + TOOLBAR_RESUME_ALL, TOOLBAR_PAUSE_ALL, NSToolbarFlexibleSpaceItemIdentifier, - TOOLBAR_PREFS, TOOLBAR_INFO, + TOOLBAR_INFO, NULL]; } -- (BOOL)validateToolbarItem:(NSToolbarItem *)toolbarItem -{ - //check remove item - if ([toolbarItem action] == @selector(removeTorrent:)) - { - int row = [fTableView selectedRow]; - return ( row >= 0 ) && ( fStat[row].status & - ( TR_STATUS_STOPPING | TR_STATUS_PAUSE ) ); - } - - return true; -} - - (void) runCustomizationPalette: (id) sender { [fToolbar runCustomizationPalette:sender]; @@ -776,37 +789,72 @@ static void sleepCallBack( void * controller, io_service_t y, [fWindow toggleToolbarShown:sender]; } +- (BOOL)validateToolbarItem:(NSToolbarItem *)toolbarItem +{ + //enable remove item + if ([toolbarItem action] == @selector(removeTorrent:)) + return [fTableView selectedRow] >= 0; + + //enable pause all and resume all items + if ([toolbarItem action] == @selector(stopAllTorrents:) + || [toolbarItem action] == @selector(resumeAllTorrents:)) + return fCount > 0; + + return YES; +} + - (BOOL)validateMenuItem:(NSMenuItem *)menuItem { + //disable menus if customize sheet is active + if ([fToolbar customizationPaletteIsRunning]) + return NO; + //enable customize toolbar item if ([menuItem action] == @selector(showHideToolbar:)) + { [menuItem setTitle: [fToolbar isVisible] ? @"Hide Toolbar" : @"Show Toolbar"]; - - if ([fToolbar customizationPaletteIsRunning]) - return false; + return YES; + } //enable show info if ([menuItem action] == @selector(showInfo:)) { [menuItem setTitle: [fInfoPanel isVisible] ? @"Hide Info" : @"Show Info"]; - return true; + return YES; } + + //enable pause all and resume all + if ([menuItem action] == @selector(stopAllTorrents:) || [menuItem action] == @selector(resumeAllTorrents:)) + return fCount > 0; int row = [fTableView selectedRow]; //enable remove items - if ([menuItem action] == @selector(removeTorrent:) || [menuItem action] == @selector(removeTorrentDeleteFile:) - || [menuItem action] == @selector(removeTorrentDeleteData:) || [menuItem action] == @selector(removeTorrentDeleteBoth:)) { - /* Can we remove it ? */ - return ( row >= 0 ) && ( fStat[row].status & - ( TR_STATUS_STOPPING | TR_STATUS_PAUSE ) ); + if ([menuItem action] == @selector(removeTorrent:) + || [menuItem action] == @selector(removeTorrentDeleteFile:) + || [menuItem action] == @selector(removeTorrentDeleteData:) + || [menuItem action] == @selector(removeTorrentDeleteBoth:)) + { + //append or remove ellipsis when needed + if (row >= 0 && fStat[row].status & ( TR_STATUS_CHECK | TR_STATUS_DOWNLOAD) + && [[fDefaults stringForKey: @"CheckRemove"] isEqualToString:@"YES"]) + { + if (![[menuItem title] hasSuffix:NS_ELLIPSIS]) + [menuItem setTitle:[[menuItem title] stringByAppendingString:NS_ELLIPSIS]]; + } + else + { + if ([[menuItem title] hasSuffix:NS_ELLIPSIS]) + [menuItem setTitle:[[menuItem title] substringToIndex:[[menuItem title] length]-[NS_ELLIPSIS length]]]; + } + return row >= 0; } //enable reveal in finder item if ([menuItem action] == @selector(revealFromMenu:)) return row >= 0; - //enable and change pause and remove item + //enable and change pause / remove item if ([menuItem action] == @selector(resumeTorrent:) || [menuItem action] == @selector(stopTorrent:)) { if (row >= 0 && fStat[row].status & TR_STATUS_PAUSE) @@ -822,7 +870,7 @@ static void sleepCallBack( void * controller, io_service_t y, return row >= 0; } - return true; + return YES; } - (void) sleepCallBack: (natural_t) messageType argument: @@ -893,40 +941,19 @@ static void sleepCallBack( void * controller, io_service_t y, - (void) showMainWindow: (id) sender { - [fWindow makeKeyAndOrderFront: NULL]; + [fWindow makeKeyAndOrderFront: nil]; } - (void) linkHomepage: (id) sender { [[NSWorkspace sharedWorkspace] openURL: [NSURL - URLWithString:@"http://transmission.m0k.org/"]]; + URLWithString: WEBSITE_URL]]; } - (void) linkForums: (id) sender { [[NSWorkspace sharedWorkspace] openURL: [NSURL - URLWithString:@"http://transmission.m0k.org/forum/"]]; -} - -- (BOOL) hasGrowl -{ - NSFileManager * manager = [NSFileManager defaultManager]; - NSString * helper = @"/Library/PreferencePanes/Growl.prefPane/" - "Contents/Resources/GrowlHelperApp.app"; - - if( [manager fileExistsAtPath: helper] ) - { - /* Growl was installed for all users */ - return YES; - } - if( [manager fileExistsAtPath: [[NSString stringWithFormat: @"~%@", - helper] stringByExpandingTildeInPath]] ) - { - /* Growl was installed for this user only */ - return YES; - } - - return NO; + URLWithString: FORUM_URL]]; } - (void) notifyGrowl: (NSString * ) file @@ -935,10 +962,8 @@ static void sleepCallBack( void * controller, io_service_t y, NSAppleScript * appleScript; NSDictionary * error; - if( ![self hasGrowl] ) - { + if( !fHasGrowl ) return; - } growlScript = [NSString stringWithFormat: @"tell application \"System Events\"\n" @@ -965,10 +990,8 @@ static void sleepCallBack( void * controller, io_service_t y, NSAppleScript * appleScript; NSDictionary * error; - if( ![self hasGrowl] ) - { + if( !fHasGrowl ) return; - } growlScript = [NSString stringWithFormat: @"tell application \"System Events\"\n" @@ -981,6 +1004,7 @@ static void sleepCallBack( void * controller, io_service_t y, " end tell\n" " end if\n" "end tell"]; + appleScript = [[NSAppleScript alloc] initWithSource: growlScript]; if( ![appleScript executeAndReturnError: &error] ) { @@ -989,19 +1013,15 @@ static void sleepCallBack( void * controller, io_service_t y, [appleScript release]; } - - (void) revealFromMenu: (id) sender { - int row; - - row = [fTableView selectedRow]; - if( row < 0 ) + int row = [fTableView selectedRow]; + if (row >= 0) { - return; + [self finderReveal: [NSString stringWithFormat: @"%@/%@", + [NSString stringWithUTF8String: fStat[row].folder], + [NSString stringWithUTF8String: fStat[row].info.name]]]; } - [self finderReveal: [NSString stringWithFormat: @"%@/%@", - [NSString stringWithUTF8String: fStat[row].folder], - [NSString stringWithUTF8String: fStat[row].info.name]]]; } - (void) finderReveal: (NSString *) path diff --git a/macosx/English.lproj/MainMenu.nib/classes.nib b/macosx/English.lproj/MainMenu.nib/classes.nib index 14b0c1066..8f5d7bd5b 100644 --- a/macosx/English.lproj/MainMenu.nib/classes.nib +++ b/macosx/English.lproj/MainMenu.nib/classes.nib @@ -18,6 +18,7 @@ showHideToolbar = id; showInfo = id; showMainWindow = id; + showPreferenceWindow = id; stopAllTorrents = id; stopTorrent = id; }; @@ -25,7 +26,6 @@ LANGUAGE = ObjC; OUTLETS = { fAdvancedBarItem = NSMenuItem; - fContextMenu = NSMenu; fInfoAnnounce = NSTextField; fInfoDownloaded = NSTextField; fInfoFolder = NSTextField; @@ -40,6 +40,7 @@ fInfoUploaded = NSTextField; fPauseResumeItem = NSMenuItem; fPrefsController = PrefsController; + fPrefsWindow = NSPanel; fRemoveBothItem = NSMenuItem; fRemoveDataItem = NSMenuItem; fRemoveItem = NSMenuItem; @@ -54,25 +55,40 @@ SUPERCLASS = NSObject; }, {CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; }, + {CLASS = NameCell; LANGUAGE = ObjC; SUPERCLASS = NSCell; }, { - ACTIONS = {cancel = id; check = id; ratio = id; save = id; show = id; }; + ACTIONS = { + folderSheetShow = id; + setDownloadLocation = id; + setLimitUploadCheck = id; + setPort = id; + setQuitMessage = id; + setRemoveMessage = id; + setUploadLimit = id; + }; CLASS = PrefsController; LANGUAGE = ObjC; OUTLETS = { + fBlankView = NSView; fFolderMatrix = NSMatrix; fFolderPopUp = NSPopUpButton; + fGeneralView = NSView; + fNetworkView = NSView; fPortField = NSTextField; - fPrefsWindow = NSWindow; + fPrefsWindow = NSPanel; + fQuitCheck = NSButton; + fRemoveCheck = NSButton; fUploadCheck = NSButton; fUploadField = NSTextField; fWindow = NSWindow; }; SUPERCLASS = NSObject; }, + {CLASS = ProgressCell; LANGUAGE = ObjC; SUPERCLASS = NSCell; }, { CLASS = TorrentTableView; LANGUAGE = ObjC; - OUTLETS = {fController = Controller; }; + OUTLETS = {fContextNoRow = NSMenu; fContextRow = NSMenu; fController = Controller; }; SUPERCLASS = NSTableView; } ); diff --git a/macosx/English.lproj/MainMenu.nib/info.nib b/macosx/English.lproj/MainMenu.nib/info.nib index 5cf994340..b18e6c982 100644 --- a/macosx/English.lproj/MainMenu.nib/info.nib +++ b/macosx/English.lproj/MainMenu.nib/info.nib @@ -3,13 +3,23 @@ IBDocumentLocation - 416 137 361 432 0 0 1152 842 + 62 66 426 365 0 0 1280 832 IBEditorPositions 29 - 79 779 371 44 0 0 1152 842 + 92 768 371 44 0 0 1280 832 456 - 134 408 144 106 0 0 1152 842 + 212 488 144 118 0 0 1152 842 + 581 + 324 628 112 68 0 0 1152 842 + 589 + 54 521 112 118 0 0 1152 842 + 783 + 428 442 385 225 0 0 1280 832 + 796 + 479 490 282 129 0 0 1280 832 + 825 + 498 523 155 107 0 0 1152 842 IBFramework Version 443.0 @@ -18,6 +28,9 @@ IBOpenObjects 29 + 783 + 796 + 21 IBSystem Version 8F46 diff --git a/macosx/English.lproj/MainMenu.nib/keyedobjects.nib b/macosx/English.lproj/MainMenu.nib/keyedobjects.nib index 9dd04a4a3e0e7ec89170411098177984939aa394..17b5885e05f9d23b1cffd51ee8624c21c4f685a6 100644 GIT binary patch literal 36554 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 RNlE$NR($op{r@)3{2y$;F023m diff --git a/macosx/Images/Network.png b/macosx/Images/Network.png new file mode 100755 index 0000000000000000000000000000000000000000..5865914f412b014d92fede78a45dcc84c7371546 GIT binary patch literal 7412 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 mPUSED`1o}n0Py37002ThYI#^N*Y4-fO8XTR## CFBundleVersion %%VERSION%% + CFBundleShortVersionString + %%VERSION%% NSMainNibFile MainMenu NSPrincipalClass diff --git a/macosx/PrefsController.h b/macosx/PrefsController.h index fbbc7427c..61870d406 100644 --- a/macosx/PrefsController.h +++ b/macosx/PrefsController.h @@ -27,23 +27,35 @@ { tr_handle_t * fHandle; + + IBOutlet NSPanel * fPrefsWindow; + NSToolbar * fToolbar; + IBOutlet NSView * fGeneralView; + IBOutlet NSView * fNetworkView; + IBOutlet NSView * fBlankView; + + IBOutlet NSMatrix * fFolderMatrix; + IBOutlet NSPopUpButton * fFolderPopUp; + IBOutlet NSTextField * fPortField; + IBOutlet NSButton * fUploadCheck; + IBOutlet NSTextField * fUploadField; + IBOutlet NSButton * fQuitCheck; + IBOutlet NSButton * fRemoveCheck; + + IBOutlet NSWindow * fWindow; - IBOutlet NSWindow * fWindow; - IBOutlet NSWindow * fPrefsWindow; - IBOutlet NSMatrix * fFolderMatrix; - IBOutlet NSPopUpButton * fFolderPopUp; - IBOutlet NSTextField * fPortField; - IBOutlet NSButton * fUploadCheck; - IBOutlet NSTextField * fUploadField; - - NSString * fDownloadFolder; + NSString * fDownloadFolder; + NSUserDefaults * fDefaults; } -- (void) setHandle: (tr_handle_t *) handle; -- (void) show: (id) sender; -- (void) ratio: (id) sender; -- (void) check: (id) sender; -- (void) cancel: (id) sender; -- (void) save: (id) sender; +- (void) setPrefsWindow: (tr_handle_t *) handle; + +- (void) setLimitUploadCheck: (id) sender; +- (void) setPort: (id) sender; +- (void) setUploadLimit: (id) sender; +- (void) setQuitMessage: (id) sender; +- (void) setRemoveMessage: (id) sender; +- (void) setDownloadLocation: (id) sender; +- (void) folderSheetShow: (id) sender; @end diff --git a/macosx/PrefsController.m b/macosx/PrefsController.m index 95473652a..24abe94dc 100644 --- a/macosx/PrefsController.m +++ b/macosx/PrefsController.m @@ -22,187 +22,271 @@ #include "PrefsController.h" +#define DEFAULT_UPLOAD @"20" +#define MIN_PORT 1 +#define MAX_PORT 65535 + +#define DOWNLOAD_FOLDER 0 +#define DOWNLOAD_TORRENT 1 +#define DOWNLOAD_ASK 2 + +#define TOOLBAR_GENERAL @"General" +#define TOOLBAR_NETWORK @"Network" + @interface PrefsController (Private) -- (void) folderSheetShow: (id) sender; +- (void) showGeneralPref: (id) sender; +- (void) showNetworkPref: (id) sender; + +- (void) setPrefView: (NSView *) view; + - (void) folderSheetClosed: (NSOpenPanel *) s returnCode: (int) code contextInfo: (void *) info; -- (void) loadSettings; -- (void) saveSettings; - (void) updatePopUp; @end @implementation PrefsController -/*********************************************************************** - * setHandle - *********************************************************************** - * - **********************************************************************/ -- (void) setHandle: (tr_handle_t *) handle ++ (void) initialize { - NSUserDefaults * defaults; NSDictionary * appDefaults; NSString * desktop, * port; - fHandle = handle; - /* Register defaults settings: - Simple bar - Always download to Desktop - Port TR_DEFAULT_PORT - - 20 KB/s upload limit */ + - Upload limit DEFAULT_UPLOAD + - Do not limit upload + - Ask before quitting + - Ask before removing */ desktop = [NSHomeDirectory() stringByAppendingString: @"/Desktop"]; - port = [NSString stringWithFormat: @"%d", TR_DEFAULT_PORT]; + port = [NSString stringWithFormat: @"%d", TR_DEFAULT_PORT]; - defaults = [NSUserDefaults standardUserDefaults]; appDefaults = [NSDictionary dictionaryWithObjectsAndKeys: - @"NO", @"UseAdvancedBar", - @"Constant", @"DownloadChoice", - desktop, @"DownloadFolder", - port, @"BindPort", - @"20", @"UploadLimit", + @"NO", @"UseAdvancedBar", + @"Constant", @"DownloadChoice", + desktop, @"DownloadFolder", + port, @"BindPort", + DEFAULT_UPLOAD, @"UploadLimit", + @"YES", @"CheckUpload", + @"YES", @"CheckQuit", + @"YES", @"CheckRemove", NULL]; - [defaults registerDefaults: appDefaults]; - - /* Apply settings */ - tr_setBindPort( fHandle, [defaults integerForKey: @"BindPort"] ); - tr_setUploadLimit( fHandle, [defaults integerForKey: @"UploadLimit"] ); + [[NSUserDefaults standardUserDefaults] registerDefaults: appDefaults]; } -/*********************************************************************** - * show - *********************************************************************** - * - **********************************************************************/ -- (void) show: (id) sender +- (void)dealloc { - NSRect mainFrame; - NSRect prefsFrame; - NSRect screenRect; - NSPoint point; - - [self loadSettings]; - - /* Place the window */ - mainFrame = [fWindow frame]; - prefsFrame = [fPrefsWindow frame]; - screenRect = [[NSScreen mainScreen] visibleFrame]; - point.x = mainFrame.origin.x + mainFrame.size.width / 2 - - prefsFrame.size.width / 2; - point.y = mainFrame.origin.y + mainFrame.size.height - 30; - - /* Make sure it is in the screen */ - if( point.x < screenRect.origin.x ) - { - point.x = screenRect.origin.x; - } - if( point.x + prefsFrame.size.width > - screenRect.origin.x + screenRect.size.width ) - { - point.x = screenRect.origin.x + - screenRect.size.width - prefsFrame.size.width; - } - if( point.y - prefsFrame.size.height < screenRect.origin.y ) - { - point.y = screenRect.origin.y + prefsFrame.size.height; - } - - [fPrefsWindow setFrameTopLeftPoint: point]; - [fPrefsWindow makeKeyAndOrderFront: NULL]; + [fDownloadFolder release]; + [super dealloc]; } -/*********************************************************************** - * ratio - *********************************************************************** - * - **********************************************************************/ -- (void) ratio: (id) sender +- (void) setPrefsWindow: (tr_handle_t *) handle { - [fFolderPopUp setEnabled: ![fFolderMatrix selectedRow]]; -} + fToolbar = [[NSToolbar alloc] initWithIdentifier: @"Preferences Toolbar"]; + [fToolbar setDelegate: self]; + [fToolbar setAllowsUserCustomization: NO]; + [fPrefsWindow setToolbar: fToolbar]; + [fToolbar setDisplayMode: NSToolbarDisplayModeIconAndLabel]; + [fToolbar setSizeMode: NSToolbarSizeModeRegular]; + [fPrefsWindow setShowsToolbarButton: NO]; + + [fToolbar setSelectedItemIdentifier: TOOLBAR_GENERAL]; + [self setPrefView: fGeneralView]; -/*********************************************************************** - * check - *********************************************************************** - * - **********************************************************************/ -- (void) check: (id) sender -{ - if( [fUploadCheck state] == NSOnState ) + fDefaults = [NSUserDefaults standardUserDefaults]; + + //set download folder + NSString * downloadChoice = [fDefaults stringForKey: @"DownloadChoice"]; + fDownloadFolder = [fDefaults stringForKey: @"DownloadFolder"]; + [fDownloadFolder retain]; + + if( [downloadChoice isEqualToString: @"Constant"] ) { - [fUploadField setEnabled: YES]; + [fFolderMatrix selectCellAtRow: DOWNLOAD_FOLDER column: 0]; + } + else if( [downloadChoice isEqualToString: @"Torrent"] ) + { + [fFolderMatrix selectCellAtRow: DOWNLOAD_TORRENT column: 0]; } else { - [fUploadField setEnabled: NO]; - [fUploadField setStringValue: @""]; + [fFolderMatrix selectCellAtRow: DOWNLOAD_ASK column: 0]; } -} + [self updatePopUp]; + [fFolderPopUp setEnabled: [fFolderMatrix selectedRow] == 0]; -/*********************************************************************** - * cancel - *********************************************************************** - * Discards changes and closes the Preferences window - **********************************************************************/ -- (void) cancel: (id) sender -{ - [fDownloadFolder release]; - [fPrefsWindow close]; -} - -/*********************************************************************** - * save - *********************************************************************** - * Checks the user-defined options. If they are correct, saves settings - * and closes the Preferences window. Otherwise corrects them and leaves - * the window open - **********************************************************************/ -- (void) save: (id) sender -{ - int bindPort; - int uploadLimit; - - /* Bind port */ - bindPort = [fPortField intValue]; - bindPort = MAX( 1, bindPort ); - bindPort = MIN( bindPort, 65535 ); - - if( ![[fPortField stringValue] isEqualToString: - [NSString stringWithFormat: @"%d", bindPort]] ) + //set bind port + int bindPort = [fDefaults integerForKey: @"BindPort"]; + [fPortField setIntValue: bindPort]; + fHandle = handle; + tr_setBindPort( fHandle, bindPort ); + + //checks for old version upload speed of -1 + if ([fDefaults integerForKey: @"UploadLimit"] < 0) { + [fDefaults setObject: DEFAULT_UPLOAD forKey: @"UploadLimit"]; + [fDefaults setObject: @"NO" forKey: @"CheckUpload"]; + } + + //set upload limit + BOOL checkUpload = [[fDefaults stringForKey: @"CheckUpload"] isEqualToString:@"YES"]; + int uploadLimit = [fDefaults integerForKey: @"UploadLimit"]; + + [fUploadCheck setState: checkUpload ? NSOnState : NSOffState]; + [fUploadField setIntValue: uploadLimit]; + [fUploadField setEnabled: checkUpload]; + + if (!checkUpload || uploadLimit == 0) + uploadLimit = -1; + tr_setUploadLimit( fHandle, uploadLimit ); + + //set remove and quit prompts + [fQuitCheck setState:([[fDefaults stringForKey: @"CheckQuit"] + isEqualToString:@"YES"] ? NSOnState : NSOffState)]; + [fRemoveCheck setState:([[fDefaults stringForKey: @"CheckRemove"] + isEqualToString:@"YES"] ? NSOnState : NSOffState)]; +} + +- (NSToolbarItem *) toolbar: (NSToolbar *) t itemForItemIdentifier: + (NSString *) ident willBeInsertedIntoToolbar: (BOOL) flag +{ + NSToolbarItem * item; + item = [[NSToolbarItem alloc] initWithItemIdentifier: ident]; + + if ([ident isEqualToString: TOOLBAR_GENERAL]) + { + [item setLabel: TOOLBAR_GENERAL]; + [item setImage: [NSImage imageNamed: @"Preferences.png"]]; + [item setTarget: self]; + [item setAction: @selector( showGeneralPref: )]; + } + else if ([ident isEqualToString: TOOLBAR_NETWORK]) + { + [item setLabel: TOOLBAR_NETWORK]; + [item setImage: [NSImage imageNamed: @"Network.png"]]; + [item setTarget: self]; + [item setAction: @selector( showNetworkPref: )]; + } + else + { + [item release]; + return nil; + } + + return item; +} + +- (NSArray *) toolbarSelectableItemIdentifiers: (NSToolbar *)toolbar +{ + return [self toolbarDefaultItemIdentifiers: nil]; +} + +- (NSArray *) toolbarDefaultItemIdentifiers: (NSToolbar *)toolbar +{ + return [self toolbarAllowedItemIdentifiers: nil]; +} + +- (NSArray *) toolbarAllowedItemIdentifiers: (NSToolbar *)toolbar +{ + return [NSArray arrayWithObjects: + TOOLBAR_GENERAL, + TOOLBAR_NETWORK, + nil]; +} + +- (void) setPort: (id) sender +{ + int bindPort = [fPortField intValue]; + + //if value entered is not an int or is not in range do not change + if (![[fPortField stringValue] isEqualToString: + [NSString stringWithFormat: @"%d", bindPort]] + || bindPort < MIN_PORT + || bindPort > MAX_PORT) + { + NSBeep(); + bindPort = [fDefaults integerForKey: @"BindPort"]; [fPortField setIntValue: bindPort]; - return; } - - /* Upload limit */ - if( [fUploadCheck state] == NSOnState ) + else { - uploadLimit = [fUploadField intValue]; - uploadLimit = MAX( 0, uploadLimit ); - - if( ![[fUploadField stringValue] isEqualToString: - [NSString stringWithFormat: @"%d", uploadLimit]] ) - { - [fUploadField setIntValue: uploadLimit]; - return; - } + tr_setBindPort( fHandle, bindPort ); + [fDefaults setObject: [NSString stringWithFormat: @"%d", bindPort] + forKey: @"BindPort"]; } - - [self saveSettings]; - [self cancel: NULL]; } -@end /* @implementation PrefsController */ +- (void) setLimitUploadCheck: (id) sender +{ + BOOL checkUpload = [fUploadCheck state] == NSOnState; -@implementation PrefsController (Private) + [fDefaults setObject: checkUpload ? @"YES" : @"NO" + forKey: @"CheckUpload"]; + + [self setUploadLimit: sender]; + [fUploadField setEnabled: checkUpload]; +} + +- (void) setUploadLimit: (id) sender +{ + int uploadLimit = [fUploadField intValue]; + + //if value entered is not an int or is less than 0 do not change + if (![[fUploadField stringValue] isEqualToString: + [NSString stringWithFormat: @"%d", uploadLimit]] + || uploadLimit < 0) + { + NSBeep(); + uploadLimit = [fDefaults integerForKey: @"UploadLimit"]; + [fUploadField setIntValue: uploadLimit]; + } + else + { + [fDefaults setObject: [NSString stringWithFormat: @"%d", uploadLimit] + forKey: @"UploadLimit"]; + } + + if ([fUploadCheck state] == NSOffState || uploadLimit == 0) + uploadLimit = -1; + tr_setUploadLimit( fHandle, uploadLimit ); +} + +- (void) setQuitMessage: (id) sender +{ + [fDefaults setObject: ([fQuitCheck state] == NSOnState ? @"YES" : @"NO") + forKey: @"CheckQuit"]; +} + +- (void) setRemoveMessage: (id) sender +{ + [fDefaults setObject: ([fRemoveCheck state] == NSOnState ? @"YES" : @"NO") + forKey: @"CheckRemove"]; +} + +- (void) setDownloadLocation: (id) sender +{ + //Download folder + switch( [fFolderMatrix selectedRow] ) + { + case DOWNLOAD_FOLDER: + [fDefaults setObject: @"Constant" forKey: @"DownloadChoice"]; + break; + case DOWNLOAD_TORRENT: + [fDefaults setObject: @"Torrent" forKey: @"DownloadChoice"]; + break; + case DOWNLOAD_ASK: + [fDefaults setObject: @"Ask" forKey: @"DownloadChoice"]; + break; + } + [fFolderPopUp setEnabled: [fFolderMatrix selectedRow] == 0]; +} - (void) folderSheetShow: (id) sender { - NSOpenPanel * panel; - - panel = [NSOpenPanel openPanel]; + NSOpenPanel * panel = [NSOpenPanel openPanel]; [panel setPrompt: @"Select"]; [panel setAllowsMultipleSelection: NO]; @@ -215,146 +299,62 @@ contextInfo: NULL]; } -- (void) folderSheetClosed: (NSOpenPanel *) s returnCode: (int) code +@end // @implementation PrefsController + +@implementation PrefsController (Private) + +- (void) showGeneralPref: (id) sender +{ + [self setPrefView: fGeneralView]; +} + +- (void) showNetworkPref: (id) sender +{ + [self setPrefView: fNetworkView]; +} + +- (void) setPrefView: (NSView *) view +{ + NSRect windowRect = [fPrefsWindow frame]; + int difference = [view frame].size.height - [[fPrefsWindow contentView] frame].size.height; + + windowRect.origin.y -= difference; + windowRect.size.height += difference; + + [fPrefsWindow setTitle: [fToolbar selectedItemIdentifier]]; + [fPrefsWindow setContentView: fBlankView]; + [fPrefsWindow setFrame:windowRect display: YES animate: YES]; + [fPrefsWindow setContentView: view]; +} + +- (void) folderSheetClosed: (NSOpenPanel *) openPanel returnCode: (int) code contextInfo: (void *) info { [fFolderPopUp selectItemAtIndex: 0]; - if( code != NSOKButton ) - { + if (code != NSOKButton) return; - } [fDownloadFolder release]; - fDownloadFolder = [[s filenames] objectAtIndex: 0]; + fDownloadFolder = [[openPanel filenames] objectAtIndex: 0]; [fDownloadFolder retain]; + + [fDefaults setObject: fDownloadFolder forKey: @"DownloadFolder"]; [self updatePopUp]; } -/*********************************************************************** - * loadSettings - *********************************************************************** - * Update the interface with the current settings - **********************************************************************/ -- (void) loadSettings -{ - NSUserDefaults * defaults; - NSString * downloadChoice; - int uploadLimit; - - /* Fill with current settings */ - defaults = [NSUserDefaults standardUserDefaults]; - - /* Download folder selection */ - downloadChoice = [defaults stringForKey: @"DownloadChoice"]; - fDownloadFolder = [defaults stringForKey: @"DownloadFolder"]; - [fDownloadFolder retain]; - - if( [downloadChoice isEqualToString: @"Constant"] ) - { - [fFolderMatrix selectCellAtRow: 0 column: 0]; - } - else if( [downloadChoice isEqualToString: @"Torrent"] ) - { - [fFolderMatrix selectCellAtRow: 1 column: 0]; - } - else - { - [fFolderMatrix selectCellAtRow: 2 column: 0]; - } - [self ratio: NULL]; - [self updatePopUp]; - - [fPortField setIntValue: [defaults integerForKey: @"BindPort"]]; - - uploadLimit = [defaults integerForKey: @"UploadLimit"]; - if( uploadLimit < 0 ) - { - [fUploadCheck setState: NSOffState]; - } - else - { - [fUploadCheck setState: NSOnState]; - [fUploadField setIntValue: uploadLimit]; - } - [self check: NULL]; -} - -/*********************************************************************** - * saveSettings - *********************************************************************** - * - **********************************************************************/ -- (void) saveSettings -{ - NSUserDefaults * defaults; - int bindPort; - int uploadLimit; - - defaults = [NSUserDefaults standardUserDefaults]; - - /* Download folder */ - switch( [fFolderMatrix selectedRow] ) - { - case 0: - [defaults setObject: @"Constant" forKey: @"DownloadChoice"]; - break; - case 1: - [defaults setObject: @"Torrent" forKey: @"DownloadChoice"]; - break; - case 2: - [defaults setObject: @"Ask" forKey: @"DownloadChoice"]; - break; - } - [defaults setObject: fDownloadFolder forKey: @"DownloadFolder"]; - - /* Bind port */ - bindPort = [fPortField intValue]; - tr_setBindPort( fHandle, bindPort ); - [defaults setObject: [NSString stringWithFormat: @"%d", bindPort] - forKey: @"BindPort"]; - - /* Upload limit */ - if( [fUploadCheck state] == NSOnState ) - { - uploadLimit = [fUploadField intValue]; - } - else - { - uploadLimit = -1; - } - tr_setUploadLimit( fHandle, uploadLimit ); - [defaults setObject: [NSString stringWithFormat: @"%d", uploadLimit] - forKey: @"UploadLimit"]; -} - -/*********************************************************************** - * updatePopUp - *********************************************************************** - * Uses fDownloadFolder to update the displayed folder name and icon - **********************************************************************/ - (void) updatePopUp { NSMenuItem * menuItem; NSImage * image32, * image16; - /* Set up the pop up */ - [fFolderPopUp removeAllItems]; - [fFolderPopUp addItemWithTitle: @""]; - [[fFolderPopUp menu] addItem: [NSMenuItem separatorItem]]; - [fFolderPopUp addItemWithTitle: @"Other..."]; - - menuItem = (NSMenuItem *) [fFolderPopUp lastItem]; - [menuItem setTarget: self]; - [menuItem setAction: @selector( folderSheetShow: )]; - - /* Get the icon for the folder */ + // Get the icon for the folder image32 = [[NSWorkspace sharedWorkspace] iconForFile: fDownloadFolder]; image16 = [[NSImage alloc] initWithSize: NSMakeSize(16,16)]; - /* 32x32 -> 16x16 scaling */ + // 32x32 -> 16x16 scaling [image16 lockFocus]; [[NSGraphicsContext currentContext] setImageInterpolation: NSImageInterpolationHigh]; @@ -363,7 +363,7 @@ fraction: 1.0]; [image16 unlockFocus]; - /* Update the menu item */ + // Update the menu item menuItem = (NSMenuItem *) [fFolderPopUp itemAtIndex: 0]; [menuItem setTitle: [fDownloadFolder lastPathComponent]]; [menuItem setImage: image16]; diff --git a/macosx/TorrentTableView.h b/macosx/TorrentTableView.h index d9af14948..db9f83354 100644 --- a/macosx/TorrentTableView.h +++ b/macosx/TorrentTableView.h @@ -10,6 +10,9 @@ tr_stat_t * fStat; NSPoint fClickPoint; + + IBOutlet NSMenu * fContextRow; + IBOutlet NSMenu * fContextNoRow; } - (void) updateUI: (tr_stat_t *) stat; diff --git a/macosx/TorrentTableView.m b/macosx/TorrentTableView.m index e1fcf4882..859ccf35d 100644 --- a/macosx/TorrentTableView.m +++ b/macosx/TorrentTableView.m @@ -56,7 +56,6 @@ return rect; } - - (BOOL) pointInPauseRect: (NSPoint) point { return NSPointInRect( point, [self pauseRectForRow: @@ -114,15 +113,10 @@ point = [self convertPoint: [e locationInWindow] fromView: NULL]; row = [self rowAtPoint: point]; + + [self selectRowIndexes:[NSIndexSet indexSetWithIndex:row] byExtendingSelection:NO]; - if( row < 0 ) - { - return NULL; - } - - [self selectRowIndexes: [NSIndexSet indexSetWithIndex: row] - byExtendingSelection: NO]; - return [fController menuForIndex: row]; + return row >= 0 ? fContextRow : fContextNoRow; } - (void) drawRect: (NSRect) r diff --git a/macosx/Transmission.xcodeproj/project.pbxproj b/macosx/Transmission.xcodeproj/project.pbxproj index 0554b71e5..f2cdc29a4 100644 --- a/macosx/Transmission.xcodeproj/project.pbxproj +++ b/macosx/Transmission.xcodeproj/project.pbxproj @@ -18,6 +18,7 @@ 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 */; }; + 4D8CEF91095870E00063BAEA /* Network.png in Resources */ = {isa = PBXBuildFile; fileRef = 4D8CEF90095870E00063BAEA /* Network.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 */; }; @@ -84,6 +85,7 @@ 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 = ""; }; + 4D8CEF90095870E00063BAEA /* Network.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Network.png; path = Images/Network.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 = ""; }; @@ -194,6 +196,7 @@ 4DA6FDC3091141AD00450CB1 /* ResumeOff.png */, 4DA6FDC4091141AD00450CB1 /* ResumeOn.png */, 4D752E920913C949008EAAD4 /* Preferences.png */, + 4D8CEF90095870E00063BAEA /* Network.png */, 8D1107310486CEB800E47090 /* Info.plist */, 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */, 29B97318FDCFA39411CA2CEA /* MainMenu.nib */, @@ -283,6 +286,7 @@ 4DA6FDC5091141AD00450CB1 /* ResumeOff.png in Resources */, 4DA6FDC6091141AD00450CB1 /* ResumeOn.png in Resources */, 4D752E930913C949008EAAD4 /* Preferences.png in Resources */, + 4D8CEF91095870E00063BAEA /* Network.png in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -331,9 +335,7 @@ ppc, i386, ); - FRAMEWORK_SEARCH_PATHS = ( - "$(FRAMEWORK_SEARCH_PATHS)", - ); + FRAMEWORK_SEARCH_PATHS = "$(FRAMEWORK_SEARCH_PATHS)"; GCC_GENERATE_DEBUGGING_SYMBOLS = YES; GCC_OPTIMIZATION_LEVEL = 3; GCC_PRECOMPILE_PREFIX_HEADER = YES;