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