Initial revision
[MenuTunes.git] / PreferencesController.m
1 #import "PreferencesController.h"
2 #import "MenuTunes.h"
3 #import "HotKeyCenter.h"
4
5 @implementation PreferencesController
6
7 - (id)initWithMenuTunes:(MenuTunes *)tunes;
8 {
9     if ( (self = [super init]) )
10     {
11         int i;
12         NSImageCell *imgCell = [[[NSImageCell alloc] init] autorelease];
13         NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
14         NSString *temp;
15         
16         mt = [tunes retain];
17         
18         //Load the nib
19         [NSBundle loadNibNamed:@"Preferences" owner:self];
20         
21         //Show our window
22         [window setLevel:NSStatusWindowLevel];
23         [window center];
24         [window makeKeyAndOrderFront:nil];
25         
26         //Set the table view cells up
27         [imgCell setImageScaling:NSScaleNone];
28         [[menuTableView tableColumnWithIdentifier:@"submenu"] setDataCell:imgCell];
29         [[allTableView tableColumnWithIdentifier:@"submenu"] setDataCell:imgCell];
30         
31         //Register for drag and drop
32         [menuTableView registerForDraggedTypes:[NSArray arrayWithObjects:@"MenuTableViewPboardType", @"AllTableViewPboardType", nil]];
33         [allTableView registerForDraggedTypes:[NSArray arrayWithObjects:@"MenuTableViewPboardType", @"AllTableViewPboardType", nil]];
34         
35         //Set the list of items you can have.
36         availableItems = [[NSMutableArray alloc] initWithObjects:@"Current Track Info",  @"Upcoming Songs", @"Playlists", @"Play/Pause", @"Next Track", @"Previous Track", @"Fast Forward", @"Rewind", @"<separator>", nil];
37         
38         //Get our preferred menu
39         myItems = [[[NSUserDefaults standardUserDefaults] arrayForKey:@"menu"] mutableCopy];
40         if (myItems == nil)
41         {
42             myItems = [[NSMutableArray alloc] initWithObjects:@"Play/Pause", @"Next Track", @"Previous Track", @"Fast Forward", @"Rewind", @"<separator>", @"Upcoming Songs", @"Playlists", @"<separator>", @"PreferencesÉ", @"Quit", @"<separator>", @"Current Track Info", nil];
43             [[NSUserDefaults standardUserDefaults] setObject:myItems forKey:@"menu"];
44         }
45         
46         //Delete items in the availableItems array that are already part of the menu
47         for (i = 0; i < [myItems count]; i++)
48         {
49             NSString *item = [myItems objectAtIndex:i];
50             if (![item isEqualToString:@"<separator>"])
51             {
52                 [availableItems removeObject:item];
53             }
54         }
55         
56         //Items that show should a submenu image
57         submenuItems = [[NSArray alloc] initWithObjects:@"Upcoming Songs", @"Playlists", nil];
58         
59         //Fill in the number of songs in advance to show field
60         if ([defaults integerForKey:@"SongsInAdvance"])
61         {
62             [songsInAdvance setIntValue:[defaults integerForKey:@"SongsInAdvance"]];
63         }
64         else
65         {
66             [songsInAdvance setIntValue:5];
67         }
68         
69         //Fill in hot key buttons
70         if ([defaults objectForKey:@"PlayPause"])
71         {
72             playPauseCombo = [defaults keyComboForKey:@"PlayPause"];
73             [playPauseButton setTitle:[playPauseCombo userDisplayRep]];
74         }
75         else
76         {
77             playPauseCombo = [[KeyCombo alloc] init];
78         }
79         
80         if ([defaults objectForKey:@"NextTrack"])
81         {
82             nextTrackCombo = [defaults keyComboForKey:@"NextTrack"];
83             [nextTrackButton setTitle:[nextTrackCombo userDisplayRep]];
84         }
85         else
86         {
87             nextTrackCombo = [[KeyCombo alloc] init];
88         }
89         
90         if ([defaults objectForKey:@"PrevTrack"])
91         {
92             prevTrackCombo = [defaults keyComboForKey:@"PrevTrack"];
93             [previousTrackButton setTitle:[prevTrackCombo userDisplayRep]];
94         }
95         else
96         {
97             prevTrackCombo = [[KeyCombo alloc] init];
98         }
99         
100         if ([defaults objectForKey:@"TrackInfo"])
101         {
102             trackInfoCombo = [defaults keyComboForKey:@"TrackInfo"];
103             [trackInfoButton setTitle:[trackInfoCombo userDisplayRep]];
104         }
105         else
106         {
107             trackInfoCombo = [[KeyCombo alloc] init];
108         }
109         
110         if ([defaults objectForKey:@"UpcomingSongs"])
111         {
112             upcomingSongsCombo = [defaults keyComboForKey:@"UpcomingSongs"];
113             [upcomingSongsButton setTitle:[upcomingSongsCombo userDisplayRep]];
114         }
115         else
116         {
117             upcomingSongsCombo = [[KeyCombo alloc] init];
118         }
119         
120         //Check current track info buttons
121         
122         //Album and name get special treatment because they are defaults
123         if ( (temp = [defaults stringForKey:@"showAlbum"]) )
124         {
125             if ((temp == nil) || [temp isEqualToString:@"1"])
126             {
127                 [albumCheckbox setState:NSOnState];
128             }
129             else
130             {
131                 [albumCheckbox setState:NSOffState];
132             }
133         }
134         
135         if ( (temp = [defaults stringForKey:@"showName"]) )
136         {
137             if ((temp == nil) || [temp isEqualToString:@"1"])
138             {
139                 [nameCheckbox setState:NSOnState];
140             }
141             else
142             {
143                 [nameCheckbox setState:NSOffState];
144             }
145         }
146         
147         [artistCheckbox setState:[defaults boolForKey:@"showArtist"] ? NSOnState : NSOffState];
148         [songRatingCheckbox setState:[defaults boolForKey:@"showRating"] ? NSOnState : NSOffState];
149         [trackNumberCheckbox setState:[defaults boolForKey:@"showTrackNum"] ? NSOnState : NSOffState];
150         [trackTimeCheckbox setState:[defaults boolForKey:@"showTime"] ? NSOnState : NSOffState];
151         [yearCheckbox setState:[defaults boolForKey:@"showYear"] ? NSOnState : NSOffState];
152     }
153     return self;
154 }
155
156 - (void)dealloc
157 {
158     [self setKeyCombo:nil];
159     [playPauseCombo release];
160     [nextTrackCombo release];
161     [prevTrackCombo release];
162     [trackInfoCombo release];
163     [upcomingSongsCombo release];
164     [keyComboPanel release];
165     [menuTableView setDataSource:nil];
166     [allTableView setDataSource:nil];
167     [mt release];
168     [availableItems release];
169     [submenuItems release];
170     [myItems release];
171 }
172
173 - (IBAction)apply:(id)sender
174 {
175     NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
176     [defaults setObject:myItems forKey:@"menu"];
177     
178     //Set key combos
179     [defaults setKeyCombo:playPauseCombo forKey:@"PlayPause"];
180     [defaults setKeyCombo:nextTrackCombo forKey:@"NextTrack"];
181     [defaults setKeyCombo:prevTrackCombo forKey:@"PrevTrack"];
182     [defaults setKeyCombo:trackInfoCombo forKey:@"TrackInfo"];
183     [defaults setKeyCombo:upcomingSongsCombo forKey:@"UpcomingSongs"];
184     
185     //Set info checkboxes
186     [defaults setBool:[albumCheckbox state] forKey:@"showAlbum"];
187     [defaults setBool:[nameCheckbox state] forKey:@"showName"];
188     [defaults setBool:[artistCheckbox state] forKey:@"showArtist"];
189     [defaults setBool:[songRatingCheckbox state] forKey:@"showRating"];
190     [defaults setBool:[trackNumberCheckbox state] forKey:@"showTrackNum"];
191     [defaults setBool:[trackTimeCheckbox state] forKey:@"showTime"];
192     [defaults setBool:[yearCheckbox state] forKey:@"showYear"];
193     
194     //Set songs in advance
195     if ([songsInAdvance intValue])
196     {
197         [defaults setInteger:[songsInAdvance intValue] forKey:@"SongsInAdvance"];
198     }
199     else
200     {
201         [defaults setInteger:5 forKey:@"SongsInAdvance"];
202     }
203     
204     [mt rebuildMenu];
205     [mt clearHotKeys];
206 }
207
208 - (IBAction)cancel:(id)sender
209 {
210     [window close];
211     [mt closePreferences];
212 }
213
214 - (IBAction)cancelHotKey:(id)sender
215 {
216     [[NSNotificationCenter defaultCenter] removeObserver:self];
217     [NSApp endSheet:keyComboPanel];
218     [keyComboPanel orderOut:nil];
219 }
220
221 - (IBAction)clearHotKey:(id)sender
222 {
223     [self setKeyCombo:[KeyCombo clearKeyCombo]];
224 }
225
226 - (IBAction)okHotKey:(id)sender
227 {
228     NSString *string;
229     if (([combo modifiers] <= 0) && ([combo keyCode] >= 0))
230     {
231         [window setLevel:NSNormalWindowLevel];
232         NSRunAlertPanel(@"Bad Key Combo", @"Please enter a valid key combo. A valid combo must have a modifier key in it. (Command, option, shift, control).", @"OK", nil, nil, nil);
233         [window setLevel:NSStatusWindowLevel];
234         return;
235     }
236     
237     string = [combo userDisplayRep];
238     
239     if (string == nil)
240     {
241         string = @"None";
242     }
243     if ([setHotKey isEqualToString:@"PlayPause"])
244     {
245         if (([combo isEqual:nextTrackCombo] || [combo isEqual:prevTrackCombo] ||
246             [combo isEqual:trackInfoCombo] || [combo isEqual:upcomingSongsCombo]) && 
247             !(([combo modifiers] == -1) && ([combo keyCode] == -1)))
248         {
249             [window setLevel:NSNormalWindowLevel];
250             NSRunAlertPanel(@"Duplicate Key Combo", @"Please choose a unique key combo.", @"OK", nil, nil, nil);
251             [window setLevel:NSStatusWindowLevel];
252             return;
253         }
254         playPauseCombo = [combo copy];
255         [playPauseButton setTitle:string];
256     }
257     else if ([setHotKey isEqualToString:@"NextTrack"])
258     {
259         if (([combo isEqual:playPauseCombo] || [combo isEqual:prevTrackCombo] ||
260             [combo isEqual:trackInfoCombo] || [combo isEqual:upcomingSongsCombo]) && 
261             !(([combo modifiers] == -1) && ([combo keyCode] == -1)))
262         {
263             [window setLevel:NSNormalWindowLevel];
264             NSRunAlertPanel(@"Duplicate Key Combo", @"Please choose a unique key combo.", @"OK", nil, nil, nil);
265             [window setLevel:NSStatusWindowLevel];
266             return;
267         }
268         nextTrackCombo = [combo copy];
269         [nextTrackButton setTitle:string];
270     }
271     else if ([setHotKey isEqualToString:@"PrevTrack"])
272     {
273         if (([combo isEqual:nextTrackCombo] || [combo isEqual:playPauseCombo] ||
274             [combo isEqual:trackInfoCombo] || [combo isEqual:upcomingSongsCombo]) && 
275             !(([combo modifiers] == -1) && ([combo keyCode] == -1)))
276         {
277             [window setLevel:NSNormalWindowLevel];
278             NSRunAlertPanel(@"Duplicate Key Combo", @"Please choose a unique key combo.", @"OK", nil, nil, nil);
279             [window setLevel:NSStatusWindowLevel];
280             return;
281         }
282         prevTrackCombo = [combo copy];
283         [previousTrackButton setTitle:string];
284     }
285     else if ([setHotKey isEqualToString:@"TrackInfo"])
286     {
287         if (([combo isEqual:nextTrackCombo] || [combo isEqual:prevTrackCombo] ||
288             [combo isEqual:playPauseCombo] || [combo isEqual:upcomingSongsCombo]) && 
289             !(([combo modifiers] == -1) && ([combo keyCode] == -1)))
290         {
291             [window setLevel:NSNormalWindowLevel];
292             NSRunAlertPanel(@"Duplicate Key Combo", @"Please choose a unique key combo.", @"OK", nil, nil, nil);
293             [window setLevel:NSStatusWindowLevel];
294             return;
295         }
296         trackInfoCombo = [combo copy];
297         [trackInfoButton setTitle:string];
298     }
299     else if ([setHotKey isEqualToString:@"UpcomingSongs"])
300     {
301         if (([combo isEqual:nextTrackCombo] || [combo isEqual:prevTrackCombo] ||
302             [combo isEqual:trackInfoCombo] || [combo isEqual:playPauseCombo]) && 
303             !(([combo modifiers] == -1) && ([combo keyCode] == -1)))
304         {
305             [window setLevel:NSNormalWindowLevel];
306             NSRunAlertPanel(@"Duplicate Key Combo", @"Please choose a unique key combo.", @"OK", nil, nil, nil);
307             [window setLevel:NSStatusWindowLevel];
308             return;
309         }
310         upcomingSongsCombo = [combo copy];
311         [upcomingSongsButton setTitle:string];
312     }
313     [self cancelHotKey:sender];
314 }
315
316 - (IBAction)save:(id)sender
317 {
318     [self apply:nil];
319     [window close];
320     [mt closePreferences];
321 }
322
323 - (IBAction)setCurrentTrackInfo:(id)sender
324 {
325     [self setKeyCombo:trackInfoCombo];
326     [self setHotKey:@"TrackInfo"];
327 }
328
329 - (IBAction)setNextTrack:(id)sender
330 {
331     [self setKeyCombo:nextTrackCombo];
332     [self setHotKey:@"NextTrack"];
333 }
334
335 - (IBAction)setPlayPause:(id)sender
336 {
337     [self setKeyCombo:playPauseCombo];
338     [self setHotKey:@"PlayPause"];
339 }
340
341 - (IBAction)setPreviousTrack:(id)sender
342 {
343     [self setKeyCombo:prevTrackCombo];
344     [self setHotKey:@"PrevTrack"];
345 }
346
347 - (IBAction)setUpcomingSongs:(id)sender
348 {
349     [self setKeyCombo:upcomingSongsCombo];
350     [self setHotKey:@"UpcomingSongs"];
351 }
352
353 - (void)setHotKey:(NSString *)key
354 {
355     setHotKey = key;
356     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyEvent:) name:@"KeyBroadcasterEvent" object:nil];
357     [NSApp beginSheet:keyComboPanel modalForWindow:window modalDelegate:self didEndSelector:nil contextInfo:nil];
358 }
359
360 - (void)keyEvent:(NSNotification *)note
361 {
362     NSDictionary *info = [note userInfo];
363     short keyCode;
364     long modifiers;
365     KeyCombo *newCombo;
366     
367     keyCode = [[info objectForKey:@"KeyCode"] shortValue];
368     modifiers = [[info objectForKey:@"Modifiers"] longValue];
369     
370     newCombo = [[KeyCombo alloc] initWithKeyCode:keyCode andModifiers:modifiers];
371     [self setKeyCombo:newCombo];
372 }
373
374 - (void)setKeyCombo:(KeyCombo *)newCombo
375 {
376     NSString *string;
377     [combo release];
378     combo = [newCombo copy];
379     
380     string = [combo userDisplayRep];
381     if (string == nil)
382     {
383         string = @"";
384     }
385     [keyComboField setStringValue:string];
386 }
387
388 //
389 //
390 // Table View Datasource Methods
391 //
392 //
393
394 - (int)numberOfRowsInTableView:(NSTableView *)aTableView
395 {
396     if (aTableView == menuTableView)
397     {
398         return [myItems count];
399     }
400     else
401     {
402         return [availableItems count];
403     }
404 }
405
406 - (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex
407 {
408     if (aTableView == menuTableView)
409     {
410         if ([[aTableColumn identifier] isEqualToString:@"name"])
411         {
412             return [myItems objectAtIndex:rowIndex];
413         }
414         else
415         {
416             if ([submenuItems containsObject:[myItems objectAtIndex:rowIndex]])
417             {
418                 return [NSImage imageNamed:@"submenu"];
419             }
420             else
421             {
422                 return nil;
423             }
424         }
425     }
426     else
427     {
428         if ([[aTableColumn identifier] isEqualToString:@"name"])
429         {
430             return [availableItems objectAtIndex:rowIndex];
431         }
432         else
433         {
434             if ([submenuItems containsObject:[availableItems objectAtIndex:rowIndex]])
435             {
436                 return [NSImage imageNamed:@"submenu"];
437             }
438             else
439             {
440                 return nil;
441             }
442         }
443     }
444 }
445
446 - (BOOL)tableView:(NSTableView *)tableView writeRows:(NSArray*)rows toPasteboard:(NSPasteboard*)pboard
447 {
448     if (tableView == menuTableView)
449     {
450         [pboard declareTypes:[NSArray arrayWithObjects:@"MenuTableViewPboardType", nil] owner:self];
451         [pboard setString:[[rows objectAtIndex:0] stringValue] forType:@"MenuTableViewPboardType"];
452         return YES;
453     }
454     
455     if (tableView == allTableView)
456     {
457         [pboard declareTypes:[NSArray arrayWithObjects:@"AllTableViewPboardType", nil] owner:self];
458         [pboard setString:[[rows objectAtIndex:0] stringValue] forType:@"AllTableViewPboardType"];
459         return YES;
460     }
461     return NO;
462 }
463
464 - (BOOL)tableView:(NSTableView*)tableView acceptDrop:(id <NSDraggingInfo>)info row:(int)row dropOperation:(NSTableViewDropOperation)operation
465 {
466     NSPasteboard *pb;
467     int dragRow;
468     NSString *dragData, *temp;
469     
470     pb = [info draggingPasteboard];
471     
472     if ([[pb types] containsObject:@"MenuTableViewPboardType"])
473     {
474         dragData = [pb stringForType:@"MenuTableViewPboardType"];
475         dragRow = [dragData intValue];
476         temp = [myItems objectAtIndex:dragRow];
477         [myItems removeObjectAtIndex:dragRow];
478         
479         if (tableView == menuTableView)
480         {
481             if (row > dragRow)
482             {
483                 [myItems insertObject:temp atIndex:row - 1];
484             }
485             else
486             {
487                 [myItems insertObject:temp atIndex:row];
488             }
489         }
490         else
491         {
492             if (![temp isEqualToString:@"<separator>"])
493             {
494                 [availableItems addObject:temp];
495             }
496         }
497     }
498     else if ([[pb types] containsObject:@"AllTableViewPboardType"])
499     {
500         dragData = [pb stringForType:@"AllTableViewPboardType"];
501         dragRow = [dragData intValue];
502         temp = [availableItems objectAtIndex:dragRow];
503         
504         if (![temp isEqualToString:@"<separator>"])
505         {
506             [availableItems removeObjectAtIndex:dragRow];
507         }
508         [myItems insertObject:temp atIndex:row];
509     }
510     
511     [menuTableView reloadData];
512     [allTableView reloadData];
513     return YES;
514 }
515
516 - (NSDragOperation)tableView:(NSTableView*)tableView validateDrop:(id <NSDraggingInfo>)info proposedRow:(int)row proposedDropOperation:(NSTableViewDropOperation)operation
517 {
518     if (tableView == allTableView)
519     {
520         if ([[[info draggingPasteboard] types] containsObject:@"AllTableViewPboardType"])
521         {
522             return NSDragOperationNone;
523         }
524         
525         if ([[[info draggingPasteboard] types] containsObject:@"MenuTableViewPboardType"])
526         {
527             NSString *item = [myItems objectAtIndex:[[[info draggingPasteboard] stringForType:@"MenuTableViewPboardType"] intValue]];
528             if ([item isEqualToString:@"PreferencesÉ"] || [item isEqualToString:@"Quit"])
529             {
530                 return NSDragOperationNone;
531             }
532         }
533         
534         [tableView setDropRow:-1 dropOperation:NSTableViewDropOn];
535         return NSDragOperationGeneric;
536     }
537     
538     if (operation == NSTableViewDropOn || row == -1)
539     {
540         return NSDragOperationNone;
541     }
542     
543     return NSDragOperationGeneric;
544 }
545
546 @end