Changes in autologin and defaults.
[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", @"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", 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     ProcessSerialNumber psn;
138     NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
139     [defaults setObject:myItems forKey:@"menu"];
140     
141     //Set key combos
142     [defaults setKeyCombo:playPauseCombo forKey:@"PlayPause"];
143     [defaults setKeyCombo:nextTrackCombo forKey:@"NextTrack"];
144     [defaults setKeyCombo:prevTrackCombo forKey:@"PrevTrack"];
145     [defaults setKeyCombo:trackInfoCombo forKey:@"TrackInfo"];
146     [defaults setKeyCombo:upcomingSongsCombo forKey:@"UpcomingSongs"];
147     
148     //Set info checkboxes
149     [defaults setBool:[albumCheckbox state] forKey:@"showAlbum"];
150     [defaults setBool:[nameCheckbox state] forKey:@"showName"];
151     [defaults setBool:[artistCheckbox state] forKey:@"showArtist"];
152     [defaults setBool:[trackTimeCheckbox state] forKey:@"showTime"];
153     
154     //Here we set whether we will launch at login by modifying loginwindow.plist
155     if ([launchAtLoginCheckbox state] == NSOnState) {
156         NSMutableDictionary *loginwindow;
157         NSMutableArray *loginarray;
158         ComponentInstance temp = OpenDefaultComponent(kOSAComponentType, kAppleScriptSubtype);;
159         int i;
160         BOOL skip = NO;
161         
162         [defaults synchronize];
163         loginwindow = [[defaults persistentDomainForName:@"loginwindow"] mutableCopy];
164         loginarray = [loginwindow objectForKey:@"AutoLaunchedApplicationDictionary"];
165         
166         for (i = 0; i < [loginarray count]; i++) {
167             NSDictionary *tempDict = [loginarray objectAtIndex:i];
168             if ([[[tempDict objectForKey:@"Path"] lastPathComponent] isEqualToString:[[[NSBundle mainBundle] bundlePath] lastPathComponent]]) {
169                 skip = YES;
170             }
171         }
172         
173         if (!skip) {
174             AEDesc scriptDesc, resultDesc;
175             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]];
176             
177             AECreateDesc(typeChar, [script cString], [script cStringLength], 
178         &scriptDesc);
179             
180             OSADoScript(temp, &scriptDesc, kOSANullScript, typeChar, kOSAModeCanInteract, &resultDesc);
181             
182             AEDisposeDesc(&scriptDesc);
183             AEDisposeDesc(&resultDesc);
184             CloseComponent(temp);
185         }
186     } else {
187         NSMutableDictionary *loginwindow;
188         NSMutableArray *loginarray;
189         int i;
190         
191         [[NSUserDefaults standardUserDefaults] synchronize];
192         loginwindow = [[[NSUserDefaults standardUserDefaults] persistentDomainForName:@"loginwindow"] mutableCopy];
193         loginarray = [loginwindow objectForKey:@"AutoLaunchedApplicationDictionary"];
194         
195         for (i = 0; i < [loginarray count]; i++) {
196             NSDictionary *tempDict = [loginarray objectAtIndex:i];
197             if ([[[tempDict objectForKey:@"Path"] lastPathComponent] isEqualToString:[[[NSBundle mainBundle] bundlePath] lastPathComponent]]) {
198                 [loginarray removeObjectAtIndex:i];
199                 [defaults setPersistentDomain:loginwindow forName:@"loginwindow"];
200                 [defaults synchronize];
201                 break;
202             }
203         }
204     }
205     
206     //Set songs in advance
207     if ([songsInAdvance intValue]) {
208         [defaults setInteger:[songsInAdvance intValue] forKey:@"SongsInAdvance"];
209     } else {
210         [defaults setInteger:5 forKey:@"SongsInAdvance"];
211     }
212     
213     psn = [mt iTunesPSN];
214     if (!((psn.highLongOfPSN == kNoProcess) && (psn.lowLongOfPSN == 0))) {
215         [mt rebuildMenu];
216     }
217     [mt clearHotKeys];
218 }
219
220 - (IBAction)cancel:(id)sender
221 {
222     [window close];
223     [mt closePreferences];
224 }
225
226 - (IBAction)cancelHotKey:(id)sender
227 {
228     [[NSNotificationCenter defaultCenter] removeObserver:self];
229     [NSApp endSheet:keyComboPanel];
230     [keyComboPanel orderOut:nil];
231 }
232
233 - (IBAction)clearHotKey:(id)sender
234 {
235     [self setKeyCombo:[KeyCombo clearKeyCombo]];
236 }
237
238 - (IBAction)okHotKey:(id)sender
239 {
240     NSString *string;
241     if (([combo modifiers] <= 0) && ([combo keyCode] >= 0)) {
242         [window setLevel:NSNormalWindowLevel];
243         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);
244         [window setLevel:NSStatusWindowLevel];
245         return;
246     }
247     
248     string = [combo userDisplayRep];
249     
250     if (string == nil) {
251         string = @"None";
252     }
253     if ([setHotKey isEqualToString:@"PlayPause"]) {
254         if (([combo isEqual:nextTrackCombo] || [combo isEqual:prevTrackCombo] ||
255             [combo isEqual:trackInfoCombo] || [combo isEqual:upcomingSongsCombo]) &&
256             !(([combo modifiers] == -1) && ([combo keyCode] == -1))) {
257             
258             [window setLevel:NSNormalWindowLevel];
259             NSRunAlertPanel(@"Duplicate Key Combo", @"Please choose a unique key combo.", @"OK", nil, nil, nil);
260             [window setLevel:NSStatusWindowLevel];
261             return;
262         }
263         playPauseCombo = [combo copy];
264         [playPauseButton setTitle:string];
265     }
266     else if ([setHotKey isEqualToString:@"NextTrack"])
267     {
268         if (([combo isEqual:playPauseCombo] || [combo isEqual:prevTrackCombo] ||
269             [combo isEqual:trackInfoCombo] || [combo isEqual:upcomingSongsCombo]) && 
270             !(([combo modifiers] == -1) && ([combo keyCode] == -1))) {
271             
272             [window setLevel:NSNormalWindowLevel];
273             NSRunAlertPanel(@"Duplicate Key Combo", @"Please choose a unique key combo.", @"OK", nil, nil, nil);
274             [window setLevel:NSStatusWindowLevel];
275             return;
276         }
277         nextTrackCombo = [combo copy];
278         [nextTrackButton setTitle:string];
279     }
280     else if ([setHotKey isEqualToString:@"PrevTrack"])
281     {
282         if (([combo isEqual:nextTrackCombo] || [combo isEqual:playPauseCombo] ||
283             [combo isEqual:trackInfoCombo] || [combo isEqual:upcomingSongsCombo]) && 
284             !(([combo modifiers] == -1) && ([combo keyCode] == -1))) {
285             
286             [window setLevel:NSNormalWindowLevel];
287             NSRunAlertPanel(@"Duplicate Key Combo", @"Please choose a unique key combo.", @"OK", nil, nil, nil);
288             [window setLevel:NSStatusWindowLevel];
289             return;
290         }
291         prevTrackCombo = [combo copy];
292         [previousTrackButton setTitle:string];
293     }
294     else if ([setHotKey isEqualToString:@"TrackInfo"])
295     {
296         if (([combo isEqual:nextTrackCombo] || [combo isEqual:prevTrackCombo] ||
297             [combo isEqual:playPauseCombo] || [combo isEqual:upcomingSongsCombo]) && 
298             !(([combo modifiers] == -1) && ([combo keyCode] == -1))) {
299             
300             [window setLevel:NSNormalWindowLevel];
301             NSRunAlertPanel(@"Duplicate Key Combo", @"Please choose a unique key combo.", @"OK", nil, nil, nil);
302             [window setLevel:NSStatusWindowLevel];
303             return;
304         }
305         trackInfoCombo = [combo copy];
306         [trackInfoButton setTitle:string];
307     }
308     else if ([setHotKey isEqualToString:@"UpcomingSongs"])
309     {
310         if (([combo isEqual:nextTrackCombo] || [combo isEqual:prevTrackCombo] ||
311             [combo isEqual:trackInfoCombo] || [combo isEqual:playPauseCombo]) && 
312             !(([combo modifiers] == -1) && ([combo keyCode] == -1))) {
313             
314             [window setLevel:NSNormalWindowLevel];
315             NSRunAlertPanel(@"Duplicate Key Combo", @"Please choose a unique key combo.", @"OK", nil, nil, nil);
316             [window setLevel:NSStatusWindowLevel];
317             return;
318         }
319         upcomingSongsCombo = [combo copy];
320         [upcomingSongsButton setTitle:string];
321     }
322     [self cancelHotKey:sender];
323 }
324
325 - (IBAction)save:(id)sender
326 {
327     [self apply:nil];
328     [window close];
329     [mt closePreferences];
330 }
331
332 - (IBAction)setCurrentTrackInfo:(id)sender
333 {
334     [self setKeyCombo:trackInfoCombo];
335     [self setHotKey:@"TrackInfo"];
336 }
337
338 - (IBAction)setNextTrack:(id)sender
339 {
340     [self setKeyCombo:nextTrackCombo];
341     [self setHotKey:@"NextTrack"];
342 }
343
344 - (IBAction)setPlayPause:(id)sender
345 {
346     [self setKeyCombo:playPauseCombo];
347     [self setHotKey:@"PlayPause"];
348 }
349
350 - (IBAction)setPreviousTrack:(id)sender
351 {
352     [self setKeyCombo:prevTrackCombo];
353     [self setHotKey:@"PrevTrack"];
354 }
355
356 - (IBAction)setUpcomingSongs:(id)sender
357 {
358     [self setKeyCombo:upcomingSongsCombo];
359     [self setHotKey:@"UpcomingSongs"];
360 }
361
362 - (void)setHotKey:(NSString *)key
363 {
364     setHotKey = key;
365     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyEvent:) name:@"KeyBroadcasterEvent" object:nil];
366     [NSApp beginSheet:keyComboPanel modalForWindow:window modalDelegate:self didEndSelector:nil contextInfo:nil];
367 }
368
369 - (void)keyEvent:(NSNotification *)note
370 {
371     NSDictionary *info = [note userInfo];
372     short keyCode;
373     long modifiers;
374     KeyCombo *newCombo;
375     
376     keyCode = [[info objectForKey:@"KeyCode"] shortValue];
377     modifiers = [[info objectForKey:@"Modifiers"] longValue];
378     
379     newCombo = [[KeyCombo alloc] initWithKeyCode:keyCode andModifiers:modifiers];
380     [self setKeyCombo:newCombo];
381 }
382
383 - (void)setKeyCombo:(KeyCombo *)newCombo
384 {
385     NSString *string;
386     [combo release];
387     combo = [newCombo copy];
388     
389     string = [combo userDisplayRep];
390     if (string == nil) {
391         string = @"";
392     }
393     [keyComboField setStringValue:string];
394 }
395
396 //
397 //
398 // Table View Datasource Methods
399 //
400 //
401
402 - (int)numberOfRowsInTableView:(NSTableView *)aTableView
403 {
404     if (aTableView == menuTableView) {
405         return [myItems count];
406     } else {
407         return [availableItems count];
408     }
409 }
410
411 - (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex
412 {
413     if (aTableView == menuTableView) {
414         if ([[aTableColumn identifier] isEqualToString:@"name"]) {
415             return [myItems objectAtIndex:rowIndex];
416         } else {
417             if ([submenuItems containsObject:[myItems objectAtIndex:rowIndex]])
418             {
419                 return [NSImage imageNamed:@"submenu"];
420             } else {
421                 return nil;
422             }
423         }
424     } else {
425         if ([[aTableColumn identifier] isEqualToString:@"name"]) {
426             return [availableItems objectAtIndex:rowIndex];
427         } else {
428             if ([submenuItems containsObject:[availableItems objectAtIndex:rowIndex]]) {
429                 return [NSImage imageNamed:@"submenu"];
430             } else {
431                 return nil;
432             }
433         }
434     }
435 }
436
437 - (BOOL)tableView:(NSTableView *)tableView writeRows:(NSArray*)rows toPasteboard:(NSPasteboard*)pboard
438 {
439     if (tableView == menuTableView) {
440         [pboard declareTypes:[NSArray arrayWithObjects:@"MenuTableViewPboardType", nil] owner:self];
441         [pboard setString:[[rows objectAtIndex:0] stringValue] forType:@"MenuTableViewPboardType"];
442         return YES;
443     }
444     
445     if (tableView == allTableView) {
446         [pboard declareTypes:[NSArray arrayWithObjects:@"AllTableViewPboardType", nil] owner:self];
447         [pboard setString:[[rows objectAtIndex:0] stringValue] forType:@"AllTableViewPboardType"];
448         return YES;
449     }
450     return NO;
451 }
452
453 - (BOOL)tableView:(NSTableView*)tableView acceptDrop:(id <NSDraggingInfo>)info row:(int)row dropOperation:(NSTableViewDropOperation)operation
454 {
455     NSPasteboard *pb;
456     int dragRow;
457     NSString *dragData, *temp;
458     
459     pb = [info draggingPasteboard];
460     
461     if ([[pb types] containsObject:@"MenuTableViewPboardType"]) {
462         dragData = [pb stringForType:@"MenuTableViewPboardType"];
463         dragRow = [dragData intValue];
464         temp = [myItems objectAtIndex:dragRow];
465         [myItems removeObjectAtIndex:dragRow];
466         
467         if (tableView == menuTableView) {
468             if (row > dragRow) {
469                 [myItems insertObject:temp atIndex:row - 1];
470             } else {
471                 [myItems insertObject:temp atIndex:row];
472             }
473         } else {
474             if (![temp isEqualToString:@"<separator>"]) {
475                 [availableItems addObject:temp];
476             }
477         }
478     } else if ([[pb types] containsObject:@"AllTableViewPboardType"]) {
479         dragData = [pb stringForType:@"AllTableViewPboardType"];
480         dragRow = [dragData intValue];
481         temp = [availableItems objectAtIndex:dragRow];
482         
483         if (![temp isEqualToString:@"<separator>"]) {
484             [availableItems removeObjectAtIndex:dragRow];
485         }
486         [myItems insertObject:temp atIndex:row];
487     }
488     
489     [menuTableView reloadData];
490     [allTableView reloadData];
491     return YES;
492 }
493
494 - (NSDragOperation)tableView:(NSTableView*)tableView validateDrop:(id <NSDraggingInfo>)info proposedRow:(int)row proposedDropOperation:(NSTableViewDropOperation)operation
495 {
496     if (tableView == allTableView) {
497         if ([[[info draggingPasteboard] types] containsObject:@"AllTableViewPboardType"]) {
498             return NSDragOperationNone;
499         }
500         
501         if ([[[info draggingPasteboard] types] containsObject:@"MenuTableViewPboardType"]) {
502             NSString *item = [myItems objectAtIndex:[[[info draggingPasteboard] stringForType:@"MenuTableViewPboardType"] intValue]];
503             if ([item isEqualToString:@"PreferencesÉ"] || [item isEqualToString:@"Quit"]) {
504                 return NSDragOperationNone;
505             }
506         }
507         
508         [tableView setDropRow:-1 dropOperation:NSTableViewDropOn];
509         return NSDragOperationGeneric;
510     }
511     
512     if (operation == NSTableViewDropOn || row == -1)
513     {
514         return NSDragOperationNone;
515     }
516     
517     return NSDragOperationGeneric;
518 }
519
520 @end