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