RAAAAAHHHHHHHHHHH NETWORK MENUTOOOONS!
[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 (![controller connectToServer]) {
241             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);
242         } else {
243             [useSharedMenuTunesCheckbox setState:NSOnState];
244         }
245         
246         if ([selectPlayerBox contentView] == manualView) {
247             [df setObject:[hostTextField stringValue] forKey:@"sharedPlayerHost"];
248         } else {
249         }
250     }
251     [df synchronize];
252 }
253
254 - (IBAction)changeStatusWindowSetting:(id)sender
255 {
256     StatusWindow *sw = [StatusWindow sharedWindow];
257     ITDebugLog(@"Changing status window setting of tag %i", [sender tag]);
258     if ( [sender tag] == 2010) {
259         [df setInteger:[sender selectedRow] forKey:@"statusWindowVerticalPosition"];
260         [df setInteger:[sender selectedColumn] forKey:@"statusWindowHorizontalPosition"];
261         // update the window's position here
262     } else if ( [sender tag] == 2020) {
263         // update screen selection
264     } else if ( [sender tag] == 2030) {
265         int effectTag = [[sender selectedItem] tag];
266         float time = ([df floatForKey:@"statusWindowAppearanceSpeed"] ? [df floatForKey:@"statusWindowAppearanceSpeed"] : 0.8);
267         [df setInteger:effectTag forKey:@"statusWindowAppearanceEffect"];
268
269         if ( effectTag == 2100 ) {
270             [sw setEntryEffect:[[[ITCutWindowEffect alloc] initWithWindow:sw] autorelease]];
271         } else if ( effectTag == 2101 ) {
272             [sw setEntryEffect:[[[ITDissolveWindowEffect alloc] initWithWindow:sw] autorelease]];
273         } else if ( effectTag == 2102 ) {
274             [sw setEntryEffect:[[[ITSlideVerticallyWindowEffect alloc] initWithWindow:sw] autorelease]];
275         } else if ( effectTag == 2103 ) {
276             [sw setEntryEffect:[[[ITSlideHorizontallyWindowEffect alloc] initWithWindow:sw] autorelease]];
277         } else if ( effectTag == 2104 ) {
278             NSLog(@"dflhgldf");
279             [sw setEntryEffect:[[[ITPivotWindowEffect alloc] initWithWindow:sw] autorelease]];
280         }
281
282         [[sw entryEffect] setEffectTime:time];
283         
284     } else if ( [sender tag] == 2040) {
285         int effectTag = [[sender selectedItem] tag];
286         float time = ([df floatForKey:@"statusWindowVanishSpeed"] ? [df floatForKey:@"statusWindowVanishSpeed"] : 0.8);
287         
288         [df setInteger:[[sender selectedItem] tag] forKey:@"statusWindowVanishEffect"];
289         
290         if ( effectTag == 2100 ) {
291             [sw setExitEffect:[[[ITCutWindowEffect alloc] initWithWindow:sw] autorelease]];
292         } else if ( effectTag == 2101 ) {
293             [sw setExitEffect:[[[ITDissolveWindowEffect alloc] initWithWindow:sw] autorelease]];
294         } else if ( effectTag == 2102 ) {
295             [sw setExitEffect:[[[ITSlideVerticallyWindowEffect alloc] initWithWindow:sw] autorelease]];
296         } else if ( effectTag == 2103 ) {
297             [sw setExitEffect:[[[ITSlideHorizontallyWindowEffect alloc] initWithWindow:sw] autorelease]];
298         } else if ( effectTag == 2104 ) {
299             [sw setExitEffect:[[[ITPivotWindowEffect alloc] initWithWindow:sw] autorelease]];
300         }
301
302         [[sw exitEffect] setEffectTime:time];
303
304     } else if ( [sender tag] == 2050) {
305         float newTime = (-([sender floatValue]));
306         [df setFloat:newTime forKey:@"statusWindowAppearanceSpeed"];
307         [[sw entryEffect] setEffectTime:newTime];
308     } else if ( [sender tag] == 2060) {
309         float newTime = (-([sender floatValue]));
310         [df setFloat:newTime forKey:@"statusWindowVanishSpeed"];
311         [[sw exitEffect] setEffectTime:newTime];
312     } else if ( [sender tag] == 2070) {
313         [df setFloat:[sender floatValue] forKey:@"statusWindowVanishDelay"];
314         [sw setExitDelay:[sender floatValue]];
315     } else if ( [sender tag] == 2080) {
316         [df setBool:SENDER_STATE forKey:@"showSongInfoOnChange"];
317     }
318     [df synchronize];
319 }
320
321 - (void)registerDefaults
322 {
323     BOOL found = NO;
324     NSMutableDictionary *loginWindow;
325     NSMutableArray *loginArray;
326     NSEnumerator *loginEnum;
327     id anItem;
328     ITDebugLog(@"Registering defaults.");
329     [df setObject:[NSArray arrayWithObjects:
330         @"trackInfo",
331         @"separator",
332         @"playPause",
333         @"prevTrack",
334         @"nextTrack",
335         @"separator",
336         @"playlists",
337         @"upcomingSongs",
338         @"separator",
339         @"preferences",
340         @"quit",
341         nil] forKey:@"menu"];
342
343     [df setInteger:5 forKey:@"SongsInAdvance"];
344     // [df setBool:YES forKey:@"showName"];  // Song info will always show song title.
345     [df setBool:YES forKey:@"showArtist"];
346     [df setBool:NO forKey:@"showAlbum"];
347     [df setBool:NO forKey:@"showTime"];
348
349     [df setInteger:2100 forKey:@"statusWindowAppearanceEffect"];
350     [df setInteger:2101 forKey:@"statusWindowVanishEffect"];
351     [df setFloat:0.8 forKey:@"statusWindowAppearanceSpeed"];
352     [df setFloat:0.8 forKey:@"statusWindowVanishSpeed"];
353     [df setFloat:4.0 forKey:@"statusWindowVanishDelay"];
354     [df setBool:YES forKey:@"showSongInfoOnChange"];
355
356     [df synchronize];
357     
358     loginWindow = [[df persistentDomainForName:@"loginwindow"] mutableCopy];
359     loginArray = [loginWindow objectForKey:@"AutoLaunchedApplicationDictionary"];
360     loginEnum = [loginArray objectEnumerator];
361
362     while ( (anItem = [loginEnum nextObject]) ) {
363         if ( [[[anItem objectForKey:@"Path"] lastPathComponent] isEqualToString:[[[NSBundle mainBundle] bundlePath] lastPathComponent]] ) {
364             found = YES;
365         }
366     }
367     [loginWindow release];
368     
369     if (!found) {
370         [[StatusWindowController sharedController] showSetupQueryWindow];
371     }
372 }
373
374 - (void)autoLaunchOK
375 {
376     [[StatusWindow sharedWindow] setLocked:NO];
377     [[StatusWindow sharedWindow] vanish:self];
378     [[StatusWindow sharedWindow] setIgnoresMouseEvents:YES];
379     
380     [self setLaunchesAtLogin:YES];
381 }
382
383 - (void)autoLaunchCancel
384 {
385     [[StatusWindow sharedWindow] setLocked:NO];
386     [[StatusWindow sharedWindow] vanish:self];
387     [[StatusWindow sharedWindow] setIgnoresMouseEvents:YES];
388 }
389
390 - (void)deletePressedInTableView:(NSTableView *)tableView
391 {
392     if (tableView == menuTableView) {
393         int selRow = [tableView selectedRow];
394         ITDebugLog(@"Delete pressed in menu table view.");
395         if (selRow != - 1) {
396             NSString *object = [myItems objectAtIndex:selRow];
397             
398             if ([object isEqualToString:@"preferences"]) {
399                 NSBeep();
400                 return;
401             }
402             
403             if (![object isEqualToString:@"separator"])
404                 [availableItems addObject:object];
405             ITDebugLog(@"Removing object named %@", object);
406             [myItems removeObjectAtIndex:selRow];
407             [menuTableView reloadData];
408             [allTableView reloadData];
409         }
410         [self changeMenus:self];
411     }
412 }
413
414 /*************************************************************************/
415 #pragma mark -
416 #pragma mark HOTKEY SUPPORT METHODS
417 /*************************************************************************/
418
419 - (IBAction)clearHotKey:(id)sender
420 {
421     [hotKeysDictionary setObject:[ITKeyCombo clearKeyCombo] forKey:[hotKeysArray objectAtIndex:[hotKeysTableView selectedRow]]];
422     [df setObject:[[ITKeyCombo clearKeyCombo] plistRepresentation] forKey:[hotKeysArray objectAtIndex:[hotKeysTableView selectedRow]]];
423     [controller setupHotKeys];
424     [hotKeysTableView reloadData];
425 }
426
427 - (IBAction)editHotKey:(id)sender
428 {
429     ITKeyComboPanel *panel = [ITKeyComboPanel sharedPanel];
430     NSString *keyComboKey = [hotKeysArray objectAtIndex:[hotKeysTableView selectedRow]];
431     ITKeyCombo *keyCombo;
432     
433     ITDebugLog(@"Setting key combo on hot key %@.", keyComboKey);
434     [controller clearHotKeys];
435     [panel setKeyCombo:[hotKeysDictionary objectForKey:[hotKeysArray objectAtIndex:[hotKeysTableView selectedRow]]]];
436     [panel setKeyBindingName:[hotKeyNamesArray objectAtIndex:[hotKeysTableView selectedRow]]];
437     if ([panel runModal] == NSOKButton) {
438         NSEnumerator *keyEnumerator = [[hotKeysDictionary allKeys] objectEnumerator];
439         NSString *nextKey;
440         keyCombo = [panel keyCombo];
441         
442         //Check for duplicate key combo
443         while ( (nextKey = [keyEnumerator nextObject]) ) {
444             if ([[hotKeysDictionary objectForKey:nextKey] isEqual:keyCombo] &&
445                 ![keyCombo isEqual:[ITKeyCombo clearKeyCombo]]) {
446                 [hotKeysDictionary setObject:[ITKeyCombo clearKeyCombo]
447                                    forKey:nextKey];
448                 [df setObject:[[ITKeyCombo clearKeyCombo] plistRepresentation]
449                     forKey:nextKey];
450             }
451         }
452         
453         [hotKeysDictionary setObject:keyCombo forKey:keyComboKey];
454         [df setObject:[keyCombo plistRepresentation] forKey:keyComboKey];
455         [controller setupHotKeys];
456         [hotKeysTableView reloadData];
457         ITDebugLog(@"Set combo %@ on hot key %@.", keyCombo, keyComboKey);
458     } else {
459         ITDebugLog(@"Hot key setting on hot key %@ cancelled.", keyComboKey);
460     }
461 }
462
463 - (void)hotKeysTableViewDoubleClicked:(id)sender
464 {
465     if ([sender clickedRow] > -1) {
466         [self editHotKey:sender];
467     }
468 }
469
470 /*************************************************************************/
471 #pragma mark -
472 #pragma mark PRIVATE METHOD IMPLEMENTATIONS
473 /*************************************************************************/
474
475 - (void)setupWindow
476 {
477     ITDebugLog(@"Loading Preferences.nib.");
478     if (![NSBundle loadNibNamed:@"Preferences" owner:self]) {
479         ITDebugLog(@"Failed to load Preferences.nib.");
480         NSBeep();
481         return;
482     }
483 }
484
485 - (void)setupCustomizationTables
486 {
487     NSImageCell *imgCell = [[[NSImageCell alloc] initImageCell:nil] autorelease];
488     ITDebugLog(@"Setting up table views.");
489     // Set the table view cells up
490     [imgCell setImageScaling:NSScaleNone];
491     [[menuTableView tableColumnWithIdentifier:@"submenu"] setDataCell:imgCell];
492     [[allTableView tableColumnWithIdentifier:@"submenu"] setDataCell:imgCell];
493
494     // Register for drag and drop
495     [menuTableView registerForDraggedTypes:[NSArray arrayWithObjects:
496         @"MenuTableViewPboardType",
497         @"AllTableViewPboardType",
498         nil]];
499     [allTableView registerForDraggedTypes:[NSArray arrayWithObjects:
500         @"MenuTableViewPboardType",
501         @"AllTableViewPboardType",
502         nil]];
503 }
504
505 - (void)setupMenuItems
506 {
507     NSEnumerator *itemEnum;
508     id            anItem;
509     ITDebugLog(@"Setting up table view arrays.");
510     // Set the list of items you can have.
511     availableItems = [[NSMutableArray alloc] initWithObjects:
512         @"separator",
513         @"trackInfo",
514         @"upcomingSongs",
515         @"playlists",
516         @"eqPresets",
517         @"songRating",
518         @"playPause",
519         @"nextTrack",
520         @"prevTrack",
521         @"fastForward",
522         @"rewind",
523         @"showPlayer",
524         @"quit",
525         nil];
526     
527     // Get our preferred menu
528     myItems = [[df arrayForKey:@"menu"] mutableCopy];
529     
530     // Delete items in the availableItems array that are already part of the menu
531     itemEnum = [myItems objectEnumerator];
532     while ( (anItem = [itemEnum nextObject]) ) {
533         if (![anItem isEqualToString:@"separator"]) {
534             [availableItems removeObject:anItem];
535         }
536     }
537     
538     // Items that show should a submenu image
539     submenuItems = [[NSArray alloc] initWithObjects:
540         @"upcomingSongs",
541         @"playlists",
542         @"eqPresets",
543         @"songRating",
544         nil];
545 }
546
547 - (void)setupUI
548 {
549     NSMutableDictionary *loginwindow;
550     NSMutableArray *loginarray;
551     NSEnumerator *loginEnum, *keyArrayEnum;
552     id anItem;
553     
554     ITDebugLog(@"Setting up preferences UI.");
555     // Fill in the number of songs in advance to show field
556     [songsInAdvance setIntValue:[df integerForKey:@"SongsInAdvance"]];
557     
558     // Fill hot key array
559     keyArrayEnum = [hotKeysArray objectEnumerator];
560     
561     while ( (anItem = [keyArrayEnum nextObject]) ) {
562         if ([df objectForKey:anItem]) {
563             ITDebugLog(@"Setting up \"%@\" hot key.", anItem);
564             [hotKeysDictionary setObject:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:anItem]] forKey:anItem];
565         } else {
566             [hotKeysDictionary setObject:[ITKeyCombo clearKeyCombo] forKey:anItem];
567         }
568     }
569     
570     ITDebugLog(@"Setting up track info checkboxes.");
571     // Check current track info buttons
572     [albumCheckbox setState:[df boolForKey:@"showAlbum"] ? NSOnState : NSOffState];
573     [nameCheckbox setState:NSOnState];  // Song info will ALWAYS show song title.
574     [nameCheckbox setEnabled:NO];  // Song info will ALWAYS show song title.
575     [artistCheckbox setState:[df boolForKey:@"showArtist"] ? NSOnState : NSOffState];
576     [trackTimeCheckbox setState:[df boolForKey:@"showTime"] ? NSOnState : NSOffState];
577     [trackNumberCheckbox setState:[df boolForKey:@"showTrackNumber"] ? NSOnState : NSOffState];
578     [ratingCheckbox setState:[df boolForKey:@"showTrackRating"] ? NSOnState : NSOffState];
579     
580     // Set the launch at login checkbox state
581     ITDebugLog(@"Setting launch at login state.");
582     [df synchronize];
583     loginwindow = [[df persistentDomainForName:@"loginwindow"] mutableCopy];
584     loginarray = [loginwindow objectForKey:@"AutoLaunchedApplicationDictionary"];
585     
586     loginEnum = [loginarray objectEnumerator];
587     while ( (anItem = [loginEnum nextObject]) ) {
588         if ([[[anItem objectForKey:@"Path"] lastPathComponent] isEqualToString:[[[NSBundle mainBundle] bundlePath] lastPathComponent]]) {
589             [launchAtLoginCheckbox setState:NSOnState];
590         }
591     }
592     
593     // Set the launch player checkbox state
594     ITDebugLog(@"Setting launch player with MenuTunes state.");
595     [launchPlayerAtLaunchCheckbox setState:[df boolForKey:@"LaunchPlayerWithMT"] ? NSOnState : NSOffState];
596     
597     // Setup the positioning controls
598     
599     // Setup effects controls
600     [appearanceEffectPopup selectItem:[appearanceEffectPopup itemAtIndex:[appearanceEffectPopup indexOfItemWithTag:[df integerForKey:@"statusWindowAppearanceEffect"]]]];
601     [vanishEffectPopup     selectItem:[vanishEffectPopup     itemAtIndex:[vanishEffectPopup     indexOfItemWithTag:[df integerForKey:@"statusWindowVanishEffect"]]]];
602     [appearanceSpeedSlider setFloatValue:-([df floatForKey:@"statusWindowAppearanceSpeed"])];
603     [vanishSpeedSlider     setFloatValue:-([df floatForKey:@"statusWindowVanishSpeed"])];
604     [vanishDelaySlider     setFloatValue:[df floatForKey:@"statusWindowVanishDelay"]];
605     [showOnChangeCheckbox  setState:([df boolForKey:@"showSongInfoOnChange"] ? NSOnState : NSOffState)];
606     
607     // Setup the sharing controls
608     if ([df boolForKey:@"enableSharing"]) {
609         [shareMenuTunesCheckbox setState:NSOnState];
610         [useSharedMenuTunesCheckbox setEnabled:NO];
611         [selectSharedPlayerButton setEnabled:NO];
612         [hostTextField setEnabled:NO];
613     } else if ([df boolForKey:@"useSharedPlayer"]) {
614         [useSharedMenuTunesCheckbox setState:NSOnState];
615         [shareMenuTunesCheckbox setEnabled:NO];
616         [sharePasswordCheckbox setEnabled:NO];
617         [sharePasswordTextField setEnabled:NO];
618     }
619     
620     [[NSNotificationCenter defaultCenter] addObserver:sharingTableView selector:@selector(reloadData) name:@"ITMTFoundNetService" object:nil];
621     
622     [selectPlayerBox setContentView:zeroConfView];
623     [sharePasswordCheckbox setState:([df boolForKey:@"enableSharingPassword"] ? NSOnState : NSOffState)];
624     //[sharePasswordTextField setStringValue:@""]; //DO THIS LATER
625     if ([df stringForKey:@"sharedPlayerHost"]) {
626         [hostTextField setStringValue:[df stringForKey:@"sharedPlayerHost"]];
627     }
628 }
629
630 - (IBAction)changeMenus:(id)sender
631 {
632     ITDebugLog(@"Synchronizing menus");
633     [df setObject:myItems forKey:@"menu"];
634     [df synchronize];
635 }
636
637 - (void)setLaunchesAtLogin:(BOOL)flag
638 {
639     NSMutableDictionary *loginwindow;
640     NSMutableArray *loginarray;
641     ITDebugLog(@"Setting launches at login: %i", flag);
642     [df synchronize];
643     loginwindow = [[df persistentDomainForName:@"loginwindow"] mutableCopy];
644     loginarray = [loginwindow objectForKey:@"AutoLaunchedApplicationDictionary"];
645     
646     if (flag) {
647         NSDictionary *itemDict = [NSDictionary dictionaryWithObjectsAndKeys:
648         [[NSBundle mainBundle] bundlePath], @"Path",
649         [NSNumber numberWithInt:0], @"Hide", nil];
650         [loginarray addObject:itemDict];
651     } else {
652         int i;
653         for (i = 0; i < [loginarray count]; i++) {
654             NSDictionary *tempDict = [loginarray objectAtIndex:i];
655             if ([[[tempDict objectForKey:@"Path"] lastPathComponent] isEqualToString:[[[NSBundle mainBundle] bundlePath] lastPathComponent]]) {
656                 [loginarray removeObjectAtIndex:i];
657                 break;
658             }
659         }
660     }
661     [df setPersistentDomain:loginwindow forName:@"loginwindow"];
662     [df synchronize];
663     [loginwindow release];
664     ITDebugLog(@"Finished setting launches at login.");
665 }
666
667
668 /*************************************************************************/
669 #pragma mark -
670 #pragma mark NSWindow DELEGATE METHODS
671 /*************************************************************************/
672
673 - (void)windowWillClose:(NSNotification *)note
674 {
675     [(MainController *)controller closePreferences]; 
676 }
677
678
679 /*************************************************************************/
680 #pragma mark -
681 #pragma mark NSTableView DATASOURCE METHODS
682 /*************************************************************************/
683
684 - (int)numberOfRowsInTableView:(NSTableView *)aTableView
685 {
686     if (aTableView == menuTableView) {
687         return [myItems count];
688     } else if (aTableView == allTableView) {
689         return [availableItems count];
690     } else if (aTableView == hotKeysTableView) {
691         return [hotKeysArray count];
692     } else {
693         return [[[NetworkController sharedController] remoteServices] count];
694     }
695 }
696
697 - (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex
698 {
699     if (aTableView == menuTableView) {
700         NSString *object = [myItems objectAtIndex:rowIndex];
701         if ([[aTableColumn identifier] isEqualToString:@"name"]) {
702             if ([object isEqualToString:@"showPlayer"]) {
703                 NSString *string;
704                 NS_DURING
705                     string = [NSString stringWithFormat:@"%@ %@", NSLocalizedString(@"show", @"Show"), [[controller currentRemote] playerSimpleName]];
706                 NS_HANDLER
707                     [controller networkError:localException];
708                 NS_ENDHANDLER
709                 return string;
710             }
711             return NSLocalizedString(object, @"ERROR");
712         } else {
713             if ([submenuItems containsObject:object])
714             {
715                 return [NSImage imageNamed:@"submenu"];
716             } else {
717                 return nil;
718             }
719         }
720     } else if (aTableView == allTableView) {
721         NSString *object = [availableItems objectAtIndex:rowIndex];
722         if ([[aTableColumn identifier] isEqualToString:@"name"]) {
723             if ([object isEqualToString:@"showPlayer"]) {
724                 NSString *string;
725                 NS_DURING
726                     string = [NSString stringWithFormat:@"%@ %@", NSLocalizedString(@"show", @"Show"), [[controller currentRemote] playerSimpleName]];
727                 NS_HANDLER
728                     [controller networkError:localException];
729                 NS_ENDHANDLER
730                 return string;
731             }
732             return NSLocalizedString(object, @"ERROR");
733         } else {
734             if ([submenuItems containsObject:object]) {
735                 return [NSImage imageNamed:@"submenu"];
736             } else {
737                 return nil;
738             }
739         }
740     } else if (aTableView == hotKeysTableView) {
741         if ([[aTableColumn identifier] isEqualToString:@"name"]) {
742             return [hotKeyNamesArray objectAtIndex:rowIndex];
743         } else {
744             return [[hotKeysDictionary objectForKey:[hotKeysArray objectAtIndex:rowIndex]] description];
745         }
746     } else {
747         if ([[aTableColumn identifier] isEqualToString:@"name"]) {
748             return [[[[NetworkController sharedController] remoteServices] objectAtIndex:rowIndex] objectForKey:@"name"];
749         } else {
750             return @"X";
751         }
752     }
753 }
754
755 - (BOOL)tableView:(NSTableView *)tableView writeRows:(NSArray*)rows toPasteboard:(NSPasteboard*)pboard
756 {
757     if (tableView == menuTableView) {
758         [pboard declareTypes:[NSArray arrayWithObjects:@"MenuTableViewPboardType", nil] owner:self];
759         [pboard setString:[[rows objectAtIndex:0] stringValue] forType:@"MenuTableViewPboardType"];
760         return YES;
761     }
762     
763     if (tableView == allTableView) {
764         [pboard declareTypes:[NSArray arrayWithObjects:@"AllTableViewPboardType", nil] owner:self];
765         [pboard setString:[[rows objectAtIndex:0] stringValue] forType:@"AllTableViewPboardType"];
766         return YES;
767     }
768     return NO;
769 }
770
771 - (BOOL)tableView:(NSTableView*)tableView acceptDrop:(id <NSDraggingInfo>)info row:(int)row dropOperation:(NSTableViewDropOperation)operation
772 {
773     NSPasteboard *pb;
774     int dragRow;
775     NSString *dragData, *temp;
776     
777     pb = [info draggingPasteboard];
778     
779     if ([[pb types] containsObject:@"MenuTableViewPboardType"]) {
780         dragData = [pb stringForType:@"MenuTableViewPboardType"];
781         dragRow = [dragData intValue];
782         temp = [myItems objectAtIndex:dragRow];
783         
784         if (tableView == menuTableView) {
785             [myItems insertObject:temp atIndex:row];
786             if (row > dragRow) {
787                 [myItems removeObjectAtIndex:dragRow];
788             } else {
789                 [myItems removeObjectAtIndex:dragRow + 1];
790             }
791         } else if (tableView == allTableView) {
792             if (![temp isEqualToString:@"separator"]) {
793                 [availableItems addObject:temp];
794             }
795             [myItems removeObjectAtIndex:dragRow];
796         }
797     } else if ([[pb types] containsObject:@"AllTableViewPboardType"]) {
798         dragData = [pb stringForType:@"AllTableViewPboardType"];
799         dragRow = [dragData intValue];
800         temp = [availableItems objectAtIndex:dragRow];
801         
802         [myItems insertObject:temp atIndex:row];
803         
804         if (![temp isEqualToString:@"separator"]) {
805             [availableItems removeObjectAtIndex:dragRow];
806         }
807     }
808     
809     [menuTableView reloadData];
810     [allTableView reloadData];
811     [self changeMenus:self];
812     return YES;
813 }
814
815 - (NSDragOperation)tableView:(NSTableView*)tableView validateDrop:(id <NSDraggingInfo>)info proposedRow:(int)row proposedDropOperation:(NSTableViewDropOperation)operation
816 {
817     if (tableView == allTableView) {
818         if ([[[info draggingPasteboard] types] containsObject:@"AllTableViewPboardType"]) {
819             return NSDragOperationNone;
820         }
821         
822         if ([[[info draggingPasteboard] types] containsObject:@"MenuTableViewPboardType"]) {
823             NSString *item = [myItems objectAtIndex:[[[info draggingPasteboard] stringForType:@"MenuTableViewPboardType"] intValue]];
824             if ([item isEqualToString:@"preferences"] || [item isEqualToString:@"quit"]) {
825                 return NSDragOperationNone;
826             }
827         }
828         
829         [tableView setDropRow:-1 dropOperation:NSTableViewDropOn];
830         return NSDragOperationGeneric;
831     }
832     
833     if (operation == NSTableViewDropOn || row == -1)
834     {
835         return NSDragOperationNone;
836     }
837     return NSDragOperationGeneric;
838 }
839
840
841 /*************************************************************************/
842 #pragma mark -
843 #pragma mark DEALLOCATION METHODS
844 /*************************************************************************/
845
846 - (void)dealloc
847 {
848     [hotKeysArray release];
849     [hotKeysDictionary release];
850     [menuTableView setDataSource:nil];
851     [allTableView setDataSource:nil];
852     [controller release];
853     [availableItems release];
854     [submenuItems release];
855     [myItems release];
856     [df release];
857 }
858
859 @end