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