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>
15 #import <ITKit/ITCategory-NSMenu.h>
17 @interface MenuController (SubmenuMethods)
18 - (NSMenu *)ratingMenu;
19 - (NSMenu *)upcomingSongsMenu;
20 - (NSMenu *)playlistsMenu;
22 - (void)setKeyEquivalentForCode:(short)code andModifiers:(long)modifiers
23 onItem:(NSMenuItem *)item;
26 @implementation MenuController
30 if ( (self = [super init]) ) {
31 _menuLayout = [[NSMutableArray alloc] initWithCapacity:0];
38 NSMenu *menu = [[NSMenu alloc] initWithTitle:@""];
39 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
40 NSArray *menuArray = [defaults arrayForKey:@"menu"];
41 NSEnumerator *enumerator = [menuArray objectEnumerator];
44 NSEnumerator *itemEnum;
46 NSArray *hotKeys = [[ITHotKeyCenter sharedCenter] allHotKeys];
49 _currentPlaylist = [currentRemote currentPlaylistIndex];
50 _playingRadio = ([currentRemote currentPlaylistClass] == ITMTRemotePlayerRadioPlaylist);
52 //Kill the old submenu items
53 if ( (tempItem = [_currentMenu itemWithTag:1]) ) {
54 [tempItem setSubmenu:nil];
57 if ( (tempItem = [_currentMenu itemWithTag:2]) ) {
58 [tempItem setSubmenu:nil];
61 if ( (tempItem = [_currentMenu itemWithTag:3]) ) {
62 [tempItem setSubmenu:nil];
65 if ( (tempItem = [_currentMenu itemWithTag:4]) ) {
66 [tempItem setSubmenu:nil];
69 ITDebugLog(@"Begin building menu.");
72 while ( (nextObject = [enumerator nextObject]) ) {
74 if ([nextObject isEqualToString:@"playPause"]) {
75 ITDebugLog(@"Add play/pause menu item.");
76 tempItem = [menu addItemWithTitle:NSLocalizedString(@"play", @"Play")
77 action:@selector(performMainMenuAction:)
79 [tempItem setTag:MTMenuPlayPauseItem];
80 [tempItem setTarget:self];
82 itemEnum = [hotKeys objectEnumerator];
83 while ( (hotKey = [itemEnum nextObject]) ) {
84 if ([[hotKey name] isEqualToString:@"PlayPause"]) {
85 ITKeyCombo *combo = [hotKey keyCombo];
86 [self setKeyEquivalentForCode:[combo keyCode]
87 andModifiers:[combo modifiers]
92 switch ([currentRemote playerPlayingState]) {
93 case ITMTRemotePlayerPlaying:
94 [tempItem setTitle:NSLocalizedString(@"pause", @"Pause")];
96 case ITMTRemotePlayerRewinding:
97 case ITMTRemotePlayerForwarding:
98 [tempItem setTitle:NSLocalizedString(@"resume", @"Resume")];
103 } else if ([nextObject isEqualToString:@"nextTrack"]) {
104 ITDebugLog(@"Add next track menu item.");
105 tempItem = [menu addItemWithTitle:NSLocalizedString(@"nextTrack", @"Next Track")
106 action:@selector(performMainMenuAction:)
109 itemEnum = [hotKeys objectEnumerator];
110 while ( (hotKey = [itemEnum nextObject]) ) {
111 if ([[hotKey name] isEqualToString:@"NextTrack"]) {
112 ITKeyCombo *combo = [hotKey keyCombo];
113 [self setKeyEquivalentForCode:[combo keyCode]
114 andModifiers:[combo modifiers]
119 if (_currentPlaylist) {
120 [tempItem setTag:MTMenuNextTrackItem];
121 [tempItem setTarget:self];
123 } else if ([nextObject isEqualToString:@"prevTrack"]) {
124 ITDebugLog(@"Add previous track menu item.");
125 tempItem = [menu addItemWithTitle:NSLocalizedString(@"prevTrack", @"Previous Track")
126 action:@selector(performMainMenuAction:)
129 itemEnum = [hotKeys objectEnumerator];
130 while ( (hotKey = [itemEnum nextObject]) ) {
131 if ([[hotKey name] isEqualToString:@"PrevTrack"]) {
132 ITKeyCombo *combo = [hotKey keyCombo];
133 [self setKeyEquivalentForCode:[combo keyCode]
134 andModifiers:[combo modifiers]
139 if (_currentPlaylist) {
140 [tempItem setTag:MTMenuPreviousTrackItem];
141 [tempItem setTarget:self];
143 } else if ([nextObject isEqualToString:@"fastForward"]) {
144 ITDebugLog(@"Add fast forward menu item.");
145 tempItem = [menu addItemWithTitle:NSLocalizedString(@"fastForward", @"Fast Forward")
146 action:@selector(performMainMenuAction:)
148 if (_currentPlaylist) {
149 [tempItem setTag:MTMenuFastForwardItem];
150 [tempItem setTarget:self];
152 } else if ([nextObject isEqualToString:@"rewind"]) {
153 ITDebugLog(@"Add rewind menu item.");
154 tempItem = [menu addItemWithTitle:NSLocalizedString(@"rewind", @"Rewind")
155 action:@selector(performMainMenuAction:)
157 if (_currentPlaylist) {
158 [tempItem setTag:MTMenuRewindItem];
159 [tempItem setTarget:self];
161 } else if ([nextObject isEqualToString:@"showPlayer"]) {
162 ITDebugLog(@"Add show player menu item.");
163 tempItem = [menu addItemWithTitle:[NSString stringWithFormat:@"%@ %@",
164 NSLocalizedString(@"show", @"Show"),
165 [[[MainController sharedController] currentRemote] playerSimpleName]]
166 action:@selector(performMainMenuAction:)
169 itemEnum = [hotKeys objectEnumerator];
170 while ( (hotKey = [itemEnum nextObject]) ) {
171 if ([[hotKey name] isEqualToString:@"ShowPlayer"]) {
172 ITKeyCombo *combo = [hotKey keyCombo];
173 [self setKeyEquivalentForCode:[combo keyCode]
174 andModifiers:[combo modifiers]
179 [tempItem setTarget:self];
180 [tempItem setTag:MTMenuShowPlayerItem];
181 } else if ([nextObject isEqualToString:@"preferences"]) {
182 ITDebugLog(@"Add preferences menu item.");
183 tempItem = [menu addItemWithTitle:NSLocalizedString(@"preferences", @"Preferences...")
184 action:@selector(performMainMenuAction:)
186 [tempItem setTag:MTMenuPreferencesItem];
187 [tempItem setTarget:self];
188 } else if ([nextObject isEqualToString:@"quit"]) {
189 tempItem = [menu addItemWithTitle:NSLocalizedString(@"quit", @"Quit")
190 action:@selector(performMainMenuAction:)
192 [tempItem setTag:MTMenuQuitItem];
193 [tempItem setTarget:self];
194 } else if ([nextObject isEqualToString:@"trackInfo"]) {
195 ITDebugLog(@"Add track info menu items.");
196 //Handle playing radio too
197 if (_currentPlaylist) {
198 NSString *title = [currentRemote currentSongTitle];
200 [menu addItemWithTitle:NSLocalizedString(@"nowPlaying", @"Now Playing") action:NULL keyEquivalent:@""];
202 if ([title length] > 0) {
204 [menu addItemWithTitle:title action:nil keyEquivalent:@""]];
207 if ([defaults boolForKey:@"showAlbum"]) {
208 NSString *curAlbum = [currentRemote currentSongAlbum];
209 if ([curAlbum length]) {
211 [menu addItemWithTitle:curAlbum action:nil keyEquivalent:@""]];
215 if ([defaults boolForKey:@"showArtist"]) {
216 NSString *curArtist = [currentRemote currentSongArtist];
217 if ([curArtist length]) {
219 [menu addItemWithTitle:curArtist action:nil keyEquivalent:@""]];
223 if ([defaults boolForKey:@"showTrackNumber"]) {
224 int track = [currentRemote currentSongTrack];
227 [menu addItemWithTitle:[NSString stringWithFormat:@"%@ %i", NSLocalizedString(@"track", @"Track"), track] action:nil keyEquivalent:@""]];
231 if ([defaults boolForKey:@"showTime"]) {
232 int left = [[currentRemote currentSongRemaining] intValue];
233 NSString *remaining = [NSString stringWithFormat:@"%i:%02i", left / 60, left % 60];
234 [menu indentItem:[menu addItemWithTitle:[NSString stringWithFormat:@"%@/%@", remaining, [currentRemote currentSongLength]] action:nil keyEquivalent:@""]];
237 if ([defaults boolForKey:@"showTrackRating"]) {
238 NSString *string = nil;
239 switch ((int)([currentRemote currentSongRating] * 5)) {
241 string = [NSString stringWithUTF8String:"☆☆☆☆☆"];
244 string = [NSString stringWithUTF8String:"★☆☆☆☆"];
247 string = [NSString stringWithUTF8String:"★★☆☆☆"];
250 string = [NSString stringWithUTF8String:"★★★☆☆"];
253 string = [NSString stringWithUTF8String:"★★★★☆"];
256 string = [NSString stringWithUTF8String:"★★★★★"];
259 [menu indentItem:[menu addItemWithTitle:string action:nil keyEquivalent:@""]];
262 [menu addItemWithTitle:NSLocalizedString(@"noSong", @"No Song") action:NULL keyEquivalent:@""];
264 } else if ([nextObject isEqualToString:@"separator"]) {
265 ITDebugLog(@"Add separator menu item.");
266 [menu addItem:[NSMenuItem separatorItem]];
268 } else if ([nextObject isEqualToString:@"songRating"]) {
269 ITDebugLog(@"Add song rating menu item.");
270 tempItem = [menu addItemWithTitle:NSLocalizedString(@"songRating", @"Song Rating")
273 [tempItem setSubmenu:_ratingMenu];
275 if (_playingRadio || !_currentPlaylist) {
276 [tempItem setEnabled:NO];
279 itemEnum = [[_ratingMenu itemArray] objectEnumerator];
280 while ( (tempItem = [itemEnum nextObject]) ) {
281 [tempItem setState:NSOffState];
284 [[_ratingMenu itemAtIndex:([currentRemote currentSongRating] * 5)] setState:NSOnState];
285 } else if ([nextObject isEqualToString:@"upcomingSongs"]) {
286 ITDebugLog(@"Add upcoming songs menu item.");
287 tempItem = [menu addItemWithTitle:NSLocalizedString(@"upcomingSongs", @"Upcoming Songs")
290 [tempItem setSubmenu:_upcomingSongsMenu];
292 if (_playingRadio || !_currentPlaylist) {
293 [tempItem setEnabled:NO];
295 } else if ([nextObject isEqualToString:@"playlists"]) {
296 tempItem = [menu addItemWithTitle:NSLocalizedString(@"playlists", @"Playlists")
299 [tempItem setSubmenu:_playlistsMenu];
301 } else if ([nextObject isEqualToString:@"eqPresets"]) {
302 ITDebugLog(@"Add eq presets menu item.");
303 tempItem = [menu addItemWithTitle:NSLocalizedString(@"eqPresets", @"EQ Presets")
306 [tempItem setSubmenu:_eqMenu];
309 itemEnum = [[_eqMenu itemArray] objectEnumerator];
310 while ( (tempItem = [itemEnum nextObject]) ) {
311 [tempItem setState:NSOffState];
313 [[_eqMenu itemAtIndex:([currentRemote currentEQPresetIndex] - 1)] setState:NSOnState];
316 ITDebugLog(@"Finished building menu.");
317 [_currentMenu release];
322 - (NSMenu *)menuForNoPlayer
324 NSMenu *menu = [[NSMenu alloc] initWithTitle:@""];
325 NSMenuItem *tempItem;
326 tempItem = [menu addItemWithTitle:[NSString stringWithFormat:@"%@ %@", NSLocalizedString(@"open", @"Open"), [[[MainController sharedController] currentRemote] playerSimpleName]] action:@selector(performMainMenuAction:) keyEquivalent:@""];
327 [tempItem setTag:MTMenuShowPlayerItem];
328 [tempItem setTarget:self];
329 [menu addItem:[NSMenuItem separatorItem]];
330 tempItem = [menu addItemWithTitle:NSLocalizedString(@"preferences", @"Preferences...") action:@selector(performMainMenuAction:) keyEquivalent:@""];
331 [tempItem setTag:MTMenuPreferencesItem];
332 [tempItem setTarget:self];
333 tempItem = [menu addItemWithTitle:NSLocalizedString(@"quit", @"Quit") action:@selector(performMainMenuAction:) keyEquivalent:@""];
334 [tempItem setTag:MTMenuQuitItem];
335 [tempItem setTarget:self];
336 return [menu autorelease];
339 - (void)rebuildSubmenus
341 currentRemote = [[MainController sharedController] currentRemote];
342 _currentPlaylist = [currentRemote currentPlaylistIndex];
343 _currentTrack = [currentRemote currentSongIndex];
344 _playingRadio = ([currentRemote currentPlaylistClass] == ITMTRemotePlayerRadioPlaylist);
346 [_ratingMenu release];
347 [_upcomingSongsMenu release];
348 [_playlistsMenu release];
350 _ratingMenu = [self ratingMenu];
351 _upcomingSongsMenu = [self upcomingSongsMenu];
352 _playlistsMenu = [self playlistsMenu];
353 _eqMenu = [self eqMenu];
356 - (NSMenu *)ratingMenu
358 NSMenu *ratingMenu = [[NSMenu alloc] initWithTitle:@""];
359 NSEnumerator *itemEnum;
362 SEL itemSelector = @selector(performRatingMenuAction:);
364 ITDebugLog(@"Building rating menu.");
366 [ratingMenu addItemWithTitle:[NSString stringWithUTF8String:"☆☆☆☆☆"] action:nil keyEquivalent:@""];
367 [ratingMenu addItemWithTitle:[NSString stringWithUTF8String:"★☆☆☆☆"] action:nil keyEquivalent:@""];
368 [ratingMenu addItemWithTitle:[NSString stringWithUTF8String:"★★☆☆☆"] action:nil keyEquivalent:@""];
369 [ratingMenu addItemWithTitle:[NSString stringWithUTF8String:"★★★☆☆"] action:nil keyEquivalent:@""];
370 [ratingMenu addItemWithTitle:[NSString stringWithUTF8String:"★★★★☆"] action:nil keyEquivalent:@""];
371 [ratingMenu addItemWithTitle:[NSString stringWithUTF8String:"★★★★★"] action:nil keyEquivalent:@""];
373 itemEnum = [[ratingMenu itemArray] objectEnumerator];
374 while ( (anItem = [itemEnum nextObject]) ) {
375 [anItem setAction:itemSelector];
376 [anItem setTarget:self];
377 [anItem setTag:itemTag];
383 - (NSMenu *)upcomingSongsMenu
385 NSMenu *upcomingSongsMenu = [[NSMenu alloc] initWithTitle:@""];
386 int numSongs = [currentRemote numberOfSongsInPlaylistAtIndex:_currentPlaylist];
387 int numSongsInAdvance = [[NSUserDefaults standardUserDefaults] integerForKey:@"SongsInAdvance"];
389 ITDebugLog(@"Building upcoming songs menu.");
391 if (_currentPlaylist && !_playingRadio) {
395 for (i = _currentTrack + 1; i <= _currentTrack + numSongsInAdvance; i++) {
397 NSString *curSong = [currentRemote songTitleAtIndex:i];
398 NSMenuItem *songItem;
399 songItem = [upcomingSongsMenu addItemWithTitle:curSong action:@selector(performUpcomingSongsMenuAction:) keyEquivalent:@""];
401 [songItem setTarget:self];
408 return upcomingSongsMenu;
411 - (NSMenu *)playlistsMenu
413 NSMenu *playlistsMenu = [[NSMenu alloc] initWithTitle:@""];
414 NSArray *playlists = [currentRemote playlists];
415 NSMenuItem *tempItem;
418 ITDebugLog(@"Building playlists menu.");
420 for (i = 0; i < [playlists count]; i++) {
421 tempItem = [playlistsMenu addItemWithTitle:[playlists objectAtIndex:i] action:@selector(performPlaylistMenuAction:) keyEquivalent:@""];
422 [tempItem setTag:i + 1];
423 [tempItem setTarget:self];
426 if (!_playingRadio && _currentPlaylist) {
427 [[playlistsMenu itemAtIndex:_currentPlaylist - 1] setState:NSOnState];
429 return playlistsMenu;
434 NSMenu *eqMenu = [[NSMenu alloc] initWithTitle:@""];
435 NSArray *eqPresets = [currentRemote eqPresets];
436 NSMenuItem *tempItem;
439 ITDebugLog(@"Building EQ presets menu.");
441 for (i = 0; i < [eqPresets count]; i++) {
443 if ( ( name = [eqPresets objectAtIndex:i] ) ) {
444 tempItem = [eqMenu addItemWithTitle:name action:@selector(performEqualizerMenuAction:) keyEquivalent:@""];
446 [tempItem setTarget:self];
452 - (void)performMainMenuAction:(id)sender
454 switch ( [sender tag] )
456 case MTMenuPlayPauseItem:
457 ITDebugLog(@"MenuController: Play/Pause");
458 [[MainController sharedController] playPause];
460 case MTMenuFastForwardItem:
461 ITDebugLog(@"MenuController: Fast Forward");
462 [[MainController sharedController] fastForward];
464 case MTMenuRewindItem:
465 ITDebugLog(@"MenuController: Rewind");
466 [[MainController sharedController] rewind];
468 case MTMenuPreviousTrackItem:
469 ITDebugLog(@"MenuController: Previous Track");
470 [[MainController sharedController] prevSong];
472 case MTMenuNextTrackItem:
473 ITDebugLog(@"MenuController: Next Track");
474 [[MainController sharedController] nextSong];
476 case MTMenuShowPlayerItem:
477 ITDebugLog(@"MainController: Show Main Interface");
478 [[MainController sharedController] showPlayer];
480 case MTMenuPreferencesItem:
481 ITDebugLog(@"MenuController: Preferences...");
482 [[MainController sharedController] showPreferences];
485 ITDebugLog(@"MenuController: Quit");
486 [[MainController sharedController] quitMenuTunes];
489 ITDebugLog(@"MenuController: Unimplemented Menu Item OR Child-bearing Menu Item");
494 - (void)performRatingMenuAction:(id)sender
496 [[MainController sharedController] selectSongRating:[sender tag]];
499 - (void)performPlaylistMenuAction:(id)sender
501 [[MainController sharedController] selectPlaylistAtIndex:[sender tag]];
504 - (void)performEqualizerMenuAction:(id)sender
506 [[MainController sharedController] selectEQPresetAtIndex:[sender tag]];
509 - (void)performUpcomingSongsMenuAction:(id)sender
511 [[MainController sharedController] selectSongAtIndex:[sender tag]];
517 [_currentMenu update];
520 - (BOOL)validateMenuItem:(id <NSMenuItem>)menuItem
525 - (NSString *)systemUIColor
527 NSDictionary *tmpDict;
529 if ( (tmpDict = [NSDictionary dictionaryWithContentsOfFile:[@"~/Library/Preferences/.GlobalPreferences.plist" stringByExpandingTildeInPath]]) ) {
530 if ( (tmpNumber = [tmpDict objectForKey:@"AppleAquaColorVariant"]) ) {
531 if ( ([tmpNumber intValue] == 1) ) {
544 - (void)setKeyEquivalentForCode:(short)code andModifiers:(long)modifiers
545 onItem:(NSMenuItem *)item
547 unichar charcode = 'a';
549 long cocoaModifiers = 0;
550 static long carbonToCocoa[6][2] =
552 { cmdKey, NSCommandKeyMask },
553 { optionKey, NSAlternateKeyMask },
554 { controlKey, NSControlKeyMask },
555 { shiftKey, NSShiftKeyMask },
558 for (i = 0; i < 6; i++) {
559 if (modifiers & carbonToCocoa[i][0]) {
560 cocoaModifiers += carbonToCocoa[i][1];
563 [item setKeyEquivalentModifierMask:cocoaModifiers];
565 //Missing key combos for some keys. Must find them later.
579 // Haven't tested this, though it should work.
580 // This doesn't work. :'(
582 [[NSString stringWithString:@"Space"] getCharacters:&buffer];
584 /*MenuRef menuRef = _NSGetCarbonMenu([item menu]);
585 ITDebugLog(@"%@", menuRef);
586 SetMenuItemCommandKey(menuRef, 0, NO, 49);
587 SetMenuItemModifiers(menuRef, 0, kMenuNoCommandModifier);
588 SetMenuItemKeyGlyph(menuRef, 0, kMenuBlankGlyph);
595 charcode = NSDeleteFunctionKey;
611 charcode = NSF5FunctionKey;
615 charcode = NSF6FunctionKey;
619 charcode = NSF7FunctionKey;
623 charcode = NSF3FunctionKey;
627 charcode = NSF8FunctionKey;
631 charcode = NSF9FunctionKey;
635 charcode = NSF11FunctionKey;
639 charcode = NSF3FunctionKey;
643 charcode = NSF14FunctionKey;
647 charcode = NSF10FunctionKey;
651 charcode = NSF12FunctionKey;
655 charcode = NSF13FunctionKey;
659 charcode = NSInsertFunctionKey;
663 charcode = NSHomeFunctionKey;
667 charcode = NSPageUpFunctionKey;
671 charcode = NSDeleteFunctionKey;
675 charcode = NSF4FunctionKey;
679 charcode = NSEndFunctionKey;
683 charcode = NSF2FunctionKey;
687 charcode = NSPageDownFunctionKey;
691 charcode = NSF1FunctionKey;
695 charcode = NSLeftArrowFunctionKey;
699 charcode = NSRightArrowFunctionKey;
703 charcode = NSDownArrowFunctionKey;
707 charcode = NSUpArrowFunctionKey;
711 if (charcode == 'a') {
717 kchr = (Ptr) GetScriptVariable(smCurrentScript, smKCHRCache);
718 keyTrans = KeyTranslate(kchr, code, &state);
720 [item setKeyEquivalent:[NSString stringWithCString:&charCode length:1]];
721 } else if (charcode != 'b') {
722 [item setKeyEquivalent:[NSString stringWithCharacters:&charcode length:1]];