5 // Created by Joseph Spiros on Wed Apr 30 2003.
6 // Copyright (c) 2003 iThink Software. All rights reserved.
9 #import "MenuController.h"
10 #import "MainController.h"
11 #import <ITFoundation/ITDebug.h>
12 #import <ITKit/ITHotKeyCenter.h>
13 #import <ITKit/ITHotKey.h>
14 #import <ITKit/ITKeyCombo.h>
16 @interface MenuController (SubmenuMethods)
17 - (NSMenu *)ratingMenu;
18 - (NSMenu *)upcomingSongsMenu;
19 - (NSMenu *)playlistsMenu;
21 - (void)setKeyEquivalentForCode:(short)code andModifiers:(long)modifiers
22 onItem:(NSMenuItem *)item;
25 @implementation MenuController
29 if ( (self = [super init]) ) {
30 _menuLayout = [[NSMutableArray alloc] initWithCapacity:0];
37 NSMenu *menu = [[NSMenu alloc] initWithTitle:@""];
38 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
39 NSArray *menuArray = [defaults arrayForKey:@"menu"];
40 NSEnumerator *enumerator = [menuArray objectEnumerator];
43 NSEnumerator *itemEnum;
45 NSArray *hotKeys = [[ITHotKeyCenter sharedCenter] allHotKeys];
48 _currentPlaylist = [currentRemote currentPlaylistIndex];
49 _playingRadio = ([currentRemote currentPlaylistClass] == ITMTRemotePlayerRadioPlaylist);
51 //Kill the old submenu items
52 if ( (tempItem = [_currentMenu itemWithTag:1]) ) {
53 [tempItem setSubmenu:nil];
56 if ( (tempItem = [_currentMenu itemWithTag:2]) ) {
57 [tempItem setSubmenu:nil];
60 if ( (tempItem = [_currentMenu itemWithTag:3]) ) {
61 [tempItem setSubmenu:nil];
64 if ( (tempItem = [_currentMenu itemWithTag:4]) ) {
65 [tempItem setSubmenu:nil];
69 while ( (nextObject = [enumerator nextObject]) ) {
71 if ([nextObject isEqualToString:@"playPause"]) {
72 tempItem = [menu addItemWithTitle:NSLocalizedString(@"play", @"Play")
73 action:@selector(performMainMenuAction:)
75 [tempItem setTag:MTMenuPlayPauseItem];
76 [tempItem setTarget:self];
78 itemEnum = [hotKeys objectEnumerator];
79 while ( (hotKey = [itemEnum nextObject]) ) {
80 if ([[hotKey name] isEqualToString:@"PlayPause"]) {
81 ITKeyCombo *combo = [hotKey keyCombo];
82 [self setKeyEquivalentForCode:[combo keyCode]
83 andModifiers:[combo modifiers]
88 switch ([currentRemote playerPlayingState]) {
89 case ITMTRemotePlayerPlaying:
90 [tempItem setTitle:NSLocalizedString(@"pause", @"Pause")];
92 case ITMTRemotePlayerRewinding:
93 case ITMTRemotePlayerForwarding:
94 [tempItem setTitle:NSLocalizedString(@"resume", @"Resume")];
99 } else if ([nextObject isEqualToString:@"nextTrack"]) {
100 tempItem = [menu addItemWithTitle:NSLocalizedString(@"nextTrack", @"Next Track")
101 action:@selector(performMainMenuAction:)
104 itemEnum = [hotKeys objectEnumerator];
105 while ( (hotKey = [itemEnum nextObject]) ) {
106 if ([[hotKey name] isEqualToString:@"NextTrack"]) {
107 ITKeyCombo *combo = [hotKey keyCombo];
108 [self setKeyEquivalentForCode:[combo keyCode]
109 andModifiers:[combo modifiers]
114 if (_currentPlaylist) {
115 [tempItem setTag:MTMenuNextTrackItem];
116 [tempItem setTarget:self];
118 } else if ([nextObject isEqualToString:@"prevTrack"]) {
119 tempItem = [menu addItemWithTitle:NSLocalizedString(@"prevTrack", @"Previous Track")
120 action:@selector(performMainMenuAction:)
123 itemEnum = [hotKeys objectEnumerator];
124 while ( (hotKey = [itemEnum nextObject]) ) {
125 if ([[hotKey name] isEqualToString:@"PrevTrack"]) {
126 ITKeyCombo *combo = [hotKey keyCombo];
127 [self setKeyEquivalentForCode:[combo keyCode]
128 andModifiers:[combo modifiers]
133 if (_currentPlaylist) {
134 [tempItem setTag:MTMenuPreviousTrackItem];
135 [tempItem setTarget:self];
137 } else if ([nextObject isEqualToString:@"fastForward"]) {
138 tempItem = [menu addItemWithTitle:NSLocalizedString(@"fastForward", @"Fast Forward")
139 action:@selector(performMainMenuAction:)
141 if (_currentPlaylist) {
142 [tempItem setTag:MTMenuFastForwardItem];
143 [tempItem setTarget:self];
145 } else if ([nextObject isEqualToString:@"rewind"]) {
146 tempItem = [menu addItemWithTitle:NSLocalizedString(@"rewind", @"Rewind")
147 action:@selector(performMainMenuAction:)
149 if (_currentPlaylist) {
150 [tempItem setTag:MTMenuRewindItem];
151 [tempItem setTarget:self];
153 } else if ([nextObject isEqualToString:@"showPlayer"]) {
154 tempItem = [menu addItemWithTitle:[NSString stringWithFormat:@"%@ %@",
155 NSLocalizedString(@"show", @"Show"),
156 [[[MainController sharedController] currentRemote] playerSimpleName]]
157 action:@selector(performMainMenuAction:)
160 itemEnum = [hotKeys objectEnumerator];
161 while ( (hotKey = [itemEnum nextObject]) ) {
162 if ([[hotKey name] isEqualToString:@"ShowPlayer"]) {
163 ITKeyCombo *combo = [hotKey keyCombo];
164 [self setKeyEquivalentForCode:[combo keyCode]
165 andModifiers:[combo modifiers]
170 [tempItem setTarget:self];
171 [tempItem setTag:MTMenuShowPlayerItem];
172 } else if ([nextObject isEqualToString:@"preferences"]) {
173 tempItem = [menu addItemWithTitle:NSLocalizedString(@"preferences", @"Preferences...")
174 action:@selector(performMainMenuAction:)
176 [tempItem setTag:MTMenuPreferencesItem];
177 [tempItem setTarget:self];
178 } else if ([nextObject isEqualToString:@"quit"]) {
179 tempItem = [menu addItemWithTitle:NSLocalizedString(@"quit", @"Quit")
180 action:@selector(performMainMenuAction:)
182 [tempItem setTag:MTMenuQuitItem];
183 [tempItem setTarget:self];
184 } else if ([nextObject isEqualToString:@"trackInfo"]) {
185 //Handle playing radio too
186 if (_currentPlaylist) {
187 NSString *title = [currentRemote currentSongTitle];
189 [menu addItemWithTitle:NSLocalizedString(@"nowPlaying", @"Now Playing") action:NULL keyEquivalent:@""];
191 if ([title length] > 0) {
192 [menu addItemWithTitle:[NSString stringWithFormat:@" %@", title]
197 if ([defaults boolForKey:@"showAlbum"]) {
198 NSString *curAlbum = [currentRemote currentSongAlbum];
199 if ([curAlbum length]) {
200 [menu addItemWithTitle:[NSString stringWithFormat:@" %@", curAlbum]
206 if ([defaults boolForKey:@"showArtist"]) {
207 NSString *curArtist = [currentRemote currentSongArtist];
208 if ([curArtist length]) {
209 [menu addItemWithTitle:[NSString stringWithFormat:@" %@", curArtist]
215 if ([defaults boolForKey:@"showTrackNumber"]) {
216 int track = [currentRemote currentSongTrack];
218 [menu addItemWithTitle:[NSString stringWithFormat:@" %@ %i", NSLocalizedString(@"track", @"Track"), track]
224 if ([defaults boolForKey:@"showTime"]) {
225 int left = [[currentRemote currentSongRemaining] intValue];
226 NSString *remaining = [NSString stringWithFormat:@"%i:%02i", left / 60, left % 60];
227 [menu addItemWithTitle:[NSString stringWithFormat:@" %@/%@", remaining, [currentRemote currentSongLength]]
232 if ([defaults boolForKey:@"showTrackRating"]) {
233 NSString *string = nil;
234 switch ((int)([currentRemote currentSongRating] * 5)) {
236 string = [NSString stringWithUTF8String:"☆☆☆☆☆"];
239 string = [NSString stringWithUTF8String:"★☆☆☆☆"];
242 string = [NSString stringWithUTF8String:"★★☆☆☆"];
245 string = [NSString stringWithUTF8String:"★★★☆☆"];
248 string = [NSString stringWithUTF8String:"★★★★☆"];
251 string = [NSString stringWithUTF8String:"★★★★★"];
254 [menu addItemWithTitle:
255 [@" " stringByAppendingString:string]
260 [menu addItemWithTitle:NSLocalizedString(@"noSong", @"No Song") action:NULL keyEquivalent:@""];
262 } else if ([nextObject isEqualToString:@"separator"]) {
263 [menu addItem:[NSMenuItem separatorItem]];
265 } else if ([nextObject isEqualToString:@"songRating"]) {
266 tempItem = [menu addItemWithTitle:NSLocalizedString(@"songRating", @"Song Rating")
269 [tempItem setSubmenu:_ratingMenu];
271 if (_playingRadio || !_currentPlaylist) {
272 [tempItem setEnabled:NO];
275 itemEnum = [[_ratingMenu itemArray] objectEnumerator];
276 while ( (tempItem = [itemEnum nextObject]) ) {
277 [tempItem setState:NSOffState];
280 [[_ratingMenu itemAtIndex:([currentRemote currentSongRating] * 5)] setState:NSOnState];
281 } else if ([nextObject isEqualToString:@"upcomingSongs"]) {
282 tempItem = [menu addItemWithTitle:NSLocalizedString(@"upcomingSongs", @"Upcoming Songs")
285 [tempItem setSubmenu:_upcomingSongsMenu];
287 if (_playingRadio || !_currentPlaylist) {
288 [tempItem setEnabled:NO];
290 } else if ([nextObject isEqualToString:@"playlists"]) {
291 tempItem = [menu addItemWithTitle:NSLocalizedString(@"playlists", @"Playlists")
294 [tempItem setSubmenu:_playlistsMenu];
296 } else if ([nextObject isEqualToString:@"eqPresets"]) {
297 tempItem = [menu addItemWithTitle:NSLocalizedString(@"eqPresets", @"EQ Presets")
300 [tempItem setSubmenu:_eqMenu];
303 itemEnum = [[_eqMenu itemArray] objectEnumerator];
304 while ( (tempItem = [itemEnum nextObject]) ) {
305 [tempItem setState:NSOffState];
307 [[_eqMenu itemAtIndex:([currentRemote currentEQPresetIndex] - 1)] setState:NSOnState];
310 [_currentMenu release];
315 - (NSMenu *)menuForNoPlayer
317 NSMenu *menu = [[NSMenu alloc] initWithTitle:@""];
318 NSMenuItem *tempItem;
319 tempItem = [menu addItemWithTitle:[NSString stringWithFormat:@"%@ %@", NSLocalizedString(@"open", @"Open"), [[[MainController sharedController] currentRemote] playerSimpleName]] action:@selector(performMainMenuAction:) keyEquivalent:@""];
320 [tempItem setTag:MTMenuShowPlayerItem];
321 [tempItem setTarget:self];
322 [menu addItem:[NSMenuItem separatorItem]];
323 tempItem = [menu addItemWithTitle:NSLocalizedString(@"preferences", @"Preferences...") action:@selector(performMainMenuAction:) keyEquivalent:@""];
324 [tempItem setTag:MTMenuPreferencesItem];
325 [tempItem setTarget:self];
326 tempItem = [menu addItemWithTitle:NSLocalizedString(@"quit", @"Quit") action:@selector(performMainMenuAction:) keyEquivalent:@""];
327 [tempItem setTag:MTMenuQuitItem];
328 [tempItem setTarget:self];
329 return [menu autorelease];
332 - (void)rebuildSubmenus
334 currentRemote = [[MainController sharedController] currentRemote];
335 _currentPlaylist = [currentRemote currentPlaylistIndex];
336 _currentTrack = [currentRemote currentSongIndex];
337 _playingRadio = ([currentRemote currentPlaylistClass] == ITMTRemotePlayerRadioPlaylist);
339 [_ratingMenu release];
340 [_upcomingSongsMenu release];
341 [_playlistsMenu release];
343 _ratingMenu = [self ratingMenu];
344 _upcomingSongsMenu = [self upcomingSongsMenu];
345 _playlistsMenu = [self playlistsMenu];
346 _eqMenu = [self eqMenu];
349 - (NSMenu *)ratingMenu
351 NSMenu *ratingMenu = [[NSMenu alloc] initWithTitle:@""];
352 NSEnumerator *itemEnum;
355 SEL itemSelector = @selector(performRatingMenuAction:);
357 [ratingMenu addItemWithTitle:[NSString stringWithUTF8String:"☆☆☆☆☆"] action:nil keyEquivalent:@""];
358 [ratingMenu addItemWithTitle:[NSString stringWithUTF8String:"★☆☆☆☆"] action:nil keyEquivalent:@""];
359 [ratingMenu addItemWithTitle:[NSString stringWithUTF8String:"★★☆☆☆"] action:nil keyEquivalent:@""];
360 [ratingMenu addItemWithTitle:[NSString stringWithUTF8String:"★★★☆☆"] action:nil keyEquivalent:@""];
361 [ratingMenu addItemWithTitle:[NSString stringWithUTF8String:"★★★★☆"] action:nil keyEquivalent:@""];
362 [ratingMenu addItemWithTitle:[NSString stringWithUTF8String:"★★★★★"] action:nil keyEquivalent:@""];
364 itemEnum = [[ratingMenu itemArray] objectEnumerator];
365 while ( (anItem = [itemEnum nextObject]) ) {
366 [anItem setAction:itemSelector];
367 [anItem setTarget:self];
368 [anItem setTag:itemTag];
374 - (NSMenu *)upcomingSongsMenu
376 NSMenu *upcomingSongsMenu = [[NSMenu alloc] initWithTitle:@""];
377 int numSongs = [currentRemote numberOfSongsInPlaylistAtIndex:_currentPlaylist];
378 int numSongsInAdvance = [[NSUserDefaults standardUserDefaults] integerForKey:@"SongsInAdvance"];
380 if (_currentPlaylist && !_playingRadio) {
384 for (i = _currentTrack + 1; i <= _currentTrack + numSongsInAdvance; i++) {
386 NSString *curSong = [currentRemote songTitleAtIndex:i];
387 NSMenuItem *songItem;
388 songItem = [upcomingSongsMenu addItemWithTitle:curSong action:@selector(performUpcomingSongsMenuAction:) keyEquivalent:@""];
390 [songItem setTarget:self];
397 return upcomingSongsMenu;
400 - (NSMenu *)playlistsMenu
402 NSMenu *playlistsMenu = [[NSMenu alloc] initWithTitle:@""];
403 NSArray *playlists = [currentRemote playlists];
404 NSMenuItem *tempItem;
407 for (i = 0; i < [playlists count]; i++) {
408 tempItem = [playlistsMenu addItemWithTitle:[playlists objectAtIndex:i] action:@selector(performPlaylistMenuAction:) keyEquivalent:@""];
409 [tempItem setTag:i + 1];
410 [tempItem setTarget:self];
413 if (!_playingRadio && _currentPlaylist) {
414 [[playlistsMenu itemAtIndex:_currentPlaylist - 1] setState:NSOnState];
416 return playlistsMenu;
421 NSMenu *eqMenu = [[NSMenu alloc] initWithTitle:@""];
422 NSArray *eqPresets = [currentRemote eqPresets];
423 NSMenuItem *tempItem;
426 for (i = 0; i < [eqPresets count]; i++) {
428 if ( ( name = [eqPresets objectAtIndex:i] ) ) {
429 tempItem = [eqMenu addItemWithTitle:name action:@selector(performEqualizerMenuAction:) keyEquivalent:@""];
431 [tempItem setTarget:self];
437 - (void)performMainMenuAction:(id)sender
439 switch ( [sender tag] )
441 case MTMenuPlayPauseItem:
442 ITDebugLog(@"MenuController: Play/Pause");
443 [[MainController sharedController] playPause];
445 case MTMenuFastForwardItem:
446 ITDebugLog(@"MenuController: Fast Forward");
447 [[MainController sharedController] fastForward];
449 case MTMenuRewindItem:
450 ITDebugLog(@"MenuController: Rewind");
451 [[MainController sharedController] rewind];
453 case MTMenuPreviousTrackItem:
454 ITDebugLog(@"MenuController: Previous Track");
455 [[MainController sharedController] prevSong];
457 case MTMenuNextTrackItem:
458 ITDebugLog(@"MenuController: Next Track");
459 [[MainController sharedController] nextSong];
461 case MTMenuShowPlayerItem:
462 ITDebugLog(@"MainController: Show Main Interface");
463 [[MainController sharedController] showPlayer];
465 case MTMenuPreferencesItem:
466 ITDebugLog(@"MenuController: Preferences...");
467 [[MainController sharedController] showPreferences];
470 ITDebugLog(@"MenuController: Quit");
471 [[MainController sharedController] quitMenuTunes];
474 ITDebugLog(@"MenuController: Unimplemented Menu Item OR Child-bearing Menu Item");
479 - (void)performRatingMenuAction:(id)sender
481 [[MainController sharedController] selectSongRating:[sender tag]];
484 - (void)performPlaylistMenuAction:(id)sender
486 [[MainController sharedController] selectPlaylistAtIndex:[sender tag]];
489 - (void)performEqualizerMenuAction:(id)sender
491 [[MainController sharedController] selectEQPresetAtIndex:[sender tag]];
494 - (void)performUpcomingSongsMenuAction:(id)sender
496 [[MainController sharedController] selectSongAtIndex:[sender tag]];
502 [_currentMenu update];
505 - (BOOL)validateMenuItem:(id <NSMenuItem>)menuItem
510 - (NSString *)systemUIColor
512 NSDictionary *tmpDict;
514 if ( (tmpDict = [NSDictionary dictionaryWithContentsOfFile:[@"~/Library/Preferences/.GlobalPreferences.plist" stringByExpandingTildeInPath]]) ) {
515 if ( (tmpNumber = [tmpDict objectForKey:@"AppleAquaColorVariant"]) ) {
516 if ( ([tmpNumber intValue] == 1) ) {
529 - (void)setKeyEquivalentForCode:(short)code andModifiers:(long)modifiers
530 onItem:(NSMenuItem *)item
532 unichar charcode = 'a';
534 long cocoaModifiers = 0;
535 static long carbonToCocoa[6][2] =
537 { cmdKey, NSCommandKeyMask },
538 { optionKey, NSAlternateKeyMask },
539 { controlKey, NSControlKeyMask },
540 { shiftKey, NSShiftKeyMask },
543 for (i = 0; i < 6; i++) {
544 if (modifiers & carbonToCocoa[i][0]) {
545 cocoaModifiers += carbonToCocoa[i][1];
548 [item setKeyEquivalentModifierMask:cocoaModifiers];
550 //Missing key combos for some keys. Must find them later.
564 // Haven't tested this, though it should work.
565 // This doesn't work. :'(
567 [[NSString stringWithString:@"Space"] getCharacters:&buffer];
569 /*MenuRef menuRef = _NSGetCarbonMenu([item menu]);
570 ITDebugLog(@"%@", menuRef);
571 SetMenuItemCommandKey(menuRef, 0, NO, 49);
572 SetMenuItemModifiers(menuRef, 0, kMenuNoCommandModifier);
573 SetMenuItemKeyGlyph(menuRef, 0, kMenuBlankGlyph);
580 charcode = NSDeleteFunctionKey;
596 charcode = NSF5FunctionKey;
600 charcode = NSF6FunctionKey;
604 charcode = NSF7FunctionKey;
608 charcode = NSF3FunctionKey;
612 charcode = NSF8FunctionKey;
616 charcode = NSF9FunctionKey;
620 charcode = NSF11FunctionKey;
624 charcode = NSF3FunctionKey;
628 charcode = NSF14FunctionKey;
632 charcode = NSF10FunctionKey;
636 charcode = NSF12FunctionKey;
640 charcode = NSF13FunctionKey;
644 charcode = NSInsertFunctionKey;
648 charcode = NSHomeFunctionKey;
652 charcode = NSPageUpFunctionKey;
656 charcode = NSDeleteFunctionKey;
660 charcode = NSF4FunctionKey;
664 charcode = NSEndFunctionKey;
668 charcode = NSF2FunctionKey;
672 charcode = NSPageDownFunctionKey;
676 charcode = NSF1FunctionKey;
680 charcode = NSLeftArrowFunctionKey;
684 charcode = NSRightArrowFunctionKey;
688 charcode = NSDownArrowFunctionKey;
692 charcode = NSUpArrowFunctionKey;
696 if (charcode == 'a') {
702 kchr = (Ptr) GetScriptVariable(smCurrentScript, smKCHRCache);
703 keyTrans = KeyTranslate(kchr, code, &state);
705 [item setKeyEquivalent:[NSString stringWithCString:&charCode length:1]];
706 } else if (charcode != 'b') {
707 [item setKeyEquivalent:[NSString stringWithCharacters:&charcode length:1]];