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