New hot key setting style in preferences.
[MenuTunes.git] / PreferencesController.m
1 #import "PreferencesController.h"
2 #import "MainController.h"
3 #import "StatusWindow.h"
4 #import "StatusWindowController.h"
5 #import "CustomMenuTableView.h"
6
7 #import <ITKit/ITHotKeyCenter.h>
8 #import <ITKit/ITKeyCombo.h>
9 #import <ITKit/ITKeyComboPanel.h>
10 #import <ITKit/ITWindowPositioning.h>
11 #import <ITKit/ITKeyBroadcaster.h>
12
13 #import <ITKit/ITCutWindowEffect.h>
14 #import <ITKit/ITDissolveWindowEffect.h>
15 #import <ITKit/ITSlideHorizontallyWindowEffect.h>
16 #import <ITKit/ITSlideVerticallyWindowEffect.h>
17 #import <ITKit/ITPivotWindowEffect.h>
18
19
20 #define SENDER_STATE (([sender state] == NSOnState) ? YES : NO)
21
22 /*************************************************************************/
23 #pragma mark -
24 #pragma mark PRIVATE INTERFACE
25 /*************************************************************************/
26
27 @interface PreferencesController (Private)
28 - (void)setupWindow;
29 - (void)setupCustomizationTables;
30 - (void)setupMenuItems;
31 - (void)setupUI;
32 - (IBAction)changeMenus:(id)sender;
33 - (void)setLaunchesAtLogin:(BOOL)flag;
34 @end
35
36
37 @implementation PreferencesController
38
39
40 /*************************************************************************/
41 #pragma mark -
42 #pragma mark STATIC VARIABLES
43 /*************************************************************************/
44
45 static PreferencesController *prefs = nil;
46
47
48 /*************************************************************************/
49 #pragma mark -
50 #pragma mark INITIALIZATION METHODS
51 /*************************************************************************/
52
53 + (PreferencesController *)sharedPrefs;
54 {
55     if (! prefs) {
56         prefs = [[self alloc] init];
57     }
58     return prefs;
59 }
60
61 - (id)init
62 {
63     if ( (self = [super init]) ) {
64         ITDebugLog(@"Preferences initialized.");
65         df = [[NSUserDefaults standardUserDefaults] retain];
66         hotKeysArray = [[NSArray alloc] initWithObjects:@"PlayPause",
67                                                        @"NextTrack",
68                                                        @"PrevTrack",
69                                                        @"ShowPlayer",
70                                                        @"TrackInfo",
71                                                        @"UpcomingSongs",
72                                                        @"IncrementVolume",
73                                                        @"DecrementVolume",
74                                                        @"IncrementRating",
75                                                        @"DecrementRating",
76                                                        @"ToggleShuffle",
77                                                        @"ToggleLoop",
78                                                        nil];
79         
80         hotKeyNamesArray = [[NSArray alloc] initWithObjects:@"Play/Pause",
81                                                        @"Next Track",
82                                                        @"Previous Track",
83                                                        @"Show Player",
84                                                        @"Track Info",
85                                                        @"Upcoming Songs",
86                                                        @"Increment Volume",
87                                                        @"Decrement Volume",
88                                                        @"Increment Rating",
89                                                        @"Decrement Rating",
90                                                        @"Toggle Shuffle",
91                                                        @"Toggle Loop",
92                                                        nil];
93         hotKeysDictionary = [[NSMutableDictionary alloc] init];
94         controller = nil;
95     }
96     return self;
97 }
98
99
100 /*************************************************************************/
101 #pragma mark -
102 #pragma mark ACCESSOR METHODS
103 /*************************************************************************/
104
105 - (id)controller
106 {
107     return controller;
108 }
109
110 - (void)setController:(id)object
111 {
112     [controller autorelease];
113     controller = [object retain];
114 }
115
116
117 /*************************************************************************/
118 #pragma mark -
119 #pragma mark INSTANCE METHODS
120 /*************************************************************************/
121
122 - (IBAction)showPrefsWindow:(id)sender
123 {
124     ITDebugLog(@"Showing preferences window.");
125     if (! window) {  // If window does not exist yet, then the nib hasn't been loaded.
126         ITDebugLog(@"Window doesn't exist, initial setup.");
127         [self setupWindow];  // Load in the nib, and perform any initial setup.
128         [self setupCustomizationTables];  // Setup the DnD manu config tables.
129         [self setupMenuItems];  // Setup the arrays of menu items
130         [self setupUI]; // Sets up additional UI
131         [window setDelegate:self];
132         [menuTableView reloadData];
133         [hotKeysTableView setDoubleAction:@selector(hotKeysTableViewDoubleClicked:)];
134         
135         //Change the launch player checkbox to the proper name
136         [launchPlayerAtLaunchCheckbox setTitle:[NSString stringWithFormat:@"Launch %@ when MenuTunes launches", [[controller currentRemote] playerSimpleName]]]; //This isn't localized...
137     }
138
139     [window center];
140     [NSApp activateIgnoringOtherApps:YES];
141     [window performSelector:@selector(makeKeyAndOrderFront:) withObject:self afterDelay:0.0];
142 }
143
144 - (IBAction)changeGeneralSetting:(id)sender
145 {
146     ITDebugLog(@"Changing general setting of tag %i.", [sender tag]);
147     if ( [sender tag] == 1010) {
148         [self setLaunchesAtLogin:SENDER_STATE];
149     } else if ( [sender tag] == 1020) {
150         [df setBool:SENDER_STATE forKey:@"LaunchPlayerWithMT"];
151     } else if ( [sender tag] == 1030) {
152         [df setInteger:[sender intValue] forKey:@"SongsInAdvance"];
153
154     } else if ( [sender tag] == 1040) {
155         // This will not be executed.  Song info always shows the title of the song.
156         // [df setBool:SENDER_STATE forKey:@"showName"];
157     } else if ( [sender tag] == 1050) {
158         [df setBool:SENDER_STATE forKey:@"showArtist"];
159     } else if ( [sender tag] == 1060) {
160         [df setBool:SENDER_STATE forKey:@"showAlbum"];
161     } else if ( [sender tag] == 1070) {
162         [df setBool:SENDER_STATE forKey:@"showTime"];
163     } else if ( [sender tag] == 1080) {
164         [df setBool:SENDER_STATE forKey:@"showTrackNumber"];
165     } else if ( [sender tag] == 1090) {
166         [df setBool:SENDER_STATE forKey:@"showTrackRating"];
167     }
168     [df synchronize];
169 }
170
171 - (IBAction)changeStatusWindowSetting:(id)sender
172 {
173     StatusWindow *sw = [StatusWindow sharedWindow];
174     ITDebugLog(@"Changing status window setting of tag %i", [sender tag]);
175     if ( [sender tag] == 2010) {
176         [df setInteger:[sender selectedRow] forKey:@"statusWindowVerticalPosition"];
177         [df setInteger:[sender selectedColumn] forKey:@"statusWindowHorizontalPosition"];
178         // update the window's position here
179     } else if ( [sender tag] == 2020) {
180         // update screen selection
181     } else if ( [sender tag] == 2030) {
182         int effectTag = [[sender selectedItem] tag];
183         float time = ([df floatForKey:@"statusWindowAppearanceSpeed"] ? [df floatForKey:@"statusWindowAppearanceSpeed"] : 0.8);
184         [df setInteger:effectTag forKey:@"statusWindowAppearanceEffect"];
185
186         if ( effectTag == 2100 ) {
187             [sw setEntryEffect:[[[ITCutWindowEffect alloc] initWithWindow:sw] autorelease]];
188         } else if ( effectTag == 2101 ) {
189             [sw setEntryEffect:[[[ITDissolveWindowEffect alloc] initWithWindow:sw] autorelease]];
190         } else if ( effectTag == 2102 ) {
191             [sw setEntryEffect:[[[ITSlideVerticallyWindowEffect alloc] initWithWindow:sw] autorelease]];
192         } else if ( effectTag == 2103 ) {
193             [sw setEntryEffect:[[[ITSlideHorizontallyWindowEffect alloc] initWithWindow:sw] autorelease]];
194         } else if ( effectTag == 2104 ) {
195             NSLog(@"dflhgldf");
196             [sw setEntryEffect:[[[ITPivotWindowEffect alloc] initWithWindow:sw] autorelease]];
197         }
198
199         [[sw entryEffect] setEffectTime:time];
200         
201     } else if ( [sender tag] == 2040) {
202         int effectTag = [[sender selectedItem] tag];
203         float time = ([df floatForKey:@"statusWindowVanishSpeed"] ? [df floatForKey:@"statusWindowVanishSpeed"] : 0.8);
204         
205         [df setInteger:[[sender selectedItem] tag] forKey:@"statusWindowVanishEffect"];
206         
207         if ( effectTag == 2100 ) {
208             [sw setExitEffect:[[[ITCutWindowEffect alloc] initWithWindow:sw] autorelease]];
209         } else if ( effectTag == 2101 ) {
210             [sw setExitEffect:[[[ITDissolveWindowEffect alloc] initWithWindow:sw] autorelease]];
211         } else if ( effectTag == 2102 ) {
212             [sw setExitEffect:[[[ITSlideVerticallyWindowEffect alloc] initWithWindow:sw] autorelease]];
213         } else if ( effectTag == 2103 ) {
214             [sw setExitEffect:[[[ITSlideHorizontallyWindowEffect alloc] initWithWindow:sw] autorelease]];
215         } else if ( effectTag == 2104 ) {
216             [sw setExitEffect:[[[ITPivotWindowEffect alloc] initWithWindow:sw] autorelease]];
217         }
218
219         [[sw exitEffect] setEffectTime:time];
220
221     } else if ( [sender tag] == 2050) {
222         float newTime = (-([sender floatValue]));
223         [df setFloat:newTime forKey:@"statusWindowAppearanceSpeed"];
224         [[sw entryEffect] setEffectTime:newTime];
225     } else if ( [sender tag] == 2060) {
226         float newTime = (-([sender floatValue]));
227         [df setFloat:newTime forKey:@"statusWindowVanishSpeed"];
228         [[sw exitEffect] setEffectTime:newTime];
229     } else if ( [sender tag] == 2070) {
230         [df setFloat:[sender floatValue] forKey:@"statusWindowVanishDelay"];
231         [sw setExitDelay:[sender floatValue]];
232     } else if ( [sender tag] == 2080) {
233         [df setBool:SENDER_STATE forKey:@"showSongInfoOnChange"];
234     }
235     [df synchronize];
236 }
237
238 - (void)registerDefaults
239 {
240     BOOL found = NO;
241     NSMutableDictionary *loginWindow;
242     NSMutableArray *loginArray;
243     NSEnumerator *loginEnum;
244     id anItem;
245     ITDebugLog(@"Registering defaults.");
246     [df setObject:[NSArray arrayWithObjects:
247         @"trackInfo",
248         @"separator",
249         @"playPause",
250         @"prevTrack",
251         @"nextTrack",
252         @"separator",
253         @"playlists",
254         @"upcomingSongs",
255         @"separator",
256         @"preferences",
257         @"quit",
258         nil] forKey:@"menu"];
259
260     [df setInteger:5 forKey:@"SongsInAdvance"];
261     // [df setBool:YES forKey:@"showName"];  // Song info will always show song title.
262     [df setBool:YES forKey:@"showArtist"];
263     [df setBool:NO forKey:@"showAlbum"];
264     [df setBool:NO forKey:@"showTime"];
265
266     [df setInteger:2100 forKey:@"statusWindowAppearanceEffect"];
267     [df setInteger:2101 forKey:@"statusWindowVanishEffect"];
268     [df setFloat:0.8 forKey:@"statusWindowAppearanceSpeed"];
269     [df setFloat:0.8 forKey:@"statusWindowVanishSpeed"];
270     [df setFloat:4.0 forKey:@"statusWindowVanishDelay"];
271     [df setBool:YES forKey:@"showSongInfoOnChange"];
272
273     [df synchronize];
274     
275     loginWindow = [[df persistentDomainForName:@"loginwindow"] mutableCopy];
276     loginArray = [loginWindow objectForKey:@"AutoLaunchedApplicationDictionary"];
277     loginEnum = [loginArray objectEnumerator];
278
279     while ( (anItem = [loginEnum nextObject]) ) {
280         if ( [[[anItem objectForKey:@"Path"] lastPathComponent] isEqualToString:[[[NSBundle mainBundle] bundlePath] lastPathComponent]] ) {
281             found = YES;
282         }
283     }
284     [loginWindow release];
285     
286     if (!found) {
287         [[StatusWindowController sharedController] showSetupQueryWindow];
288     }
289 }
290
291 - (void)autoLaunchOK
292 {
293     [[StatusWindow sharedWindow] setLocked:NO];
294     [[StatusWindow sharedWindow] vanish:self];
295     [[StatusWindow sharedWindow] setIgnoresMouseEvents:YES];
296     
297     [self setLaunchesAtLogin:YES];
298 }
299
300 - (void)autoLaunchCancel
301 {
302     [[StatusWindow sharedWindow] setLocked:NO];
303     [[StatusWindow sharedWindow] vanish:self];
304     [[StatusWindow sharedWindow] setIgnoresMouseEvents:YES];
305 }
306
307 - (void)deletePressedInTableView:(NSTableView *)tableView
308 {
309     if (tableView == menuTableView) {
310         int selRow = [tableView selectedRow];
311         ITDebugLog(@"Delete pressed in menu table view.");
312         if (selRow != - 1) {
313             NSString *object = [myItems objectAtIndex:selRow];
314             
315             if ([object isEqualToString:@"preferences"]) {
316                 NSBeep();
317                 return;
318             }
319             
320             if (![object isEqualToString:@"separator"])
321                 [availableItems addObject:object];
322             ITDebugLog(@"Removing object named %@", object);
323             [myItems removeObjectAtIndex:selRow];
324             [menuTableView reloadData];
325             [allTableView reloadData];
326         }
327         [self changeMenus:self];
328     }
329 }
330
331 /*************************************************************************/
332 #pragma mark -
333 #pragma mark HOTKEY SUPPORT METHODS
334 /*************************************************************************/
335
336 - (IBAction)clearHotKey:(id)sender
337 {
338     [hotKeysDictionary setObject:[ITKeyCombo clearKeyCombo] forKey:[hotKeysArray objectAtIndex:[hotKeysTableView selectedRow]]];
339     [df setObject:[[ITKeyCombo clearKeyCombo] plistRepresentation] forKey:[hotKeysArray objectAtIndex:[hotKeysTableView selectedRow]]];
340     [controller setupHotKeys];
341     [hotKeysTableView reloadData];
342 }
343
344 - (IBAction)editHotKey:(id)sender
345 {
346     ITKeyComboPanel *panel = [ITKeyComboPanel sharedPanel];
347     NSString *keyComboKey = [hotKeysArray objectAtIndex:[hotKeysTableView selectedRow]];
348     ITKeyCombo *keyCombo;
349     
350     ITDebugLog(@"Setting key combo on hot key %@.", keyComboKey);
351     [controller clearHotKeys];
352     [panel setKeyCombo:[hotKeysDictionary objectForKey:[hotKeysArray objectAtIndex:[hotKeysTableView selectedRow]]]];
353     [panel setKeyBindingName:[hotKeyNamesArray objectAtIndex:[hotKeysTableView selectedRow]]];
354     if ([panel runModal] == NSOKButton) {
355         NSEnumerator *keyEnumerator = [[hotKeysDictionary allKeys] objectEnumerator];
356         NSString *nextKey;
357         keyCombo = [panel keyCombo];
358         
359         //Check for duplicate key combo
360         while ( (nextKey = [keyEnumerator nextObject]) ) {
361             if ([[hotKeysDictionary objectForKey:nextKey] isEqual:keyCombo] &&
362                 ![keyCombo isEqual:[ITKeyCombo clearKeyCombo]]) {
363                 [hotKeysDictionary setObject:[ITKeyCombo clearKeyCombo]
364                                    forKey:nextKey];
365                 [df setObject:[[ITKeyCombo clearKeyCombo] plistRepresentation]
366                     forKey:nextKey];
367             }
368         }
369         
370         [hotKeysDictionary setObject:keyCombo forKey:keyComboKey];
371         [df setObject:[keyCombo plistRepresentation] forKey:keyComboKey];
372         [controller setupHotKeys];
373         [hotKeysTableView reloadData];
374         ITDebugLog(@"Set combo %@ on hot key %@.", keyCombo, keyComboKey);
375     } else {
376         ITDebugLog(@"Hot key setting on hot key %@ cancelled.", keyComboKey);
377     }
378 }
379
380 - (void)hotKeysTableViewDoubleClicked:(id)sender
381 {
382     if ([sender clickedRow] > -1) {
383         [self editHotKey:sender];
384     }
385 }
386
387 /*************************************************************************/
388 #pragma mark -
389 #pragma mark PRIVATE METHOD IMPLEMENTATIONS
390 /*************************************************************************/
391
392 - (void)setupWindow
393 {
394     ITDebugLog(@"Loading Preferences.nib.");
395     if (![NSBundle loadNibNamed:@"Preferences" owner:self]) {
396         ITDebugLog(@"Failed to load Preferences.nib.");
397         NSBeep();
398         return;
399     }
400 }
401
402 - (void)setupCustomizationTables
403 {
404     NSImageCell *imgCell = [[[NSImageCell alloc] initImageCell:nil] autorelease];
405     ITDebugLog(@"Setting up table views.");
406     // Set the table view cells up
407     [imgCell setImageScaling:NSScaleNone];
408     [[menuTableView tableColumnWithIdentifier:@"submenu"] setDataCell:imgCell];
409     [[allTableView tableColumnWithIdentifier:@"submenu"] setDataCell:imgCell];
410
411     // Register for drag and drop
412     [menuTableView registerForDraggedTypes:[NSArray arrayWithObjects:
413         @"MenuTableViewPboardType",
414         @"AllTableViewPboardType",
415         nil]];
416     [allTableView registerForDraggedTypes:[NSArray arrayWithObjects:
417         @"MenuTableViewPboardType",
418         @"AllTableViewPboardType",
419         nil]];
420 }
421
422 - (void)setupMenuItems
423 {
424     NSEnumerator *itemEnum;
425     id            anItem;
426     ITDebugLog(@"Setting up table view arrays.");
427     // Set the list of items you can have.
428     availableItems = [[NSMutableArray alloc] initWithObjects:
429         @"separator",
430         @"trackInfo",
431         @"upcomingSongs",
432         @"playlists",
433         @"eqPresets",
434         @"songRating",
435         @"playPause",
436         @"nextTrack",
437         @"prevTrack",
438         @"fastForward",
439         @"rewind",
440         @"showPlayer",
441         @"quit",
442         nil];
443     
444     // Get our preferred menu
445     myItems = [[df arrayForKey:@"menu"] mutableCopy];
446     
447     // Delete items in the availableItems array that are already part of the menu
448     itemEnum = [myItems objectEnumerator];
449     while ( (anItem = [itemEnum nextObject]) ) {
450         if (![anItem isEqualToString:@"separator"]) {
451             [availableItems removeObject:anItem];
452         }
453     }
454     
455     // Items that show should a submenu image
456     submenuItems = [[NSArray alloc] initWithObjects:
457         @"upcomingSongs",
458         @"playlists",
459         @"eqPresets",
460         @"songRating",
461         nil];
462 }
463
464 - (void)setupUI
465 {
466     NSMutableDictionary *loginwindow;
467     NSMutableArray *loginarray;
468     NSEnumerator *loginEnum, *keyArrayEnum;
469     id anItem;
470     
471     ITDebugLog(@"Setting up preferences UI.");
472     // Fill in the number of songs in advance to show field
473     [songsInAdvance setIntValue:[df integerForKey:@"SongsInAdvance"]];
474     
475     // Fill hot key array
476     keyArrayEnum = [hotKeysArray objectEnumerator];
477     
478     while ( (anItem = [keyArrayEnum nextObject]) ) {
479         if ([df objectForKey:anItem]) {
480             ITDebugLog(@"Setting up \"%@\" hot key.", anItem);
481             [hotKeysDictionary setObject:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:anItem]] forKey:anItem];
482         } else {
483             [hotKeysDictionary setObject:[ITKeyCombo clearKeyCombo] forKey:anItem];
484         }
485     }
486     
487     ITDebugLog(@"Setting up track info checkboxes.");
488     // Check current track info buttons
489     [albumCheckbox setState:[df boolForKey:@"showAlbum"] ? NSOnState : NSOffState];
490     [nameCheckbox setState:NSOnState];  // Song info will ALWAYS show song title.
491     [nameCheckbox setEnabled:NO];  // Song info will ALWAYS show song title.
492     [artistCheckbox setState:[df boolForKey:@"showArtist"] ? NSOnState : NSOffState];
493     [trackTimeCheckbox setState:[df boolForKey:@"showTime"] ? NSOnState : NSOffState];
494     [trackNumberCheckbox setState:[df boolForKey:@"showTrackNumber"] ? NSOnState : NSOffState];
495     [ratingCheckbox setState:[df boolForKey:@"showTrackRating"] ? NSOnState : NSOffState];
496     
497     // Set the launch at login checkbox state
498     ITDebugLog(@"Setting launch at login state.");
499     [df synchronize];
500     loginwindow = [[df persistentDomainForName:@"loginwindow"] mutableCopy];
501     loginarray = [loginwindow objectForKey:@"AutoLaunchedApplicationDictionary"];
502     
503     loginEnum = [loginarray objectEnumerator];
504     while ( (anItem = [loginEnum nextObject]) ) {
505         if ([[[anItem objectForKey:@"Path"] lastPathComponent] isEqualToString:[[[NSBundle mainBundle] bundlePath] lastPathComponent]]) {
506             [launchAtLoginCheckbox setState:NSOnState];
507         }
508     }
509     
510     // Set the launch player checkbox state
511     ITDebugLog(@"Setting launch player with MenuTunes state.");
512     [launchPlayerAtLaunchCheckbox setState:[df boolForKey:@"LaunchPlayerWithMT"] ? NSOnState : NSOffState];
513     
514     // Setup the positioning controls
515     
516     // Setup effects controls
517     [appearanceEffectPopup selectItem:[appearanceEffectPopup itemAtIndex:[appearanceEffectPopup indexOfItemWithTag:[df integerForKey:@"statusWindowAppearanceEffect"]]]];
518     [vanishEffectPopup     selectItem:[vanishEffectPopup     itemAtIndex:[vanishEffectPopup     indexOfItemWithTag:[df integerForKey:@"statusWindowVanishEffect"]]]];
519     [appearanceSpeedSlider setFloatValue:-([df floatForKey:@"statusWindowAppearanceSpeed"])];
520     [vanishSpeedSlider     setFloatValue:-([df floatForKey:@"statusWindowVanishSpeed"])];
521     [vanishDelaySlider     setFloatValue:[df floatForKey:@"statusWindowVanishDelay"]];
522     [showOnChangeCheckbox  setState:([df boolForKey:@"showSongInfoOnChange"] ? NSOnState : NSOffState)];
523 }
524
525 - (IBAction)changeMenus:(id)sender
526 {
527     ITDebugLog(@"Synchronizing menus");
528     [df setObject:myItems forKey:@"menu"];
529     [df synchronize];
530 }
531
532 - (void)setLaunchesAtLogin:(BOOL)flag
533 {
534     NSMutableDictionary *loginwindow;
535     NSMutableArray *loginarray;
536     ITDebugLog(@"Setting launches at login: %i", flag);
537     [df synchronize];
538     loginwindow = [[df persistentDomainForName:@"loginwindow"] mutableCopy];
539     loginarray = [loginwindow objectForKey:@"AutoLaunchedApplicationDictionary"];
540     
541     if (flag) {
542         NSDictionary *itemDict = [NSDictionary dictionaryWithObjectsAndKeys:
543         [[NSBundle mainBundle] bundlePath], @"Path",
544         [NSNumber numberWithInt:0], @"Hide", nil];
545         [loginarray addObject:itemDict];
546     } else {
547         int i;
548         for (i = 0; i < [loginarray count]; i++) {
549             NSDictionary *tempDict = [loginarray objectAtIndex:i];
550             if ([[[tempDict objectForKey:@"Path"] lastPathComponent] isEqualToString:[[[NSBundle mainBundle] bundlePath] lastPathComponent]]) {
551                 [loginarray removeObjectAtIndex:i];
552                 break;
553             }
554         }
555     }
556     [df setPersistentDomain:loginwindow forName:@"loginwindow"];
557     [df synchronize];
558     [loginwindow release];
559     ITDebugLog(@"Finished setting launches at login.");
560 }
561
562
563 /*************************************************************************/
564 #pragma mark -
565 #pragma mark NSWindow DELEGATE METHODS
566 /*************************************************************************/
567
568 - (void)windowWillClose:(NSNotification *)note
569 {
570     [(MainController *)controller closePreferences]; 
571 }
572
573
574 /*************************************************************************/
575 #pragma mark -
576 #pragma mark NSTableView DATASOURCE METHODS
577 /*************************************************************************/
578
579 - (int)numberOfRowsInTableView:(NSTableView *)aTableView
580 {
581     if (aTableView == menuTableView) {
582         return [myItems count];
583     } else if (aTableView == allTableView) {
584         return [availableItems count];
585     } else {
586         return [hotKeysArray count];
587     }
588 }
589
590 - (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex
591 {
592     if (aTableView == menuTableView) {
593         NSString *object = [myItems objectAtIndex:rowIndex];
594         if ([[aTableColumn identifier] isEqualToString:@"name"]) {
595             if ([object isEqualToString:@"showPlayer"]) {
596                 return [NSString stringWithFormat:@"%@ %@", NSLocalizedString(@"show", @"Show"), [[controller currentRemote] playerSimpleName]];
597             }
598             return NSLocalizedString(object, @"ERROR");
599         } else {
600             if ([submenuItems containsObject:object])
601             {
602                 return [NSImage imageNamed:@"submenu"];
603             } else {
604                 return nil;
605             }
606         }
607     } else if (aTableView == allTableView) {
608         NSString *object = [availableItems objectAtIndex:rowIndex];
609         if ([[aTableColumn identifier] isEqualToString:@"name"]) {
610             if ([object isEqualToString:@"showPlayer"]) {
611                 return [NSString stringWithFormat:@"%@ %@", NSLocalizedString(@"show", @"Show"), [[controller currentRemote] playerSimpleName]];
612             }
613             return NSLocalizedString(object, @"ERROR");
614         } else {
615             if ([submenuItems containsObject:object]) {
616                 return [NSImage imageNamed:@"submenu"];
617             } else {
618                 return nil;
619             }
620         }
621     } else {
622         if ([[aTableColumn identifier] isEqualToString:@"name"]) {
623             return [hotKeyNamesArray objectAtIndex:rowIndex];
624         } else {
625             return [[hotKeysDictionary objectForKey:[hotKeysArray objectAtIndex:rowIndex]] description];
626         }
627     }
628 }
629
630 - (BOOL)tableView:(NSTableView *)tableView writeRows:(NSArray*)rows toPasteboard:(NSPasteboard*)pboard
631 {
632     if (tableView == menuTableView) {
633         [pboard declareTypes:[NSArray arrayWithObjects:@"MenuTableViewPboardType", nil] owner:self];
634         [pboard setString:[[rows objectAtIndex:0] stringValue] forType:@"MenuTableViewPboardType"];
635         return YES;
636     }
637     
638     if (tableView == allTableView) {
639         [pboard declareTypes:[NSArray arrayWithObjects:@"AllTableViewPboardType", nil] owner:self];
640         [pboard setString:[[rows objectAtIndex:0] stringValue] forType:@"AllTableViewPboardType"];
641         return YES;
642     }
643     return NO;
644 }
645
646 - (BOOL)tableView:(NSTableView*)tableView acceptDrop:(id <NSDraggingInfo>)info row:(int)row dropOperation:(NSTableViewDropOperation)operation
647 {
648     NSPasteboard *pb;
649     int dragRow;
650     NSString *dragData, *temp;
651     
652     pb = [info draggingPasteboard];
653     
654     if ([[pb types] containsObject:@"MenuTableViewPboardType"]) {
655         dragData = [pb stringForType:@"MenuTableViewPboardType"];
656         dragRow = [dragData intValue];
657         temp = [myItems objectAtIndex:dragRow];
658         
659         if (tableView == menuTableView) {
660             [myItems insertObject:temp atIndex:row];
661             if (row > dragRow) {
662                 [myItems removeObjectAtIndex:dragRow];
663             } else {
664                 [myItems removeObjectAtIndex:dragRow + 1];
665             }
666         } else if (tableView == allTableView) {
667             if (![temp isEqualToString:@"separator"]) {
668                 [availableItems addObject:temp];
669             }
670             [myItems removeObjectAtIndex:dragRow];
671         }
672     } else if ([[pb types] containsObject:@"AllTableViewPboardType"]) {
673         dragData = [pb stringForType:@"AllTableViewPboardType"];
674         dragRow = [dragData intValue];
675         temp = [availableItems objectAtIndex:dragRow];
676         
677         [myItems insertObject:temp atIndex:row];
678         
679         if (![temp isEqualToString:@"separator"]) {
680             [availableItems removeObjectAtIndex:dragRow];
681         }
682     }
683     
684     [menuTableView reloadData];
685     [allTableView reloadData];
686     [self changeMenus:self];
687     return YES;
688 }
689
690 - (NSDragOperation)tableView:(NSTableView*)tableView validateDrop:(id <NSDraggingInfo>)info proposedRow:(int)row proposedDropOperation:(NSTableViewDropOperation)operation
691 {
692     if (tableView == allTableView) {
693         if ([[[info draggingPasteboard] types] containsObject:@"AllTableViewPboardType"]) {
694             return NSDragOperationNone;
695         }
696         
697         if ([[[info draggingPasteboard] types] containsObject:@"MenuTableViewPboardType"]) {
698             NSString *item = [myItems objectAtIndex:[[[info draggingPasteboard] stringForType:@"MenuTableViewPboardType"] intValue]];
699             if ([item isEqualToString:@"preferences"] || [item isEqualToString:@"quit"]) {
700                 return NSDragOperationNone;
701             }
702         }
703         
704         [tableView setDropRow:-1 dropOperation:NSTableViewDropOn];
705         return NSDragOperationGeneric;
706     }
707     
708     if (operation == NSTableViewDropOn || row == -1)
709     {
710         return NSDragOperationNone;
711     }
712     return NSDragOperationGeneric;
713 }
714
715
716 /*************************************************************************/
717 #pragma mark -
718 #pragma mark DEALLOCATION METHODS
719 /*************************************************************************/
720
721 - (void)dealloc
722 {
723     [hotKeysArray release];
724     [hotKeysDictionary release];
725     [menuTableView setDataSource:nil];
726     [allTableView setDataSource:nil];
727     [controller release];
728     [availableItems release];
729     [submenuItems release];
730     [myItems release];
731     [df release];
732 }
733
734 @end