From d26b3a470445d99026903832a3936f4f5c2018cd Mon Sep 17 00:00:00 2001 From: Ugo Landini Date: Sun, 7 Jun 2009 17:24:27 +0200 Subject: [PATCH] 0.21 - ticking sound and global hotkeys added --- About.xib | 21 ++++---- English.lproj/MainMenu.xib | 74 +++++++++++++++++++++++++--- Pomodoro.m | 8 +-- PomodoroController.h | 7 +++ PomodoroController.m | 76 ++++++++++++++++++++++++++++- README.txt | 2 + SoundsLicense.txt | 1 + Stats.xib | 26 +++++++--- StatsController.m | 1 - pomodoro.xcodeproj/project.pbxproj | 8 +++ tick.wav | Bin 0 -> 8010 bytes 11 files changed, 195 insertions(+), 29 deletions(-) create mode 100644 SoundsLicense.txt create mode 100644 tick.wav diff --git a/About.xib b/About.xib index be04711..a97a21c 100644 --- a/About.xib +++ b/About.xib @@ -8,7 +8,6 @@ 353.00 YES - YES @@ -39,14 +38,14 @@ 8211 2 - {{196, 164}, {496, 346}} + {{196, 116}, {496, 394}} 603979776 About Pomodoro NSPanel {3.40282e+38, 3.40282e+38} - + 256 YES @@ -65,7 +64,7 @@ NeXT TIFF v4.0 pasteboard type - {{15, 208}, {97, 152}} + {{15, 256}, {97, 152}} YES @@ -85,19 +84,20 @@ 268 - {{117, 20}, {362, 306}} + {{117, 34}, {362, 340}} YES 68288064 1279263744 - UG9tb2Rvcm8gRGVza3RvcCDigKhyZWxlYXNlIDAuMjAg4oCoMDUvMDYvMjAwOeKAqCAK4oCoQ3JlYXRl + UG9tb2Rvcm8gRGVza3RvcCDigKhyZWxlYXNlIDAuMjEg4oCoMDcvMDYvMjAwOeKAqCAK4oCoQ3JlYXRl ZCBieSBVZ28gTGFuZGluaeKAqGh0dHA6Ly9wb21vZG9yby51Z29sYW5kaW5pLmNvbeKAqApjb3B5cmln aHQgKGMpIDIwMDksIFVnbyBMYW5kaW5pCgpncmFwaGljIGRlc2lnbiBieSBTdGVmYW5vIExpbmd1ZXJy aQpodHRwOi8vd3d3LmVsamVrby5uZXQvCgpyZWxlYXNlZCB1bmRlciBCU0QgTGljZW5zZQpUaGlzIHNv ZnR3YXJlIGNvbnRhaW5zIHBvcnRpb25zIG9mIEJTRCBsaWNlbnNlZCBjb2RlOgpHcm93bCBmcmFtZXdv cms6IGh0dHA6Ly9ncm93bC5pbmZvLwpCR0h1ZCBBcHBraXQ6IGh0dHA6Ly9jb2RlLmdvb2dsZS5jb20v -cC9iZ2h1ZGFwcGtpdC8KA +cC9iZ2h1ZGFwcGtpdC8KClRoaXMgc29mdHdhcmUgY29udGFpbnMgc291bmRzIGZyb206Cmh0dHA6Ly93 +d3cuZnJlZXNvdW5kLm9yZw LucidaGrande 1.300000e+01 @@ -121,8 +121,7 @@ cC9iZ2h1ZGFwcGtpdC8KA gradientTheme - {496, 346} - + {496, 394} {{0, 0}, {1280, 778}} {3.40282e+38, 3.40282e+38} @@ -240,9 +239,9 @@ cC9iZ2h1ZGFwcGtpdC8KA com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilderKit com.apple.InterfaceBuilderKit - {{100, 218}, {496, 346}} + {{100, 170}, {496, 394}} com.apple.InterfaceBuilder.CocoaPlugin - {{100, 218}, {496, 346}} + {{100, 170}, {496, 394}} com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin diff --git a/English.lproj/MainMenu.xib b/English.lproj/MainMenu.xib index 0ee48af..fa3b5d8 100644 --- a/English.lproj/MainMenu.xib +++ b/English.lproj/MainMenu.xib @@ -103,6 +103,29 @@ gradientTheme + + + 268 + {{15, 193}, {231, 18}} + + YES + + -2080244224 + 0 + Ticking sound + + + 1211912703 + 2 + + + + + 200 + 25 + gradientTheme + + 268 @@ -1738,7 +1761,8 @@ Start pomodoro - + 75yAA + 1572864 2147483647 @@ -1747,7 +1771,8 @@ YES Reset pomodoro - + 75yBA + 1572864 2147483647 @@ -1766,7 +1791,8 @@ YES Interrupt pomodoro - + 75yCA + 1572864 2147483647 @@ -1775,7 +1801,8 @@ YES Resume pomodoro - + 75yDA + 1572864 2147483647 @@ -2979,6 +3006,22 @@ 709 + + + value: values.tickEnabled + + + + + + value: values.tickEnabled + value + values.tickEnabled + 2 + + + 713 + @@ -3100,6 +3143,7 @@ + @@ -3981,6 +4025,20 @@ + + 710 + + + YES + + + + + + 711 + + + @@ -4123,6 +4181,8 @@ 658.IBPluginDependency 659.IBPluginDependency 660.IBPluginDependency + 710.IBPluginDependency + 711.IBPluginDependency YES @@ -4235,7 +4295,7 @@ com.binarymethod.BGHUDAppKitPlugin com.binarymethod.BGHUDAppKitPlugin com.binarymethod.BGHUDAppKitPlugin - {{390, 520}, {191, 203}} + {{390, 520}, {233, 203}} com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin @@ -4272,6 +4332,8 @@ com.binarymethod.BGHUDAppKitPlugin com.binarymethod.BGHUDAppKitPlugin com.binarymethod.BGHUDAppKitPlugin + com.binarymethod.BGHUDAppKitPlugin + com.binarymethod.BGHUDAppKitPlugin @@ -4294,7 +4356,7 @@ - 709 + 713 diff --git a/Pomodoro.m b/Pomodoro.m index 3709fdf..6eaf840 100644 --- a/Pomodoro.m +++ b/Pomodoro.m @@ -126,16 +126,16 @@ - (void) checkIfBreakFinished { - (void)oncePersecond:(NSTimer *)aTimer { - //time--; - time=time-60; + time--; + //time=time-60; [delegate oncePerSecond:time]; [self checkIfFinished]; } - (void)oncePersecondBreak:(NSTimer *)aTimer { - //time--; - time=time-60; + time--; + //time=time-60; [delegate oncePerSecondBreak:time]; [self checkIfBreakFinished]; } diff --git a/PomodoroController.h b/PomodoroController.h index 043d998..5469747 100644 --- a/PomodoroController.h +++ b/PomodoroController.h @@ -59,6 +59,7 @@ NSImage* pomodoroImage; NSSound* ringing; + NSSound* tick; NSSpeechSynthesizer* speech; GrowlNotifier* growl; @@ -67,6 +68,12 @@ } +@property (nonatomic, readonly) NSMenuItem* startPomodoro; +@property (nonatomic, readonly) NSMenuItem* interruptPomodoro; +@property (nonatomic, readonly) NSMenuItem* invalidatePomodoro; +@property (nonatomic, readonly) NSMenuItem* resumePomodoro; + + -(void) pomodoroStarted; -(IBAction) about:(id)sender; diff --git a/PomodoroController.m b/PomodoroController.m index f46d10b..80101e2 100644 --- a/PomodoroController.m +++ b/PomodoroController.m @@ -29,10 +29,39 @@ #import "PomodoroStats.h" #import "AboutController.h" #import "StatsController.h" +#import "Carbon/Carbon.h" +#pragma mark ---- HotKey Function (Carbon) ---- + +OSStatus hotKey(EventHandlerCallRef nextHandler,EventRef anEvent, + void *userData) +{ + + PomodoroController* controller = (PomodoroController*)userData; + + EventHotKeyID hkRef; + GetEventParameter(anEvent,kEventParamDirectObject,typeEventHotKeyID,NULL,sizeof(hkRef),NULL,&hkRef); + + switch (hkRef.id) { + case 1: + if ([controller.startPomodoro isEnabled]) [controller start:nil]; + break; + case 2: + if ([controller.invalidatePomodoro isEnabled]) [controller reset:nil]; + break; + case 3: + if ([controller.interruptPomodoro isEnabled]) [controller interrupt:nil]; + break; + case 4: + if ([controller.resumePomodoro isEnabled]) [controller resume:nil]; + break; + } + return noErr; +} @implementation PomodoroController +@synthesize startPomodoro, invalidatePomodoro, interruptPomodoro, resumePomodoro; #pragma mark ---- Helper methods ---- @@ -44,6 +73,39 @@ - (BOOL) checkDefault:(NSString*) property { return [[[NSUserDefaults standardUserDefaults] objectForKey:property] boolValue]; } + +- (void) installGlobalHotKeyHandler { + + EventHotKeyRef gMyHotKeyRef; + + EventHotKeyID startKey; + EventHotKeyID resetKey; + EventHotKeyID interruptKey; + EventHotKeyID resumeKey; + + EventTypeSpec eventType; + eventType.eventClass=kEventClassKeyboard; + eventType.eventKind=kEventHotKeyPressed; + InstallApplicationEventHandler(&hotKey,1,&eventType,self, NULL); + + startKey.signature='strt'; + startKey.id=1; + RegisterEventHotKey(0x7E, cmdKey+optionKey, startKey, + GetApplicationEventTarget(), 0, &gMyHotKeyRef); + resetKey.signature='rest'; + resetKey.id=2; + RegisterEventHotKey(0x7D, cmdKey+optionKey, resetKey, + GetApplicationEventTarget(), 0, &gMyHotKeyRef); + interruptKey.signature='intr'; + interruptKey.id=3; + RegisterEventHotKey(0x7B, cmdKey+optionKey, interruptKey, + GetApplicationEventTarget(), 0, &gMyHotKeyRef); + resumeKey.signature='resu'; + resumeKey.id=4; + RegisterEventHotKey(0x7C, cmdKey+optionKey, resumeKey, + GetApplicationEventTarget(), 0, &gMyHotKeyRef); +} + #pragma mark ---- Voice combo box delegate/datasource methods ---- - (NSInteger)numberOfItemsInComboBox:(NSComboBox *)aComboBox { @@ -271,10 +333,16 @@ -(void) pomodoroFinished { - (void) oncePerSecondBreak:(NSInteger) time { [self showTimeOnStatusBar: time]; + if ([self checkDefault:@"tickEnabled"]) { + [tick play]; + } } - (void) oncePerSecond:(NSInteger) time { [self showTimeOnStatusBar: time]; + if ([self checkDefault:@"tickEnabled"]) { + [tick play]; + } NSInteger timePassed = (_initialTime*60) - time; NSString* timePassedString = [NSString stringWithFormat:@"%d", timePassed/60]; NSString* timeString = [NSString stringWithFormat:@"%d", time/60]; @@ -344,6 +412,7 @@ + (void)initialize [defaultValues setObject:[NSNumber numberWithBool:NO] forKey:@"speechAtResumeEnabled"]; [defaultValues setObject:[NSNumber numberWithBool:YES] forKey:@"ringAtEndEnabled"]; [defaultValues setObject:[NSNumber numberWithBool:YES] forKey:@"ringAtBreakEnabled"]; + [defaultValues setObject:[NSNumber numberWithBool:YES] forKey:@"tickEnabled"]; [defaultValues setObject:[NSNumber numberWithBool:YES] forKey:@"growlAtEndEnabled"]; [defaultValues setObject:[NSNumber numberWithBool:YES] forKey:@"speechAtEndEnabled"]; [defaultValues setObject:[NSNumber numberWithBool:NO] forKey:@"growlAtEveryEnabled"]; @@ -381,6 +450,7 @@ - (void)awakeFromNib { NSBundle *bundle = [NSBundle mainBundle]; pomodoroImage = [[NSImage alloc] initWithContentsOfFile:[bundle pathForResource:@"pomodoro" ofType:@"png"]]; ringing = [NSSound soundNamed:@"ring.wav"]; + tick = [NSSound soundNamed:@"tick.wav"]; [statusItem setImage:pomodoroImage]; //[statusItem setAlternateImage:pomodoroImage]; alternate image speech = [[NSSpeechSynthesizer alloc] init]; @@ -405,8 +475,11 @@ - (void)awakeFromNib { pomoStats = [[PomodoroStats alloc] init]; stats = [[StatsController alloc] init]; [stats window]; - [pomodoro setDelegate: self]; + [self installGlobalHotKeyHandler]; + + [pomodoro setDelegate: self]; + } -(void)dealloc { @@ -429,6 +502,7 @@ -(void)dealloc { [pomodoroImage release]; [ringing release]; + [tick release]; [speech release]; [growl release]; diff --git a/README.txt b/README.txt index b11e6f9..7cddc88 100644 --- a/README.txt +++ b/README.txt @@ -48,6 +48,8 @@ This code is released under BSD license (see License.txt for details) and contai Growl framework: http://growl.info/ BGHud Appkit: http://code.google.com/p/bghudappkit/ +Sound samples come from http://www.freesound.org and are licensed under Creative Commons http://creativecommons.org/licenses/sampling+/1.0/ + ------- Thanks ------- diff --git a/SoundsLicense.txt b/SoundsLicense.txt new file mode 100644 index 0000000..e19d10d --- /dev/null +++ b/SoundsLicense.txt @@ -0,0 +1 @@ +http://creativecommons.org/licenses/sampling+/1.0/ diff --git a/Stats.xib b/Stats.xib index f167f2a..c19e88f 100644 --- a/Stats.xib +++ b/Stats.xib @@ -8,7 +8,6 @@ 353.00 YES - YES @@ -55,12 +54,13 @@ 12 {{13, 10}, {476, 426}} + YES 2 - + 256 YES @@ -69,6 +69,7 @@ 268 {{102, 267}, {122, 17}} + YES 68288064 @@ -101,6 +102,7 @@ 268 {{264, 267}, {38, 17}} + YES 68288064 @@ -118,6 +120,7 @@ 268 {{102, 242}, {122, 17}} + YES 68288064 @@ -135,6 +138,7 @@ 268 {{264, 242}, {38, 17}} + YES 68288064 @@ -152,6 +156,7 @@ 268 {{167, 45}, {98, 32}} + YES 67239424 @@ -178,6 +183,7 @@ 268 {{102, 217}, {122, 17}} + YES 68288064 @@ -195,6 +201,7 @@ 268 {{264, 217}, {38, 17}} + YES 68288064 @@ -212,6 +219,7 @@ 268 {{102, 192}, {160, 17}} + YES 68288064 @@ -229,6 +237,7 @@ 268 {{264, 192}, {38, 17}} + YES 68288064 @@ -246,6 +255,7 @@ 268 {{102, 167}, {131, 17}} + YES 68288064 @@ -263,6 +273,7 @@ 268 {{264, 167}, {38, 17}} + YES 68288064 @@ -292,6 +303,7 @@ {{17, 308}, {55, 74}} + YES 130560 @@ -309,6 +321,8 @@ {{10, 33}, {456, 380}} + + Session stats @@ -317,7 +331,7 @@ Item 2 - + 256 YES @@ -586,14 +600,13 @@ {{10, 33}, {456, 380}} - Backlog - + 0 YES @@ -601,12 +614,13 @@ gradientTheme YES - + {502, 442} + {{0, 0}, {1280, 778}} {3.40282e+38, 3.40282e+38} diff --git a/StatsController.m b/StatsController.m index 4265cfb..55abed8 100644 --- a/StatsController.m +++ b/StatsController.m @@ -142,7 +142,6 @@ - (id) init { - (void)awakeFromNib { - NSLog(@"Stats awakw from nib"); NSSortDescriptor* sort = [[NSSortDescriptor alloc] initWithKey:@"when" ascending:NO]; [pomos setSortDescriptors: diff --git a/pomodoro.xcodeproj/project.pbxproj b/pomodoro.xcodeproj/project.pbxproj index abe160b..7e1d2da 100644 --- a/pomodoro.xcodeproj/project.pbxproj +++ b/pomodoro.xcodeproj/project.pbxproj @@ -12,6 +12,8 @@ 692148BE0FCF47800016B59D /* Growl.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 692148B50FCF47740016B59D /* Growl.framework */; }; 69509E380FCFF56A00BD19B5 /* Pomodoros.xcdatamodel in Sources */ = {isa = PBXBuildFile; fileRef = 69509E370FCFF56A00BD19B5 /* Pomodoros.xcdatamodel */; }; 69563A700FC9CC83004AC179 /* PomodoroStats.m in Sources */ = {isa = PBXBuildFile; fileRef = 69563A6F0FC9CC83004AC179 /* PomodoroStats.m */; }; + 6968AF120FD950AE00028CE6 /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6968AF110FD950AE00028CE6 /* Carbon.framework */; }; + 6986976E0FDB3B03006281E8 /* tick.wav in Resources */ = {isa = PBXBuildFile; fileRef = 6986976D0FDB3B03006281E8 /* tick.wav */; }; 69885A480FD3FD770020D132 /* BGHUDAppKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 69885A470FD3FD770020D132 /* BGHUDAppKit.framework */; }; 69885A6B0FD3FD8B0020D132 /* BGHUDAppKit.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 69885A470FD3FD770020D132 /* BGHUDAppKit.framework */; }; 69B1A3740FC96D22009B0ACF /* GrowlNotifier.m in Sources */ = {isa = PBXBuildFile; fileRef = 69B1A36D0FC96D22009B0ACF /* GrowlNotifier.m */; }; @@ -57,6 +59,8 @@ 69509E370FCFF56A00BD19B5 /* Pomodoros.xcdatamodel */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = wrapper.xcdatamodel; path = Pomodoros.xcdatamodel; sourceTree = ""; }; 69563A6E0FC9CC83004AC179 /* PomodoroStats.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PomodoroStats.h; sourceTree = ""; }; 69563A6F0FC9CC83004AC179 /* PomodoroStats.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PomodoroStats.m; sourceTree = ""; }; + 6968AF110FD950AE00028CE6 /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = /System/Library/Frameworks/Carbon.framework; sourceTree = ""; }; + 6986976D0FDB3B03006281E8 /* tick.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; path = tick.wav; sourceTree = ""; }; 69885A470FD3FD770020D132 /* BGHUDAppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = BGHUDAppKit.framework; sourceTree = ""; }; 69B1A36C0FC96D22009B0ACF /* GrowlNotifier.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrowlNotifier.h; sourceTree = ""; }; 69B1A36D0FC96D22009B0ACF /* GrowlNotifier.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GrowlNotifier.m; sourceTree = ""; }; @@ -87,6 +91,7 @@ 8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */, 692148B60FCF47740016B59D /* Growl.framework in Frameworks */, 69885A480FD3FD770020D132 /* BGHUDAppKit.framework in Frameworks */, + 6968AF120FD950AE00028CE6 /* Carbon.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -106,6 +111,7 @@ 1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */ = { isa = PBXGroup; children = ( + 6968AF110FD950AE00028CE6 /* Carbon.framework */, 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */, ); name = "Linked Frameworks"; @@ -155,6 +161,7 @@ 29B97317FDCFA39411CA2CEA /* Resources */ = { isa = PBXGroup; children = ( + 6986976D0FDB3B03006281E8 /* tick.wav */, 69B1A37C0FC96D6E009B0ACF /* icon.icns */, 69B1A37D0FC96D6E009B0ACF /* pomodoro.png */, 69B1A37A0FC96D63009B0ACF /* ring.wav */, @@ -264,6 +271,7 @@ 69B1A37F0FC96D6E009B0ACF /* pomodoro.png in Resources */, 69B1A3820FC96D96009B0ACF /* About.xib in Resources */, 69B1A3830FC96D96009B0ACF /* Stats.xib in Resources */, + 6986976E0FDB3B03006281E8 /* tick.wav in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/tick.wav b/tick.wav new file mode 100644 index 0000000000000000000000000000000000000000..e9c2e43ed3332acbe1458049388d3e5431dd442b GIT binary patch literal 8010 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