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 [menu addItemWithTitle:NSLocalizedString(@"noSong", @"No Song") action:NULL keyEquivalent:@""];
234 } else if ([nextObject isEqualToString:@"separator"]) {
235 [menu addItem:[NSMenuItem separatorItem]];
237 } else if ([nextObject isEqualToString:@"songRating"]) {
238 tempItem = [menu addItemWithTitle:NSLocalizedString(@"songRating", @"Song Rating")
241 [tempItem setSubmenu:_ratingMenu];
243 if (_playingRadio || !_currentPlaylist) {
244 [tempItem setEnabled:NO];
247 itemEnum = [[_ratingMenu itemArray] objectEnumerator];
248 while ( (tempItem = [itemEnum nextObject]) ) {
249 [tempItem setState:NSOffState];
252 [[_ratingMenu itemAtIndex:([currentRemote currentSongRating] * 5)] setState:NSOnState];
253 } else if ([nextObject isEqualToString:@"upcomingSongs"]) {
254 tempItem = [menu addItemWithTitle:NSLocalizedString(@"upcomingSongs", @"Upcoming Songs")
257 [tempItem setSubmenu:_upcomingSongsMenu];
259 if (_playingRadio || !_currentPlaylist) {
260 [tempItem setEnabled:NO];
262 } else if ([nextObject isEqualToString:@"playlists"]) {
263 tempItem = [menu addItemWithTitle:NSLocalizedString(@"playlists", @"Playlists")
266 [tempItem setSubmenu:_playlistsMenu];
268 } else if ([nextObject isEqualToString:@"eqPresets"]) {
269 tempItem = [menu addItemWithTitle:NSLocalizedString(@"eqPresets", @"EQ Presets")
272 [tempItem setSubmenu:_eqMenu];
275 itemEnum = [[_eqMenu itemArray] objectEnumerator];
276 while ( (tempItem = [itemEnum nextObject]) ) {
277 [tempItem setState:NSOffState];
279 [[_eqMenu itemAtIndex:([currentRemote currentEQPresetIndex] - 1)] setState:NSOnState];
282 [_currentMenu release];
287 - (NSMenu *)menuForNoPlayer
289 NSMenu *menu = [[NSMenu alloc] initWithTitle:@""];
290 NSMenuItem *tempItem;
291 tempItem = [menu addItemWithTitle:[NSString stringWithFormat:@"%@ %@", NSLocalizedString(@"open", @"Open"), [[[MainController sharedController] currentRemote] playerSimpleName]] action:@selector(performMainMenuAction:) keyEquivalent:@""];
292 [tempItem setTag:MTMenuShowPlayerItem];
293 [tempItem setTarget:self];
294 [menu addItem:[NSMenuItem separatorItem]];
295 tempItem = [menu addItemWithTitle:NSLocalizedString(@"preferences", @"Preferences...") action:@selector(performMainMenuAction:) keyEquivalent:@""];
296 [tempItem setTag:MTMenuPreferencesItem];
297 [tempItem setTarget:self];
298 tempItem = [menu addItemWithTitle:NSLocalizedString(@"quit", @"Quit") action:@selector(performMainMenuAction:) keyEquivalent:@""];
299 [tempItem setTag:MTMenuQuitItem];
300 [tempItem setTarget:self];
301 return [menu autorelease];
304 - (void)rebuildSubmenus
306 currentRemote = [[MainController sharedController] currentRemote];
307 _currentPlaylist = [currentRemote currentPlaylistIndex];
308 _currentTrack = [currentRemote currentSongIndex];
309 _playingRadio = ([currentRemote currentPlaylistClass] == ITMTRemotePlayerRadioPlaylist);
311 [_ratingMenu release];
312 [_upcomingSongsMenu release];
313 [_playlistsMenu release];
315 _ratingMenu = [self ratingMenu];
316 _upcomingSongsMenu = [self upcomingSongsMenu];
317 _playlistsMenu = [self playlistsMenu];
318 _eqMenu = [self eqMenu];
321 - (NSMenu *)ratingMenu
323 NSMenu *ratingMenu = [[NSMenu alloc] initWithTitle:@""];
324 NSEnumerator *itemEnum;
327 SEL itemSelector = @selector(performRatingMenuAction:);
329 [ratingMenu addItemWithTitle:[NSString stringWithUTF8String:"☆☆☆☆☆"] action:nil keyEquivalent:@""];
330 [ratingMenu addItemWithTitle:[NSString stringWithUTF8String:"★☆☆☆☆"] action:nil keyEquivalent:@""];
331 [ratingMenu addItemWithTitle:[NSString stringWithUTF8String:"★★☆☆☆"] action:nil keyEquivalent:@""];
332 [ratingMenu addItemWithTitle:[NSString stringWithUTF8String:"★★★☆☆"] action:nil keyEquivalent:@""];
333 [ratingMenu addItemWithTitle:[NSString stringWithUTF8String:"★★★★☆"] action:nil keyEquivalent:@""];
334 [ratingMenu addItemWithTitle:[NSString stringWithUTF8String:"★★★★★"] action:nil keyEquivalent:@""];
336 itemEnum = [[ratingMenu itemArray] objectEnumerator];
337 while ( (anItem = [itemEnum nextObject]) ) {
338 [anItem setAction:itemSelector];
339 [anItem setTarget:self];
340 [anItem setTag:itemTag];
346 - (NSMenu *)upcomingSongsMenu
348 NSMenu *upcomingSongsMenu = [[NSMenu alloc] initWithTitle:@""];
349 int numSongs = [currentRemote numberOfSongsInPlaylistAtIndex:_currentPlaylist];
350 int numSongsInAdvance = [[NSUserDefaults standardUserDefaults] integerForKey:@"SongsInAdvance"];
352 if (_currentPlaylist && !_playingRadio) {
356 for (i = _currentTrack + 1; i <= _currentTrack + numSongsInAdvance; i++) {
358 NSString *curSong = [currentRemote songTitleAtIndex:i];
359 NSMenuItem *songItem;
360 songItem = [upcomingSongsMenu addItemWithTitle:curSong action:@selector(performUpcomingSongsMenuAction:) keyEquivalent:@""];
362 [songItem setTarget:self];
369 return upcomingSongsMenu;
372 - (NSMenu *)playlistsMenu
374 NSMenu *playlistsMenu = [[NSMenu alloc] initWithTitle:@""];
375 NSArray *playlists = [currentRemote playlists];
376 NSMenuItem *tempItem;
379 for (i = 0; i < [playlists count]; i++) {
380 tempItem = [playlistsMenu addItemWithTitle:[playlists objectAtIndex:i] action:@selector(performPlaylistMenuAction:) keyEquivalent:@""];
381 [tempItem setTag:i + 1];
382 [tempItem setTarget:self];
385 if (!_playingRadio && _currentPlaylist) {
386 [[playlistsMenu itemAtIndex:_currentPlaylist - 1] setState:NSOnState];
388 return playlistsMenu;
393 NSMenu *eqMenu = [[NSMenu alloc] initWithTitle:@""];
394 NSArray *eqPresets = [currentRemote eqPresets];
395 NSMenuItem *tempItem;
398 for (i = 0; i < [eqPresets count]; i++) {
400 if ( ( name = [eqPresets objectAtIndex:i] ) ) {
401 tempItem = [eqMenu addItemWithTitle:name action:@selector(performEqualizerMenuAction:) keyEquivalent:@""];
403 [tempItem setTarget:self];
409 - (void)performMainMenuAction:(id)sender
411 switch ( [sender tag] )
413 case MTMenuPlayPauseItem:
414 ITDebugLog(@"MenuController: Play/Pause");
415 [[MainController sharedController] playPause];
417 case MTMenuFastForwardItem:
418 ITDebugLog(@"MenuController: Fast Forward");
419 [[MainController sharedController] fastForward];
421 case MTMenuRewindItem:
422 ITDebugLog(@"MenuController: Rewind");
423 [[MainController sharedController] rewind];
425 case MTMenuPreviousTrackItem:
426 ITDebugLog(@"MenuController: Previous Track");
427 [[MainController sharedController] prevSong];
429 case MTMenuNextTrackItem:
430 ITDebugLog(@"MenuController: Next Track");
431 [[MainController sharedController] nextSong];
433 case MTMenuShowPlayerItem:
434 ITDebugLog(@"MainController: Show Main Interface");
435 [[MainController sharedController] showPlayer];
437 case MTMenuPreferencesItem:
438 ITDebugLog(@"MenuController: Preferences...");
439 [[MainController sharedController] showPreferences];
442 ITDebugLog(@"MenuController: Quit");
443 [[MainController sharedController] quitMenuTunes];
446 ITDebugLog(@"MenuController: Unimplemented Menu Item OR Child-bearing Menu Item");
451 - (void)performRatingMenuAction:(id)sender
453 [[MainController sharedController] selectSongRating:[sender tag]];
456 - (void)performPlaylistMenuAction:(id)sender
458 [[MainController sharedController] selectPlaylistAtIndex:[sender tag]];
461 - (void)performEqualizerMenuAction:(id)sender
463 [[MainController sharedController] selectEQPresetAtIndex:[sender tag]];
466 - (void)performUpcomingSongsMenuAction:(id)sender
468 [[MainController sharedController] selectSongAtIndex:[sender tag]];
474 [_currentMenu update];
477 - (BOOL)validateMenuItem:(id <NSMenuItem>)menuItem
482 - (NSString *)systemUIColor
484 NSDictionary *tmpDict;
486 if ( (tmpDict = [NSDictionary dictionaryWithContentsOfFile:[@"~/Library/Preferences/.GlobalPreferences.plist" stringByExpandingTildeInPath]]) ) {
487 if ( (tmpNumber = [tmpDict objectForKey:@"AppleAquaColorVariant"]) ) {
488 if ( ([tmpNumber intValue] == 1) ) {
501 - (void)setKeyEquivalentForCode:(short)code andModifiers:(long)modifiers
502 onItem:(NSMenuItem *)item
504 unichar charcode = 'a';
506 long cocoaModifiers = 0;
507 static long carbonToCocoa[6][2] =
509 { cmdKey, NSCommandKeyMask },
510 { optionKey, NSAlternateKeyMask },
511 { controlKey, NSControlKeyMask },
512 { shiftKey, NSShiftKeyMask },
515 for (i = 0; i < 6; i++) {
516 if (modifiers & carbonToCocoa[i][0]) {
517 cocoaModifiers += carbonToCocoa[i][1];
520 [item setKeyEquivalentModifierMask:cocoaModifiers];
522 //Missing key combos for some keys. Must find them later.
536 // Haven't tested this, though it should work.
537 // This doesn't work. :'(
539 [[NSString stringWithString:@"Space"] getCharacters:&buffer];
541 /*MenuRef menuRef = _NSGetCarbonMenu([item menu]);
542 ITDebugLog(@"%@", menuRef);
543 SetMenuItemCommandKey(menuRef, 0, NO, 49);
544 SetMenuItemModifiers(menuRef, 0, kMenuNoCommandModifier);
545 SetMenuItemKeyGlyph(menuRef, 0, kMenuBlankGlyph);
552 charcode = NSDeleteFunctionKey;
568 charcode = NSF5FunctionKey;
572 charcode = NSF6FunctionKey;
576 charcode = NSF7FunctionKey;
580 charcode = NSF3FunctionKey;
584 charcode = NSF8FunctionKey;
588 charcode = NSF9FunctionKey;
592 charcode = NSF11FunctionKey;
596 charcode = NSF3FunctionKey;
600 charcode = NSF14FunctionKey;
604 charcode = NSF10FunctionKey;
608 charcode = NSF12FunctionKey;
612 charcode = NSF13FunctionKey;
616 charcode = NSInsertFunctionKey;
620 charcode = NSHomeFunctionKey;
624 charcode = NSPageUpFunctionKey;
628 charcode = NSDeleteFunctionKey;
632 charcode = NSF4FunctionKey;
636 charcode = NSEndFunctionKey;
640 charcode = NSF2FunctionKey;
644 charcode = NSPageDownFunctionKey;
648 charcode = NSF1FunctionKey;
652 charcode = NSLeftArrowFunctionKey;
656 charcode = NSRightArrowFunctionKey;
660 charcode = NSDownArrowFunctionKey;
664 charcode = NSUpArrowFunctionKey;
668 if (charcode == 'a') {
674 kchr = (Ptr) GetScriptVariable(smCurrentScript, smKCHRCache);
675 keyTrans = KeyTranslate(kchr, code, &state);
677 [item setKeyEquivalent:[NSString stringWithCString:&charCode length:1]];
678 } else if (charcode != 'b') {
679 [item setKeyEquivalent:[NSString stringWithCharacters:&charcode length:1]];