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