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