:( It got the best of me... Removing NSLog I had for testing
[MenuTunes.git] / PreferencesController.m
1 #import "PreferencesController.h"
2 #import "MainController.h"
3 #import "StatusWindow.h"
4 #import "CustomMenuTableView.h"
5
6 #import <ITKit/ITHotKeyCenter.h>
7 #import <ITKit/ITKeyCombo.h>
8 #import <ITKit/ITWindowPositioning.h>
9 #import <ITKit/ITKeyBroadcaster.h>
10
11 #import <ITKit/ITCutWindowEffect.h>
12 #import <ITKit/ITDissolveWindowEffect.h>
13 #import <ITKit/ITSlideHorizontallyWindowEffect.h>
14 #import <ITKit/ITSlideVerticallyWindowEffect.h>
15 #import <ITKit/ITPivotWindowEffect.h>
16
17
18 #define SENDER_STATE (([sender state] == NSOnState) ? YES : NO)
19
20 /*************************************************************************/
21 #pragma mark -
22 #pragma mark PRIVATE INTERFACE
23 /*************************************************************************/
24
25 @interface PreferencesController (Private)
26 - (void)setupWindow;
27 - (void)setupCustomizationTables;
28 - (void)setupMenuItems;
29 - (void)setupUI;
30 - (IBAction)changeMenus:(id)sender;
31 - (void)setLaunchesAtLogin:(BOOL)flag;
32 @end
33
34
35 @implementation PreferencesController
36
37
38 /*************************************************************************/
39 #pragma mark -
40 #pragma mark STATIC VARIABLES
41 /*************************************************************************/
42
43 static PreferencesController *prefs = nil;
44
45
46 /*************************************************************************/
47 #pragma mark -
48 #pragma mark INITIALIZATION METHODS
49 /*************************************************************************/
50
51 + (PreferencesController *)sharedPrefs;
52 {
53     if (! prefs) {
54         prefs = [[self alloc] init];
55     }
56     return prefs;
57 }
58
59 - (id)init
60 {
61     if ( (self = [super init]) ) {
62         ITDebugLog(@"Preferences initialized.");
63         df = [[NSUserDefaults standardUserDefaults] retain];
64         hotKeysDictionary = [[NSMutableDictionary alloc] init];
65         controller = nil;
66     }
67     return self;
68 }
69
70
71 /*************************************************************************/
72 #pragma mark -
73 #pragma mark ACCESSOR METHODS
74 /*************************************************************************/
75
76 - (id)controller
77 {
78     return controller;
79 }
80
81 - (void)setController:(id)object
82 {
83     [controller autorelease];
84     controller = [object retain];
85 }
86
87
88 /*************************************************************************/
89 #pragma mark -
90 #pragma mark INSTANCE METHODS
91 /*************************************************************************/
92
93 - (IBAction)showPrefsWindow:(id)sender
94 {
95     ITDebugLog(@"Showing preferences window.");
96     if (! window) {  // If window does not exist yet, then the nib hasn't been loaded.
97         ITDebugLog(@"Window doesn't exist, initial setup.");
98         [self setupWindow];  // Load in the nib, and perform any initial setup.
99         [self setupCustomizationTables];  // Setup the DnD manu config tables.
100         [self setupMenuItems];  // Setup the arrays of menu items
101         [self setupUI]; // Sets up additional UI
102         [window setDelegate:self];
103         [menuTableView reloadData];
104         
105         //Change the launch player checkbox to the proper name
106         [launchPlayerAtLaunchCheckbox setTitle:[NSString stringWithFormat:@"Launch %@ when MenuTunes launches", [[controller currentRemote] playerSimpleName]]]; //This isn't localized...
107     }
108     [window setLevel:NSStatusWindowLevel];
109     [window center];
110     [window makeKeyAndOrderFront:self];
111 }
112
113 - (IBAction)changeGeneralSetting:(id)sender
114 {
115     ITDebugLog(@"Changing general setting of tag %i.", [sender tag]);
116     if ( [sender tag] == 1010) {
117         [self setLaunchesAtLogin:SENDER_STATE];
118     } else if ( [sender tag] == 1020) {
119         [df setBool:SENDER_STATE forKey:@"LaunchPlayerWithMT"];
120     } else if ( [sender tag] == 1030) {
121         [df setInteger:[sender intValue] forKey:@"SongsInAdvance"];
122
123     } else if ( [sender tag] == 1040) {
124         // This will not be executed.  Song info always shows the title of the song.
125         // [df setBool:SENDER_STATE forKey:@"showName"];
126     } else if ( [sender tag] == 1050) {
127         [df setBool:SENDER_STATE forKey:@"showArtist"];
128     } else if ( [sender tag] == 1060) {
129         [df setBool:SENDER_STATE forKey:@"showAlbum"];
130     } else if ( [sender tag] == 1070) {
131         [df setBool:SENDER_STATE forKey:@"showTime"];
132     } else if ( [sender tag] == 1080) {
133         [df setBool:SENDER_STATE forKey:@"showTrackNumber"];
134     } else if ( [sender tag] == 1090) {
135         [df setBool:SENDER_STATE forKey:@"showTrackRating"];
136     }
137     [df synchronize];
138 }
139
140 - (IBAction)changeStatusWindowSetting:(id)sender
141 {
142     StatusWindow *sw = [StatusWindow sharedWindow];
143     ITDebugLog(@"Changing status window setting of tag %i", [sender tag]);
144     if ( [sender tag] == 2010) {
145         [df setInteger:[sender selectedRow] forKey:@"statusWindowVerticalPosition"];
146         [df setInteger:[sender selectedColumn] forKey:@"statusWindowHorizontalPosition"];
147         // update the window's position here
148     } else if ( [sender tag] == 2020) {
149         // update screen selection
150     } else if ( [sender tag] == 2030) {
151         int effectTag = [[sender selectedItem] tag];
152         float time = ([df floatForKey:@"statusWindowAppearanceSpeed"] ? [df floatForKey:@"statusWindowAppearanceSpeed"] : 0.8);
153         [df setInteger:effectTag forKey:@"statusWindowAppearanceEffect"];
154
155         if ( effectTag == 2100 ) {
156             [sw setEntryEffect:[[[ITCutWindowEffect alloc] initWithWindow:sw] autorelease]];
157         } else if ( effectTag == 2101 ) {
158             [sw setEntryEffect:[[[ITDissolveWindowEffect alloc] initWithWindow:sw] autorelease]];
159         } else if ( effectTag == 2102 ) {
160             [sw setEntryEffect:[[[ITSlideVerticallyWindowEffect alloc] initWithWindow:sw] autorelease]];
161         } else if ( effectTag == 2103 ) {
162             [sw setEntryEffect:[[[ITSlideHorizontallyWindowEffect alloc] initWithWindow:sw] autorelease]];
163         } else if ( effectTag == 2104 ) {
164             NSLog(@"dflhgldf");
165             [sw setEntryEffect:[[[ITPivotWindowEffect alloc] initWithWindow:sw] autorelease]];
166         }
167
168         [[sw entryEffect] setEffectTime:time];
169         
170     } else if ( [sender tag] == 2040) {
171         int effectTag = [[sender selectedItem] tag];
172         float time = ([df floatForKey:@"statusWindowVanishSpeed"] ? [df floatForKey:@"statusWindowVanishSpeed"] : 0.8);
173         
174         [df setInteger:[[sender selectedItem] tag] forKey:@"statusWindowVanishEffect"];
175         
176         if ( effectTag == 2100 ) {
177             [sw setExitEffect:[[[ITCutWindowEffect alloc] initWithWindow:sw] autorelease]];
178         } else if ( effectTag == 2101 ) {
179             [sw setExitEffect:[[[ITDissolveWindowEffect alloc] initWithWindow:sw] autorelease]];
180         } else if ( effectTag == 2102 ) {
181             [sw setExitEffect:[[[ITSlideVerticallyWindowEffect alloc] initWithWindow:sw] autorelease]];
182         } else if ( effectTag == 2103 ) {
183             [sw setExitEffect:[[[ITSlideHorizontallyWindowEffect alloc] initWithWindow:sw] autorelease]];
184         } else if ( effectTag == 2104 ) {
185             [sw setExitEffect:[[[ITPivotWindowEffect alloc] initWithWindow:sw] autorelease]];
186         }
187
188         [[sw exitEffect] setEffectTime:time];
189
190     } else if ( [sender tag] == 2050) {
191         float newTime = (-([sender floatValue]));
192         [df setFloat:newTime forKey:@"statusWindowAppearanceSpeed"];
193         [[sw entryEffect] setEffectTime:newTime];
194     } else if ( [sender tag] == 2060) {
195         float newTime = (-([sender floatValue]));
196         [df setFloat:newTime forKey:@"statusWindowVanishSpeed"];
197         [[sw exitEffect] setEffectTime:newTime];
198     } else if ( [sender tag] == 2070) {
199         [df setFloat:[sender floatValue] forKey:@"statusWindowVanishDelay"];
200         [sw setExitDelay:[sender floatValue]];
201     } else if ( [sender tag] == 2080) {
202         [df setBool:SENDER_STATE forKey:@"showSongInfoOnChange"];
203     }
204     [df synchronize];
205 }
206
207 - (IBAction)changeHotKey:(id)sender
208 {
209     ITDebugLog(@"Changing hot keys.");
210     [controller clearHotKeys];
211     switch ([sender tag])
212     {
213         case 4010:
214             [self setKeyCombo:[hotKeysDictionary objectForKey:@"PlayPause"]];
215             [self setCurrentHotKey:@"PlayPause"];
216             break;
217         case 4020:
218             [self setKeyCombo:[hotKeysDictionary objectForKey:@"NextTrack"]];
219             [self setCurrentHotKey:@"NextTrack"];
220             break;
221         case 4030:
222             [self setKeyCombo:[hotKeysDictionary objectForKey:@"PrevTrack"]];
223             [self setCurrentHotKey:@"PrevTrack"];
224             break;
225         case 4035:
226             [self setKeyCombo:[hotKeysDictionary objectForKey:@"ShowPlayer"]];
227             [self setCurrentHotKey:@"ShowPlayer"];
228             break;
229         case 4040:
230             [self setKeyCombo:[hotKeysDictionary objectForKey:@"ToggleLoop"]];
231             [self setCurrentHotKey:@"ToggleLoop"];
232             break;
233         case 4050:
234             [self setKeyCombo:[hotKeysDictionary objectForKey:@"ToggleShuffle"]];
235             [self setCurrentHotKey:@"ToggleShuffle"];
236             break;
237         case 4060:
238             [self setKeyCombo:[hotKeysDictionary objectForKey:@"TrackInfo"]];
239             [self setCurrentHotKey:@"TrackInfo"];
240             break;
241         case 4070:
242             [self setKeyCombo:[hotKeysDictionary objectForKey:@"UpcomingSongs"]];
243             [self setCurrentHotKey:@"UpcomingSongs"];
244             break;
245         case 4080:
246             [self setKeyCombo:[hotKeysDictionary objectForKey:@"IncrementVolume"]];
247             [self setCurrentHotKey:@"IncrementVolume"];
248             break;
249         case 4090:
250             [self setKeyCombo:[hotKeysDictionary objectForKey:@"DecrementVolume"]];
251             [self setCurrentHotKey:@"DecrementVolume"];
252             break;
253         case 4100:
254             [self setKeyCombo:[hotKeysDictionary objectForKey:@"IncrementRating"]];
255             [self setCurrentHotKey:@"IncrementRating"];
256             break;
257         case 4110:
258             [self setKeyCombo:[hotKeysDictionary objectForKey:@"DecrementRating"]];
259             [self setCurrentHotKey:@"DecrementRating"];
260             break;
261     }
262 }
263
264 - (void)registerDefaults
265 {
266     BOOL found = NO;
267     NSMutableDictionary *loginWindow;
268     NSMutableArray *loginArray;
269     NSEnumerator *loginEnum;
270     id anItem;
271     ITDebugLog(@"Registering defaults.");
272     [df setObject:[NSArray arrayWithObjects:
273         @"playPause",
274         @"prevTrack",
275         @"nextTrack",
276         @"fastForward",
277         @"rewind",
278         @"showPlayer",
279         @"separator",
280         @"songRating",
281         @"eqPresets",
282         @"playlists",
283         @"upcomingSongs",
284         @"separator",
285         @"preferences",
286         @"quit",
287         @"separator",
288         @"trackInfo",
289         nil] forKey:@"menu"];
290
291     [df setInteger:5 forKey:@"SongsInAdvance"];
292     // [df setBool:YES forKey:@"showName"];  // Song info will always show song title.
293     [df setBool:YES forKey:@"showArtist"];
294     [df setBool:NO forKey:@"showAlbum"];
295     [df setBool:NO forKey:@"showTime"];
296
297     [df synchronize];
298     
299     loginWindow = [[df persistentDomainForName:@"loginwindow"] mutableCopy];
300     loginArray = [loginWindow objectForKey:@"AutoLaunchedApplicationDictionary"];
301     loginEnum = [loginArray objectEnumerator];
302
303     while ( (anItem = [loginEnum nextObject]) ) {
304         if ( [[[anItem objectForKey:@"Path"] lastPathComponent] isEqualToString:[[[NSBundle mainBundle] bundlePath] lastPathComponent]] ) {
305             found = YES;
306         }
307     }
308     [loginWindow release];
309     
310     if (!found) {
311         if (NSRunInformationalAlertPanel(NSLocalizedString(@"autolaunch", @"Auto-launch MenuTunes"), NSLocalizedString(@"autolaunch_msg", @"Would you like MenuTunes to automatically launch at login?"), @"Yes", @"No", nil) == NSOKButton) {
312             [self setLaunchesAtLogin:YES];
313         }
314     }
315 }
316
317 - (IBAction)cancelHotKey:(id)sender
318 {
319     ITDebugLog(@"Hot key canceled.");
320     [[NSNotificationCenter defaultCenter] removeObserver:self];
321     [NSApp endSheet:keyComboPanel];
322     [keyComboPanel orderOut:nil];
323 }
324
325 - (IBAction)clearHotKey:(id)sender
326 {
327     ITDebugLog(@"Hot key cleared.");
328     [self setKeyCombo:[ITKeyCombo clearKeyCombo]];
329 }
330
331 - (IBAction)okHotKey:(id)sender
332 {
333     NSString *string = [combo description];
334     NSEnumerator *enumerator = [hotKeysDictionary keyEnumerator];
335     NSString *enumKey;
336     
337     ITDebugLog(@"Hot key ok'd, saving.");
338     
339     if (string == nil) {
340         string = @"";
341     }
342     
343     ITDebugLog(@"Checking for duplicate hot keys.");
344     while ( (enumKey = [enumerator nextObject]) ) {
345         if (![enumKey isEqualToString:currentHotKey]) {
346             if (![combo isEqual:[ITKeyCombo clearKeyCombo]] &&
347                  [combo isEqual:[hotKeysDictionary objectForKey:enumKey]]) {
348                 [window setLevel:NSNormalWindowLevel];
349                 ITDebugLog(@"Duplicate hot key found: %@", enumKey);
350                 if ( NSRunAlertPanel(NSLocalizedString(@"duplicateCombo", @"Duplicate Key Combo") , NSLocalizedString(@"duplicateCombo_msg", @"The specified key combo is already in use..."), NSLocalizedString(@"replace", @"Replace"), NSLocalizedString(@"cancel", @"Cancel"), nil) ) {
351                     [hotKeysDictionary setObject:[ITKeyCombo clearKeyCombo] forKey:currentHotKey];
352                     if ([enumKey isEqualToString:@"PlayPause"]) {
353                         [playPauseButton setTitle:@"(None)"];
354                     } else if ([enumKey isEqualToString:@"NextTrack"]) {
355                         [nextTrackButton setTitle:@"(None)"];
356                     } else if ([enumKey isEqualToString:@"PrevTrack"]) {
357                         [previousTrackButton setTitle:@"(None)"];
358                     } else if ([enumKey isEqualToString:@"ShowPlayer"]) {
359                         [showPlayerButton setTitle:@"(None)"];
360                     } else if ([enumKey isEqualToString:@"TrackInfo"]) {
361                         [trackInfoButton setTitle:@"(None)"];
362                     } else if ([enumKey isEqualToString:@"UpcomingSongs"]) {
363                         [upcomingSongsButton setTitle:@"(None)"];
364                     } else if ([enumKey isEqualToString:@"IncrementVolume"]) {
365                         [volumeIncrementButton setTitle:@"(None)"];
366                     } else if ([enumKey isEqualToString:@"DecrementVolume"]) {
367                         [volumeDecrementButton setTitle:@"(None)"];
368                     } else if ([enumKey isEqualToString:@"IncrementRating"]) {
369                         [ratingIncrementButton setTitle:@"(None)"];
370                     } else if ([enumKey isEqualToString:@"DecrementRating"]) {
371                         [ratingDecrementButton setTitle:@"(None)"];
372                     } else if ([enumKey isEqualToString:@"ToggleShuffle"]) {
373                         [toggleShuffleButton setTitle:@"(None)"];
374                     } else if ([enumKey isEqualToString:@"ToggleLoop"]) {
375                         [toggleLoopButton setTitle:@"(None)"];
376                     }
377                     ITDebugLog(@"Saved hot key named %@.", enumKey);
378                     [df setObject:[[ITKeyCombo clearKeyCombo] plistRepresentation] forKey:enumKey];
379                     [hotKeysDictionary setObject:[ITKeyCombo clearKeyCombo] forKey:enumKey];
380                 } else {
381                     return;
382                 }
383                 [window setLevel:NSStatusWindowLevel];
384             }
385         }
386     }
387     
388     ITDebugLog(@"Saved hot key named %@.", currentHotKey);
389     [hotKeysDictionary setObject:combo forKey:currentHotKey];
390     [df setObject:[combo plistRepresentation] forKey:currentHotKey];
391     
392     ITDebugLog(@"Setting button name.");
393     if ([currentHotKey isEqualToString:@"PlayPause"]) {
394         [playPauseButton setTitle:string];
395     } else if ([currentHotKey isEqualToString:@"NextTrack"]) {
396         [nextTrackButton setTitle:string];
397     } else if ([currentHotKey isEqualToString:@"PrevTrack"]) {
398         [previousTrackButton setTitle:string];
399     } else if ([currentHotKey isEqualToString:@"ShowPlayer"]) {
400         [showPlayerButton setTitle:string];
401     } else if ([currentHotKey isEqualToString:@"TrackInfo"]) {
402         [trackInfoButton setTitle:string];
403     } else if ([currentHotKey isEqualToString:@"UpcomingSongs"]) {
404         [upcomingSongsButton setTitle:string];
405     } else if ([currentHotKey isEqualToString:@"IncrementVolume"]) {
406         [volumeIncrementButton setTitle:string];
407     } else if ([currentHotKey isEqualToString:@"DecrementVolume"]) {
408         [volumeDecrementButton setTitle:string];
409     } else if ([currentHotKey isEqualToString:@"IncrementRating"]) {
410         [ratingIncrementButton setTitle:string];
411     } else if ([currentHotKey isEqualToString:@"DecrementRating"]) {
412         [ratingDecrementButton setTitle:string];
413     } else if ([currentHotKey isEqualToString:@"ToggleShuffle"]) {
414         [toggleShuffleButton setTitle:string];
415     } else if ([currentHotKey isEqualToString:@"ToggleLoop"]) {
416         [toggleLoopButton setTitle:string];
417     }
418     [controller setupHotKeys];
419     [self cancelHotKey:sender];
420 }
421
422 - (void)deletePressedInTableView:(NSTableView *)tableView
423 {
424     if (tableView == menuTableView) {
425         int selRow = [tableView selectedRow];
426         ITDebugLog(@"Delete pressed in menu table view.");
427         if (selRow != - 1) {
428             NSString *object = [myItems objectAtIndex:selRow];
429             
430             if ([object isEqualToString:@"preferences"]) {
431                 NSBeep();
432                 return;
433             }
434             
435             if (![object isEqualToString:@"separator"])
436                 [availableItems addObject:object];
437             ITDebugLog(@"Removing object named %@", object);
438             [myItems removeObjectAtIndex:selRow];
439             [menuTableView reloadData];
440             [allTableView reloadData];
441         }
442         [self changeMenus:self];
443     }
444 }
445
446
447 /*************************************************************************/
448 #pragma mark -
449 #pragma mark HOTKEY SUPPORT METHODS
450 /*************************************************************************/
451
452 - (void)setCurrentHotKey:(NSString *)key
453 {
454     ITDebugLog(@"Setting current hot key to %@", key);
455     [currentHotKey autorelease];
456     currentHotKey = [key copy];
457     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyEvent:) name:ITKeyBroadcasterKeyEvent object:nil];
458     [NSApp beginSheet:keyComboPanel modalForWindow:window modalDelegate:self didEndSelector:nil contextInfo:nil];
459 }
460
461 - (void)keyEvent:(NSNotification *)note
462 {
463     [self setKeyCombo:[[[note userInfo] objectForKey:@"keyCombo"] copy]];
464 }
465
466 - (void)setKeyCombo:(ITKeyCombo *)newCombo
467 {
468     NSString *string;
469     [combo release];
470     combo = [newCombo copy];
471     ITDebugLog(@"Setting key combo to %@", newCombo);
472     string = [combo description];
473     if (string == nil) {
474         string = @"(None)";
475     }
476     [keyComboField setStringValue:string];
477 }
478
479
480 /*************************************************************************/
481 #pragma mark -
482 #pragma mark PRIVATE METHOD IMPLEMENTATIONS
483 /*************************************************************************/
484
485 - (void)setupWindow
486 {
487     ITDebugLog(@"Loading Preferences.nib.");
488     if (![NSBundle loadNibNamed:@"Preferences" owner:self]) {
489         ITDebugLog(@"Failed to load Preferences.nib.");
490         NSBeep();
491         return;
492     }
493 }
494
495 - (void)setupCustomizationTables
496 {
497     NSImageCell *imgCell = [[[NSImageCell alloc] initImageCell:nil] autorelease];
498     ITDebugLog(@"Setting up table views.");
499     // Set the table view cells up
500     [imgCell setImageScaling:NSScaleNone];
501     [[menuTableView tableColumnWithIdentifier:@"submenu"] setDataCell:imgCell];
502     [[allTableView tableColumnWithIdentifier:@"submenu"] setDataCell:imgCell];
503
504     // Register for drag and drop
505     [menuTableView registerForDraggedTypes:[NSArray arrayWithObjects:
506         @"MenuTableViewPboardType",
507         @"AllTableViewPboardType",
508         nil]];
509     [allTableView registerForDraggedTypes:[NSArray arrayWithObjects:
510         @"MenuTableViewPboardType",
511         @"AllTableViewPboardType",
512         nil]];
513 }
514
515 - (void)setupMenuItems
516 {
517     NSEnumerator *itemEnum;
518     id            anItem;
519     ITDebugLog(@"Setting up table view arrays.");
520     // Set the list of items you can have.
521     availableItems = [[NSMutableArray alloc] initWithObjects:
522         @"separator",
523         @"trackInfo",
524         @"upcomingSongs",
525         @"playlists",
526         @"eqPresets",
527         @"songRating",
528         @"playPause",
529         @"nextTrack",
530         @"prevTrack",
531         @"fastForward",
532         @"rewind",
533         @"showPlayer",
534         @"quit",
535         nil];
536     
537     // Get our preferred menu
538     myItems = [[df arrayForKey:@"menu"] mutableCopy];
539     
540     // Delete items in the availableItems array that are already part of the menu
541     itemEnum = [myItems objectEnumerator];
542     while ( (anItem = [itemEnum nextObject]) ) {
543         if (![anItem isEqualToString:@"separator"]) {
544             [availableItems removeObject:anItem];
545         }
546     }
547     
548     // Items that show should a submenu image
549     submenuItems = [[NSArray alloc] initWithObjects:
550         @"upcomingSongs",
551         @"playlists",
552         @"eqPresets",
553         @"songRating",
554         nil];
555 }
556
557 - (void)setupUI
558 {
559     NSMutableDictionary *loginwindow;
560     NSMutableArray *loginarray;
561     NSEnumerator *loginEnum;
562     id anItem;
563     ITDebugLog(@"Setting up preferences UI.");
564     // Fill in the number of songs in advance to show field
565     [songsInAdvance setIntValue:[df integerForKey:@"SongsInAdvance"]];
566     
567     // Fill in hot key buttons
568     if ([df objectForKey:@"PlayPause"]) {
569         ITDebugLog(@"Setting up \"PlayPause\" hot key.");
570         anItem = [ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"PlayPause"]];
571         [hotKeysDictionary setObject:anItem forKey:@"PlayPause"];
572         [playPauseButton setTitle:[anItem description]];
573     } else {
574         [hotKeysDictionary setObject:[ITKeyCombo clearKeyCombo] forKey:@"PlayPause"];
575         [playPauseButton setTitle:[[ITKeyCombo clearKeyCombo] description]];
576     }
577     
578     if ([df objectForKey:@"NextTrack"]) {
579         ITDebugLog(@"Setting up \"NextTrack\" hot key.");
580         anItem = [ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"NextTrack"]];
581         [hotKeysDictionary setObject:anItem forKey:@"NextTrack"];
582         [nextTrackButton setTitle:[anItem description]];
583     } else {
584         [hotKeysDictionary setObject:[ITKeyCombo clearKeyCombo] forKey:@"NextTrack"];
585         [nextTrackButton setTitle:[[ITKeyCombo clearKeyCombo] description]];
586     }
587     
588     if ([df objectForKey:@"PrevTrack"]) {
589         ITDebugLog(@"Setting up \"PrevTrack\" hot key.");
590         anItem = [ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"PrevTrack"]];
591         [hotKeysDictionary setObject:anItem forKey:@"PrevTrack"];
592         [previousTrackButton setTitle:[anItem description]];
593     } else {
594         [hotKeysDictionary setObject:[ITKeyCombo clearKeyCombo] forKey:@"PrevTrack"];
595         [previousTrackButton setTitle:[[ITKeyCombo clearKeyCombo] description]];
596     }
597     
598     if ([df objectForKey:@"ShowPlayer"]) {
599         ITDebugLog(@"Setting up \"ShowPlayer\" hot key.");
600         anItem = [ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"ShowPlayer"]];
601         [hotKeysDictionary setObject:anItem forKey:@"ShowPlayer"];
602         [showPlayerButton setTitle:[anItem description]];
603     } else {
604         [hotKeysDictionary setObject:[ITKeyCombo clearKeyCombo] forKey:@"ShowPlayer"];
605         [showPlayerButton setTitle:[[ITKeyCombo clearKeyCombo] description]];
606     }
607     
608     if ([df objectForKey:@"TrackInfo"]) {
609         ITDebugLog(@"Setting up \"TrackInfo\" hot key.");
610         anItem = [ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"TrackInfo"]];
611         [hotKeysDictionary setObject:anItem forKey:@"TrackInfo"];
612         [trackInfoButton setTitle:[anItem description]];
613     } else {
614         [hotKeysDictionary setObject:[ITKeyCombo clearKeyCombo] forKey:@"TrackInfo"];
615         [trackInfoButton setTitle:[[ITKeyCombo clearKeyCombo] description]];
616     }
617     
618     if ([df objectForKey:@"UpcomingSongs"]) {
619         ITDebugLog(@"Setting up \"UpcomingSongs\" hot key.");
620         anItem = [ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"UpcomingSongs"]];
621         [hotKeysDictionary setObject:anItem forKey:@"UpcomingSongs"];
622         [upcomingSongsButton setTitle:[anItem description]];
623     } else {
624         [hotKeysDictionary setObject:[ITKeyCombo clearKeyCombo] forKey:@"UpcomingSongs"];
625         [upcomingSongsButton setTitle:[[ITKeyCombo clearKeyCombo] description]];
626     }
627     
628     if ([df objectForKey:@"IncrementVolume"]) {
629         ITDebugLog(@"Setting up \"IncrementVolume\" hot key.");
630         anItem = [ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"IncrementVolume"]];
631         [hotKeysDictionary setObject:anItem forKey:@"IncrementVolume"];
632         [volumeIncrementButton setTitle:[anItem description]];
633     } else {
634         [hotKeysDictionary setObject:[ITKeyCombo clearKeyCombo] forKey:@"IncrementVolume"];
635         [volumeIncrementButton setTitle:[[ITKeyCombo clearKeyCombo] description]];
636     }
637     
638     if ([df objectForKey:@"DecrementVolume"]) {
639         ITDebugLog(@"Setting up \"DecrementVolume\" hot key.");
640         anItem = [ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"DecrementVolume"]];
641         [hotKeysDictionary setObject:anItem forKey:@"DecrementVolume"];
642         [volumeDecrementButton setTitle:[anItem description]];
643     } else {
644         [hotKeysDictionary setObject:[ITKeyCombo clearKeyCombo] forKey:@"DecrementVolume"];
645         [volumeDecrementButton setTitle:[[ITKeyCombo clearKeyCombo] description]];
646     }
647     
648     if ([df objectForKey:@"IncrementRating"]) {
649         ITDebugLog(@"Setting up \"IncrementRating\" hot key.");
650         anItem = [ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"IncrementRating"]];
651         [hotKeysDictionary setObject:anItem forKey:@"IncrementRating"];
652         [ratingIncrementButton setTitle:[anItem description]];
653     } else {
654         [hotKeysDictionary setObject:[ITKeyCombo clearKeyCombo] forKey:@"IncrementRating"];
655         [ratingIncrementButton setTitle:[[ITKeyCombo clearKeyCombo] description]];
656     }
657     
658     if ([df objectForKey:@"DecrementRating"]) {
659         ITDebugLog(@"Setting up \"DecrementRating\" hot key.");
660         anItem = [ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"DecrementRating"]];
661         [hotKeysDictionary setObject:anItem forKey:@"DecrementRating"];
662         [ratingDecrementButton setTitle:[anItem description]];
663     } else {
664         [hotKeysDictionary setObject:[ITKeyCombo clearKeyCombo] forKey:@"DecrementRating"];
665         [ratingDecrementButton setTitle:[[ITKeyCombo clearKeyCombo] description]];
666     }
667     
668     if ([df objectForKey:@"ToggleLoop"]) {
669         ITDebugLog(@"Setting up \"ToggleLoop\" hot key.");
670         anItem = [ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"ToggleLoop"]];
671         [hotKeysDictionary setObject:anItem forKey:@"ToggleLoop"];
672         [toggleLoopButton setTitle:[anItem description]];
673     } else {
674         [hotKeysDictionary setObject:[ITKeyCombo clearKeyCombo] forKey:@"ToggleLoop"];
675         [toggleLoopButton setTitle:[[ITKeyCombo clearKeyCombo] description]];
676     }
677     
678     if ([df objectForKey:@"ToggleShuffle"]) {
679         ITDebugLog(@"Setting up \"ToggleShuffle\" hot key.");
680         anItem = [ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"ToggleShuffle"]];
681         [hotKeysDictionary setObject:anItem forKey:@"ToggleShuffle"];
682         [toggleShuffleButton setTitle:[anItem description]];
683     } else {
684         [hotKeysDictionary setObject:[ITKeyCombo clearKeyCombo] forKey:@"ToggleShuffle"];
685         [toggleShuffleButton setTitle:[[ITKeyCombo clearKeyCombo] description]];
686     }
687     
688     ITDebugLog(@"Setting up track info checkboxes.");
689     // Check current track info buttons
690     [albumCheckbox setState:[df boolForKey:@"showAlbum"] ? NSOnState : NSOffState];
691     [nameCheckbox setState:NSOnState];  // Song info will ALWAYS show song title.
692     [nameCheckbox setEnabled:NO];  // Song info will ALWAYS show song title.
693     [artistCheckbox setState:[df boolForKey:@"showArtist"] ? NSOnState : NSOffState];
694     [trackTimeCheckbox setState:[df boolForKey:@"showTime"] ? NSOnState : NSOffState];
695     [trackNumberCheckbox setState:[df boolForKey:@"showTrackNumber"] ? NSOnState : NSOffState];
696     [ratingCheckbox setState:[df boolForKey:@"showTrackRating"] ? NSOnState : NSOffState];
697     
698     // Set the launch at login checkbox state
699     ITDebugLog(@"Setting launch at login state.");
700     [df synchronize];
701     loginwindow = [[df persistentDomainForName:@"loginwindow"] mutableCopy];
702     loginarray = [loginwindow objectForKey:@"AutoLaunchedApplicationDictionary"];
703     
704     loginEnum = [loginarray objectEnumerator];
705     while ( (anItem = [loginEnum nextObject]) ) {
706         if ([[[anItem objectForKey:@"Path"] lastPathComponent] isEqualToString:[[[NSBundle mainBundle] bundlePath] lastPathComponent]]) {
707             [launchAtLoginCheckbox setState:NSOnState];
708         }
709     }
710 }
711
712 - (IBAction)changeMenus:(id)sender
713 {
714     ITDebugLog(@"Synchronizing menus");
715     [df setObject:myItems forKey:@"menu"];
716     [df synchronize];
717 }
718
719 - (void)setLaunchesAtLogin:(BOOL)flag
720 {
721     NSMutableDictionary *loginwindow;
722     NSMutableArray *loginarray;
723     ITDebugLog(@"Setting launches at login: %i", flag);
724     [df synchronize];
725     loginwindow = [[df persistentDomainForName:@"loginwindow"] mutableCopy];
726     loginarray = [loginwindow objectForKey:@"AutoLaunchedApplicationDictionary"];
727     
728     if (flag) {
729         NSDictionary *itemDict = [NSDictionary dictionaryWithObjectsAndKeys:
730         [[NSBundle mainBundle] bundlePath], @"Path",
731         [NSNumber numberWithInt:0], @"Hide", nil];
732         [loginarray addObject:itemDict];
733     } else {
734         int i;
735         for (i = 0; i < [loginarray count]; i++) {
736             NSDictionary *tempDict = [loginarray objectAtIndex:i];
737             if ([[[tempDict objectForKey:@"Path"] lastPathComponent] isEqualToString:[[[NSBundle mainBundle] bundlePath] lastPathComponent]]) {
738                 [loginarray removeObjectAtIndex:i];
739                 break;
740             }
741         }
742     }
743     [df setPersistentDomain:loginwindow forName:@"loginwindow"];
744     [df synchronize];
745     [loginwindow release];
746     ITDebugLog(@"Finished setting launches at login.");
747 }
748
749
750 /*************************************************************************/
751 #pragma mark -
752 #pragma mark NSWindow DELEGATE METHODS
753 /*************************************************************************/
754
755 - (void)windowWillClose:(NSNotification *)note
756 {
757     [(MainController *)controller closePreferences]; 
758 }
759
760
761 /*************************************************************************/
762 #pragma mark -
763 #pragma mark NSTableView DATASOURCE METHODS
764 /*************************************************************************/
765
766 - (int)numberOfRowsInTableView:(NSTableView *)aTableView
767 {
768     if (aTableView == menuTableView) {
769         return [myItems count];
770     } else {
771         return [availableItems count];
772     }
773 }
774
775 - (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex
776 {
777     if (aTableView == menuTableView) {
778         NSString *object = [myItems objectAtIndex:rowIndex];
779         if ([[aTableColumn identifier] isEqualToString:@"name"]) {
780             if ([object isEqualToString:@"showPlayer"]) {
781                 return [NSString stringWithFormat:@"%@ %@", NSLocalizedString(@"show", @"Show"), [[controller currentRemote] playerSimpleName]];
782             }
783             return NSLocalizedString(object, @"ERROR");
784         } else {
785             if ([submenuItems containsObject:object])
786             {
787                 return [NSImage imageNamed:@"submenu"];
788             } else {
789                 return nil;
790             }
791         }
792     } else {
793         NSString *object = [availableItems objectAtIndex:rowIndex];
794         if ([[aTableColumn identifier] isEqualToString:@"name"]) {
795             if ([object isEqualToString:@"showPlayer"]) {
796                 return [NSString stringWithFormat:@"%@ %@", NSLocalizedString(@"show", @"Show"), [[controller currentRemote] playerSimpleName]];
797             }
798             return NSLocalizedString(object, @"ERROR");
799         } else {
800             if ([submenuItems containsObject:object]) {
801                 return [NSImage imageNamed:@"submenu"];
802             } else {
803                 return nil;
804             }
805         }
806     }
807 }
808
809 - (BOOL)tableView:(NSTableView *)tableView writeRows:(NSArray*)rows toPasteboard:(NSPasteboard*)pboard
810 {
811     if (tableView == menuTableView) {
812         [pboard declareTypes:[NSArray arrayWithObjects:@"MenuTableViewPboardType", nil] owner:self];
813         [pboard setString:[[rows objectAtIndex:0] stringValue] forType:@"MenuTableViewPboardType"];
814         return YES;
815     }
816     
817     if (tableView == allTableView) {
818         [pboard declareTypes:[NSArray arrayWithObjects:@"AllTableViewPboardType", nil] owner:self];
819         [pboard setString:[[rows objectAtIndex:0] stringValue] forType:@"AllTableViewPboardType"];
820         return YES;
821     }
822     return NO;
823 }
824
825 - (BOOL)tableView:(NSTableView*)tableView acceptDrop:(id <NSDraggingInfo>)info row:(int)row dropOperation:(NSTableViewDropOperation)operation
826 {
827     NSPasteboard *pb;
828     int dragRow;
829     NSString *dragData, *temp;
830     
831     pb = [info draggingPasteboard];
832     
833     if ([[pb types] containsObject:@"MenuTableViewPboardType"]) {
834         dragData = [pb stringForType:@"MenuTableViewPboardType"];
835         dragRow = [dragData intValue];
836         temp = [myItems objectAtIndex:dragRow];
837         
838         if (tableView == menuTableView) {
839             [myItems insertObject:temp atIndex:row];
840             if (row > dragRow) {
841                 [myItems removeObjectAtIndex:dragRow];
842             } else {
843                 [myItems removeObjectAtIndex:dragRow + 1];
844             }
845         } else {
846             if (![temp isEqualToString:@"separator"]) {
847                 [availableItems addObject:temp];
848             }
849             [myItems removeObjectAtIndex:dragRow];
850         }
851     } else if ([[pb types] containsObject:@"AllTableViewPboardType"]) {
852         dragData = [pb stringForType:@"AllTableViewPboardType"];
853         dragRow = [dragData intValue];
854         temp = [availableItems objectAtIndex:dragRow];
855         
856         [myItems insertObject:temp atIndex:row];
857         
858         if (![temp isEqualToString:@"separator"]) {
859             [availableItems removeObjectAtIndex:dragRow];
860         }
861     }
862     
863     [menuTableView reloadData];
864     [allTableView reloadData];
865     [self changeMenus:self];
866     return YES;
867 }
868
869 - (NSDragOperation)tableView:(NSTableView*)tableView validateDrop:(id <NSDraggingInfo>)info proposedRow:(int)row proposedDropOperation:(NSTableViewDropOperation)operation
870 {
871     if (tableView == allTableView) {
872         if ([[[info draggingPasteboard] types] containsObject:@"AllTableViewPboardType"]) {
873             return NSDragOperationNone;
874         }
875         
876         if ([[[info draggingPasteboard] types] containsObject:@"MenuTableViewPboardType"]) {
877             NSString *item = [myItems objectAtIndex:[[[info draggingPasteboard] stringForType:@"MenuTableViewPboardType"] intValue]];
878             if ([item isEqualToString:@"preferences"]) {
879                 return NSDragOperationNone;
880             }
881         }
882         
883         [tableView setDropRow:-1 dropOperation:NSTableViewDropOn];
884         return NSDragOperationGeneric;
885     }
886     
887     if (operation == NSTableViewDropOn || row == -1)
888     {
889         return NSDragOperationNone;
890     }
891     
892     return NSDragOperationGeneric;
893 }
894
895
896 /*************************************************************************/
897 #pragma mark -
898 #pragma mark DEALLOCATION METHODS
899 /*************************************************************************/
900
901 - (void)dealloc
902 {
903     [self setKeyCombo:nil];
904     [hotKeysDictionary release];
905     [keyComboPanel release];
906     [menuTableView setDataSource:nil];
907     [allTableView setDataSource:nil];
908     [controller release];
909     [availableItems release];
910     [submenuItems release];
911     [myItems release];
912     [df release];
913 }
914
915
916 @end