diff --git a/macosx/Badger.h b/macosx/Badger.h new file mode 100644 index 000000000..d06d9b2f1 --- /dev/null +++ b/macosx/Badger.h @@ -0,0 +1,31 @@ +// +// Badger.h +// Transmission +// +// Created by Mitchell Livingston on 12/21/05. +// + +#ifndef BADGER_H +#define BADGER_H + +#import + +@interface Badger : NSObject { + + NSImage * fBadge, * fDockIcon, * fBadgedDockIcon; + + NSDictionary * fBadgeAttributes, * fStringAttributes; + + NSColor * fUploadingColor, * fDownloadingColor; + + int fCompleted; +} + +- (void) updateBadgeWithCompleted: (int) completed + uploadRate: (NSString *) uploadRate + downloadRate: (NSString *) downloadRate; +- (void) clearBadge; + +@end + +#endif diff --git a/macosx/Badger.m b/macosx/Badger.m new file mode 100644 index 000000000..14926def3 --- /dev/null +++ b/macosx/Badger.m @@ -0,0 +1,190 @@ +// +// Badger.m +// Transmission +// +// Created by Mitchell Livingston on 12/21/05. +// + +#import "Badger.h" + +@interface Badger (Private) + +- (NSImage *) badgeWithNum: (int) num; + +@end + +@implementation Badger + +- (id) init +{ + if ((self = [super init])) + { + fBadge = [NSImage imageNamed: @"Badge"]; + fDockIcon = [[NSApp applicationIconImage] copy]; + fBadgedDockIcon = [fDockIcon copy]; + + fBadgeAttributes = [[NSDictionary dictionaryWithObjectsAndKeys: + [NSColor whiteColor], NSForegroundColorAttributeName, + [NSFont fontWithName: @"Helvetica-Bold" size: 24], NSFontAttributeName, + nil] retain]; + + fStringAttributes = [[NSDictionary dictionaryWithObjectsAndKeys: + [NSColor whiteColor], NSForegroundColorAttributeName, + [NSFont fontWithName: @"Helvetica-Bold" size: 20], NSFontAttributeName, + nil] retain]; + + fUploadingColor = [[[NSColor greenColor] colorWithAlphaComponent: 0.65] retain]; + fDownloadingColor = [[[NSColor blueColor] colorWithAlphaComponent: 0.65] retain]; + + fCompleted = 0; + } + + return self; +} + +- (void) dealloc +{ + [fDockIcon release]; + [fBadgedDockIcon release]; + + [fBadgeAttributes release]; + [fStringAttributes release]; + + [fUploadingColor release]; + [fDownloadingColor release]; + + [super dealloc]; +} + +- (void) updateBadgeWithCompleted: (int) completed + uploadRate: (NSString *) uploadRate + downloadRate: (NSString *) downloadRate +{ + NSImage * dockIcon; + NSSize iconSize = [fDockIcon size]; + + //set seeding and downloading badges if there was a change + if (completed != fCompleted) + { + fCompleted = completed; + + dockIcon = [fDockIcon copy]; + [dockIcon lockFocus]; + + //set completed badge to top right + if (completed > 0) + { + NSSize badgeSize = [fBadge size]; + [[self badgeWithNum: completed] + compositeToPoint: NSMakePoint(iconSize.width - badgeSize.width, + iconSize.height - badgeSize.height) + operation: NSCompositeSourceOver]; + } + + [dockIcon unlockFocus]; + + [fBadgedDockIcon release]; + fBadgedDockIcon = [dockIcon copy]; + } + else + dockIcon = [fBadgedDockIcon copy]; + + if (uploadRate || downloadRate) + { + //upload rate at bottom + float mainY = 5, + mainHeight = 25; + NSRect shapeRect = NSMakeRect(12.5, mainY, iconSize.width - 25, mainHeight); + + NSRect leftRect, rightRect; + leftRect.origin.x = 0; + leftRect.origin.y = mainY; + leftRect.size.width = shapeRect.origin.x * 2.0; + leftRect.size.height = mainHeight; + + rightRect = leftRect; + rightRect.origin.x = iconSize.width - rightRect.size.width; + + NSRect textRect; + textRect.origin.y = mainY; + textRect.size.height = mainHeight; + + [dockIcon lockFocus]; + + if (uploadRate) + { + float width = [uploadRate sizeWithAttributes: fStringAttributes].width; + textRect.origin.x = (iconSize.width - width) * 0.5; + textRect.size.width = width; + + NSBezierPath * uploadOval = [NSBezierPath bezierPathWithRect: shapeRect]; + [uploadOval appendBezierPathWithOvalInRect: leftRect]; + [uploadOval appendBezierPathWithOvalInRect: rightRect]; + + [fUploadingColor set]; + [uploadOval fill]; + [uploadRate drawInRect: textRect withAttributes: fStringAttributes]; + + //shift up for download rate if there is an upload rate + float heightDiff = 27; + shapeRect.origin.y += heightDiff; + leftRect.origin.y += heightDiff; + rightRect.origin.y += heightDiff; + textRect.origin.y += heightDiff; + } + + //download rate above upload rate + if (downloadRate) + { + float width = [downloadRate sizeWithAttributes: fStringAttributes].width; + textRect.origin.x = (iconSize.width - width) * 0.5; + textRect.size.width = width; + + NSBezierPath * downloadOval = [NSBezierPath bezierPathWithRect: shapeRect]; + [downloadOval appendBezierPathWithOvalInRect: leftRect]; + [downloadOval appendBezierPathWithOvalInRect: rightRect]; + + [fDownloadingColor set]; + [downloadOval fill]; + [downloadRate drawInRect: textRect withAttributes: fStringAttributes]; + } + + [dockIcon unlockFocus]; + } + + [NSApp setApplicationIconImage: dockIcon]; + [dockIcon release]; +} + +- (void) clearBadge +{ + [fBadgedDockIcon release]; + fBadgedDockIcon = [fDockIcon copy]; + + [NSApp setApplicationIconImage: fDockIcon]; +} + +@end + +@implementation Badger (Private) + +- (NSImage *) badgeWithNum: (int) num +{ + NSImage * badge = [[fBadge copy] autorelease]; + NSString * numString = [NSString stringWithFormat: @"%d", num]; + + //number is in center of image + NSRect badgeRect; + NSSize numSize = [numString sizeWithAttributes: fBadgeAttributes]; + badgeRect.size = [badge size]; + badgeRect.origin.x = (badgeRect.size.width - numSize.width) * 0.5; + badgeRect.origin.y = badgeRect.size.height * 0.5 - numSize.height * 1.2; + + [badge lockFocus]; + [numString drawInRect: badgeRect withAttributes: fBadgeAttributes]; + [badge unlockFocus]; + + return badge; +} + +@end diff --git a/macosx/Controller.h b/macosx/Controller.h index dcd56a30c..3a153e8e4 100644 --- a/macosx/Controller.h +++ b/macosx/Controller.h @@ -26,13 +26,14 @@ #import #import #import "PrefsController.h" +#import "Badger.h" @class TorrentTableView; @interface Controller : NSObject { tr_handle_t * fHandle; - int fCount; + int fCount, fCompleted; tr_stat_t * fStat; int fResumeOnWake[TR_MAX_TORRENT_COUNT]; @@ -74,6 +75,7 @@ NSUserDefaults * fDefaults; BOOL fHasGrowl; + Badger * fBadger; } - (void) advancedChanged: (id) sender; diff --git a/macosx/Controller.m b/macosx/Controller.m index c4231ac00..b152a0991 100644 --- a/macosx/Controller.m +++ b/macosx/Controller.m @@ -133,6 +133,10 @@ static void sleepCallBack( void * controller, io_service_t y, || [manager fileExistsAtPath: [[NSString stringWithFormat: @"~%@", GROWL_PATH] stringByExpandingTildeInPath]]; [self growlRegister: self]; + + //initialize badging + fBadger = [[Badger alloc] init]; + fCompleted = 0; //update the interface every 500 ms fCount = 0; @@ -143,6 +147,12 @@ static void sleepCallBack( void * controller, io_service_t y, forMode: NSEventTrackingRunLoopMode]; } +- (void) windowDidBecomeKey: (NSNotification *) n +{ + /* Reset the number of recently completed downloads */ + fCompleted = 0; +} + - (void) windowDidResize: (NSNotification *) n { [fTableView sizeToFit]; @@ -557,8 +567,10 @@ static void sleepCallBack( void * controller, io_service_t y, //Update the global DL/UL rates tr_torrentRates( fHandle, &dl, &ul ); - [fTotalDLField setStringValue: [NSString stringForSpeed: dl]]; - [fTotalULField setStringValue: [NSString stringForSpeed: ul]]; + NSString * downloadRate = [NSString stringForSpeed: dl]; + NSString * uploadRate = [NSString stringForSpeed: ul]; + [fTotalDLField setStringValue: downloadRate]; + [fTotalULField setStringValue: uploadRate]; //Update DL/UL totals in the Info panel row = [fTableView selectedRow]; @@ -575,11 +587,18 @@ static void sleepCallBack( void * controller, io_service_t y, { if( !tr_getFinished( fHandle, i ) ) continue; - + + fCompleted++; [self notifyGrowl: [NSString stringWithUTF8String: fStat[i].info.name]]; tr_setFinished( fHandle, i, 0 ); } + + //badge dock + NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults]; + [fBadger updateBadgeWithCompleted: [defaults boolForKey: @"BadgeCompleted"] ? fCompleted : 0 + uploadRate: ul >= 0.1 && [defaults boolForKey: @"BadgeUploadRate"] ? uploadRate : nil + downloadRate: dl >= 0.1 && [defaults boolForKey: @"BadgeDownloadRate"] ? downloadRate : nil]; } - (int) numberOfRowsInTableView: (NSTableView *) t diff --git a/macosx/Images/Badge.png b/macosx/Images/Badge.png new file mode 100644 index 000000000..03ead6e84 Binary files /dev/null and b/macosx/Images/Badge.png differ diff --git a/macosx/Transmission.xcodeproj/project.pbxproj b/macosx/Transmission.xcodeproj/project.pbxproj index a3c572800..c3c6a62f1 100644 --- a/macosx/Transmission.xcodeproj/project.pbxproj +++ b/macosx/Transmission.xcodeproj/project.pbxproj @@ -24,6 +24,8 @@ 4DA6FDC5091141AD00450CB1 /* ResumeOff.png in Resources */ = {isa = PBXBuildFile; fileRef = 4DA6FDC3091141AD00450CB1 /* ResumeOff.png */; }; 4DA6FDC6091141AD00450CB1 /* ResumeOn.png in Resources */ = {isa = PBXBuildFile; fileRef = 4DA6FDC4091141AD00450CB1 /* ResumeOn.png */; }; 4DE5CC9D0980656F00BE280E /* StringAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 4DE5CC9C0980656F00BE280E /* StringAdditions.m */; }; + 4DE5CCA70980735700BE280E /* Badger.m in Sources */ = {isa = PBXBuildFile; fileRef = 4DE5CCA60980735700BE280E /* Badger.m */; }; + 4DE5CCA90980739100BE280E /* Badge.png in Resources */ = {isa = PBXBuildFile; fileRef = 4DE5CCA80980739100BE280E /* Badge.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.png in Resources */ = {isa = PBXBuildFile; fileRef = 4DF7500708A103AD007B0D70 /* Open.png */; }; @@ -93,6 +95,9 @@ 4DA6FDC4091141AD00450CB1 /* ResumeOn.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = ResumeOn.png; path = Images/ResumeOn.png; sourceTree = ""; }; 4DE5CC9B0980656F00BE280E /* StringAdditions.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = StringAdditions.h; sourceTree = ""; }; 4DE5CC9C0980656F00BE280E /* StringAdditions.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = StringAdditions.m; sourceTree = ""; }; + 4DE5CCA50980735700BE280E /* Badger.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = Badger.h; sourceTree = ""; }; + 4DE5CCA60980735700BE280E /* Badger.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = Badger.m; sourceTree = ""; }; + 4DE5CCA80980739100BE280E /* Badge.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Badge.png; path = Images/Badge.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; }; @@ -132,6 +137,8 @@ 4D364D9F091FBB2C00377D12 /* TorrentTableView.m */, 4DE5CC9B0980656F00BE280E /* StringAdditions.h */, 4DE5CC9C0980656F00BE280E /* StringAdditions.m */, + 4DE5CCA50980735700BE280E /* Badger.h */, + 4DE5CCA60980735700BE280E /* Badger.m */, ); name = Classes; sourceTree = ""; @@ -202,6 +209,7 @@ 4DA6FDC4091141AD00450CB1 /* ResumeOn.png */, 4D752E920913C949008EAAD4 /* Preferences.png */, 4D8CEF90095870E00063BAEA /* Network.png */, + 4DE5CCA80980739100BE280E /* Badge.png */, 8D1107310486CEB800E47090 /* Info.plist */, 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */, 29B97318FDCFA39411CA2CEA /* MainMenu.nib */, @@ -292,6 +300,7 @@ 4DA6FDC6091141AD00450CB1 /* ResumeOn.png in Resources */, 4D752E930913C949008EAAD4 /* Preferences.png in Resources */, 4D8CEF91095870E00063BAEA /* Network.png in Resources */, + 4DE5CCA90980739100BE280E /* Badge.png in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -309,6 +318,7 @@ 4D118E1A08CB46B20033958F /* PrefsController.m in Sources */, 4D364DA0091FBB2C00377D12 /* TorrentTableView.m in Sources */, 4DE5CC9D0980656F00BE280E /* StringAdditions.m in Sources */, + 4DE5CCA70980735700BE280E /* Badger.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; };