From 93f1f93114865d18b5870f04763c921ff33b8e59 Mon Sep 17 00:00:00 2001 From: Alexander Strange Date: Sun, 1 Dec 2002 06:58:09 +0000 Subject: [PATCH] Initial revision --- English.lproj/InfoPlist.strings | Bin 0 -> 502 bytes English.lproj/KeyCodes.plist | 102 ++++ English.lproj/MainMenu.nib/classes.nib | 8 + English.lproj/MainMenu.nib/info.nib | 10 + English.lproj/MainMenu.nib/objects.nib | Bin 0 -> 365 bytes English.lproj/Preferences.nib/classes.nib | 46 ++ English.lproj/Preferences.nib/info.nib | 26 + English.lproj/Preferences.nib/objects.nib | Bin 0 -> 8624 bytes English.lproj/StatusWindow.nib/classes.nib | 13 + English.lproj/StatusWindow.nib/info.nib | 14 + English.lproj/StatusWindow.nib/objects.nib | Bin 0 -> 962 bytes HotKeyCenter.h | 34 ++ HotKeyCenter.m | 344 +++++++++++ KeyBroadcaster.h | 9 + KeyBroadcaster.m | 58 ++ KeyCombo.h | 41 ++ KeyCombo.m | 215 +++++++ MTApplication.h | 8 + MTApplication.m | 12 + MenuInverted.tiff | Bin 0 -> 16364 bytes MenuNormal.tiff | Bin 0 -> 16606 bytes MenuTunes.h | 54 ++ MenuTunes.m | 677 +++++++++++++++++++++ MenuTunesView.h | 16 + MenuTunesView.m | 44 ++ PreferencesController.h | 52 ++ PreferencesController.m | 546 +++++++++++++++++ StatusWindow.h | 8 + StatusWindow.m | 18 + StatusWindowController.h | 15 + StatusWindowController.m | 50 ++ main.m | 14 + submenu.tiff | Bin 0 -> 1500 bytes 33 files changed, 2434 insertions(+) create mode 100755 English.lproj/InfoPlist.strings create mode 100755 English.lproj/KeyCodes.plist create mode 100755 English.lproj/MainMenu.nib/classes.nib create mode 100755 English.lproj/MainMenu.nib/info.nib create mode 100755 English.lproj/MainMenu.nib/objects.nib create mode 100755 English.lproj/Preferences.nib/classes.nib create mode 100755 English.lproj/Preferences.nib/info.nib create mode 100755 English.lproj/Preferences.nib/objects.nib create mode 100755 English.lproj/StatusWindow.nib/classes.nib create mode 100755 English.lproj/StatusWindow.nib/info.nib create mode 100755 English.lproj/StatusWindow.nib/objects.nib create mode 100755 HotKeyCenter.h create mode 100755 HotKeyCenter.m create mode 100755 KeyBroadcaster.h create mode 100755 KeyBroadcaster.m create mode 100755 KeyCombo.h create mode 100755 KeyCombo.m create mode 100755 MTApplication.h create mode 100755 MTApplication.m create mode 100755 MenuInverted.tiff create mode 100755 MenuNormal.tiff create mode 100755 MenuTunes.h create mode 100755 MenuTunes.m create mode 100755 MenuTunesView.h create mode 100755 MenuTunesView.m create mode 100755 PreferencesController.h create mode 100755 PreferencesController.m create mode 100755 StatusWindow.h create mode 100755 StatusWindow.m create mode 100755 StatusWindowController.h create mode 100755 StatusWindowController.m create mode 100755 main.m create mode 100755 submenu.tiff diff --git a/English.lproj/InfoPlist.strings b/English.lproj/InfoPlist.strings new file mode 100755 index 0000000000000000000000000000000000000000..d7798ba7f3c8daf702d4a736edb11f27477f2082 GIT binary patch literal 502 zcmb7=OAoaGiT13%T+a% zWof957V@-Gtg{07hO@-ysH=|lpnY(G z#_+F+mf9R!Bkn2nslf#F+G-W_xr&&UF=a-_Taim9LtU!y3#X+9O6woEdoboNNB=Qa znChTxi|@OR5S{l;#5jtKu7sCswwCLN`;2MTr;;(1KF-(VQ%$IN?8(#I?5c?Ov9+Q~ L6MuCzf1P~;qsLZk literal 0 HcmV?d00001 diff --git a/English.lproj/KeyCodes.plist b/English.lproj/KeyCodes.plist new file mode 100755 index 0000000..7856ab6 --- /dev/null +++ b/English.lproj/KeyCodes.plist @@ -0,0 +1,102 @@ +{ + 0 = "A"; + 1 = "S"; + 2 = "D"; + 3 = "F"; + 4 = "H"; + 5 = "G"; + 6 = "Z"; + 7 = "X"; + 8 = "C"; + 9 = "V"; + 11 = "B"; + 12 = "Q"; + 13 = "W"; + 14 = "E"; + 15 = "R"; + 16 = "Y"; + 17 = "T"; + 18 = "1"; + 19 = "2"; + 20 = "3"; + 21 = "4"; + 22 = "6"; + 23 = "5"; + 24 = "="; + 25 = "9"; + 26 = "7"; + 27 = "-"; + 28 = "8"; + 29 = "0"; + 30 = "\]"; + 31 = "O"; + 32 = "U"; + 33 = "\["; + 34 = "I"; + 35 = "P"; + 36 = "Return"; + 37 = "L"; + 38 = "J"; + 39 = "\'"; + 40 = "K"; + 41 = ";"; + 42 = "\\"; + 43 = "Pad ,"; + 44 = "Pad /"; + 45 = "N"; + 46 = "M"; + 47 = "Pad ."; + 48 = "Tab"; + 49 = "Space"; + 50 = "\`"; + 51 = "Delete"; + 53 = "ESC"; + 55 = "Command"; + 56 = "Shift"; + 57 = "Caps Lock"; + 58 = "Option"; + 59 = "Control"; + 65 = "."; + 67 = "Pad *"; + 69 = "Pad +"; + 71 = "Clear"; + 75 = "\/"; + 76 = "Pad Enter"; + 78 = "Pad -"; + 81 = "Pad ="; + 82 = "Pad 0"; + 83 = "Pad 1"; + 84 = "Pad 2"; + 85 = "Pad 3"; + 86 = "Pad 4"; + 87 = "Pad 5"; + 88 = "Pad 6"; + 89 = "Pad 7"; + 91 = "Pad 8"; + 92 = "Pad 9"; + 96 = "F5"; + 97 = "F6"; + 98 = "F7"; + 99 = "F3"; + 100 = "F8"; + 101 = "F9"; + 103 = "F11"; + 105 = "F13"; + 107 = "F14"; + 109 = "F10"; + 111 = "F12"; + 113 = "F15"; + 114 = "Ins"; + 115 = "Home"; + 116 = "Page Up"; + 117 = "Del"; + 118 = "F4"; + 119 = "End"; + 120 = "F2"; + 121 = "Page Down"; + 122 = "F1"; + 123 = "Left Arrow"; + 124 = "Right Arrow"; + 125 = "Down Arrow"; + 126 = "Up Arrow"; +} \ No newline at end of file diff --git a/English.lproj/MainMenu.nib/classes.nib b/English.lproj/MainMenu.nib/classes.nib new file mode 100755 index 0000000..a5a8984 --- /dev/null +++ b/English.lproj/MainMenu.nib/classes.nib @@ -0,0 +1,8 @@ +{ + IBClasses = ( + {CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; }, + {CLASS = MTApplication; LANGUAGE = ObjC; SUPERCLASS = NSApplication; }, + {CLASS = MenuTunes; LANGUAGE = ObjC; SUPERCLASS = NSObject; } + ); + IBVersion = 1; +} \ No newline at end of file diff --git a/English.lproj/MainMenu.nib/info.nib b/English.lproj/MainMenu.nib/info.nib new file mode 100755 index 0000000..9b3242d --- /dev/null +++ b/English.lproj/MainMenu.nib/info.nib @@ -0,0 +1,10 @@ + + + + + IBFramework Version + 286.0 + IBSystem Version + 6F21 + + diff --git a/English.lproj/MainMenu.nib/objects.nib b/English.lproj/MainMenu.nib/objects.nib new file mode 100755 index 0000000000000000000000000000000000000000..50ca73ea5d51fac346114de49e8875087163935c GIT binary patch literal 365 zcmYL^O-{ow5QXg)L=ojr;sERr;u2|#Ds|CxkzT-UN0lM5BgawIWu&POP$f}}*ySp~ zmMgI27&xSY-P4mI%KzR zkx`mx8MWqpjvcRAby%Pk7i*d$eK?=a2XP$oCDU9AvolCJEwL=Is!pdM(s?Q%aeiSt zW#QH@{a~7Ewg};HrKQI6JKG3QrnE0jeE**VO%RV=tP?JCLd@a6!=ZsvZ0nE{ zKWJrcX+zxjfzKDSREMBPQV3U8s*HU8)_0RX?>fh>=Xr&7Pxn?3!kTF?#_V^t>ss@I h);1$=b)AuSCq69M<7;&_8II&qvaw + + + + IBDocumentLocation + 61 9 356 240 0 0 1152 746 + IBFramework Version + 286.0 + IBGroupedObjects + + 0 + + 123 + 124 + + + IBLastGroupID + 1 + IBOpenObjects + + 6 + + IBSystem Version + 6F21 + + diff --git a/English.lproj/Preferences.nib/objects.nib b/English.lproj/Preferences.nib/objects.nib new file mode 100755 index 0000000000000000000000000000000000000000..bbf55df183a76d19a2309f9544fdc5f8273571c6 GIT binary patch literal 8624 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 + + + + IBFramework Version + 286.0 + IBOpenObjects + + 5 + + IBSystem Version + 6F21 + + diff --git a/English.lproj/StatusWindow.nib/objects.nib b/English.lproj/StatusWindow.nib/objects.nib new file mode 100755 index 0000000000000000000000000000000000000000..6ea0e70cc9b6e681aa58b3724db3470642c75e48 GIT binary patch literal 962 zcmYjQL2DC16rM@8+cu5bN)@aZ5kbU@D0nC$n?xdox-H$J2l24kOzP-nx9mtGd&8#T@a-7Is9w0xX$Uo*{9PLu#E+`0YG-s}BW3Ek&3m0`48qKG55jEn$mJ z_>6l^zgzP?5%{jl0vSu7yf{SsifQe$BT?tfZC(LbHmw=#7JzPAPdV#igs43h9{OIB z1*D(A`y@^Ylns1ZvnS{Dq$BKx%PK)&9}|E@)5^nsN|RK^aZHyZ`rwEV^-bdZm1*xH zA!Ll@cx^qR8@sB$v73zZI%wQ+3_@{3xG=}J#$5LyKpC4jg8QEOzc!_XzH8;Pz z2%Y1gY>nlAJ()nQ7SPTZpqICZOyEOWP^fj&GI?XCBV2~EcplypKZwYe>CM5)|J+*t zbf8+4!?}Ba(!nR>i20Q~KXw09(p1NL#0PY8x-c2*laGs&bZPQ=IW46mLHz1gwdOm% NT@TPgT|am& + +#import "KeyCombo.h" + +@interface HotKeyCenter : NSObject +{ + BOOL mEnabled; + NSMutableDictionary *mHotKeys; +} + ++ (id)sharedCenter; + +- (BOOL)addHotKey:(NSString *)name combo:(KeyCombo *)combo target:(id)target action:(SEL)action; +- (void)removeHotKey:(NSString *)name; + +- (NSArray *)allNames; +- (KeyCombo *)keyComboForName:(NSString *)name; + +- (void)setEnabled:(BOOL)enabled; +- (BOOL)enabled; + +- (void)sendEvent:(NSEvent *)event; + +@end diff --git a/HotKeyCenter.m b/HotKeyCenter.m new file mode 100755 index 0000000..9698fdb --- /dev/null +++ b/HotKeyCenter.m @@ -0,0 +1,344 @@ +// +// HotKeyCenter.m +// +// Created by Quentin D. Carnicelli on Thu Jun 06 2002. +// Copyright (c) 2002 Subband inc.. All rights reserved. +// +// Feedback welcome at qdc@subband.com +// This code is provided AS IS, so don't hurt yourself with it... +// + +#import "HotKeyCenter.h" +#import "KeyCombo.h" + +#import + +#define kHotKeyCenterSignature 'HKyC' + +//*** _HotKeyData +@interface _HotKeyData : NSObject +{ + @public + BOOL mRegistered; + EventHotKeyRef mRef; + KeyCombo *mCombo; + id mTarget; + SEL mAction; +} +@end + +@implementation _HotKeyData +@end + +//**** HotKeyCenter +@interface HotKeyCenter (Private) + - (OSStatus)handleHotKeyEvent: (EventRef)inEvent; + + - (BOOL)_registerHotKeyIfNeeded:(_HotKeyData *)hk; + - (void)_unregisterHotKeyIfNeeded:(_HotKeyData *)hk; + + + (BOOL)_systemSupportsHotKeys; + - (void)_hotKeyUp:(_HotKeyData *)hotKey; + - (void)_hotKeyDown:(_HotKeyData *)hotKey; + - (void)_hotKeyDownWithRef:(EventHotKeyRef)ref; + - (void)_hotKeyUpWithRef:(EventHotKeyRef)ref; + - (_HotKeyData*)_findHotKeyWithRef:(EventHotKeyRef)ref; + + pascal OSErr keyEventHandler(EventHandlerCallRef inHandlerRef, EventRef inEvent, void *refCon); +@end + +@implementation HotKeyCenter + +static id _sharedHKCenter = nil; + ++ (id)sharedCenter +{ + if (_sharedHKCenter != nil) + { + return _sharedHKCenter; + } + + _sharedHKCenter = [[HotKeyCenter alloc] init]; + + if ([self _systemSupportsHotKeys]) + { + EventTypeSpec eventSpec[2] = + { + { kEventClassKeyboard, kEventHotKeyPressed }, + { kEventClassKeyboard, kEventHotKeyReleased } + }; + + InstallEventHandler( GetEventDispatcherTarget(), NewEventHandlerUPP((EventHandlerProcPtr) keyEventHandler), 2, eventSpec, nil, nil); + } + + return _sharedHKCenter; +} + +- (id)init +{ + if ( (self = [super init]) ) + { + mEnabled = YES; + mHotKeys = [[NSMutableDictionary alloc] init]; + } + return self; +} + +- (void)dealloc +{ + [mHotKeys release]; + [super dealloc]; +} + +- (BOOL)addHotKey:(NSString *)name combo:(KeyCombo *)combo target:(id)target action:(SEL)action +{ + _HotKeyData *oldHotKey; + _HotKeyData *newHotKey; + + NSParameterAssert(name != nil); + NSParameterAssert(combo != nil); + NSParameterAssert(target != nil); + NSParameterAssert(action != nil); + + //** Check if we have one of these yet + oldHotKey = [mHotKeys objectForKey:name]; + + if (oldHotKey) //Registered already? + { + [self removeHotKey:name]; + } + + //** Save the hot key to our own list + newHotKey = [[[_HotKeyData alloc] init] autorelease]; + newHotKey->mRegistered = NO; + newHotKey->mRef = nil; + newHotKey->mCombo = [combo retain]; + newHotKey->mTarget = target; //Retain this? + newHotKey->mAction = action; + + [mHotKeys setObject:newHotKey forKey:name]; + + return [self _registerHotKeyIfNeeded:newHotKey]; +} + +- (void)removeHotKey:(NSString *)name; +{ + _HotKeyData *hotKey; + + hotKey = [mHotKeys objectForKey:name]; + if (hotKey == nil) //Not registered + return; + + [self _unregisterHotKeyIfNeeded:hotKey]; + [hotKey->mCombo release]; + + //Drop it from our hot key list + [mHotKeys removeObjectForKey: name]; +} + +- (NSArray *)allNames +{ + return [mHotKeys allKeys]; +} + +- (KeyCombo *)keyComboForName:(NSString *)name +{ + _HotKeyData * hotKey; + + hotKey = [mHotKeys objectForKey:name]; + if( hotKey == nil ) //Not registered + return nil; + + return hotKey->mCombo; +} + +- (void)setEnabled:(BOOL)enabled +{ + NSEnumerator *enumerator; + _HotKeyData *hotKey; + + enumerator = [mHotKeys objectEnumerator]; + + while ((hotKey = [enumerator nextObject]) != nil) + { + if (enabled) + [self _registerHotKeyIfNeeded:hotKey]; + else + [self _unregisterHotKeyIfNeeded:hotKey]; + } + + mEnabled = enabled; +} + +- (BOOL)enabled +{ + return mEnabled; +} + +- (void)sendEvent:(NSEvent *)event; +{ + long subType; + EventHotKeyRef hotKeyRef; + + //We only have to intercept sendEvent to do hot keys on old system versions + if ([HotKeyCenter _systemSupportsHotKeys] == YES) + return; + + if ([event type] == NSSystemDefined) + { + subType = [event subtype]; + + if (subType == 6) //6 is hot key down + { + hotKeyRef = (EventHotKeyRef)[event data1]; //data1 is our hot key ref + if (hotKeyRef != nil) + { + [self _hotKeyDownWithRef:hotKeyRef]; + } + } + else if (subType == 9) //9 is hot key up + { + hotKeyRef = (EventHotKeyRef)[event data1]; + if (hotKeyRef != nil) + { + [self _hotKeyUpWithRef:hotKeyRef]; + } + } + } +} + +- (OSStatus)handleHotKeyEvent:(EventRef)inEvent +{ + OSStatus err; + EventHotKeyID hotKeyID; + _HotKeyData *hk; + + //Shouldnt get here on non-hotkey supporting system versions + NSAssert([HotKeyCenter _systemSupportsHotKeys] == YES, @""); + NSAssert(GetEventClass(inEvent) == kEventClassKeyboard, @"Got unhandled event class"); + + err = GetEventParameter(inEvent, kEventParamDirectObject, typeEventHotKeyID, nil, sizeof(EventHotKeyID), nil, &hotKeyID); + + if (err) + { + return err; + } + + NSAssert(hotKeyID.signature == kHotKeyCenterSignature, @"Got unknown hot key"); + + hk = (_HotKeyData *)hotKeyID.id; + NSAssert(hk != nil, @"Got bad hot key"); + + switch (GetEventKind(inEvent)) + { + case kEventHotKeyPressed: + [self _hotKeyDown:hk]; + break; + + case kEventHotKeyReleased: + [self _hotKeyUp:hk]; + break; + + default: + break; + } + + return noErr; +} + ++ (BOOL)_systemSupportsHotKeys +{ + SInt32 vers; + Gestalt(gestaltSystemVersion,&vers); + return (vers >= 0x00001020); +} + +- (BOOL)_registerHotKeyIfNeeded:(_HotKeyData *)hk +{ + KeyCombo *combo; + + NSParameterAssert(hk != nil); + + combo = hk->mCombo; + + if( mEnabled == YES && hk->mRegistered == NO && [combo isValid] == YES ) + { + EventHotKeyID keyID; + OSStatus err; + + keyID.signature = kHotKeyCenterSignature; + keyID.id = (unsigned long)hk; + err = RegisterEventHotKey([combo keyCode], [combo modifiers], + keyID, GetEventDispatcherTarget(), 0, &hk->mRef); + if (err) + { + return NO; + } + + hk->mRegistered = YES; + } + + return YES; +} + +- (void)_unregisterHotKeyIfNeeded:(_HotKeyData *)hk +{ + NSParameterAssert(hk != nil); + + if (hk->mRegistered && hk->mRef != nil) + { + UnregisterEventHotKey(hk->mRef); + } +} + +- (void)_hotKeyDown:(_HotKeyData *)hotKey +{ + id target = hotKey->mTarget; + SEL action = hotKey->mAction; + [target performSelector:action withObject:self]; +} + +- (void)_hotKeyUp: (_HotKeyData *)hotKey +{ +} + +- (void)_hotKeyDownWithRef:(EventHotKeyRef)ref +{ + _HotKeyData *hotKey; + + hotKey = [self _findHotKeyWithRef:ref]; + if (hotKey) + { + [self _hotKeyDown:hotKey]; + } +} + +- (void)_hotKeyUpWithRef:(EventHotKeyRef)ref +{ +} + +- (_HotKeyData *)_findHotKeyWithRef:(EventHotKeyRef)ref +{ + NSEnumerator *enumerator; + _HotKeyData *hotKey; + + enumerator = [mHotKeys objectEnumerator]; + + while ((hotKey = [enumerator nextObject]) != nil) + { + if (hotKey->mRef == ref) + { + return hotKey; + } + } + + return nil; +} + +pascal OSErr keyEventHandler(EventHandlerCallRef inHandlerRef, EventRef inEvent, void *refCon) +{ + return [[HotKeyCenter sharedCenter] handleHotKeyEvent:inEvent]; +} + +@end + diff --git a/KeyBroadcaster.h b/KeyBroadcaster.h new file mode 100755 index 0000000..19df649 --- /dev/null +++ b/KeyBroadcaster.h @@ -0,0 +1,9 @@ +/* KeyBroadcaster */ + +#import + +@interface KeyBroadcaster : NSButton +{ +} ++ (long)cocoaToCarbonModifiers:(long)modifiers; +@end diff --git a/KeyBroadcaster.m b/KeyBroadcaster.m new file mode 100755 index 0000000..7b47d83 --- /dev/null +++ b/KeyBroadcaster.m @@ -0,0 +1,58 @@ +#import "KeyBroadcaster.h" +#import + +@interface KeyBroadcaster (Private) +- (void)_broadcastKeyCode:(short)keyCode andModifiers:(long)modifiers; +@end + +@implementation KeyBroadcaster + +- (void)keyDown:(NSEvent *)event +{ + short keyCode; + long modifiers; + + keyCode = [event keyCode]; + modifiers = [event modifierFlags]; + + modifiers = [KeyBroadcaster cocoaToCarbonModifiers:modifiers]; + [self _broadcastKeyCode:keyCode andModifiers:modifiers]; +} + +- (BOOL)performKeyEquivalent:(NSEvent *)event +{ + [self keyDown:event]; + return YES; +} + +- (void)_broadcastKeyCode:(short)keyCode andModifiers:(long)modifiers +{ + NSNumber *keycodeNum = [NSNumber numberWithShort:keyCode]; + NSNumber *modifiersNum = [NSNumber numberWithLong:modifiers]; + NSDictionary *info = [NSDictionary dictionaryWithObjectsAndKeys: + keycodeNum, @"KeyCode", modifiersNum, @"Modifiers", nil, nil]; + [[NSNotificationCenter defaultCenter] postNotificationName:@"KeyBroadcasterEvent" object:self userInfo:info]; +} + ++ (long)cocoaToCarbonModifiers:(long)modifiers +{ + long carbonModifiers = 0; + int i; + static long cocoaToCarbon[6][2] = + { + { NSCommandKeyMask, cmdKey }, + { NSAlternateKeyMask, optionKey }, + { NSControlKeyMask, controlKey }, + { NSShiftKeyMask, shiftKey }, + }; + for (i = 0; i < 6; i++) + { + if (modifiers & cocoaToCarbon[i][0]) + { + carbonModifiers += cocoaToCarbon[i][1]; + } + } + return carbonModifiers; +} + +@end diff --git a/KeyCombo.h b/KeyCombo.h new file mode 100755 index 0000000..743c77b --- /dev/null +++ b/KeyCombo.h @@ -0,0 +1,41 @@ +// +// KeyCombo.h +// +// Created by Quentin D. Carnicelli on Tue Jun 18 2002. +// Copyright (c) 2001 Subband inc.. All rights reserved. +// + +#import + + +@interface KeyCombo : NSObject +{ + short mKeyCode; + long mModifiers; +} + ++ (id)keyCombo; ++ (id)clearKeyCombo; ++ (id)keyComboWithKeyCode:(short)keycode andModifiers:(long)modifiers; + +- (id)initWithKeyCode:(short)keycode andModifiers:(long)modifiers; + +- (id)copyWithZone:(NSZone *)zone; +- (BOOL)isEqual:(id)object; + +- (short)keyCode; +- (short)modifiers; + +- (BOOL)isValid; + +- (NSString *)userDisplayRep; ++ (NSDictionary *)keyCodesDictionary; + +@end + +@interface NSUserDefaults (KeyComboAdditions) + +- (void)setKeyCombo: (KeyCombo *)combo forKey: (NSString *)key; +- (KeyCombo *)keyComboForKey: (NSString *)key; + +@end diff --git a/KeyCombo.m b/KeyCombo.m new file mode 100755 index 0000000..716d4ae --- /dev/null +++ b/KeyCombo.m @@ -0,0 +1,215 @@ +// +// KeyCombo.m +// +// Created by Quentin D. Carnicelli on Tue Jun 18 2002. +// Copyright (c) 2001 Subband inc.. All rights reserved. +// + +#import "KeyCombo.h" + +#import +#import + +@interface KeyCombo (Private) + + (NSString*)_stringForModifiers:(long)modifiers; + + (NSString*)_stringForKeyCode:(short)keyCode; +@end + + +@implementation KeyCombo + ++ (id)keyCombo +{ + return [[[self alloc] init] autorelease]; +} + ++ (id)clearKeyCombo +{ + return [self keyComboWithKeyCode:-1 andModifiers:-1]; +} + ++ (id)keyComboWithKeyCode: (short)keycode andModifiers: (long)modifiers +{ + return [[[self alloc] initWithKeyCode:keycode andModifiers:modifiers] autorelease]; +} + +- (id)initWithKeyCode: (short)keycode andModifiers: (long)modifiers +{ + if ( (self = [super init]) ) + { + mKeyCode = keycode; + mModifiers = modifiers; + } + return self; +} + +- (id)init +{ + return [self initWithKeyCode: -1 andModifiers: -1]; +} + +- (id)copyWithZone:(NSZone *)zone; +{ + return [self retain]; +} + +- (id)initWithCoder:(NSCoder *)aDecoder +{ + self = [self init]; + + if( self ) + { + [aDecoder decodeValueOfObjCType: @encode(short) at: &mKeyCode]; + [aDecoder decodeValueOfObjCType: @encode(long) at: &mModifiers]; + } + + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeValueOfObjCType:@encode(short) at:&mKeyCode]; + [aCoder encodeValueOfObjCType:@encode(long) at:&mModifiers]; +} + +- (BOOL)isEqual:(KeyCombo *)object +{ + return ([object isKindOfClass:[KeyCombo class]]) && + ([object keyCode] == [self keyCode]) && + ([object modifiers] == [self modifiers]); +} + +- (NSString *)description +{ + return [self userDisplayRep]; +} + +- (short)keyCode +{ + return mKeyCode; +} + +- (short)modifiers +{ + return mModifiers; +} + +- (BOOL)isValid +{ + return ((mKeyCode >= 0) && (mModifiers >= 0)); +} + +- (NSString *)userDisplayRep +{ + if ([self isValid] == NO) + { + return @"None"; + } + else + { + return [NSString stringWithFormat: @"%@%@", + [KeyCombo _stringForModifiers: mModifiers], + [KeyCombo _stringForKeyCode: mKeyCode]]; + } +} + ++ (NSString *)_stringForModifiers: (long)modifiers +{ + static long modToChar[4][2] = + { + { cmdKey, 0x23180000 }, + { optionKey, 0x23250000 }, + { controlKey, 0x005E0000 }, + { shiftKey, 0x21e70000 } + }; + + NSString *str = [NSString string]; + NSString *charStr; + long i; + + for (i = 0; i < 4; i++) + { + if (modifiers & modToChar[i][0]) + { + charStr = [NSString stringWithCharacters:(const unichar *)&modToChar[i][1] length:1]; + str = [str stringByAppendingString:charStr]; + } + } + + return str; +} + ++ (NSString *)_stringForKeyCode:(short)keyCode +{ + NSDictionary *dict; + id key; + NSString *str; + + dict = [self keyCodesDictionary]; + key = [NSString stringWithFormat: @"%d", keyCode]; + str = [dict objectForKey: key]; + + if( !str ) + str = [NSString stringWithFormat: @"%X", keyCode]; + + return str; +} + ++ (NSDictionary *)keyCodesDictionary +{ + static NSDictionary *keyCodes = nil; + + if (keyCodes == nil) + { + NSString *path; + NSString *contents; + + path = [[NSBundle bundleForClass: [KeyCombo class]] pathForResource: @"KeyCodes" ofType: @"plist"]; + + contents = [NSString stringWithContentsOfFile: path]; + keyCodes = [[contents propertyList] retain]; + } + + return keyCodes; +} + +@end + +@implementation NSUserDefaults (KeyComboAdditions) + +- (void)setKeyCombo:(KeyCombo *)combo forKey:(NSString *)key +{ + NSData *data; + if (combo) + { + data = [NSArchiver archivedDataWithRootObject:combo]; + } + else + { + data = nil; + } + [self setObject:data forKey:key]; +} + +- (KeyCombo *)keyComboForKey:(NSString *)key +{ + NSData *data = [self objectForKey:key]; + KeyCombo *combo; + if (data) + { + combo = [[NSUnarchiver unarchiveObjectWithData:data] retain]; + } + + if (combo == nil) + { + combo = [[KeyCombo alloc] init]; + } + return combo; +} + +@end + + + + + diff --git a/MTApplication.h b/MTApplication.h new file mode 100755 index 0000000..5692b39 --- /dev/null +++ b/MTApplication.h @@ -0,0 +1,8 @@ +/* MTApplication */ + +#import + +@interface MTApplication : NSApplication +{ +} +@end diff --git a/MTApplication.m b/MTApplication.m new file mode 100755 index 0000000..24ff6a4 --- /dev/null +++ b/MTApplication.m @@ -0,0 +1,12 @@ +#import "MTApplication.h" +#import "HotKeyCenter.h" + +@implementation MTApplication + +- (void)sendEvent:(NSEvent *)event +{ + [[HotKeyCenter sharedCenter] sendEvent:event]; + [super sendEvent:event]; +} + +@end diff --git a/MenuInverted.tiff b/MenuInverted.tiff new file mode 100755 index 0000000000000000000000000000000000000000..37ed1b6b3f5213a5715b24d1f2fde97c389670ba GIT binary patch literal 16364 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 literal 0 HcmV?d00001 diff --git a/MenuNormal.tiff b/MenuNormal.tiff new file mode 100755 index 0000000000000000000000000000000000000000..8e94b1d0ca368c25f2cf015992358d0c6e1ca424 GIT binary patch literal 16606 zcmeGjTW}lI^{!;gmMl9~94CReR52l&XU~0|-5nS}-$V$NA`6;9MNpz31X=%YJ_Z+Nd9l_n2gbZaYnTInNT>|W_$PH$8FYdu+4T! z!+>ib*2Rp4d$;h+@Rqf{z?ShqbI{h&ZffnIgPfnSPtq~_XqXilUa&GkpsluRs1{dG zwm?mU5ozmWgpCR=2d$trgj8{i9&2=0)jYAtNI4?M{#JVW*1aPSnFm8<3PNltrkKdwN{8daKUgi)K)v< z>#;YHxX$wKbzQzo2UlBTY*-Mj!

GRu5QfAq2!gAco}vG&{x#qH}mk5SfUxKkVme ze#+VH>lU1YJQrhlajG9$I)Y-b7D5yfn`FN(M>EC-!vV3~WNaO0rrIGm~aKnh88(qX=7{^L|`Av z><&;7ItpC^rA%QmYBCOk-km&8Po-GX7zDwfp(5HGJmyypGXc@rcCW+ZN=wYQTyBW| z;5yuurjP+P%tXbcgg~5+`r5oBT(FxK88!1D!0jNe$L?ydyPJF-cZioeOGSEuc?d>iVIE4(ImXX44_ypP zWf)xPOSrIQQ7!3Cg=I~dgU{@;rr11V7=KxowmH;Gx>aG8I%94n#Qi>L4;`anKff^? z9pyGko1om*ytGYjXK+&-jtW7H9AW~gJ!#T2wKuXE+xR>;vRWIu*&BIdtFoWOex>X( zae3Z@%A9KA)+(+5k(JXm->RB7q1RDYpSDoLI_*)f(+bB1hqqCiFtwh6^x2hHHpQ9e zj6Cm;Huu>l4Y(`095pm$&UV&FZIUx)WmwyjexNc-3Y(8>z-anP41r8N=NJQax1%Y| z5nY)_T*YN`r}R3!xsHP7c?eavyD$nq+b94>=!4Y*0y%?tDOn3|{3uI^YJSZ+gp>&; z$NAq8i#P@$Ff~lx=~2gWH0OfTUjwvM#BL+UU81y|Wkh(5Dor%)>2KB3!bT{7htWO2Rx113}=mgE-;>AG)l7qb2bg$_yOdL-f*Bf+0`cU!t`cDzOa}RY=?E&5S>qDVk7_~#J=jOxa)pb`#YAAR3#NhZ18_1W?L;)xpCH&?L{YP~Qv}NmmRa1YdTLjLT{Mh7pj|I5n+Q zJI7TP2-imAEDL{FYy*6u5Dg0J{F?#*$&7~rq8lLM7(Om$Ww~ewV3PWoQBe)A61j*B zV^PphXiTZcut8wM0Y4}nb;%|V+^k8i-fH3_j&8DkMpc=^?Dz4 zB^wtTKOHk5gqSppfo4S?9ZJJh1Q=+?Op1L%uWz8=58u52PgJU)(};~@9KWTL4Tls< zEs&Il*B&G^v@8iSqja1Vp=jI;&y2E=t<`BrBi^TFXVT zt&JiVTNf7vMsW;WXJWyTW?{{KP6UUVg)f6!JT{xWTpC3d%n*NIMaJeL0%7Mh5ZH`i zeIH!#9Z<$gH$ue+0B=W25tTTQcpX)KyQi-QQ93*~!|a?mhRmJ#TcwR%14x%Rjw%R* zf>&csiC55d$Xr@lYAiJyjb=-ksmxNnz-(Sny|k*Tx~gia#Y~d?CjpsPs=TbMyrR6a zqM~w9MMcFTe5qI@HL>KvAn^xeDFu}l>#16#vru{qm3S4#tuFBt)d5l#QzU`FIO##T zqGCfysnJvhOHX-tQvJz26lwMb)D=smV7VFJ*P-)Q@Enny^?!1Vus=a)h$FS(8 z+wNI)?uzQ6C;WBw{5AW#O3u4^_`NsY4+ss5Z@>52mEAw`1y?_LhuFB}xsk)n2lrjK z_xbpnANJUOeCPLn|C6Ww`uC51dH)|@_{WKhM>k*p(_cOCrx*Wu(!1u8(8LdZw)?@S zUwZ3f3)1O8+9D!TiJ`cGh_Kwfum~i$ytb;?v+bruSb`^p_P z;mzCsT!rGZRNH=sT|?@$8}L_isG0aNk|qxEFWcy6K?@uDq*z!`Rk|Q;&9yG}awSpyugK zkM7zx<3BpR>z&76J~{KkM;)PK=RA0D^R)Y1?$<|WZai}A;A7tdR{aTd;Ny3=Q)^CD zg+5*Ka^t$0y<4_Ex3O-;&ASpWOQUQ#aj6lw!ni|d0qR6S#34UyKCEaEjX_z2ERYf` z(x_@43M=v=2XbMCkiQZM@A&E>&laLjj_zb5FMPG$3(3mKo1U$ +#import + +@class MenuTunesView, PreferencesController, StatusWindowController; + +@interface MenuTunes : NSObject +{ + NSStatusItem *statusItem; + NSMenu *menu; + MenuTunesView *view; + + //Used in updating the menu automatically + NSTimer *refreshTimer; + int curTrackIndex; + NSString *curPlaylist; + int trackInfoIndex; + + ProcessSerialNumber iTunesPSN; + bool didHaveAlbumName; //Helper variable for creating the menu + + //For upcoming songs + NSMenuItem *upcomingSongsItem; + NSMenu *upcomingSongsMenu; + + //For playlist selection + NSMenuItem *playlistItem; + NSMenu *playlistMenu; + + NSMenuItem *playPauseMenuItem; //Toggle between 'Play' and 'Pause' + + PreferencesController *prefsController; + StatusWindowController *statusController; //Shows track info and upcoming songs. +} + +- (void)rebuildMenu; +- (void)updateMenu; +- (void)rebuildUpcomingSongsMenu; +- (void)rebuildPlaylistMenu; + +- (void)clearHotKeys; +- (void)setupHotKeys; + +- (NSString *)runScriptAndReturnResult:(NSString *)script; +- (void)timerUpdate; + +- (ProcessSerialNumber)iTunesPSN; + +- (void)sendAEWithEventClass:(AEEventClass)eventClass andEventID:(AEEventID)eventID; + +- (void)closePreferences; + +@end diff --git a/MenuTunes.m b/MenuTunes.m new file mode 100755 index 0000000..9879f8a --- /dev/null +++ b/MenuTunes.m @@ -0,0 +1,677 @@ +// +// MenuTunes.m +// +// iThink Software, Copyright 2002 +// +// + +/* +Things to do: +¥ Radio mode makes things ugly +¥ Add other options to the menu + - EQ sets + - set song rating +¥ Make preferences window pretty +¥ Hot Keys + - hot keys can't be set when NSBGOnly is on. The window is not key, + so the KeyBroadcaster does not pick up key combos. Bad... + - the hotkey classes are ugly, I didn't write them +¥ Optimize code +¥ Apple Events! +*/ + +#import "MenuTunes.h" +#import "MenuTunesView.h" +#import "PreferencesController.h" +#import "HotKeyCenter.h" +#import "StatusWindowController.h" + +@implementation MenuTunes + +- (void)applicationDidFinishLaunching:(NSNotification *)note +{ + menu = [[NSMenu alloc] initWithTitle:@""]; + + if (![[NSUserDefaults standardUserDefaults] objectForKey:@"menu"]) + { + [[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObjects:@"Play/Pause", @"Next Track", @"Previous Track", @"Fast Forward", @"Rewind", @"", @"Upcoming Songs", @"Playlists", @"", @"PreferencesÉ", @"Quit", @"", @"Current Track Info", nil] forKey:@"menu"]; + } + + iTunesPSN = [self iTunesPSN]; //Get PSN of iTunes if it's running + [self rebuildMenu]; //Create the status item menu + + statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength]; + [statusItem setImage:[NSImage imageNamed:@"menu.tiff"]]; + [statusItem setHighlightMode:YES]; + [statusItem setMenu:menu]; + [statusItem retain]; + + view = [[MenuTunesView alloc] initWithFrame:[[statusItem view] frame]]; + //[statusItem setView:view]; + + //If iTunes is running, start the timer + if (!((iTunesPSN.highLongOfPSN == kNoProcess) && (iTunesPSN.lowLongOfPSN == 0))) + { + refreshTimer = [NSTimer scheduledTimerWithTimeInterval:3.5 +target:self selector:@selector(timerUpdate) userInfo:nil repeats:YES]; + } + else + { + NSMenu *menu2 = [[[NSMenu alloc] initWithTitle:@""] autorelease]; + + //Register for the workspace note + [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self selector:@selector(iTunesLaunched:) name:NSWorkspaceDidLaunchApplicationNotification object:nil]; + refreshTimer = NULL; + + [[menu2 addItemWithTitle:@"Open iTunes" action:@selector(openiTunes:) keyEquivalent:@""] setTarget:self]; + [[menu2 addItemWithTitle:@"Preferences" action:@selector(showPreferences:) keyEquivalent:@""] setTarget:self]; + [[menu2 addItemWithTitle:@"Quit" action:@selector(quitMenuTunes:) keyEquivalent:@""] setTarget:self]; + [statusItem setMenu:menu2]; + } +} + +- (void)applicationWillTerminate:(NSNotification *)note +{ + [self clearHotKeys]; + [[NSStatusBar systemStatusBar] removeStatusItem:statusItem]; +} + +- (void)dealloc +{ + if (refreshTimer) + { + [refreshTimer invalidate]; + } + [statusItem release]; + [menu release]; + [view release]; + [super dealloc]; +} + +//Recreate the status item menu +- (void)rebuildMenu +{ + NSArray *myMenu = [[NSUserDefaults standardUserDefaults] arrayForKey:@"menu"]; + int i; + trackInfoIndex = -1; + + if (!((iTunesPSN.highLongOfPSN == kNoProcess) && (iTunesPSN.lowLongOfPSN == 0))) + { + didHaveAlbumName = (([[self runScriptAndReturnResult:@"tell application \"iTunes\"\nreturn album of current track\nend tell"] length] > 0) ? YES : NO); + } + else + { + didHaveAlbumName = NO; + } + + while ([menu numberOfItems] > 0) + { + [menu removeItemAtIndex:0]; + } + + playPauseMenuItem = nil; + upcomingSongsItem = nil; + playlistItem = nil; + + for (i = 0; i < [myMenu count]; i++) + { + NSString *item = [myMenu objectAtIndex:i]; + if ([item isEqualToString:@"Play/Pause"]) + { + playPauseMenuItem = [menu addItemWithTitle:@"Play" action:@selector(playPause:) keyEquivalent:@""]; + [playPauseMenuItem setTarget:self]; + } + else if ([item isEqualToString:@"Next Track"]) + { + [[menu addItemWithTitle:@"Next Track" action:@selector(nextSong:) keyEquivalent:@""] setTarget:self]; + } + else if ([item isEqualToString:@"Previous Track"]) + { + [[menu addItemWithTitle:@"Previous Track" action:@selector(prevSong:) keyEquivalent:@""] setTarget:self]; + } + else if ([item isEqualToString:@"Fast Forward"]) + { + [[menu addItemWithTitle:@"Fast Forward" action:@selector(fastForward:) keyEquivalent:@""] setTarget:self]; + } + else if ([item isEqualToString:@"Rewind"]) + { + [[menu addItemWithTitle:@"Rewind" action:@selector(rewind:) keyEquivalent:@""] setTarget:self]; + } + else if ([item isEqualToString:@"Upcoming Songs"]) + { + upcomingSongsItem = [menu addItemWithTitle:@"Upcoming Songs" action:NULL keyEquivalent:@""]; + } + else if ([item isEqualToString:@"Playlists"]) + { + playlistItem = [menu addItemWithTitle:@"Playlists" action:NULL keyEquivalent:@""]; + } + else if ([item isEqualToString:@"PreferencesÉ"]) + { + [[menu addItemWithTitle:@"PreferencesÉ" action:@selector(showPreferences:) keyEquivalent:@""] setTarget:self]; + } + else if ([item isEqualToString:@"Quit"]) + { + [[menu addItemWithTitle:@"Quit" action:@selector(quitMenuTunes:) keyEquivalent:@""] setTarget:self]; + } + else if ([item isEqualToString:@"Current Track Info"]) + { + trackInfoIndex = [menu numberOfItems]; + [menu addItemWithTitle:@"No Song" action:NULL keyEquivalent:@""]; + } + else if ([item isEqualToString:@""]) + { + [menu addItem:[NSMenuItem separatorItem]]; + } + } + curTrackIndex = -1; //Force update of everything + [self timerUpdate]; //Updates dynamic info in the menu + + [self clearHotKeys]; + [self setupHotKeys]; +} + +//Updates the menu with current player state, song, and upcoming songs +- (void)updateMenu +{ + NSString *curSongName, *curAlbumName; + NSMenuItem *menuItem; + + if ((iTunesPSN.highLongOfPSN == kNoProcess) && (iTunesPSN.lowLongOfPSN == 0)) + { + return; + } + + //Get the current track name and album. + curSongName = [self runScriptAndReturnResult:@"tell application \"iTunes\"\nreturn name of current track\nend tell"]; + curAlbumName = [self runScriptAndReturnResult:@"tell application \"iTunes\"\nreturn album of current track\nend tell"]; + + if (upcomingSongsItem) + { + [self rebuildUpcomingSongsMenu]; + } + if (playlistItem) + { + [self rebuildPlaylistMenu]; + } + + if ([curSongName length] > 0) + { + int index = [menu indexOfItemWithTitle:@"Now Playing"]; + + if (index > -1) + { + [menu removeItemAtIndex:index + 1]; + if (didHaveAlbumName) + { + [menu removeItemAtIndex:index + 1]; + } + } + + if ([curAlbumName length] > 0) + { + menuItem = [[NSMenuItem alloc] initWithTitle:[NSString stringWithFormat:@" %@", curAlbumName] action:NULL keyEquivalent:@""]; + [menu insertItem:menuItem atIndex:trackInfoIndex + 1]; + [menuItem release]; + } + + menuItem = [[NSMenuItem alloc] initWithTitle:[NSString stringWithFormat:@" %@", curSongName] action:NULL keyEquivalent:@""]; + [menu insertItem:menuItem atIndex:trackInfoIndex + 1]; + [menuItem release]; + + if (index == -1) + { + menuItem = [[NSMenuItem alloc] initWithTitle:@"Now Playing" action:NULL keyEquivalent:@""]; + [menu removeItemAtIndex:[menu indexOfItemWithTitle:@"No Song"]]; + [menu insertItem:menuItem atIndex:trackInfoIndex]; + [menuItem release]; + } + } + else if ([menu indexOfItemWithTitle:@"No Song"] == -1) + { + [menu removeItemAtIndex:trackInfoIndex]; + [menu removeItemAtIndex:trackInfoIndex]; + if (didHaveAlbumName) + { + [menu removeItemAtIndex:trackInfoIndex]; + } + menuItem = [[NSMenuItem alloc] initWithTitle:@"No Song" action:NULL keyEquivalent:@""]; + [menu insertItem:menuItem atIndex:trackInfoIndex]; + [menuItem release]; + } + + didHaveAlbumName = (([curAlbumName length] > 0) ? YES : NO); +} + +//Rebuild the upcoming songs submenu. Can be improved a lot. +- (void)rebuildUpcomingSongsMenu +{ + int numSongs = [[self runScriptAndReturnResult:@"tell application \"iTunes\"\nreturn number of tracks in current playlist\nend tell"] intValue]; + int numSongsInAdvance = [[NSUserDefaults standardUserDefaults] integerForKey:@"SongsInAdvance"]; + + if (numSongs > 0) + { + int curTrack = [[self runScriptAndReturnResult:@"tell application \"iTunes\"\nreturn index of current track\nend tell"] intValue]; + int i; + + [upcomingSongsMenu release]; + upcomingSongsMenu = [[NSMenu alloc] initWithTitle:@""]; + + for (i = curTrack + 1; i <= curTrack + numSongsInAdvance; i++) + { + if (i <= numSongs) + { + NSString *curSong = [self runScriptAndReturnResult:[NSString stringWithFormat:@"tell application \"iTunes\"\nreturn name of track %i of current playlist\nend tell", i]]; + NSMenuItem *songItem; + songItem = [[NSMenuItem alloc] initWithTitle:curSong action:@selector(playTrack:) keyEquivalent:@""]; + [songItem setTarget:self]; + [songItem setRepresentedObject:[NSNumber numberWithInt:i]]; + [upcomingSongsMenu addItem:songItem]; + [songItem release]; + } + else + { + [upcomingSongsMenu addItemWithTitle:@"End of playlist." action:NULL keyEquivalent:@""]; + break; + } + } + [upcomingSongsItem setSubmenu:upcomingSongsMenu]; + } +} + +- (void)rebuildPlaylistMenu +{ + int numPlaylists = [[self runScriptAndReturnResult:@"tell application \"iTunes\"\nreturn number of playlists\nend tell"] intValue]; + int i; + + [playlistMenu release]; + playlistMenu = [[NSMenu alloc] initWithTitle:@""]; + + for (i = 1; i <= numPlaylists; i++) + { + NSString *playlistName = [self runScriptAndReturnResult:[NSString stringWithFormat:@"tell application \"iTunes\"\nreturn name of playlist %i\nend tell", i]]; + NSMenuItem *tempItem; + tempItem = [[NSMenuItem alloc] initWithTitle:playlistName action:@selector(selectPlaylist:) keyEquivalent:@""]; + [tempItem setTarget:self]; + [tempItem setRepresentedObject:[NSNumber numberWithInt:i]]; + [playlistMenu addItem:tempItem]; + [tempItem release]; + } + [playlistItem setSubmenu:playlistMenu]; +} + +- (void)clearHotKeys +{ + [[HotKeyCenter sharedCenter] removeHotKey:@"PlayPause"]; + [[HotKeyCenter sharedCenter] removeHotKey:@"NextTrack"]; + [[HotKeyCenter sharedCenter] removeHotKey:@"PrevTrack"]; + [[HotKeyCenter sharedCenter] removeHotKey:@"TrackInfo"]; + [[HotKeyCenter sharedCenter] removeHotKey:@"UpcomingSongs"]; +} + +- (void)setupHotKeys +{ + NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; + + if ([defaults objectForKey:@"PlayPause"] != nil) + { + [[HotKeyCenter sharedCenter] addHotKey:@"PlayPause" + combo:[defaults keyComboForKey:@"PlayPause"] + target:self action:@selector(playPause:)]; + } + + if ([defaults objectForKey:@"NextTrack"] != nil) + { + [[HotKeyCenter sharedCenter] addHotKey:@"NextTrack" + combo:[defaults keyComboForKey:@"NextTrack"] + target:self action:@selector(nextSong:)]; + } + + if ([defaults objectForKey:@"PrevTrack"] != nil) + { + [[HotKeyCenter sharedCenter] addHotKey:@"PrevTrack" + combo:[defaults keyComboForKey:@"PrevTrack"] + target:self action:@selector(prevSong:)]; + } + + if ([defaults objectForKey:@"TrackInfo"] != nil) + { + [[HotKeyCenter sharedCenter] addHotKey:@"TrackInfo" + combo:[defaults keyComboForKey:@"TrackInfo"] + target:self action:@selector(showCurrentTrackInfo)]; + } + + if ([defaults objectForKey:@"UpcomingSongs"] != nil) + { + [[HotKeyCenter sharedCenter] addHotKey:@"UpcomingSongs" + combo:[defaults keyComboForKey:@"UpcomingSongs"] + target:self action:@selector(showUpcomingSongs)]; + } +} + +//Runs an AppleScript and returns the result as an NSString after stripping quotes, if needed. +- (NSString *)runScriptAndReturnResult:(NSString *)script +{ + AEDesc scriptDesc, resultDesc; + Size length; + NSString *result; + Ptr buffer; + + AECreateDesc(typeChar, [script cString], [script cStringLength], +&scriptDesc); + + OSADoScript(OpenDefaultComponent(kOSAComponentType, kAppleScriptSubtype), &scriptDesc, kOSANullScript, typeChar, kOSAModeCanInteract, &resultDesc); + + length = AEGetDescDataSize(&resultDesc); + buffer = malloc(length); + + AEGetDescData(&resultDesc, buffer, length); + result = [NSString stringWithCString:buffer length:length]; + if (![result isEqualToString:@""] && + ([result characterAtIndex:0] == '\"') && + ([result characterAtIndex:[result length] - 1] == '\"')) + { + result = [result substringWithRange:NSMakeRange(1, [result length] - 2)]; + } + free(buffer); + buffer = NULL; + return result; +} + +//Called when the timer fires. +- (void)timerUpdate +{ + int pid; + if ((GetProcessPID(&iTunesPSN, &pid) == noErr) && (pid > 0)) + { + int trackPlayingIndex = [[self runScriptAndReturnResult:@"tell application \"iTunes\"\nreturn index of current track\nend tell"] intValue]; + if (trackPlayingIndex != curTrackIndex) + { + [self updateMenu]; + curTrackIndex = trackPlayingIndex; + } + /*else + { + NSString *playlist = [self runScriptAndReturnResult:@"tell application\n\"iTunes\"\nreturn name of current playlist\nend tell"]; + + if (![playlist isEqualToString:curPlaylist]) + { + [self updateMenu]; + NSLog(@"update due to playlist change"); + curPlaylist = [NSString stringWithString:playlist]; + } + }*/ + //Update Play/Pause menu item + if (playPauseMenuItem) + { + if ([[self runScriptAndReturnResult:@"tell application \"iTunes\"\nreturn player state\nend tell"] isEqualToString:@"playing"]) + { + [playPauseMenuItem setTitle:@"Pause"]; + } + else + { + [playPauseMenuItem setTitle:@"Play"]; + } + } + } + else + { + NSMenu *menu2 = [[[NSMenu alloc] initWithTitle:@""] autorelease]; + + [refreshTimer invalidate]; //Stop the timer + refreshTimer = NULL; + [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self selector:@selector(iTunesLaunched:) name:NSWorkspaceDidLaunchApplicationNotification object:nil]; + + [[menu2 addItemWithTitle:@"Open iTunes" +action:@selector(openiTunes:) keyEquivalent:@""] setTarget:self]; + [[menu2 addItemWithTitle:@"Preferences" +action:@selector(showPreferences:) keyEquivalent:@""] setTarget:self]; + [[menu2 addItemWithTitle:@"Quit" action:@selector(quitMenuTunes:) +keyEquivalent:@""] setTarget:self]; + [statusItem setMenu:menu2]; + } +} + +- (void)iTunesLaunched:(NSNotification *)note +{ + NSDictionary *info = [note userInfo]; + + iTunesPSN.highLongOfPSN = [[info objectForKey:@"NSApplicationProcessSerialNumberHigh"] longValue]; + iTunesPSN.lowLongOfPSN = [[info objectForKey:@"NSApplicationProcessSerialNumberLow"] longValue]; + + //Restart the timer + refreshTimer = [NSTimer scheduledTimerWithTimeInterval:3.5 target:self selector:@selector(timerUpdate) userInfo:nil repeats:YES]; + + [self rebuildMenu]; //Rebuild the menu since no songs will be playing + [statusItem setMenu:menu]; //Set the menu back to the main one + + [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:self]; +} + +//Return the PSN of iTunes, if it's running +- (ProcessSerialNumber)iTunesPSN +{ + ProcessSerialNumber procNum; + procNum.highLongOfPSN = kNoProcess; + procNum.lowLongOfPSN = 0; + + while ( (GetNextProcess(&procNum) == noErr) ) + { + CFStringRef procName; + + if ( (CopyProcessName(&procNum, &procName) == noErr) ) + { + if ([(NSString *)procName isEqualToString:@"iTunes"]) + { + return procNum; + } + [(NSString *)procName release]; + } + } + return procNum; +} + +//Send an AppleEvent with a given event ID +- (void)sendAEWithEventClass:(AEEventClass)eventClass +andEventID:(AEEventID)eventID +{ + OSType iTunesType = 'hook'; + AppleEvent event, reply; + + AEBuildAppleEvent(eventClass, eventID, typeApplSignature, &iTunesType, sizeof(iTunesType), kAutoGenerateReturnID, kAnyTransactionID, &event, NULL, ""); + + AESend(&event, &reply, kAENoReply, kAENormalPriority, kAEDefaultTimeout, nil, nil); + AEDisposeDesc(&event); + AEDisposeDesc(&reply); +} + +// +// Selectors - called from status item menu +// + +- (void)playTrack:(id)sender +{ + [self runScriptAndReturnResult:[NSString stringWithFormat:@"tell application \"iTunes\"\nplay track %i of current playlist\nend tell", [[sender representedObject] intValue]]]; + [self updateMenu]; +} + +- (void)selectPlaylist:(id)sender +{ + [self runScriptAndReturnResult:[NSString stringWithFormat:@"tell application \"iTunes\"\nplay playlist %i\nend tell", [[sender representedObject] intValue]]]; + [self updateMenu]; +} + +- (void)playPause:(id)sender +{ + NSString *state = [self runScriptAndReturnResult:@"tell application \"iTunes\"\nreturn player state\nend tell"]; + if ([state isEqualToString:@"playing"]) + { + [self sendAEWithEventClass:'hook' andEventID:'Paus']; + [playPauseMenuItem setTitle:@"Play"]; + } + else if ([state isEqualToString:@"fast forwarding"] || [state +isEqualToString:@"rewinding"]) + { + [self sendAEWithEventClass:'hook' andEventID:'Paus']; + [self sendAEWithEventClass:'hook' andEventID:'Play']; + } + else + { + [self sendAEWithEventClass:'hook' andEventID:'Play']; + [playPauseMenuItem setTitle:@"Pause"]; + } +} + +- (void)nextSong:(id)sender +{ + [self sendAEWithEventClass:'hook' andEventID:'Next']; +} + +- (void)prevSong:(id)sender +{ + [self sendAEWithEventClass:'hook' andEventID:'Prev']; +} + +- (void)fastForward:(id)sender +{ + [self sendAEWithEventClass:'hook' andEventID:'Fast']; +} + +- (void)rewind:(id)sender +{ + [self sendAEWithEventClass:'hook' andEventID:'Rwnd']; +} + +- (void)quitMenuTunes:(id)sender +{ + [NSApp terminate:self]; +} + +- (void)openiTunes:(id)sender +{ + [[NSWorkspace sharedWorkspace] launchApplication:@"iTunes"]; +} + +- (void)showPreferences:(id)sender +{ + if (!prefsController) + { + prefsController = [[PreferencesController alloc] initWithMenuTunes:self]; + [self clearHotKeys]; + } +} + + +- (void)closePreferences +{ + [self setupHotKeys]; + [prefsController release]; + prefsController = nil; +} + +// +// +// Show Current Track Info And Show Upcoming Songs Floaters +// +// + +- (void)showCurrentTrackInfo +{ + NSString *trackName = [self runScriptAndReturnResult:@"tell application \"iTunes\"\nreturn name of current track\nend tell"]; + if (!statusController && [trackName length]) + { + NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; + NSString *stringToShow = @""; + int lines = 1; + + if ([defaults boolForKey:@"showName"]) + { + stringToShow = [stringToShow stringByAppendingString:trackName]; + stringToShow = [stringToShow stringByAppendingString:@"\n"]; + lines++; + } + + if ([defaults boolForKey:@"showArtist"]) + { + NSString *trackArtist = [self runScriptAndReturnResult:@"tell application \"iTunes\"\nreturn artist of current track\nend tell"]; + stringToShow = [stringToShow stringByAppendingString:trackArtist]; + stringToShow = [stringToShow stringByAppendingString:@"\n"]; + lines++; + } + + if ([defaults boolForKey:@"showAlbum"]) + { + NSString *trackAlbum = [self runScriptAndReturnResult:@"tell application \"iTunes\"\nreturn album of current track\nend tell"]; + stringToShow = [stringToShow stringByAppendingString:trackAlbum]; + stringToShow = [stringToShow stringByAppendingString:@"\n"]; + lines++; + } + + //Rating - maybe + //Year - maybe + + if ([defaults boolForKey:@"showTime"]) + { + NSString *trackLength = [self runScriptAndReturnResult:@"tell application \"iTunes\"\nreturn time of current track\nend tell"]; + stringToShow = [stringToShow stringByAppendingString:trackLength]; + stringToShow = [stringToShow stringByAppendingString:@"\n"]; + lines++; + } + + { + int trackTimeLeft = [[self runScriptAndReturnResult:@"tell application \"iTunes\"\nreturn (duration of current track) - player position\nend tell"] intValue]; + int minutes = trackTimeLeft / 60, seconds = trackTimeLeft % 60; + if (seconds < 10) + { + stringToShow = [stringToShow stringByAppendingString: + [NSString stringWithFormat:@"Time Remaining: %i:0%i", minutes, seconds]]; + } + else + { + stringToShow = [stringToShow stringByAppendingString: + [NSString stringWithFormat:@"Time Remaining: %i:%i", minutes, seconds]]; + } + } + + statusController = [[StatusWindowController alloc] init]; + [statusController setTrackInfo:stringToShow lines:lines]; + [NSTimer scheduledTimerWithTimeInterval:3.0 target:self selector:@selector(fadeAndCloseStatusWindow) userInfo:nil repeats:NO]; + } +} + +- (void)showUpcomingSongs +{ + if (!statusController) + { + int numSongs = [[self runScriptAndReturnResult:@"tell application \"iTunes\"\nreturn number of tracks in current playlist\nend tell"] intValue]; + + if (numSongs > 0) + { + int numSongsInAdvance = [[NSUserDefaults standardUserDefaults] integerForKey:@"SongsInAdvance"]; + int curTrack = [[self runScriptAndReturnResult:@"tell application \"iTunes\"\nreturn index of current track\nend tell"] intValue]; + int i; + NSString *songs = @""; + + statusController = [[StatusWindowController alloc] init]; + for (i = curTrack + 1; i <= curTrack + numSongsInAdvance; i++) + { + if (i <= numSongs) + { + NSString *curSong = [self runScriptAndReturnResult:[NSString stringWithFormat:@"tell application \"iTunes\"\nreturn name of track %i of current playlist\nend tell", i]]; + songs = [songs stringByAppendingString:curSong]; + songs = [songs stringByAppendingString:@"\n"]; + } + } + [statusController setUpcomingSongs:songs numSongs:numSongsInAdvance]; + [NSTimer scheduledTimerWithTimeInterval:3.0 target:self selector:@selector(fadeAndCloseStatusWindow) userInfo:nil repeats:NO]; + } + } +} + +- (void)fadeAndCloseStatusWindow +{ + [statusController fadeWindowOut]; + [statusController release]; + statusController = nil; +} + +@end diff --git a/MenuTunesView.h b/MenuTunesView.h new file mode 100755 index 0000000..1eeb4f4 --- /dev/null +++ b/MenuTunesView.h @@ -0,0 +1,16 @@ +// +// MenuTunesView.h +// MenuTunes +// +// Created by Kent Sutherland on Tue Nov 19 2002. +// Copyright (c) 2002 Kent Sutherland. All rights reserved. +// + +#import + +@interface MenuTunesView : NSView +{ + NSImage *image, *altImage, *curImage; +} + +@end diff --git a/MenuTunesView.m b/MenuTunesView.m new file mode 100755 index 0000000..824683b --- /dev/null +++ b/MenuTunesView.m @@ -0,0 +1,44 @@ +// +// MenuTunesView.m +// MenuTunes +// +// Created by Kent Sutherland on Tue Nov 19 2002. +// Copyright (c) 2002 Kent Sutherland. All rights reserved. +// + +#import "MenuTunesView.h" + + +@implementation MenuTunesView + +- (id)initWithFrame:(NSRect)frame +{ + if ( (self = [super initWithFrame:frame]) ) + { + image = [NSImage imageNamed:@"menu"]; + altImage = [NSImage imageNamed:@"selected_image"]; + curImage = image; + } + return self; +} + +- (void)drawRect:(NSRect)rect +{ + [curImage compositeToPoint:NSMakePoint(0, 0) operation:NSCompositeSourceOver]; +} + +- (void)mouseDown:(NSEvent *)event +{ + curImage = altImage; + [self setNeedsDisplay:YES]; + [super mouseDown:event]; +} + +- (void)mouseUp:(NSEvent *)event +{ + curImage = image; + [self setNeedsDisplay:YES]; + [super mouseUp:event]; +} + +@end diff --git a/PreferencesController.h b/PreferencesController.h new file mode 100755 index 0000000..f1e3be7 --- /dev/null +++ b/PreferencesController.h @@ -0,0 +1,52 @@ +/* PreferencesController */ + +#import + +@class MenuTunes, KeyCombo; + +@interface PreferencesController : NSObject +{ + IBOutlet NSButton *albumCheckbox; + IBOutlet NSTableView *allTableView; + IBOutlet NSButton *artistCheckbox; + IBOutlet NSTextField *keyComboField; + IBOutlet NSPanel *keyComboPanel; + IBOutlet NSTableView *menuTableView; + IBOutlet NSButton *nameCheckbox; + IBOutlet NSButton *nextTrackButton; + IBOutlet NSButton *playPauseButton; + IBOutlet NSButton *previousTrackButton; + IBOutlet NSButton *songRatingCheckbox; + IBOutlet NSTextField *songsInAdvance; + IBOutlet NSButton *trackInfoButton; + IBOutlet NSButton *trackNumberCheckbox; + IBOutlet NSButton *trackTimeCheckbox; + IBOutlet NSButton *upcomingSongsButton; + IBOutlet NSWindow *window; + IBOutlet NSButton *yearCheckbox; + + MenuTunes *mt; + NSMutableArray *availableItems, *myItems; + NSArray *submenuItems; + + KeyCombo *combo, *playPauseCombo, *nextTrackCombo, + *prevTrackCombo, *trackInfoCombo, *upcomingSongsCombo; + NSString *setHotKey; +} +- (id)initWithMenuTunes:(MenuTunes *)menutunes; + +- (IBAction)apply:(id)sender; +- (IBAction)cancel:(id)sender; +- (IBAction)cancelHotKey:(id)sender; +- (IBAction)clearHotKey:(id)sender; +- (IBAction)okHotKey:(id)sender; +- (IBAction)save:(id)sender; +- (IBAction)setCurrentTrackInfo:(id)sender; +- (IBAction)setNextTrack:(id)sender; +- (IBAction)setPlayPause:(id)sender; +- (IBAction)setPreviousTrack:(id)sender; +- (IBAction)setUpcomingSongs:(id)sender; + +- (void)setHotKey:(NSString *)key; +- (void)setKeyCombo:(KeyCombo *)newCombo; +@end diff --git a/PreferencesController.m b/PreferencesController.m new file mode 100755 index 0000000..9568696 --- /dev/null +++ b/PreferencesController.m @@ -0,0 +1,546 @@ +#import "PreferencesController.h" +#import "MenuTunes.h" +#import "HotKeyCenter.h" + +@implementation PreferencesController + +- (id)initWithMenuTunes:(MenuTunes *)tunes; +{ + if ( (self = [super init]) ) + { + int i; + NSImageCell *imgCell = [[[NSImageCell alloc] init] autorelease]; + NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; + NSString *temp; + + mt = [tunes retain]; + + //Load the nib + [NSBundle loadNibNamed:@"Preferences" owner:self]; + + //Show our window + [window setLevel:NSStatusWindowLevel]; + [window center]; + [window makeKeyAndOrderFront:nil]; + + //Set the table view cells up + [imgCell setImageScaling:NSScaleNone]; + [[menuTableView tableColumnWithIdentifier:@"submenu"] setDataCell:imgCell]; + [[allTableView tableColumnWithIdentifier:@"submenu"] setDataCell:imgCell]; + + //Register for drag and drop + [menuTableView registerForDraggedTypes:[NSArray arrayWithObjects:@"MenuTableViewPboardType", @"AllTableViewPboardType", nil]]; + [allTableView registerForDraggedTypes:[NSArray arrayWithObjects:@"MenuTableViewPboardType", @"AllTableViewPboardType", nil]]; + + //Set the list of items you can have. + availableItems = [[NSMutableArray alloc] initWithObjects:@"Current Track Info", @"Upcoming Songs", @"Playlists", @"Play/Pause", @"Next Track", @"Previous Track", @"Fast Forward", @"Rewind", @"", nil]; + + //Get our preferred menu + myItems = [[[NSUserDefaults standardUserDefaults] arrayForKey:@"menu"] mutableCopy]; + if (myItems == nil) + { + myItems = [[NSMutableArray alloc] initWithObjects:@"Play/Pause", @"Next Track", @"Previous Track", @"Fast Forward", @"Rewind", @"", @"Upcoming Songs", @"Playlists", @"", @"PreferencesÉ", @"Quit", @"", @"Current Track Info", nil]; + [[NSUserDefaults standardUserDefaults] setObject:myItems forKey:@"menu"]; + } + + //Delete items in the availableItems array that are already part of the menu + for (i = 0; i < [myItems count]; i++) + { + NSString *item = [myItems objectAtIndex:i]; + if (![item isEqualToString:@""]) + { + [availableItems removeObject:item]; + } + } + + //Items that show should a submenu image + submenuItems = [[NSArray alloc] initWithObjects:@"Upcoming Songs", @"Playlists", nil]; + + //Fill in the number of songs in advance to show field + if ([defaults integerForKey:@"SongsInAdvance"]) + { + [songsInAdvance setIntValue:[defaults integerForKey:@"SongsInAdvance"]]; + } + else + { + [songsInAdvance setIntValue:5]; + } + + //Fill in hot key buttons + if ([defaults objectForKey:@"PlayPause"]) + { + playPauseCombo = [defaults keyComboForKey:@"PlayPause"]; + [playPauseButton setTitle:[playPauseCombo userDisplayRep]]; + } + else + { + playPauseCombo = [[KeyCombo alloc] init]; + } + + if ([defaults objectForKey:@"NextTrack"]) + { + nextTrackCombo = [defaults keyComboForKey:@"NextTrack"]; + [nextTrackButton setTitle:[nextTrackCombo userDisplayRep]]; + } + else + { + nextTrackCombo = [[KeyCombo alloc] init]; + } + + if ([defaults objectForKey:@"PrevTrack"]) + { + prevTrackCombo = [defaults keyComboForKey:@"PrevTrack"]; + [previousTrackButton setTitle:[prevTrackCombo userDisplayRep]]; + } + else + { + prevTrackCombo = [[KeyCombo alloc] init]; + } + + if ([defaults objectForKey:@"TrackInfo"]) + { + trackInfoCombo = [defaults keyComboForKey:@"TrackInfo"]; + [trackInfoButton setTitle:[trackInfoCombo userDisplayRep]]; + } + else + { + trackInfoCombo = [[KeyCombo alloc] init]; + } + + if ([defaults objectForKey:@"UpcomingSongs"]) + { + upcomingSongsCombo = [defaults keyComboForKey:@"UpcomingSongs"]; + [upcomingSongsButton setTitle:[upcomingSongsCombo userDisplayRep]]; + } + else + { + upcomingSongsCombo = [[KeyCombo alloc] init]; + } + + //Check current track info buttons + + //Album and name get special treatment because they are defaults + if ( (temp = [defaults stringForKey:@"showAlbum"]) ) + { + if ((temp == nil) || [temp isEqualToString:@"1"]) + { + [albumCheckbox setState:NSOnState]; + } + else + { + [albumCheckbox setState:NSOffState]; + } + } + + if ( (temp = [defaults stringForKey:@"showName"]) ) + { + if ((temp == nil) || [temp isEqualToString:@"1"]) + { + [nameCheckbox setState:NSOnState]; + } + else + { + [nameCheckbox setState:NSOffState]; + } + } + + [artistCheckbox setState:[defaults boolForKey:@"showArtist"] ? NSOnState : NSOffState]; + [songRatingCheckbox setState:[defaults boolForKey:@"showRating"] ? NSOnState : NSOffState]; + [trackNumberCheckbox setState:[defaults boolForKey:@"showTrackNum"] ? NSOnState : NSOffState]; + [trackTimeCheckbox setState:[defaults boolForKey:@"showTime"] ? NSOnState : NSOffState]; + [yearCheckbox setState:[defaults boolForKey:@"showYear"] ? NSOnState : NSOffState]; + } + return self; +} + +- (void)dealloc +{ + [self setKeyCombo:nil]; + [playPauseCombo release]; + [nextTrackCombo release]; + [prevTrackCombo release]; + [trackInfoCombo release]; + [upcomingSongsCombo release]; + [keyComboPanel release]; + [menuTableView setDataSource:nil]; + [allTableView setDataSource:nil]; + [mt release]; + [availableItems release]; + [submenuItems release]; + [myItems release]; +} + +- (IBAction)apply:(id)sender +{ + NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; + [defaults setObject:myItems forKey:@"menu"]; + + //Set key combos + [defaults setKeyCombo:playPauseCombo forKey:@"PlayPause"]; + [defaults setKeyCombo:nextTrackCombo forKey:@"NextTrack"]; + [defaults setKeyCombo:prevTrackCombo forKey:@"PrevTrack"]; + [defaults setKeyCombo:trackInfoCombo forKey:@"TrackInfo"]; + [defaults setKeyCombo:upcomingSongsCombo forKey:@"UpcomingSongs"]; + + //Set info checkboxes + [defaults setBool:[albumCheckbox state] forKey:@"showAlbum"]; + [defaults setBool:[nameCheckbox state] forKey:@"showName"]; + [defaults setBool:[artistCheckbox state] forKey:@"showArtist"]; + [defaults setBool:[songRatingCheckbox state] forKey:@"showRating"]; + [defaults setBool:[trackNumberCheckbox state] forKey:@"showTrackNum"]; + [defaults setBool:[trackTimeCheckbox state] forKey:@"showTime"]; + [defaults setBool:[yearCheckbox state] forKey:@"showYear"]; + + //Set songs in advance + if ([songsInAdvance intValue]) + { + [defaults setInteger:[songsInAdvance intValue] forKey:@"SongsInAdvance"]; + } + else + { + [defaults setInteger:5 forKey:@"SongsInAdvance"]; + } + + [mt rebuildMenu]; + [mt clearHotKeys]; +} + +- (IBAction)cancel:(id)sender +{ + [window close]; + [mt closePreferences]; +} + +- (IBAction)cancelHotKey:(id)sender +{ + [[NSNotificationCenter defaultCenter] removeObserver:self]; + [NSApp endSheet:keyComboPanel]; + [keyComboPanel orderOut:nil]; +} + +- (IBAction)clearHotKey:(id)sender +{ + [self setKeyCombo:[KeyCombo clearKeyCombo]]; +} + +- (IBAction)okHotKey:(id)sender +{ + NSString *string; + if (([combo modifiers] <= 0) && ([combo keyCode] >= 0)) + { + [window setLevel:NSNormalWindowLevel]; + NSRunAlertPanel(@"Bad Key Combo", @"Please enter a valid key combo. A valid combo must have a modifier key in it. (Command, option, shift, control).", @"OK", nil, nil, nil); + [window setLevel:NSStatusWindowLevel]; + return; + } + + string = [combo userDisplayRep]; + + if (string == nil) + { + string = @"None"; + } + if ([setHotKey isEqualToString:@"PlayPause"]) + { + if (([combo isEqual:nextTrackCombo] || [combo isEqual:prevTrackCombo] || + [combo isEqual:trackInfoCombo] || [combo isEqual:upcomingSongsCombo]) && + !(([combo modifiers] == -1) && ([combo keyCode] == -1))) + { + [window setLevel:NSNormalWindowLevel]; + NSRunAlertPanel(@"Duplicate Key Combo", @"Please choose a unique key combo.", @"OK", nil, nil, nil); + [window setLevel:NSStatusWindowLevel]; + return; + } + playPauseCombo = [combo copy]; + [playPauseButton setTitle:string]; + } + else if ([setHotKey isEqualToString:@"NextTrack"]) + { + if (([combo isEqual:playPauseCombo] || [combo isEqual:prevTrackCombo] || + [combo isEqual:trackInfoCombo] || [combo isEqual:upcomingSongsCombo]) && + !(([combo modifiers] == -1) && ([combo keyCode] == -1))) + { + [window setLevel:NSNormalWindowLevel]; + NSRunAlertPanel(@"Duplicate Key Combo", @"Please choose a unique key combo.", @"OK", nil, nil, nil); + [window setLevel:NSStatusWindowLevel]; + return; + } + nextTrackCombo = [combo copy]; + [nextTrackButton setTitle:string]; + } + else if ([setHotKey isEqualToString:@"PrevTrack"]) + { + if (([combo isEqual:nextTrackCombo] || [combo isEqual:playPauseCombo] || + [combo isEqual:trackInfoCombo] || [combo isEqual:upcomingSongsCombo]) && + !(([combo modifiers] == -1) && ([combo keyCode] == -1))) + { + [window setLevel:NSNormalWindowLevel]; + NSRunAlertPanel(@"Duplicate Key Combo", @"Please choose a unique key combo.", @"OK", nil, nil, nil); + [window setLevel:NSStatusWindowLevel]; + return; + } + prevTrackCombo = [combo copy]; + [previousTrackButton setTitle:string]; + } + else if ([setHotKey isEqualToString:@"TrackInfo"]) + { + if (([combo isEqual:nextTrackCombo] || [combo isEqual:prevTrackCombo] || + [combo isEqual:playPauseCombo] || [combo isEqual:upcomingSongsCombo]) && + !(([combo modifiers] == -1) && ([combo keyCode] == -1))) + { + [window setLevel:NSNormalWindowLevel]; + NSRunAlertPanel(@"Duplicate Key Combo", @"Please choose a unique key combo.", @"OK", nil, nil, nil); + [window setLevel:NSStatusWindowLevel]; + return; + } + trackInfoCombo = [combo copy]; + [trackInfoButton setTitle:string]; + } + else if ([setHotKey isEqualToString:@"UpcomingSongs"]) + { + if (([combo isEqual:nextTrackCombo] || [combo isEqual:prevTrackCombo] || + [combo isEqual:trackInfoCombo] || [combo isEqual:playPauseCombo]) && + !(([combo modifiers] == -1) && ([combo keyCode] == -1))) + { + [window setLevel:NSNormalWindowLevel]; + NSRunAlertPanel(@"Duplicate Key Combo", @"Please choose a unique key combo.", @"OK", nil, nil, nil); + [window setLevel:NSStatusWindowLevel]; + return; + } + upcomingSongsCombo = [combo copy]; + [upcomingSongsButton setTitle:string]; + } + [self cancelHotKey:sender]; +} + +- (IBAction)save:(id)sender +{ + [self apply:nil]; + [window close]; + [mt closePreferences]; +} + +- (IBAction)setCurrentTrackInfo:(id)sender +{ + [self setKeyCombo:trackInfoCombo]; + [self setHotKey:@"TrackInfo"]; +} + +- (IBAction)setNextTrack:(id)sender +{ + [self setKeyCombo:nextTrackCombo]; + [self setHotKey:@"NextTrack"]; +} + +- (IBAction)setPlayPause:(id)sender +{ + [self setKeyCombo:playPauseCombo]; + [self setHotKey:@"PlayPause"]; +} + +- (IBAction)setPreviousTrack:(id)sender +{ + [self setKeyCombo:prevTrackCombo]; + [self setHotKey:@"PrevTrack"]; +} + +- (IBAction)setUpcomingSongs:(id)sender +{ + [self setKeyCombo:upcomingSongsCombo]; + [self setHotKey:@"UpcomingSongs"]; +} + +- (void)setHotKey:(NSString *)key +{ + setHotKey = key; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyEvent:) name:@"KeyBroadcasterEvent" object:nil]; + [NSApp beginSheet:keyComboPanel modalForWindow:window modalDelegate:self didEndSelector:nil contextInfo:nil]; +} + +- (void)keyEvent:(NSNotification *)note +{ + NSDictionary *info = [note userInfo]; + short keyCode; + long modifiers; + KeyCombo *newCombo; + + keyCode = [[info objectForKey:@"KeyCode"] shortValue]; + modifiers = [[info objectForKey:@"Modifiers"] longValue]; + + newCombo = [[KeyCombo alloc] initWithKeyCode:keyCode andModifiers:modifiers]; + [self setKeyCombo:newCombo]; +} + +- (void)setKeyCombo:(KeyCombo *)newCombo +{ + NSString *string; + [combo release]; + combo = [newCombo copy]; + + string = [combo userDisplayRep]; + if (string == nil) + { + string = @""; + } + [keyComboField setStringValue:string]; +} + +// +// +// Table View Datasource Methods +// +// + +- (int)numberOfRowsInTableView:(NSTableView *)aTableView +{ + if (aTableView == menuTableView) + { + return [myItems count]; + } + else + { + return [availableItems count]; + } +} + +- (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex +{ + if (aTableView == menuTableView) + { + if ([[aTableColumn identifier] isEqualToString:@"name"]) + { + return [myItems objectAtIndex:rowIndex]; + } + else + { + if ([submenuItems containsObject:[myItems objectAtIndex:rowIndex]]) + { + return [NSImage imageNamed:@"submenu"]; + } + else + { + return nil; + } + } + } + else + { + if ([[aTableColumn identifier] isEqualToString:@"name"]) + { + return [availableItems objectAtIndex:rowIndex]; + } + else + { + if ([submenuItems containsObject:[availableItems objectAtIndex:rowIndex]]) + { + return [NSImage imageNamed:@"submenu"]; + } + else + { + return nil; + } + } + } +} + +- (BOOL)tableView:(NSTableView *)tableView writeRows:(NSArray*)rows toPasteboard:(NSPasteboard*)pboard +{ + if (tableView == menuTableView) + { + [pboard declareTypes:[NSArray arrayWithObjects:@"MenuTableViewPboardType", nil] owner:self]; + [pboard setString:[[rows objectAtIndex:0] stringValue] forType:@"MenuTableViewPboardType"]; + return YES; + } + + if (tableView == allTableView) + { + [pboard declareTypes:[NSArray arrayWithObjects:@"AllTableViewPboardType", nil] owner:self]; + [pboard setString:[[rows objectAtIndex:0] stringValue] forType:@"AllTableViewPboardType"]; + return YES; + } + return NO; +} + +- (BOOL)tableView:(NSTableView*)tableView acceptDrop:(id )info row:(int)row dropOperation:(NSTableViewDropOperation)operation +{ + NSPasteboard *pb; + int dragRow; + NSString *dragData, *temp; + + pb = [info draggingPasteboard]; + + if ([[pb types] containsObject:@"MenuTableViewPboardType"]) + { + dragData = [pb stringForType:@"MenuTableViewPboardType"]; + dragRow = [dragData intValue]; + temp = [myItems objectAtIndex:dragRow]; + [myItems removeObjectAtIndex:dragRow]; + + if (tableView == menuTableView) + { + if (row > dragRow) + { + [myItems insertObject:temp atIndex:row - 1]; + } + else + { + [myItems insertObject:temp atIndex:row]; + } + } + else + { + if (![temp isEqualToString:@""]) + { + [availableItems addObject:temp]; + } + } + } + else if ([[pb types] containsObject:@"AllTableViewPboardType"]) + { + dragData = [pb stringForType:@"AllTableViewPboardType"]; + dragRow = [dragData intValue]; + temp = [availableItems objectAtIndex:dragRow]; + + if (![temp isEqualToString:@""]) + { + [availableItems removeObjectAtIndex:dragRow]; + } + [myItems insertObject:temp atIndex:row]; + } + + [menuTableView reloadData]; + [allTableView reloadData]; + return YES; +} + +- (NSDragOperation)tableView:(NSTableView*)tableView validateDrop:(id )info proposedRow:(int)row proposedDropOperation:(NSTableViewDropOperation)operation +{ + if (tableView == allTableView) + { + if ([[[info draggingPasteboard] types] containsObject:@"AllTableViewPboardType"]) + { + return NSDragOperationNone; + } + + if ([[[info draggingPasteboard] types] containsObject:@"MenuTableViewPboardType"]) + { + NSString *item = [myItems objectAtIndex:[[[info draggingPasteboard] stringForType:@"MenuTableViewPboardType"] intValue]]; + if ([item isEqualToString:@"PreferencesÉ"] || [item isEqualToString:@"Quit"]) + { + return NSDragOperationNone; + } + } + + [tableView setDropRow:-1 dropOperation:NSTableViewDropOn]; + return NSDragOperationGeneric; + } + + if (operation == NSTableViewDropOn || row == -1) + { + return NSDragOperationNone; + } + + return NSDragOperationGeneric; +} + +@end diff --git a/StatusWindow.h b/StatusWindow.h new file mode 100755 index 0000000..c97566c --- /dev/null +++ b/StatusWindow.h @@ -0,0 +1,8 @@ +/* StatusWindow */ + +#import + +@interface StatusWindow : NSWindow +{ +} +@end diff --git a/StatusWindow.m b/StatusWindow.m new file mode 100755 index 0000000..946e544 --- /dev/null +++ b/StatusWindow.m @@ -0,0 +1,18 @@ +#import "StatusWindow.h" + +@implementation StatusWindow + +- (id)initWithContentRect:(NSRect)rect styleMask:(unsigned int)mask backing:(NSBackingStoreType)type defer:(BOOL)flag +{ + if ( (self = [super initWithContentRect:rect styleMask:NSBorderlessWindowMask backing:type defer:flag]) ) + { + [self setHasShadow:NO]; + [self setOpaque:NO]; + [self setLevel:NSStatusWindowLevel]; + [self setIgnoresMouseEvents:YES]; + [self setBackgroundColor:[NSColor colorWithCalibratedRed:0.5 green:0.5 blue:0.5 alpha:0.6]]; + } + return self; +} + +@end diff --git a/StatusWindowController.h b/StatusWindowController.h new file mode 100755 index 0000000..ccf3d8a --- /dev/null +++ b/StatusWindowController.h @@ -0,0 +1,15 @@ +/* StatusWindowController */ + +#import + +@class StatusWindow; + +@interface StatusWindowController : NSObject +{ + IBOutlet NSTextField *statusField; + IBOutlet StatusWindow *statusWindow; +} +- (void)setUpcomingSongs:(NSString *)string numSongs:(int)songs; +- (void)setTrackInfo:(NSString *)string lines:(int)lines; +- (void)fadeWindowOut; +@end diff --git a/StatusWindowController.m b/StatusWindowController.m new file mode 100755 index 0000000..a9859c3 --- /dev/null +++ b/StatusWindowController.m @@ -0,0 +1,50 @@ +#import "StatusWindowController.h" +#import "StatusWindow.h" + +@implementation StatusWindowController + +- (id)init +{ + if ( (self = [super init]) ) + { + [NSBundle loadNibNamed:@"StatusWindow" owner:self]; + [statusWindow center]; + } + return self; +} + +- (void)setUpcomingSongs:(NSString *)string numSongs:(int)songs +{ + [statusField setStringValue:string]; + [statusWindow setFrame:NSMakeRect(0, 0, 300, 40 + (songs * 17)) display:NO]; + [statusWindow center]; + [statusWindow makeKeyAndOrderFront:nil]; +} + +- (void)setTrackInfo:(NSString *)string lines:(int)lines +{ + [statusField setStringValue:string]; + [statusWindow setFrame:NSMakeRect(0, 0, 316, 40 + (lines * 17)) display:NO]; + [statusWindow center]; + [statusWindow makeKeyAndOrderFront:nil]; +} + +- (void)fadeWindowOut +{ + [NSThread detachNewThreadSelector:@selector(fadeOutAux) toTarget:self withObject:nil]; + +} + +- (void)fadeOutAux +{ + NSAutoreleasePool *p00l = [[NSAutoreleasePool alloc] init]; + float i; + for (i = 1.0; i > 0; i -= .003) + { + [statusWindow setAlphaValue:i]; + } + [statusWindow close]; + [p00l release]; +} + +@end diff --git a/main.m b/main.m new file mode 100755 index 0000000..0eb6e57 --- /dev/null +++ b/main.m @@ -0,0 +1,14 @@ +// +// main.m +// MenuTunes +// +// Created by Kent Sutherland on Sun Nov 17 2002. +// Copyright (c) 2002 Kent Sutherland. All rights reserved. +// + +#import + +int main(int argc, const char *argv[]) +{ + return NSApplicationMain(argc, argv); +} diff --git a/submenu.tiff b/submenu.tiff new file mode 100755 index 0000000000000000000000000000000000000000..b35f0f2731d1253c0443acd2e48ca60b067069cc GIT binary patch literal 1500 zcmebEWzb?^V7b6B^1y#8c^PC5HOzvUL#bKF=1^i5x;f;Uh0Ppt%)(|4IZ=pi4moCk z^dOr