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