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