Moving registerDefaults to the preferences controller where it belongs. Some other...
[MenuTunes.git] / PreferencesController.m
1 #import "PreferencesController.h"
2 #import "MainController.h"
3 #import "HotKeyCenter.h"
4
5 /*************************************************************************/
6 #pragma mark -
7 #pragma mark PRIVATE INTERFACE
8 /*************************************************************************/
9
10 @interface PreferencesController (Private)
11 - (void)setupWindow;
12 - (void)setupCustomizationTables;
13 - (void)setupMenuItems;
14 - (void)setupUI;
15 @end
16
17
18 @implementation PreferencesController
19
20
21 /*************************************************************************/
22 #pragma mark -
23 #pragma mark STATIC VARIABLES
24 /*************************************************************************/
25
26 static PreferencesController *prefs = nil;
27
28
29 /*************************************************************************/
30 #pragma mark -
31 #pragma mark INITIALIZATION METHODS
32 /*************************************************************************/
33
34 + (PreferencesController *)sharedPrefs;
35 {
36     if (! prefs) {
37         prefs = [[self alloc] init];
38     }
39     return prefs;
40 }
41
42 - (id)init
43 {
44     if ( (self = [super init]) ) {
45         df = [[NSUserDefaults standardUserDefaults] retain];
46         controller = nil;
47     }
48     return self;
49 }
50
51
52 /*************************************************************************/
53 #pragma mark -
54 #pragma mark ACCESSOR METHODS
55 /*************************************************************************/
56
57 - (id)controller
58 {
59     return controller;
60 }
61
62 - (void)setController:(id)object
63 {
64     [controller autorelease];
65     controller = [object retain];
66 }
67
68
69 /*************************************************************************/
70 #pragma mark -
71 #pragma mark INSTANCE METHODS
72 /*************************************************************************/
73
74 - (IBAction)showPrefsWindow:(id)sender
75 {
76     if (! window) {  // If window does not exist yet, then the nib hasn't been loaded.
77         [self setupWindow];  // Load in the nib, and perform any initial setup.
78         [self setupCustomizationTables];  // Setup the DnD manu config tables.
79         [self setupMenuItems];  // Setup the arrays of menu items
80         [self setupUI]; // Sets up additional UI
81         [window setDelegate:self];
82     }
83     
84     [window setLevel:NSStatusWindowLevel];
85     [window center];
86     [window makeKeyAndOrderFront:self];
87     [NSApp activateIgnoringOtherApps:YES];
88 }
89
90 - (IBAction)apply:(id)sender
91 {
92     [df setObject:myItems forKey:@"menu"];
93     
94     //Set key combos
95     [df setKeyCombo:playPauseCombo forKey:@"PlayPause"];
96     [df setKeyCombo:nextTrackCombo forKey:@"NextTrack"];
97     [df setKeyCombo:prevTrackCombo forKey:@"PrevTrack"];
98     [df setKeyCombo:trackInfoCombo forKey:@"TrackInfo"];
99     [df setKeyCombo:upcomingSongsCombo forKey:@"UpcomingSongs"];
100     
101     //Set info checkboxes
102     [df setBool:[albumCheckbox state] forKey:@"showAlbum"];
103     [df setBool:[nameCheckbox state] forKey:@"showName"];
104     [df setBool:[artistCheckbox state] forKey:@"showArtist"];
105     [df setBool:[trackTimeCheckbox state] forKey:@"showTime"];
106     
107     //Here we set whether we will launch at login by modifying loginwindow.plist
108     if ([launchAtLoginCheckbox state] == NSOnState) {
109         NSMutableDictionary *loginwindow;
110         NSMutableArray *loginarray;
111         ComponentInstance temp = OpenDefaultComponent(kOSAComponentType, kAppleScriptSubtype);;
112         int i;
113         BOOL skip = NO;
114         
115         [df synchronize];
116         loginwindow = [[df persistentDomainForName:@"loginwindow"] mutableCopy];
117         loginarray = [loginwindow objectForKey:@"AutoLaunchedApplicationDictionary"];
118         
119         for (i = 0; i < [loginarray count]; i++) {
120             NSDictionary *tempDict = [loginarray objectAtIndex:i];
121             if ([[[tempDict objectForKey:@"Path"] lastPathComponent] isEqualToString:[[[NSBundle mainBundle] bundlePath] lastPathComponent]]) {
122                 skip = YES;
123             }
124         }
125         
126         if (!skip) {
127             AEDesc scriptDesc, resultDesc;
128             NSString *script = [NSString stringWithFormat:@"tell application \"System Events\"\nmake new login item at end of login items with properties {path:\"%@\", kind:\"APPLICATION\"}\nend tell", [[NSBundle mainBundle] bundlePath]];
129             
130             AECreateDesc(typeChar, [script cString], [script cStringLength], 
131         &scriptDesc);
132             
133             OSADoScript(temp, &scriptDesc, kOSANullScript, typeChar, kOSAModeCanInteract, &resultDesc);
134             
135             AEDisposeDesc(&scriptDesc);
136             AEDisposeDesc(&resultDesc);
137             CloseComponent(temp);
138         }
139     } else {
140         NSMutableDictionary *loginwindow;
141         NSMutableArray *loginarray;
142         int i;
143         
144         [df synchronize];
145         loginwindow = [[df persistentDomainForName:@"loginwindow"] mutableCopy];
146         loginarray = [loginwindow objectForKey:@"AutoLaunchedApplicationDictionary"];
147         
148         for (i = 0; i < [loginarray count]; i++) {
149             NSDictionary *tempDict = [loginarray objectAtIndex:i];
150             if ([[[tempDict objectForKey:@"Path"] lastPathComponent] isEqualToString:[[[NSBundle mainBundle] bundlePath] lastPathComponent]]) {
151                 [loginarray removeObjectAtIndex:i];
152                 [df setPersistentDomain:loginwindow forName:@"loginwindow"];
153                 [df synchronize];
154                 break;
155             }
156         }
157     }
158     
159     //Set songs in advance
160     if ([songsInAdvance intValue]) {
161         [df setInteger:[songsInAdvance intValue] forKey:@"SongsInAdvance"];
162     } else {
163         [df setInteger:5 forKey:@"SongsInAdvance"];
164     }
165     
166     /*{
167         NSArray *apps = [[NSWorkspace sharedWorkspace] launchedApplications];
168         int i;
169         
170         for (i = 0; i < [apps count]; i++) {
171             if ([[[apps objectAtIndex:i] objectForKey:@"NSApplicationName"]
172                     isEqualToString:@"iTunes"]) {
173                 [controller rebuildMenu];
174             }
175         }
176     }*/
177     [controller clearHotKeys];
178 }
179
180 - (void)registerDefaults
181 {
182     BOOL found = NO;
183     NSMutableDictionary *loginWindow;
184     NSMutableArray *loginArray;
185     NSEnumerator *loginEnum;
186     id anItem;
187
188     [df setObject:[NSArray arrayWithObjects:
189         @"Play/Pause",
190         @"Next Track",
191         @"Previous Track",
192         @"Fast Forward",
193         @"Rewind",
194         @"<separator>",
195         @"Upcoming Songs",
196         @"Playlists",
197         @"Song Rating",
198         @"<separator>",
199         @"PreferencesÉ",
200         @"Quit",
201         @"<separator>",
202         @"Current Track Info",
203         nil] forKey:@"menu"];
204
205     [df setInteger:5 forKey:@"SongsInAdvance"];
206     [df setBool:YES forKey:@"showName"];
207     [df setBool:YES forKey:@"showArtist"];
208     [df setBool:NO forKey:@"showAlbum"];
209     [df setBool:NO forKey:@"showTime"];
210
211     [df synchronize];
212     
213     loginWindow = [[df persistentDomainForName:@"loginwindow"] mutableCopy];
214     loginArray = [loginWindow objectForKey:@"AutoLaunchedApplicationDictionary"];
215     loginEnum = [loginArray objectEnumerator];
216
217     while ( (anItem = [loginEnum nextObject]) ) {
218         if ( [[[anItem objectForKey:@"Path"] lastPathComponent] isEqualToString:[[[NSBundle mainBundle] bundlePath] lastPathComponent]] ) {
219             found = YES;
220         }
221     }
222
223     [loginWindow release];
224     
225     // This is teh sux
226     // We must fix it so it is no longer suxy
227     if (!found) {
228         if (NSRunInformationalAlertPanel(@"Auto-launch MenuTunes", @"Would you like MenuTunes to automatically launch at login?", @"Yes", @"No", nil) == NSOKButton) {
229             AEDesc scriptDesc, resultDesc;
230             NSString *script = [NSString stringWithFormat:@"tell application \"System Events\"\nmake new login item at end of login items with properties {path:\"%@\", kind:\"APPLICATION\"}\nend tell", [[NSBundle mainBundle] bundlePath]];
231             ComponentInstance asComponent = OpenDefaultComponent(kOSAComponentType, kAppleScriptSubtype);
232
233             AECreateDesc(typeChar, [script cString], [script cStringLength],
234                          &scriptDesc);
235
236             OSADoScript(asComponent, &scriptDesc, kOSANullScript, typeChar, kOSAModeCanInteract, &resultDesc);
237
238             AEDisposeDesc(&scriptDesc);
239             AEDisposeDesc(&resultDesc);
240
241             CloseComponent(asComponent);
242         }
243     }
244 }
245
246 - (IBAction)cancelHotKey:(id)sender
247 {
248     [[NSNotificationCenter defaultCenter] removeObserver:self];
249     [NSApp endSheet:keyComboPanel];
250     [keyComboPanel orderOut:nil];
251 }
252
253 - (IBAction)clearHotKey:(id)sender
254 {
255     [self setKeyCombo:[KeyCombo clearKeyCombo]];
256 }
257
258 - (IBAction)okHotKey:(id)sender
259 {
260     NSString *string = [combo userDisplayRep];
261     
262     if (string == nil) {
263         string = @"";
264     }
265     if ([setHotKey isEqualToString:@"PlayPause"]) {
266         if (([combo isEqual:nextTrackCombo] || [combo isEqual:prevTrackCombo] ||
267             [combo isEqual:trackInfoCombo] || [combo isEqual:upcomingSongsCombo]) &&
268             !(([combo modifiers] == -1) && ([combo keyCode] == -1))) {
269             
270             [window setLevel:NSNormalWindowLevel];
271             NSRunAlertPanel(@"Duplicate Key Combo", @"Please choose a unique key combo.", @"OK", nil, nil, nil);
272             [window setLevel:NSStatusWindowLevel];
273             return;
274         }
275         playPauseCombo = [combo copy];
276         [playPauseButton setTitle:string];
277     } else if ([setHotKey isEqualToString:@"NextTrack"]) {
278         if (([combo isEqual:playPauseCombo] || [combo isEqual:prevTrackCombo] ||
279             [combo isEqual:trackInfoCombo] || [combo isEqual:upcomingSongsCombo]) && 
280             !(([combo modifiers] == -1) && ([combo keyCode] == -1))) {
281             
282             [window setLevel:NSNormalWindowLevel];
283             NSRunAlertPanel(@"Duplicate Key Combo", @"Please choose a unique key combo.", @"OK", nil, nil, nil);
284             [window setLevel:NSStatusWindowLevel];
285             return;
286         }
287         nextTrackCombo = [combo copy];
288         [nextTrackButton setTitle:string];
289     } else if ([setHotKey isEqualToString:@"PrevTrack"]) {
290         if (([combo isEqual:nextTrackCombo] || [combo isEqual:playPauseCombo] ||
291             [combo isEqual:trackInfoCombo] || [combo isEqual:upcomingSongsCombo]) && 
292             !(([combo modifiers] == -1) && ([combo keyCode] == -1))) {
293             
294             [window setLevel:NSNormalWindowLevel];
295             NSRunAlertPanel(@"Duplicate Key Combo", @"Please choose a unique key combo.", @"OK", nil, nil, nil);
296             [window setLevel:NSStatusWindowLevel];
297             return;
298         }
299         prevTrackCombo = [combo copy];
300         [previousTrackButton setTitle:string];
301     } else if ([setHotKey isEqualToString:@"TrackInfo"]) {
302         if (([combo isEqual:nextTrackCombo] || [combo isEqual:prevTrackCombo] ||
303             [combo isEqual:playPauseCombo] || [combo isEqual:upcomingSongsCombo]) && 
304             !(([combo modifiers] == -1) && ([combo keyCode] == -1))) {
305             
306             [window setLevel:NSNormalWindowLevel];
307             NSRunAlertPanel(@"Duplicate Key Combo", @"Please choose a unique key combo.", @"OK", nil, nil, nil);
308             [window setLevel:NSStatusWindowLevel];
309             return;
310         }
311         trackInfoCombo = [combo copy];
312         [trackInfoButton setTitle:string];
313     } else if ([setHotKey isEqualToString:@"UpcomingSongs"]) {
314         if (([combo isEqual:nextTrackCombo] || [combo isEqual:prevTrackCombo] ||
315             [combo isEqual:trackInfoCombo] || [combo isEqual:playPauseCombo]) && 
316             !(([combo modifiers] == -1) && ([combo keyCode] == -1))) {
317             
318             [window setLevel:NSNormalWindowLevel];
319             NSRunAlertPanel(@"Duplicate Key Combo", @"Please choose a unique key combo.", @"OK", nil, nil, nil);
320             [window setLevel:NSStatusWindowLevel];
321             return;
322         }
323         upcomingSongsCombo = [combo copy];
324         [upcomingSongsButton setTitle:string];
325     }
326     [self cancelHotKey:sender];
327 }
328
329 - (IBAction)setCurrentTrackInfo:(id)sender
330 {
331     [self setKeyCombo:trackInfoCombo];
332     [self setHotKey:@"TrackInfo"];
333 }
334
335 - (IBAction)setNextTrack:(id)sender
336 {
337     [self setKeyCombo:nextTrackCombo];
338     [self setHotKey:@"NextTrack"];
339 }
340
341 - (IBAction)setPlayPause:(id)sender
342 {
343     [self setKeyCombo:playPauseCombo];
344     [self setHotKey:@"PlayPause"];
345 }
346
347 - (IBAction)setPreviousTrack:(id)sender
348 {
349     [self setKeyCombo:prevTrackCombo];
350     [self setHotKey:@"PrevTrack"];
351 }
352
353 - (IBAction)setUpcomingSongs:(id)sender
354 {
355     [self setKeyCombo:upcomingSongsCombo];
356     [self setHotKey:@"UpcomingSongs"];
357 }
358
359
360 /*************************************************************************/
361 #pragma mark -
362 #pragma mark HOTKEY SUPPORT METHODS
363 /*************************************************************************/
364
365 - (void)setHotKey:(NSString *)key
366 {
367     setHotKey = key;
368     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyEvent:) name:@"KeyBroadcasterEvent" object:nil];
369     [NSApp beginSheet:keyComboPanel modalForWindow:window modalDelegate:self didEndSelector:nil contextInfo:nil];
370 }
371
372 - (void)keyEvent:(NSNotification *)note
373 {
374     NSDictionary *info = [note userInfo];
375     short keyCode;
376     long modifiers;
377     KeyCombo *newCombo;
378     
379     keyCode = [[info objectForKey:@"KeyCode"] shortValue];
380     modifiers = [[info objectForKey:@"Modifiers"] longValue];
381     
382     newCombo = [[KeyCombo alloc] initWithKeyCode:keyCode andModifiers:modifiers];
383     [self setKeyCombo:newCombo];
384 }
385
386 - (void)setKeyCombo:(KeyCombo *)newCombo
387 {
388     NSString *string;
389     [combo release];
390     combo = [newCombo copy];
391     
392     string = [combo userDisplayRep];
393     if (string == nil) {
394         string = @"";
395     }
396     [keyComboField setStringValue:string];
397 }
398
399
400 /*************************************************************************/
401 #pragma mark -
402 #pragma mark PRIVATE METHOD IMPLEMENTATIONS
403 /*************************************************************************/
404
405 - (void)setupWindow
406 {
407     if ( ! [NSBundle loadNibNamed:@"Preferences" owner:self] ) {
408         NSLog( @"Failed to load Preferences.nib" );
409         NSBeep();
410         return;
411     }
412 }
413
414 - (void)setupCustomizationTables
415 {
416     NSImageCell *imgCell = [[[NSImageCell alloc] initImageCell:nil] autorelease];
417     
418     // Set the table view cells up
419     [imgCell setImageScaling:NSScaleNone];
420     [[menuTableView tableColumnWithIdentifier:@"submenu"] setDataCell:imgCell];
421     [[allTableView tableColumnWithIdentifier:@"submenu"] setDataCell:imgCell];
422
423     // Register for drag and drop
424     [menuTableView registerForDraggedTypes:[NSArray arrayWithObjects:
425         @"MenuTableViewPboardType",
426         @"AllTableViewPboardType",
427         nil]];
428     [allTableView registerForDraggedTypes:[NSArray arrayWithObjects:
429         @"MenuTableViewPboardType",
430         @"AllTableViewPboardType",
431         nil]];
432 }
433
434 - (void)setupMenuItems
435 {
436     NSEnumerator *itemEnum;
437     id            anItem;
438     // Set the list of items you can have.
439     availableItems = [[NSMutableArray alloc] initWithObjects:
440         @"Current Track Info",
441         @"Upcoming Songs",
442         @"Playlists",
443         @"EQ Presets",
444         @"Song Rating",
445         @"Play/Pause",
446         @"Next Track",
447         @"Previous Track",
448         @"Fast Forward",
449         @"Rewind",
450         @"<separator>",
451         nil];
452     
453     // Get our preferred menu
454     myItems = [[df arrayForKey:@"menu"] mutableCopy];
455     
456     // Delete items in the availableItems array that are already part of the menu
457     itemEnum = [myItems objectEnumerator];
458     while ( (anItem = [itemEnum nextObject]) ) {
459         if ( ! [anItem isEqualToString:@"<separator>"] ) {
460             [availableItems removeObject:anItem];
461         }
462     }
463     
464     // Items that show should a submenu image
465     submenuItems = [[NSArray alloc] initWithObjects:
466         @"Upcoming Songs",
467         @"Playlists",
468         @"EQ Presets",
469         @"Song Rating",
470         nil];
471 }
472
473 - (void)setupUI
474 {
475     NSMutableDictionary *loginwindow;
476     NSMutableArray *loginarray;
477     NSEnumerator *loginEnum;
478     id anItem;
479
480     // Fill in the number of songs in advance to show field
481     [songsInAdvance setIntValue:[df integerForKey:@"SongsInAdvance"]];
482     
483     // Fill in hot key buttons
484     if ([df objectForKey:@"PlayPause"]){
485         playPauseCombo = [df keyComboForKey:@"PlayPause"];
486         [playPauseButton setTitle:[playPauseCombo userDisplayRep]];
487     } else {
488         playPauseCombo = [[KeyCombo alloc] init];
489     }
490
491     if ([df objectForKey:@"NextTrack"]) {
492         nextTrackCombo = [df keyComboForKey:@"NextTrack"];
493         [nextTrackButton setTitle:[nextTrackCombo userDisplayRep]];
494     } else {
495         nextTrackCombo = [[KeyCombo alloc] init];
496     }
497
498     if ([df objectForKey:@"PrevTrack"]) {
499         prevTrackCombo = [df keyComboForKey:@"PrevTrack"];
500         [previousTrackButton setTitle:[prevTrackCombo userDisplayRep]];
501     } else {
502         prevTrackCombo = [[KeyCombo alloc] init];
503     }
504
505     if ([df objectForKey:@"TrackInfo"]) {
506         trackInfoCombo = [df keyComboForKey:@"TrackInfo"];
507         [trackInfoButton setTitle:[trackInfoCombo userDisplayRep]];
508     } else {
509         trackInfoCombo = [[KeyCombo alloc] init];
510     }
511
512     if ([df objectForKey:@"UpcomingSongs"]) {
513         upcomingSongsCombo = [df keyComboForKey:@"UpcomingSongs"];
514         [upcomingSongsButton setTitle:[upcomingSongsCombo userDisplayRep]];
515     } else {
516         upcomingSongsCombo = [[KeyCombo alloc] init];
517     }
518     
519     // Check current track info buttons
520     [albumCheckbox setState:[df boolForKey:@"showAlbum"] ? NSOnState : NSOffState];
521     [nameCheckbox setState:[df boolForKey:@"showName"] ? NSOnState : NSOffState];
522     [artistCheckbox setState:[df boolForKey:@"showArtist"] ? NSOnState : NSOffState];
523     [trackTimeCheckbox setState:[df boolForKey:@"showTime"] ? NSOnState : NSOffState];
524     
525     // Set the launch at login checkbox state
526     [df synchronize];
527     loginwindow = [[df persistentDomainForName:@"loginwindow"] mutableCopy];
528     loginarray = [loginwindow objectForKey:@"AutoLaunchedApplicationDictionary"];
529
530     loginEnum = [loginarray objectEnumerator];
531     while ( (anItem = [loginEnum nextObject]) ) {
532         if ([[[anItem objectForKey:@"Path"] lastPathComponent] isEqualToString:[[[NSBundle mainBundle] bundlePath] lastPathComponent]]) {
533             [launchAtLoginCheckbox setState:NSOnState];
534         }
535     }
536 }
537
538
539 /*************************************************************************/
540 #pragma mark -
541 #pragma mark NSWindow DELEGATE METHODS
542 /*************************************************************************/
543
544 - (void)windowWillClose:(NSNotification *)note
545 {
546     [(MainController *)controller closePreferences]; 
547 }
548
549
550 /*************************************************************************/
551 #pragma mark -
552 #pragma mark NSTableView DATASOURCE METHODS
553 /*************************************************************************/
554
555 - (int)numberOfRowsInTableView:(NSTableView *)aTableView
556 {
557     if (aTableView == menuTableView) {
558         return [myItems count];
559     } else {
560         return [availableItems count];
561     }
562 }
563
564 - (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex
565 {
566     if (aTableView == menuTableView) {
567         if ([[aTableColumn identifier] isEqualToString:@"name"]) {
568             return [myItems objectAtIndex:rowIndex];
569         } else {
570             if ([submenuItems containsObject:[myItems objectAtIndex:rowIndex]])
571             {
572                 return [NSImage imageNamed:@"submenu"];
573             } else {
574                 return nil;
575             }
576         }
577     } else {
578         if ([[aTableColumn identifier] isEqualToString:@"name"]) {
579             return [availableItems objectAtIndex:rowIndex];
580         } else {
581             if ([submenuItems containsObject:[availableItems objectAtIndex:rowIndex]]) {
582                 return [NSImage imageNamed:@"submenu"];
583             } else {
584                 return nil;
585             }
586         }
587     }
588 }
589
590 - (BOOL)tableView:(NSTableView *)tableView writeRows:(NSArray*)rows toPasteboard:(NSPasteboard*)pboard
591 {
592     if (tableView == menuTableView) {
593         [pboard declareTypes:[NSArray arrayWithObjects:@"MenuTableViewPboardType", nil] owner:self];
594         [pboard setString:[[rows objectAtIndex:0] stringValue] forType:@"MenuTableViewPboardType"];
595         return YES;
596     }
597     
598     if (tableView == allTableView) {
599         [pboard declareTypes:[NSArray arrayWithObjects:@"AllTableViewPboardType", nil] owner:self];
600         [pboard setString:[[rows objectAtIndex:0] stringValue] forType:@"AllTableViewPboardType"];
601         return YES;
602     }
603     return NO;
604 }
605
606 - (BOOL)tableView:(NSTableView*)tableView acceptDrop:(id <NSDraggingInfo>)info row:(int)row dropOperation:(NSTableViewDropOperation)operation
607 {
608     NSPasteboard *pb;
609     int dragRow;
610     NSString *dragData, *temp;
611     
612     pb = [info draggingPasteboard];
613     
614     if ([[pb types] containsObject:@"MenuTableViewPboardType"]) {
615         dragData = [pb stringForType:@"MenuTableViewPboardType"];
616         dragRow = [dragData intValue];
617         temp = [myItems objectAtIndex:dragRow];
618         [myItems removeObjectAtIndex:dragRow];
619         
620         if (tableView == menuTableView) {
621             if (row > dragRow) {
622                 [myItems insertObject:temp atIndex:row - 1];
623             } else {
624                 [myItems insertObject:temp atIndex:row];
625             }
626         } else {
627             if (![temp isEqualToString:@"<separator>"]) {
628                 [availableItems addObject:temp];
629             }
630         }
631     } else if ([[pb types] containsObject:@"AllTableViewPboardType"]) {
632         dragData = [pb stringForType:@"AllTableViewPboardType"];
633         dragRow = [dragData intValue];
634         temp = [availableItems objectAtIndex:dragRow];
635         
636         if (![temp isEqualToString:@"<separator>"]) {
637             [availableItems removeObjectAtIndex:dragRow];
638         }
639         [myItems insertObject:temp atIndex:row];
640     }
641     
642     [menuTableView reloadData];
643     [allTableView reloadData];
644     return YES;
645 }
646
647 - (NSDragOperation)tableView:(NSTableView*)tableView validateDrop:(id <NSDraggingInfo>)info proposedRow:(int)row proposedDropOperation:(NSTableViewDropOperation)operation
648 {
649     if (tableView == allTableView) {
650         if ([[[info draggingPasteboard] types] containsObject:@"AllTableViewPboardType"]) {
651             return NSDragOperationNone;
652         }
653         
654         if ([[[info draggingPasteboard] types] containsObject:@"MenuTableViewPboardType"]) {
655             NSString *item = [myItems objectAtIndex:[[[info draggingPasteboard] stringForType:@"MenuTableViewPboardType"] intValue]];
656             if ([item isEqualToString:@"PreferencesÉ"] || [item isEqualToString:@"Quit"]) {
657                 return NSDragOperationNone;
658             }
659         }
660         
661         [tableView setDropRow:-1 dropOperation:NSTableViewDropOn];
662         return NSDragOperationGeneric;
663     }
664     
665     if (operation == NSTableViewDropOn || row == -1)
666     {
667         return NSDragOperationNone;
668     }
669     
670     return NSDragOperationGeneric;
671 }
672
673
674 /*************************************************************************/
675 #pragma mark -
676 #pragma mark DEALLOCATION METHODS
677 /*************************************************************************/
678
679 - (void)dealloc
680 {
681     [self setKeyCombo:nil];
682     [playPauseCombo release];
683     [nextTrackCombo release];
684     [prevTrackCombo release];
685     [trackInfoCombo release];
686     [upcomingSongsCombo release];
687     [keyComboPanel release];
688     [menuTableView setDataSource:nil];
689     [allTableView setDataSource:nil];
690     [controller release];
691     [availableItems release];
692     [submenuItems release];
693     [myItems release];
694     [df release];
695 }
696
697
698 @end