Poked around a bit, added Show Player to MenuController
[MenuTunes.git] / MenuController.m
1 //
2 //  MenuController.m
3 //  MenuTunes
4 //
5 //  Created by Joseph Spiros on Wed Apr 30 2003.
6 //  Copyright (c) 2003 iThink Software. All rights reserved.
7 //
8
9 #import "MenuController.h"
10 #import "NewMainController.h"
11 #import "HotKeyCenter.h"
12 #import "KeyCombo.h"
13
14 @interface MenuController (SubmenuMethods)
15 - (NSMenu *)ratingMenu;
16 - (NSMenu *)upcomingSongsMenu;
17 - (NSMenu *)playlistsMenu;
18 - (NSMenu *)eqMenu;
19 - (void)setKeyEquivalentForCode:(short)code andModifiers:(long)modifiers
20         onItem:(NSMenuItem *)item;
21 @end
22
23 @implementation MenuController
24
25 - (id)init
26 {
27     if ( (self = [super init]) ) {
28         _menuLayout = [[NSMutableArray alloc] initWithCapacity:0];
29     }
30     return self;
31 }
32
33 - (NSMenu *)menu
34 {
35     NSMenu *menu = [[NSMenu alloc] initWithTitle:@""];
36     NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
37     NSArray *menuArray = [defaults arrayForKey:@"menu"];
38     NSEnumerator *enumerator = [menuArray objectEnumerator];
39     NSString *nextObject;
40     NSMenuItem *tempItem;
41     NSEnumerator *itemEnum;
42     KeyCombo *keyCombo;
43     
44     //Get the information
45     _currentPlaylist = [currentRemote currentPlaylistIndex];
46     _playingRadio = ([currentRemote currentPlaylistClass] == ITMTRemotePlayerRadioPlaylist);
47     
48     //Kill the old submenu items
49     if ( (tempItem = [_currentMenu itemWithTag:1]) ) {
50         [tempItem setSubmenu:nil];
51     }
52     
53     if ( (tempItem = [_currentMenu itemWithTag:2]) ) {
54         [tempItem setSubmenu:nil];
55     }
56     
57     if ( (tempItem = [_currentMenu itemWithTag:3]) ) {
58         [tempItem setSubmenu:nil];
59     }
60     
61     if ( (tempItem = [_currentMenu itemWithTag:4]) ) {
62         [tempItem setSubmenu:nil];
63     }
64     
65     //create our menu
66     while ( (nextObject = [enumerator nextObject]) ) {
67         //Main menu items
68         if ([nextObject isEqualToString:@"Play/Pause"]) {
69             tempItem = [menu addItemWithTitle:@"Play"
70                     action:@selector(performMainMenuAction:)
71                     keyEquivalent:@""];
72             [tempItem setTag:MTMenuPlayPauseItem];
73             [tempItem setTarget:self];
74             
75             if ( (keyCombo = [[HotKeyCenter sharedCenter] keyComboForName:@"PlayPause"]) ) {
76                 [self setKeyEquivalentForCode:[keyCombo keyCode]
77                         andModifiers:[keyCombo modifiers]
78                         onItem:tempItem];
79             }
80             
81             switch ([currentRemote playerPlayingState]) {
82                 case ITMTRemotePlayerPlaying:
83                     [tempItem setTitle:@"Pause"];
84                 break;
85                 case ITMTRemotePlayerRewinding:
86                 case ITMTRemotePlayerForwarding:
87                     [tempItem setTitle:@"Resume"];
88                 break;
89                 default:
90                 break;
91             }
92         } else if ([nextObject isEqualToString:@"Next Track"]) {
93             tempItem = [menu addItemWithTitle:@"Next Track"
94                     action:@selector(performMainMenuAction:)
95                     keyEquivalent:@""];
96             
97             if ( (keyCombo = [[HotKeyCenter sharedCenter] keyComboForName:@"NextTrack"]) ) {
98                 [self setKeyEquivalentForCode:[keyCombo keyCode]
99                         andModifiers:[keyCombo modifiers]
100                         onItem:tempItem];
101             }
102             
103             if (_currentPlaylist) {
104                 [tempItem setTag:MTMenuNextTrackItem];
105                 [tempItem setTarget:self];
106             }
107         } else if ([nextObject isEqualToString:@"Previous Track"]) {
108             tempItem = [menu addItemWithTitle:@"Previous Track"
109                     action:@selector(performMainMenuAction:)
110                     keyEquivalent:@""];
111             
112             if ( (keyCombo = [[HotKeyCenter sharedCenter] keyComboForName:@"PrevTrack"]) ) {
113                 [self setKeyEquivalentForCode:[keyCombo keyCode]
114                         andModifiers:[keyCombo modifiers]
115                         onItem:tempItem];
116             }
117             
118             if (_currentPlaylist) {
119                 [tempItem setTag:MTMenuPreviousTrackItem];
120                 [tempItem setTarget:self];
121             }
122         } else if ([nextObject isEqualToString:@"Fast Forward"]) {
123             tempItem = [menu addItemWithTitle:@"Fast Forward"
124                     action:@selector(performMainMenuAction:)
125                     keyEquivalent:@""];
126             if (_currentPlaylist) {
127                 [tempItem setTag:MTMenuFastForwardItem];
128                 [tempItem setTarget:self];
129             }
130         } else if ([nextObject isEqualToString:@"Rewind"]) {
131             tempItem = [menu addItemWithTitle:@"Rewind"
132                     action:@selector(performMainMenuAction:)
133                     keyEquivalent:@""];
134             if (_currentPlaylist) {
135                 [tempItem setTag:MTMenuRewindItem];
136                 [tempItem setTarget:self];
137             }
138         } else if ([nextObject isEqualToString:@"Show Player"]) {
139             tempItem = [menu addItemWithTitle:[NSString stringWithFormat:@"Show %@", [[[MainController sharedController] currentRemote] playerSimpleName]] action:@selector(performMainMenuAction:) keyEquivalent:@""];
140             [tempItem setTarget:self];
141             [tempItem setTag:MTMenuShowPlayerItem];
142         } else if ([nextObject isEqualToString:@"Preferences"]) {
143             tempItem = [menu addItemWithTitle:@"Preferences..."
144                     action:@selector(performMainMenuAction:)
145                     keyEquivalent:@""];
146             [tempItem setTag:MTMenuPreferencesItem];
147             [tempItem setTarget:self];
148         } else if ([nextObject isEqualToString:@"Quit"]) {
149             tempItem = [menu addItemWithTitle:@"Quit"
150                     action:@selector(performMainMenuAction:)
151                     keyEquivalent:@""];
152             [tempItem setTag:MTMenuQuitItem];
153             [tempItem setTarget:self];
154         } else if ([nextObject isEqualToString:@"Current Track Info"]) {
155             //Handle playing radio too
156             if (_currentPlaylist) {
157                 NSString *title = [currentRemote currentSongTitle];
158                 
159                 [menu addItemWithTitle:@"Now Playing" action:NULL keyEquivalent:@""];
160                 
161                 if ([title length] > 0) {
162                     [menu addItemWithTitle:[NSString stringWithFormat:@"         %@", title]
163                             action:nil
164                             keyEquivalent:@""];
165                 }
166                 
167                 if ([defaults boolForKey:@"showAlbum"]) {
168                     [menu addItemWithTitle:[NSString stringWithFormat:@"         %@", [currentRemote currentSongAlbum]]
169                             action:nil
170                             keyEquivalent:@""];
171                 }
172                 
173                 if ([defaults boolForKey:@"showArtist"]) {
174                     [menu addItemWithTitle:[NSString stringWithFormat:@"         %@", [currentRemote currentSongArtist]]
175                             action:nil
176                             keyEquivalent:@""];
177                 }
178                 
179                 if ([defaults boolForKey:@"showTrackNumber"]) {
180                     [menu addItemWithTitle:[NSString stringWithFormat:@"         Track %i", [currentRemote currentSongTrack]]
181                             action:nil
182                             keyEquivalent:@""];
183                 }
184                 
185                 if ([defaults boolForKey:@"showTime"]) {
186                     int left = [[currentRemote currentSongRemaining] intValue];
187                     NSString *remaining = [NSString stringWithFormat:@"%i:%02i", left / 60, left % 60];
188                     [menu addItemWithTitle:[NSString stringWithFormat:@"         %@/%@", remaining, [currentRemote currentSongLength]]
189                             action:nil
190                             keyEquivalent:@""];
191                 }
192             } else {
193                 [menu addItemWithTitle:@"No Song" action:NULL keyEquivalent:@""];
194             }
195         } else if ([nextObject isEqualToString:@"<separator>"]) {
196             [menu addItem:[NSMenuItem separatorItem]];
197         //Submenu items
198         } else if ([nextObject isEqualToString:@"Song Rating"]) {
199             tempItem = [menu addItemWithTitle:@"Song Rating"
200                     action:nil
201                     keyEquivalent:@""];
202             [tempItem setSubmenu:_ratingMenu];
203             [tempItem setTag:1];
204             
205             itemEnum = [[_ratingMenu itemArray] objectEnumerator];
206             while ( (tempItem = [itemEnum nextObject]) ) {
207                 [tempItem setState:NSOffState];
208             }
209             
210             [[_ratingMenu itemAtIndex:([currentRemote currentSongRating] * 5)] setState:NSOnState];
211             if (_playingRadio || !_currentPlaylist) {
212                 [tempItem setEnabled:NO];
213             }
214         } else if ([nextObject isEqualToString:@"Upcoming Songs"]) {
215             tempItem = [menu addItemWithTitle:@"Upcoming Songs"
216                     action:nil
217                     keyEquivalent:@""];
218             [tempItem setSubmenu:_upcomingSongsMenu];
219             [tempItem setTag:2];
220             if (_playingRadio || !_currentPlaylist) {
221                 [tempItem setEnabled:NO];
222             }
223         } else if ([nextObject isEqualToString:@"Playlists"]) {
224             tempItem = [menu addItemWithTitle:@"Playlists"
225                     action:nil
226                     keyEquivalent:@""];
227             [tempItem setSubmenu:_playlistsMenu];
228             [tempItem setTag:3];
229         } else if ([nextObject isEqualToString:@"EQ Presets"]) {
230             tempItem = [menu addItemWithTitle:@"EQ Presets"
231                     action:nil
232                     keyEquivalent:@""];
233             [tempItem setSubmenu:_eqMenu];
234             [tempItem setTag:4];
235             
236             itemEnum = [[_eqMenu itemArray] objectEnumerator];
237             while ( (tempItem = [itemEnum nextObject]) ) {
238                 [tempItem setState:NSOffState];
239             }
240             [[_eqMenu itemAtIndex:([currentRemote currentEQPresetIndex] - 1)] setState:NSOnState];
241         }
242     }
243     [_currentMenu release];
244     _currentMenu = menu;
245     return _currentMenu;
246 }
247
248 - (NSMenu *)menuForNoPlayer
249 {
250     NSMenu *menu = [[NSMenu alloc] initWithTitle:@""];
251     NSMenuItem *tempItem;
252     tempItem = [menu addItemWithTitle:[NSString stringWithFormat:@"Open %@", [[[MainController sharedController] currentRemote] playerSimpleName]] action:@selector(performMainMenuAction:) keyEquivalent:@""];
253     [tempItem setTag:MTMenuShowPlayerItem];
254     [tempItem setTarget:self];
255     [menu addItem:[NSMenuItem separatorItem]];
256     tempItem = [menu addItemWithTitle:@"Preferences" action:@selector(performMainMenuAction:) keyEquivalent:@""];
257     [tempItem setTag:MTMenuPreferencesItem];
258     [tempItem setTarget:self];
259     tempItem = [menu addItemWithTitle:@"Quit" action:@selector(performMainMenuAction:) keyEquivalent:@""];
260     [tempItem setTag:MTMenuQuitItem];
261     [tempItem setTarget:self];
262     return [menu autorelease];
263 }
264
265 - (void)rebuildSubmenus
266 {
267     currentRemote = [[MainController sharedController] currentRemote];
268     _currentPlaylist = [currentRemote currentPlaylistIndex];
269     _currentTrack = [currentRemote currentSongIndex];
270     _playingRadio = ([currentRemote currentPlaylistClass] == ITMTRemotePlayerRadioPlaylist);
271     
272     [_ratingMenu release];
273     [_upcomingSongsMenu release];
274     [_playlistsMenu release];
275     [_eqMenu release];
276     _ratingMenu = [self ratingMenu];
277     _upcomingSongsMenu = [self upcomingSongsMenu];
278     _playlistsMenu = [self playlistsMenu];
279     _eqMenu = [self eqMenu];
280 }
281
282 - (NSMenu *)ratingMenu
283 {
284     NSMenu *ratingMenu = [[NSMenu alloc] initWithTitle:@""];
285     if (_currentPlaylist && !_playingRadio) {
286         NSEnumerator *itemEnum;
287         id  anItem;
288         int itemTag = 0;
289         SEL itemSelector = @selector(performRatingMenuAction:);
290         
291         [ratingMenu addItemWithTitle:[NSString stringWithUTF8String:"☆☆☆☆☆"] action:nil keyEquivalent:@""];
292         [ratingMenu addItemWithTitle:[NSString stringWithUTF8String:"★☆☆☆☆"] action:nil keyEquivalent:@""];
293         [ratingMenu addItemWithTitle:[NSString stringWithUTF8String:"★★☆☆☆"] action:nil keyEquivalent:@""];
294         [ratingMenu addItemWithTitle:[NSString stringWithUTF8String:"★★★☆☆"] action:nil keyEquivalent:@""];
295         [ratingMenu addItemWithTitle:[NSString stringWithUTF8String:"★★★★☆"] action:nil keyEquivalent:@""];
296         [ratingMenu addItemWithTitle:[NSString stringWithUTF8String:"★★★★★"] action:nil keyEquivalent:@""];
297         
298         itemEnum = [[ratingMenu itemArray] objectEnumerator];
299         while ( (anItem = [itemEnum nextObject]) ) {
300             [anItem setAction:itemSelector];
301             [anItem setTarget:self];
302             [anItem setTag:itemTag];
303             itemTag += 20;
304         }
305     }
306     return ratingMenu;
307 }
308
309 - (NSMenu *)upcomingSongsMenu
310 {
311     NSMenu *upcomingSongsMenu = [[NSMenu alloc] initWithTitle:@""];
312     int numSongs = [currentRemote numberOfSongsInPlaylistAtIndex:_currentPlaylist];
313     int numSongsInAdvance = [[NSUserDefaults standardUserDefaults] integerForKey:@"SongsInAdvance"];
314     
315     if (_currentPlaylist && !_playingRadio) {
316         if (numSongs > 0) {
317             int i;
318             
319             for (i = _currentTrack + 1; i <= _currentTrack + numSongsInAdvance; i++) {
320                 if (i <= numSongs) {
321                     NSString *curSong = [currentRemote songTitleAtIndex:i];
322                     NSMenuItem *songItem;
323                     songItem = [upcomingSongsMenu addItemWithTitle:curSong action:@selector(performUpcomingSongsMenuAction:) keyEquivalent:@""];
324                     [songItem setTag:i];
325                     [songItem setTarget:self];
326                 } else {
327                     break;
328                 }
329             }
330         }
331     }
332     return upcomingSongsMenu;
333 }
334
335 - (NSMenu *)playlistsMenu
336 {
337     NSMenu *playlistsMenu = [[NSMenu alloc] initWithTitle:@""];
338     NSArray *playlists = [currentRemote playlists];
339     NSMenuItem *tempItem;
340     int i;
341     
342     for (i = 0; i < [playlists count]; i++) {
343         tempItem = [playlistsMenu addItemWithTitle:[playlists objectAtIndex:i] action:@selector(performPlaylistMenuAction:) keyEquivalent:@""];
344         [tempItem setTag:i + 1];
345         [tempItem setTarget:self];
346     }
347     
348     if (!_playingRadio && _currentPlaylist) {
349         [[playlistsMenu itemAtIndex:_currentPlaylist - 1] setState:NSOnState];
350     }
351     return playlistsMenu;
352 }
353
354 - (NSMenu *)eqMenu
355 {
356     NSMenu *eqMenu = [[NSMenu alloc] initWithTitle:@""];
357     NSArray *eqPresets = [currentRemote eqPresets];
358     NSMenuItem *tempItem;
359     int i;
360     
361     for (i = 0; i < [eqPresets count]; i++) {
362         NSString *name;
363         if ( ( name = [eqPresets objectAtIndex:i] ) ) {
364             tempItem = [eqMenu addItemWithTitle:name action:@selector(performEqualizerMenuAction:) keyEquivalent:@""];
365             [tempItem setTag:i];
366             [tempItem setTarget:self];
367         }
368     }
369     return eqMenu;
370 }
371
372 - (void)performMainMenuAction:(id)sender
373 {
374     switch ( [sender tag] )
375     {
376         case MTMenuPlayPauseItem:
377             NSLog(@"MenuController: Play/Pause");
378             [[MainController sharedController] playPause];
379             break;
380         case MTMenuFastForwardItem:
381             NSLog(@"MenuController: Fast Forward");
382             [[MainController sharedController] fastForward];
383             break;
384         case MTMenuRewindItem:
385             NSLog(@"MenuController: Rewind");
386             [[MainController sharedController] rewind];
387             break;
388         case MTMenuPreviousTrackItem:
389             NSLog(@"MenuController: Previous Track");
390             [[MainController sharedController] prevSong];
391             break;
392         case MTMenuNextTrackItem:
393             NSLog(@"MenuController: Next Track");
394             [[MainController sharedController] nextSong];
395             break;
396         case MTMenuShowPlayerItem:
397             NSLog(@"MainController: Show Main Interface");
398             [[MainController sharedController] showPlayer];
399             break;
400         case MTMenuPreferencesItem:
401             NSLog(@"MenuController: Preferences...");
402             [[MainController sharedController] showPreferences];
403             break;
404         case MTMenuQuitItem:
405             NSLog(@"MenuController: Quit");
406             [[MainController sharedController] quitMenuTunes];
407             break;
408         default:
409             NSLog(@"MenuController: Unimplemented Menu Item OR Child-bearing Menu Item");
410             break;
411     }
412 }
413
414 - (void)performRatingMenuAction:(id)sender
415 {
416     [[MainController sharedController] selectSongRating:[sender tag]];
417 }
418
419 - (void)performPlaylistMenuAction:(id)sender
420 {
421     [[MainController sharedController] selectPlaylistAtIndex:[sender tag]];
422 }
423
424 - (void)performEqualizerMenuAction:(id)sender
425 {
426     [[MainController sharedController] selectEQPresetAtIndex:[sender tag]];
427 }
428
429 - (void)performUpcomingSongsMenuAction:(id)sender
430 {
431     [[MainController sharedController] selectSongAtIndex:[sender tag]];
432 }
433
434 - (void)updateMenu
435 {
436     
437     [_currentMenu update];
438 }
439
440 - (BOOL)validateMenuItem:(id <NSMenuItem>)menuItem
441 {
442     return YES;
443 }
444
445 - (NSString *)systemUIColor
446 {
447     NSDictionary *tmpDict;
448     NSNumber *tmpNumber;
449     if ( (tmpDict = [NSDictionary dictionaryWithContentsOfFile:[@"~/Library/Preferences/.GlobalPreferences.plist" stringByExpandingTildeInPath]]) ) {
450         if ( (tmpNumber = [tmpDict objectForKey:@"AppleAquaColorVariant"]) ) {
451             if ( ([tmpNumber intValue] == 1) ) {
452                 return @"Aqua";
453             } else {
454                 return @"Graphite";
455             }
456         } else {
457             return @"Aqua";
458         }
459     } else {
460         return @"Aqua";
461     }
462 }
463
464 - (void)setKeyEquivalentForCode:(short)code andModifiers:(long)modifiers
465         onItem:(NSMenuItem *)item
466 {
467     unichar charcode = 'a';
468     int i;
469     long cocoaModifiers = 0;
470     static long carbonToCocoa[6][2] = 
471     {
472         { cmdKey, NSCommandKeyMask },
473         { optionKey, NSAlternateKeyMask },
474         { controlKey, NSControlKeyMask },
475         { shiftKey, NSShiftKeyMask },
476     };
477     
478     for (i = 0; i < 6; i++) {
479         if (modifiers & carbonToCocoa[i][0]) {
480             cocoaModifiers += carbonToCocoa[i][1];
481         }
482     }
483     [item setKeyEquivalentModifierMask:cocoaModifiers];
484     
485     //Missing key combos for some keys. Must find them later.
486     switch (code)
487     {
488         case 36:
489             charcode = '\r';
490         break;
491         
492         case 48:
493             charcode = '\t';
494         break;
495         
496         //Space -- ARGH!
497         case 49:
498         {
499             // Haven't tested this, though it should work.
500             // This doesn't work. :'(
501             unichar buffer;
502             [[NSString stringWithString:@"Space"] getCharacters:&buffer];
503             charcode = buffer;
504             /*MenuRef menuRef = _NSGetCarbonMenu([item menu]);
505             NSLog(@"%@", menuRef);
506             SetMenuItemCommandKey(menuRef, 0, NO, 49);
507             SetMenuItemModifiers(menuRef, 0, kMenuNoCommandModifier);
508             SetMenuItemKeyGlyph(menuRef, 0, kMenuBlankGlyph);
509             charcode = 'b';*/
510             
511         }
512         break;
513         
514         case 51:
515             charcode = NSDeleteFunctionKey;
516         break;
517         
518         case 53:
519             charcode = '\e';
520         break;
521         
522         case 71:
523             charcode = '\e';
524         break;
525         
526         case 76:
527             charcode = '\r';
528         break;
529         
530         case 96:
531             charcode = NSF5FunctionKey;
532         break;
533         
534         case 97:
535             charcode = NSF6FunctionKey;
536         break;
537         
538         case 98:
539             charcode = NSF7FunctionKey;
540         break;
541         
542         case 99:
543             charcode = NSF3FunctionKey;
544         break;
545         
546         case 100:
547             charcode = NSF8FunctionKey;
548         break;
549         
550         case 101:
551             charcode = NSF9FunctionKey;
552         break;
553         
554         case 103:
555             charcode = NSF11FunctionKey;
556         break;
557         
558         case 105:
559             charcode = NSF3FunctionKey;
560         break;
561         
562         case 107:
563             charcode = NSF14FunctionKey;
564         break;
565         
566         case 109:
567             charcode = NSF10FunctionKey;
568         break;
569         
570         case 111:
571             charcode = NSF12FunctionKey;
572         break;
573         
574         case 113:
575             charcode = NSF13FunctionKey;
576         break;
577         
578         case 114:
579             charcode = NSInsertFunctionKey;
580         break;
581         
582         case 115:
583             charcode = NSHomeFunctionKey;
584         break;
585         
586         case 116:
587             charcode = NSPageUpFunctionKey;
588         break;
589         
590         case 117:
591             charcode = NSDeleteFunctionKey;
592         break;
593         
594         case 118:
595             charcode = NSF4FunctionKey;
596         break;
597         
598         case 119:
599             charcode = NSEndFunctionKey;
600         break;
601         
602         case 120:
603             charcode = NSF2FunctionKey;
604         break;
605         
606         case 121:
607             charcode = NSPageDownFunctionKey;
608         break;
609         
610         case 122:
611             charcode = NSF1FunctionKey;
612         break;
613         
614         case 123:
615             charcode = NSLeftArrowFunctionKey;
616         break;
617         
618         case 124:
619             charcode = NSRightArrowFunctionKey;
620         break;
621         
622         case 125:
623             charcode = NSDownArrowFunctionKey;
624         break;
625         
626         case 126:
627             charcode = NSUpArrowFunctionKey;
628         break;
629     }
630     
631     if (charcode == 'a') {
632         unsigned long state;
633         long keyTrans;
634         char charCode;
635         Ptr kchr;
636         state = 0;
637         kchr = (Ptr) GetScriptVariable(smCurrentScript, smKCHRCache);
638         keyTrans = KeyTranslate(kchr, code, &state);
639         charCode = keyTrans;
640         [item setKeyEquivalent:[NSString stringWithCString:&charCode length:1]];
641     } else if (charcode != 'b') {
642         [item setKeyEquivalent:[NSString stringWithCharacters:&charcode length:1]];
643     }
644 }
645
646 @end