1 #import "PreferencesController.h"
2 #import "MainController.h"
3 #import "MenuController.h"
4 #import "NetworkController.h"
5 #import "NetworkObject.h"
6 #import "StatusWindow.h"
7 #import "StatusWindowController.h"
8 #import "CustomMenuTableView.h"
10 #import <Security/Security.h>
12 #import <netinet/in.h>
14 #import <openssl/sha.h>
18 #import <ITKit/ITLoginItem.h>
20 #import <ITKit/ITHotKeyCenter.h>
21 #import <ITKit/ITKeyCombo.h>
22 #import <ITKit/ITKeyComboPanel.h>
23 #import <ITKit/ITWindowPositioning.h>
24 #import <ITKit/ITKeyBroadcaster.h>
26 #import <ITKit/ITTSWBackgroundView.h>
27 #import <ITKit/ITWindowEffect.h>
28 #import <ITKit/ITCutWindowEffect.h>
29 #import <ITKit/ITDissolveWindowEffect.h>
30 #import <ITKit/ITSlideHorizontallyWindowEffect.h>
31 #import <ITKit/ITSlideVerticallyWindowEffect.h>
32 #import <ITKit/ITPivotWindowEffect.h>
35 #define SENDER_STATE (([sender state] == NSOnState) ? YES : NO)
37 /*************************************************************************/
39 #pragma mark PRIVATE INTERFACE
40 /*************************************************************************/
42 @interface PreferencesController (Private)
44 - (void)setupCustomizationTables;
45 - (void)setupMenuItems;
47 - (void)setupScreenPopup;
48 - (void)setStatusWindowEntryEffect:(Class)effectClass;
49 - (void)setStatusWindowExitEffect:(Class)effectClass;
50 - (void)setCustomColor:(NSColor *)color updateWell:(BOOL)update;
51 - (void)repopulateEffectPopupsForVerticalPosition:(ITVerticalWindowPosition)vPos horizontalPosition:(ITHorizontalWindowPosition)hPos;
52 - (BOOL)effect:(Class)effectClass supportsVerticalPosition:(ITVerticalWindowPosition)vPos withHorizontalPosition:(ITHorizontalWindowPosition)hPos;
53 - (IBAction)changeMenus:(id)sender;
57 @implementation PreferencesController
60 /*************************************************************************/
62 #pragma mark STATIC VARIABLES
63 /*************************************************************************/
65 static PreferencesController *prefs = nil;
68 /*************************************************************************/
70 #pragma mark INITIALIZATION METHODS
71 /*************************************************************************/
73 + (PreferencesController *)sharedPrefs;
76 prefs = [[self alloc] init];
83 if ( (self = [super init]) ) {
84 ITDebugLog(@"Preferences initialized.");
85 df = [[NSUserDefaults standardUserDefaults] retain];
87 effectClasses = [[ITWindowEffect effectClasses] retain];
89 hotKeysArray = [[NSArray alloc] initWithObjects:@"PlayPause",
103 @"ToggleShufflability",
113 hotKeyNamesArray = [[NSArray alloc] initWithObjects:@"Play/Pause",
127 @"Toggle Song Included In Shuffle",
128 @"Pop-up status menu",
129 [NSString stringWithUTF8String:"Set Rating: ☆☆☆☆☆"],
130 [NSString stringWithUTF8String:"Set Rating: ★☆☆☆☆"],
131 [NSString stringWithUTF8String:"Set Rating: ★★☆☆☆"],
132 [NSString stringWithUTF8String:"Set Rating: ★★★☆☆"],
133 [NSString stringWithUTF8String:"Set Rating: ★★★★☆"],
134 [NSString stringWithUTF8String:"Set Rating: ★★★★★"],
136 hotKeysDictionary = [[NSMutableDictionary alloc] init];
139 [self setupWindow]; // Load in the nib, and perform any initial setup.
140 [[NSColorPanel sharedColorPanel] setShowsAlpha:YES];
146 /*************************************************************************/
148 #pragma mark ACCESSOR METHODS
149 /*************************************************************************/
156 - (void)setController:(id)object
158 [controller autorelease];
159 controller = [object retain];
163 /*************************************************************************/
165 #pragma mark INSTANCE METHODS
166 /*************************************************************************/
168 - (BOOL)showPasswordPanel
170 [passwordPanel setLevel:NSStatusWindowLevel];
171 [passwordPanelOKButton setTitle:@"Connect"];
172 [passwordPanelTitle setStringValue:@"Password Required"];
173 [passwordPanelMessage setStringValue:[NSString stringWithFormat:@"Please enter a password for access to the MenuTunes player named %@ at %@.", [[[NetworkController sharedController] networkObject] serverName], [[NetworkController sharedController] remoteHost]]];
174 [passwordPanel setLevel:NSStatusWindowLevel];
175 [NSApp activateIgnoringOtherApps:YES];
176 [passwordPanel center];
177 [passwordPanel orderFrontRegardless];
178 [passwordPanel makeKeyWindow];
179 if ([NSApp runModalForWindow:passwordPanel]) {
186 - (BOOL)showInvalidPasswordPanel
188 [passwordPanel setLevel:NSStatusWindowLevel];
189 [passwordPanelOKButton setTitle:@"Retry"];
190 [passwordPanelTitle setStringValue:@"Invalid Password"];
191 [passwordPanelMessage setStringValue:[NSString stringWithFormat:@"The password entered for access to the MenuTunes player named %@ at %@ is invalid. Please provide a new password.", [[[NetworkController sharedController] networkObject] serverName], [[NetworkController sharedController] remoteHost]]];
192 [passwordPanel setLevel:NSStatusWindowLevel];
193 [NSApp activateIgnoringOtherApps:YES];
194 [passwordPanel center];
195 [passwordPanel orderFrontRegardless];
196 [passwordPanel makeKeyWindow];
197 if ([NSApp runModalForWindow:passwordPanel]) {
204 - (IBAction)showPrefsWindow:(id)sender
206 ITDebugLog(@"Showing preferences window.");
207 if (!myItems) { // If menu array does not exist yet, then the window hasn't been setup.
208 ITDebugLog(@"Window doesn't exist, initial setup.");
209 [self setupCustomizationTables]; // Setup the DnD manu config tables.
210 [self setupMenuItems]; // Setup the arrays of menu items
211 [self setupUI]; // Sets up additional UI
212 [window setDelegate:self];
213 [menuTableView reloadData];
214 [hotKeysTableView reloadData];
215 [hotKeysTableView setDoubleAction:@selector(hotKeysTableViewDoubleClicked:)];
217 //Change the launch player checkbox to the proper name
219 [launchPlayerAtLaunchCheckbox setTitle:[NSString stringWithFormat:@"Launch %@ when MenuTunes launches", [[controller currentRemote] playerSimpleName]]]; //This isn't localized...
221 [controller networkError:localException];
225 [self resetRemotePlayerTextFields];
226 [launchAtLoginCheckbox becomeFirstResponder];
227 [NSApp activateIgnoringOtherApps:YES];
228 if (![window isVisible]) {
231 [window orderFrontRegardless];
232 [window makeKeyWindow];
235 - (IBAction)changeGeneralSetting:(id)sender
237 ITDebugLog(@"Changing general setting of tag %i.", [sender tag]);
238 if ( [sender tag] == 1010) {
239 ITSetApplicationLaunchOnLogin([[NSBundle mainBundle] bundlePath], SENDER_STATE);
240 } else if ( [sender tag] == 1020) {
241 [df setBool:SENDER_STATE forKey:@"LaunchPlayerWithMT"];
242 } else if ( [sender tag] == 1030) {
243 [df setInteger:[sender intValue] forKey:@"SongsInAdvance"];
244 if ([[controller currentRemote] playerRunningState] == ITMTRemotePlayerRunning) {
245 [[controller menuController] performSelector:@selector(rebuildSubmenus) withObject:nil afterDelay:0];
247 } else if ( [sender tag] == 1040) {
248 // This will not be executed. Song info always shows the title of the song.
249 // [df setBool:SENDER_STATE forKey:@"showName"];
250 } else if ( [sender tag] == 1050) {
251 [df setBool:SENDER_STATE forKey:@"showArtist"];
252 } else if ( [sender tag] == 1055) {
253 [df setBool:SENDER_STATE forKey:@"showComposer"];
254 } else if ( [sender tag] == 1060) {
255 [df setBool:SENDER_STATE forKey:@"showAlbum"];
256 } else if ( [sender tag] == 1070) {
257 [df setBool:SENDER_STATE forKey:@"showTime"];
258 } else if ( [sender tag] == 1080) {
259 [df setBool:SENDER_STATE forKey:@"showTrackNumber"];
260 } else if ( [sender tag] == 1085) {
261 [df setBool:SENDER_STATE forKey:@"showPlayCount"];
262 } else if ( [sender tag] == 1090) {
263 [df setBool:SENDER_STATE forKey:@"showTrackRating"];
264 } else if ( [sender tag] == 1100) {
265 [df setBool:SENDER_STATE forKey:@"showAlbumArtwork"];
266 } else if ( [sender tag] == 1110) {
267 [df setBool:SENDER_STATE forKey:@"runScripts"];
269 [runScriptsCheckbox setState:NSOnState];
270 [showScriptsButton setEnabled:YES];
272 [showScriptsButton setEnabled:NO];
274 } else if ( [sender tag] == 1120) {
275 mkdir([[NSHomeDirectory() stringByAppendingPathComponent:@"Library/Application Support/MenuTunes/Scripts"] cString], 0744);
276 [[NSWorkspace sharedWorkspace] openFile:[NSHomeDirectory() stringByAppendingPathComponent:@"Library/Application Support/MenuTunes/Scripts"]];
277 } else if ( [sender tag] == 6010) {
278 //Toggle the other Audioscrobbler options
279 [df setBool:SENDER_STATE forKey:@"audioscrobblerEnabled"];
280 [audioscrobblerUseCacheCheckbox setEnabled:SENDER_STATE];
281 [audioscrobblerUserTextField setEnabled:SENDER_STATE];
282 [audioscrobblerPasswordTextField setEnabled:SENDER_STATE];
283 } else if ( [sender tag ] == 6015) {
284 [df setString:[sender stringValue] forKey:@"audioscrobblerUser"];
285 } else if ( [sender tag ] == 6030) {
286 //Set the password in the keychain
287 char *service = "Audioscrobbler";
288 NSString *account = [df stringForKey:@"audioscrobblerUser"];
289 SecKeychainItemRef item;
290 OSStatus status = SecKeychainFindGenericPassword(NULL, strlen(service), service, [account length], [account UTF8String], NULL, NULL, &item);
291 if (status == errSecItemNotFound) {
292 //Create the keychain
293 } else if (status == noErr) {
294 //Modify the current item
295 //SecKeychainItemFreeContent(NULL, item);
297 ITDebugLog(@"Audioscrobbler: Unable to retrieve keychain password.");
299 } else if ( [sender tag] == 6045) {
300 [df setBool:SENDER_STATE forKey:@"audioscrobblerCacheSubmissions"];
305 - (IBAction)changeSharingSetting:(id)sender
307 ITDebugLog(@"Changing sharing setting of tag %i.", [sender tag]);
308 if ( [sender tag] == 5010 ) {
309 BOOL state = SENDER_STATE;
310 [df setBool:state forKey:@"enableSharing"];
311 //Disable/enable the use of shared player options
312 [useSharedMenuTunesCheckbox setEnabled:!state];
313 [passwordTextField setEnabled:state];
314 [nameTextField setEnabled:state];
315 [selectSharedPlayerButton setEnabled:NO];
316 [controller setServerStatus:state]; //Set server status
317 } else if ( [sender tag] == 5015 ) {
318 [df setObject:[sender stringValue] forKey:@"sharedPlayerName"];
319 [[NetworkController sharedController] resetServerName];
320 } else if ( [sender tag] == 5030 ) {
321 //Set the server password
322 const char *instring = [[sender stringValue] UTF8String];
323 const char *password = "p4s5w0rdMT1.2";
324 unsigned char *result;
325 NSData *hashedPass, *passwordStringHash;
326 if ([[sender stringValue] length] == 0) {
327 [df setObject:[NSData data] forKey:@"sharedPlayerPassword"];
330 result = SHA1(instring, strlen(instring), NULL);
331 hashedPass = [NSData dataWithBytes:result length:strlen(result)];
332 result = SHA1(password, strlen(password), NULL);
333 passwordStringHash = [NSData dataWithBytes:result length:strlen(result)];
334 if (![hashedPass isEqualToData:passwordStringHash]) {
335 [df setObject:hashedPass forKey:@"sharedPlayerPassword"];
336 [sender setStringValue:@"p4s5w0rdMT1.2"];
338 } else if ( [sender tag] == 5040 ) {
339 BOOL state = SENDER_STATE;
340 [df setBool:state forKey:@"useSharedPlayer"];
341 //Disable/enable the use of sharing options
342 [shareMenuTunesCheckbox setEnabled:!state];
343 [passwordTextField setEnabled:NO];
344 [nameTextField setEnabled:NO];
345 [selectSharedPlayerButton setEnabled:state];
347 if (state && ([controller connectToServer] == 1)) {
348 [selectedPlayerTextField setStringValue:[[[NetworkController sharedController] networkObject] serverName]];
349 [locationTextField setStringValue:[[NetworkController sharedController] remoteHost]];
351 [selectedPlayerTextField setStringValue:@"No shared player selected."];
352 [locationTextField setStringValue:@"-"];
353 if ([[NetworkController sharedController] isConnectedToServer]) {
354 [controller disconnectFromServer];
358 } else if ( [sender tag] == 5050 ) {
359 //If no player is selected in the table view, turn off OK button.
360 if ([sender clickedRow] == -1 ) {
361 [sharingPanelOKButton setEnabled:NO];
363 [sharingPanelOKButton setEnabled:YES];
365 } else if ( [sender tag] == 5051 ) {
366 [df setObject:[sender stringValue] forKey:@"sharedPlayerHost"];
367 } else if ( [sender tag] == 5060 ) {
368 //Set OK button state
369 if (([selectPlayerBox contentView] == zeroConfView && [sharingTableView selectedRow] == -1) ||
370 ([selectPlayerBox contentView] == manualView && [[hostTextField stringValue] length] == 0)) {
371 [sharingPanelOKButton setEnabled:NO];
373 [sharingPanelOKButton setEnabled:YES];
375 //Show selection sheet
376 [NSApp beginSheet:selectPlayerSheet modalForWindow:window modalDelegate:self didEndSelector:NULL contextInfo:nil];
377 } else if ( [sender tag] == 5100 ) {
379 if ( ([sender indexOfItem:[sender selectedItem]] == 0) && ([selectPlayerBox contentView] != zeroConfView) ) {
380 NSRect frame = [selectPlayerSheet frame];
381 frame.origin.y -= 58;
382 frame.size.height = 273;
383 if ([sharingTableView selectedRow] == -1) {
384 [sharingPanelOKButton setEnabled:NO];
386 [selectPlayerBox setContentView:zeroConfView];
387 [selectPlayerSheet setFrame:frame display:YES animate:YES];
388 } else if ( ([sender indexOfItem:[sender selectedItem]] == 1) && ([selectPlayerBox contentView] != manualView) ){
389 NSRect frame = [selectPlayerSheet frame];
390 frame.origin.y += 58;
391 frame.size.height = 215;
392 if ([[hostTextField stringValue] length] == 0) {
393 [sharingPanelOKButton setEnabled:NO];
395 [sharingPanelOKButton setEnabled:YES];
397 [selectPlayerBox setContentView:manualView];
398 [selectPlayerSheet setFrame:frame display:YES animate:YES];
399 [hostTextField selectText:nil];
401 } else if ( [sender tag] == 5150 ) {
402 const char *instring = [[sender stringValue] UTF8String];
403 unsigned char *result;
404 result = SHA1(instring, strlen(instring), NULL);
405 [df setObject:[NSData dataWithBytes:result length:strlen(result)] forKey:@"connectPassword"];
406 } else if ( [sender tag] == 5110 ) {
408 [NSApp endSheet:selectPlayerSheet];
409 [selectPlayerSheet orderOut:nil];
410 if ([selectPlayerBox contentView] == manualView) {
411 [hostTextField setStringValue:[df stringForKey:@"sharedPlayerHost"]];
414 } else if ( [sender tag] == 5120 ) {
416 [NSApp endSheet:selectPlayerSheet];
417 [selectPlayerSheet orderOut:nil];
419 [self changeSharingSetting:clientPasswordTextField];
421 if ([selectPlayerBox contentView] == manualView) {
422 [df setObject:[hostTextField stringValue] forKey:@"sharedPlayerHost"];
424 if ([sharingTableView selectedRow] > -1) {
425 [df setObject:[NSString stringWithCString:inet_ntoa((*(struct sockaddr_in*)[[[[[[NetworkController sharedController] remoteServices] objectAtIndex:[sharingTableView selectedRow]] addresses] objectAtIndex:0] bytes]).sin_addr)] forKey:@"sharedPlayerHost"];
429 if ([controller connectToServer] == 1) {
430 [useSharedMenuTunesCheckbox setState:NSOnState];
431 [selectedPlayerTextField setStringValue:[[[NetworkController sharedController] networkObject] serverName]];
432 [locationTextField setStringValue:[[NetworkController sharedController] remoteHost]];
434 NSRunAlertPanel(@"Connection error.", @"The MenuTunes server you attempted to connect to was not responding. MenuTunes will revert back to the local player.", @"OK", nil, nil);
436 } else if ( [sender tag] == 6010 ) {
437 //Cancel password entry
438 [passwordPanel orderOut:nil];
439 [NSApp stopModalWithCode:0];
440 } else if ( [sender tag] == 6020 ) {
441 //OK password entry, retry connect
442 const char *instring = [[passwordPanelTextField stringValue] UTF8String];
443 unsigned char *result;
444 result = SHA1(instring, strlen(instring), NULL);
445 [df setObject:[NSData dataWithBytes:result length:strlen(result)] forKey:@"connectPassword"];
446 [passwordPanel orderOut:nil];
447 [NSApp stopModalWithCode:1];
452 - (IBAction)changeStatusWindowSetting:(id)sender
454 StatusWindow *sw = [StatusWindow sharedWindow];
455 ITDebugLog(@"Changing status window setting of tag %i", [sender tag]);
457 if ( [sender tag] == 2010) {
459 BOOL entryEffectValid = YES;
460 BOOL exitEffectValid = YES;
462 [df setInteger:[sender selectedRow] forKey:@"statusWindowVerticalPosition"];
463 [df setInteger:[sender selectedColumn] forKey:@"statusWindowHorizontalPosition"];
464 [sw setVerticalPosition:[sender selectedRow]];
465 [sw setHorizontalPosition:[sender selectedColumn]];
467 // Enable/disable the items in the popups.
468 [self repopulateEffectPopupsForVerticalPosition:[sw verticalPosition]
469 horizontalPosition:[sw horizontalPosition]];
471 // Make sure the effects support the new position.
472 entryEffectValid = ( [self effect:[[sw entryEffect] class]
473 supportsVerticalPosition:[sw verticalPosition]
474 withHorizontalPosition:[sw horizontalPosition]] );
475 exitEffectValid = ( [self effect:[[sw exitEffect] class]
476 supportsVerticalPosition:[sw verticalPosition]
477 withHorizontalPosition:[sw horizontalPosition]] );
479 if ( ! entryEffectValid ) {
480 [appearanceEffectPopup selectItemAtIndex:[[appearanceEffectPopup menu] indexOfItemWithRepresentedObject:NSClassFromString(@"ITCutWindowEffect")]];
481 [self setStatusWindowEntryEffect:NSClassFromString(@"ITCutWindowEffect")];
483 [appearanceEffectPopup selectItemAtIndex:[[appearanceEffectPopup menu] indexOfItemWithRepresentedObject:[[sw entryEffect] class]]];
486 if ( ! exitEffectValid ) {
487 [vanishEffectPopup selectItemAtIndex:[[vanishEffectPopup menu] indexOfItemWithRepresentedObject:NSClassFromString(@"ITDissolveWindowEffect")]];
488 [self setStatusWindowExitEffect:NSClassFromString(@"ITDissolveWindowEffect")];
490 [vanishEffectPopup selectItemAtIndex:[[vanishEffectPopup menu] indexOfItemWithRepresentedObject:[[sw exitEffect] class]]];
493 [(MainController *)controller showCurrentTrackInfo];
495 } else if ( [sender tag] == 2020) {
497 // Update screen selection.
498 [[StatusWindow sharedWindow] setScreen:[[NSScreen screens] objectAtIndex:[sender indexOfSelectedItem]]];
499 [df setInteger:[sender indexOfSelectedItem] forKey:@"statusWindowScreenIndex"];
500 [(MainController *)controller showCurrentTrackInfo];
502 } else if ( [sender tag] == 2030) {
504 [self setStatusWindowEntryEffect:[[sender selectedItem] representedObject]];
505 [(MainController *)controller showCurrentTrackInfo];
507 } else if ( [sender tag] == 2040) {
509 [self setStatusWindowExitEffect:[[sender selectedItem] representedObject]];
510 [(MainController *)controller showCurrentTrackInfo];
512 } else if ( [sender tag] == 2050) {
513 float newTime = ( -([sender floatValue]) );
514 [df setFloat:newTime forKey:@"statusWindowAppearanceSpeed"];
515 [[sw entryEffect] setEffectTime:newTime];
516 } else if ( [sender tag] == 2060) {
517 float newTime = ( -([sender floatValue]) );
518 [df setFloat:newTime forKey:@"statusWindowVanishSpeed"];
519 [[sw exitEffect] setEffectTime:newTime];
520 } else if ( [sender tag] == 2070) {
521 [df setFloat:[sender floatValue] forKey:@"statusWindowVanishDelay"];
522 [sw setExitDelay:[sender floatValue]];
523 } else if ( [sender tag] == 2080) {
524 [df setBool:SENDER_STATE forKey:@"showSongInfoOnChange"];
525 } else if ( [sender tag] == 2090) {
527 int setting = [sender indexOfSelectedItem];
529 if ( setting == 0 ) {
530 [(ITTSWBackgroundView *)[sw contentView] setBackgroundMode:ITTSWBackgroundApple];
531 [backgroundColorWell setEnabled:NO];
532 [backgroundColorPopup setEnabled:NO];
533 } else if ( setting == 1 ) {
534 [(ITTSWBackgroundView *)[sw contentView] setBackgroundMode:ITTSWBackgroundReadable];
535 [backgroundColorWell setEnabled:NO];
536 [backgroundColorPopup setEnabled:NO];
537 } else if ( setting == 2 ) {
538 [(ITTSWBackgroundView *)[sw contentView] setBackgroundMode:ITTSWBackgroundColored];
539 [backgroundColorWell setEnabled:YES];
540 [backgroundColorPopup setEnabled:YES];
543 [df setInteger:setting forKey:@"statusWindowBackgroundMode"];
544 [(MainController *)controller showCurrentTrackInfo];
546 } else if ( [sender tag] == 2091) {
547 [self setCustomColor:[sender color] updateWell:NO];
548 [(MainController *)controller showCurrentTrackInfo];
549 } else if ( [sender tag] == 2092) {
551 int selectedItem = [sender indexOfSelectedItem];
553 if ( selectedItem == 1 ) { // An NSPopUpButton in PullDown mode uses item 0 as its title. Its first selectable item is 1.
554 [self setCustomColor:[NSColor colorWithCalibratedRed:0.92549 green:0.686275 blue:0.0 alpha:1.0] updateWell:YES];
555 } else if ( selectedItem == 2 ) {
556 [self setCustomColor:[NSColor colorWithCalibratedRed:0.380392 green:0.670588 blue:0.0 alpha:1.0] updateWell:YES];
557 } else if ( selectedItem == 3 ) {
558 [self setCustomColor:[NSColor colorWithCalibratedRed:0.443137 green:0.231373 blue:0.619608 alpha:1.0] updateWell:YES];
559 } else if ( selectedItem == 4 ) {
560 [self setCustomColor:[NSColor colorWithCalibratedRed:0.831373 green:0.12549 blue:0.509804 alpha:1.0] updateWell:YES];
561 } else if ( selectedItem == 5 ) {
562 [self setCustomColor:[NSColor colorWithCalibratedRed:0.00784314 green:0.611765 blue:0.662745 alpha:1.0] updateWell:YES];
564 [self setCustomColor:[NSColor colorWithCalibratedWhite:0.15 alpha:0.70] updateWell:YES];
566 [(MainController *)controller showCurrentTrackInfo];
568 } else if ( [sender tag] == 2095) {
569 [df setInteger:[sender indexOfSelectedItem] forKey:@"statusWindowSizing"];
570 [(MainController *)controller showCurrentTrackInfo];
576 - (void)registerDefaults
578 ITDebugLog(@"Registering defaults.");
579 [df setObject:[NSArray arrayWithObjects:
592 nil] forKey:@"menu"];
594 [df setInteger:MT_CURRENT_VERSION forKey:@"appVersion"];
595 [df setInteger:5 forKey:@"SongsInAdvance"];
596 // [df setBool:YES forKey:@"showName"]; // Song info will always show song title.
597 [df setBool:YES forKey:@"showArtist"];
598 [df setBool:YES forKey:@"showAlbumArtwork"];
599 [df setBool:NO forKey:@"showAlbum"];
600 [df setBool:NO forKey:@"showComposer"];
601 [df setBool:NO forKey:@"showTime"];
602 [df setBool:NO forKey:@"showToolTip"];
604 [df setObject:@"ITCutWindowEffect" forKey:@"statusWindowAppearanceEffect"];
605 [df setObject:@"ITDissolveWindowEffect" forKey:@"statusWindowVanishEffect"];
606 [df setFloat:0.8 forKey:@"statusWindowAppearanceSpeed"];
607 [df setFloat:0.8 forKey:@"statusWindowVanishSpeed"];
608 [df setFloat:4.0 forKey:@"statusWindowVanishDelay"];
609 [df setInteger:(int)ITWindowPositionBottom forKey:@"statusWindowVerticalPosition"];
610 [df setInteger:(int)ITWindowPositionLeft forKey:@"statusWindowHorizontalPosition"];
611 [df setInteger:0 forKey:@"statusWindowScreenIndex"];
612 [[StatusWindow sharedWindow] setVerticalPosition:(int)ITWindowPositionBottom];
613 [[StatusWindow sharedWindow] setHorizontalPosition:(int)ITWindowPositionLeft];
614 [df setBool:YES forKey:@"showSongInfoOnChange"];
616 [df setObject:[NSArchiver archivedDataWithRootObject:[NSColor blueColor]] forKey:@"statusWindowBackgroundColor"];
620 if (ITDoesApplicationLaunchOnLogin([[NSBundle mainBundle] bundlePath])) {
621 [[StatusWindowController sharedController] showSetupQueryWindow];
627 [[StatusWindow sharedWindow] setLocked:NO];
628 [[StatusWindow sharedWindow] vanish:self];
629 [[StatusWindow sharedWindow] setIgnoresMouseEvents:YES];
631 ITSetApplicationLaunchOnLogin([[NSBundle mainBundle] bundlePath], YES);
634 - (void)autoLaunchCancel
636 [[StatusWindow sharedWindow] setLocked:NO];
637 [[StatusWindow sharedWindow] vanish:self];
638 [[StatusWindow sharedWindow] setIgnoresMouseEvents:YES];
641 - (void)deletePressedInTableView:(NSTableView *)tableView
643 if (tableView == menuTableView) {
644 int selRow = [tableView selectedRow];
645 ITDebugLog(@"Delete pressed in menu table view.");
647 NSString *object = [myItems objectAtIndex:selRow];
649 if ([object isEqualToString:@"preferences"]) {
654 if (![object isEqualToString:@"separator"])
655 [availableItems addObject:object];
656 ITDebugLog(@"Removing object named %@", object);
657 [myItems removeObjectAtIndex:selRow];
658 [menuTableView reloadData];
659 [allTableView reloadData];
661 [self changeMenus:self];
665 - (void)resetRemotePlayerTextFields
667 if ([[NetworkController sharedController] isConnectedToServer]) {
668 [selectedPlayerTextField setStringValue:[[[NetworkController sharedController] networkObject] serverName]];
669 [locationTextField setStringValue:[[NetworkController sharedController] remoteHost]];
671 [selectedPlayerTextField setStringValue:@"No shared player selected."];
672 [locationTextField setStringValue:@"-"];
676 /*************************************************************************/
678 #pragma mark HOTKEY SUPPORT METHODS
679 /*************************************************************************/
681 - (IBAction)clearHotKey:(id)sender
683 [hotKeysDictionary setObject:[ITKeyCombo clearKeyCombo] forKey:[hotKeysArray objectAtIndex:[hotKeysTableView selectedRow]]];
684 [df setObject:[[ITKeyCombo clearKeyCombo] plistRepresentation] forKey:[hotKeysArray objectAtIndex:[hotKeysTableView selectedRow]]];
685 [controller setupHotKeys];
686 [hotKeysTableView reloadData];
689 - (IBAction)editHotKey:(id)sender
691 ITKeyComboPanel *panel = [ITKeyComboPanel sharedPanel];
692 NSString *keyComboKey = [hotKeysArray objectAtIndex:[hotKeysTableView selectedRow]];
693 ITKeyCombo *keyCombo;
695 ITDebugLog(@"Setting key combo on hot key %@.", keyComboKey);
696 [controller clearHotKeys];
697 [panel setKeyCombo:[hotKeysDictionary objectForKey:[hotKeysArray objectAtIndex:[hotKeysTableView selectedRow]]]];
698 [panel setKeyBindingName:[hotKeyNamesArray objectAtIndex:[hotKeysTableView selectedRow]]];
699 if ([panel runModal] == NSOKButton) {
700 NSEnumerator *keyEnumerator = [[hotKeysDictionary allKeys] objectEnumerator];
702 keyCombo = [panel keyCombo];
704 //Check for duplicate key combo
705 while ( (nextKey = [keyEnumerator nextObject]) ) {
706 if ([[hotKeysDictionary objectForKey:nextKey] isEqual:keyCombo] &&
707 ![keyCombo isEqual:[ITKeyCombo clearKeyCombo]]) {
708 [hotKeysDictionary setObject:[ITKeyCombo clearKeyCombo]
710 [df setObject:[[ITKeyCombo clearKeyCombo] plistRepresentation]
715 [hotKeysDictionary setObject:keyCombo forKey:keyComboKey];
716 [df setObject:[keyCombo plistRepresentation] forKey:keyComboKey];
717 [controller setupHotKeys];
718 [hotKeysTableView reloadData];
719 ITDebugLog(@"Set combo %@ on hot key %@.", keyCombo, keyComboKey);
721 ITDebugLog(@"Hot key setting on hot key %@ cancelled.", keyComboKey);
725 - (void)hotKeysTableViewDoubleClicked:(id)sender
727 if ([sender clickedRow] > -1) {
728 [self editHotKey:sender];
732 /*************************************************************************/
734 #pragma mark PRIVATE METHOD IMPLEMENTATIONS
735 /*************************************************************************/
739 ITDebugLog(@"Loading Preferences.nib.");
740 if (![NSBundle loadNibNamed:@"Preferences" owner:self]) {
741 ITDebugLog(@"Failed to load Preferences.nib.");
747 - (void)setupCustomizationTables
749 NSImageCell *imgCell = [[[NSImageCell alloc] initImageCell:nil] autorelease];
750 ITDebugLog(@"Setting up table views.");
751 // Set the table view cells up
752 [imgCell setImageScaling:NSScaleNone];
753 [[menuTableView tableColumnWithIdentifier:@"submenu"] setDataCell:imgCell];
754 [[allTableView tableColumnWithIdentifier:@"submenu"] setDataCell:imgCell];
756 // Register for drag and drop
757 [menuTableView registerForDraggedTypes:[NSArray arrayWithObjects:
758 @"MenuTableViewPboardType",
759 @"AllTableViewPboardType",
761 [allTableView registerForDraggedTypes:[NSArray arrayWithObjects:
762 @"MenuTableViewPboardType",
763 @"AllTableViewPboardType",
767 - (void)setupMenuItems
769 NSEnumerator *itemEnum;
771 ITDebugLog(@"Setting up table view arrays.");
772 // Set the list of items you can have.
773 availableItems = [[NSMutableArray alloc] initWithObjects:
792 // Get our preferred menu
793 myItems = [[df arrayForKey:@"menu"] mutableCopy];
795 // Delete items in the availableItems array that are already part of the menu
796 itemEnum = [myItems objectEnumerator];
797 while ( (anItem = [itemEnum nextObject]) ) {
798 if (![anItem isEqualToString:@"separator"]) {
799 [availableItems removeObject:anItem];
803 // Items that show should a submenu image
804 submenuItems = [[NSArray alloc] initWithObjects:
816 NSEnumerator *keyArrayEnum;
817 NSString *serverName;
822 [df setInteger:MT_CURRENT_VERSION forKey:@"appVersion"];
824 ITDebugLog(@"Setting up preferences UI.");
825 // Fill in the number of songs in advance to show field
826 [songsInAdvance setIntValue:[df integerForKey:@"SongsInAdvance"]];
828 // Fill hot key array
829 keyArrayEnum = [hotKeysArray objectEnumerator];
831 while ( (anItem = [keyArrayEnum nextObject]) ) {
832 if ([df objectForKey:anItem]) {
833 ITDebugLog(@"Setting up \"%@\" hot key.", anItem);
834 [hotKeysDictionary setObject:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:anItem]] forKey:anItem];
836 [hotKeysDictionary setObject:[ITKeyCombo clearKeyCombo] forKey:anItem];
840 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(setupScreenPopup) name:NSApplicationDidChangeScreenParametersNotification object:nil];
841 [self setupScreenPopup];
843 ITDebugLog(@"Setting up track info checkboxes.");
844 // Check current track info buttons
845 [albumCheckbox setState:[df boolForKey:@"showAlbum"] ? NSOnState : NSOffState];
846 [nameCheckbox setState:NSOnState]; // Song info will ALWAYS show song title.
847 [nameCheckbox setEnabled:NO]; // Song info will ALWAYS show song title.
848 [artistCheckbox setState:[df boolForKey:@"showArtist"] ? NSOnState : NSOffState];
849 [composerCheckbox setState:[df boolForKey:@"showComposer"] ? NSOnState : NSOffState];
850 [trackTimeCheckbox setState:[df boolForKey:@"showTime"] ? NSOnState : NSOffState];
851 [trackNumberCheckbox setState:[df boolForKey:@"showTrackNumber"] ? NSOnState : NSOffState];
852 [playCountCheckbox setState:[df boolForKey:@"showPlayCount"] ? NSOnState : NSOffState];
853 [ratingCheckbox setState:[df boolForKey:@"showTrackRating"] ? NSOnState : NSOffState];
854 [albumArtworkCheckbox setState:[df boolForKey:@"showAlbumArtwork"] ? NSOnState : NSOffState];
856 if ([df boolForKey:@"runScripts"]) {
857 [runScriptsCheckbox setState:NSOnState];
858 [showScriptsButton setEnabled:YES];
860 [showScriptsButton setEnabled:NO];
863 // Set the launch at login checkbox state
864 ITDebugLog(@"Setting launch at login state.");
865 if (ITDoesApplicationLaunchOnLogin([[NSBundle mainBundle] bundlePath])) {
866 [launchAtLoginCheckbox setState:NSOnState];
869 // Set the launch player checkbox state
870 ITDebugLog(@"Setting launch player with MenuTunes state.");
871 [launchPlayerAtLaunchCheckbox setState:[df boolForKey:@"LaunchPlayerWithMT"] ? NSOnState : NSOffState];
873 // Setup the positioning controls
874 [positionMatrix selectCellAtRow:[df integerForKey:@"statusWindowVerticalPosition"]
875 column:[df integerForKey:@"statusWindowHorizontalPosition"]];
877 // Setup effects controls
878 // Populate the effects popups
879 [appearanceEffectPopup setAutoenablesItems:NO];
880 [vanishEffectPopup setAutoenablesItems:NO];
881 [self repopulateEffectPopupsForVerticalPosition:[df integerForKey:@"statusWindowVerticalPosition"]
882 horizontalPosition:[df integerForKey:@"statusWindowHorizontalPosition"]];
884 // Attempt to find the pref'd effect in the list.
885 // If it's not there, use cut/dissolve.
886 if ( [effectClasses containsObject:NSClassFromString([df stringForKey:@"statusWindowAppearanceEffect"])] ) {
887 [appearanceEffectPopup selectItemAtIndex:[effectClasses indexOfObject:NSClassFromString([df stringForKey:@"statusWindowAppearanceEffect"])]];
889 [appearanceEffectPopup selectItemAtIndex:[effectClasses indexOfObject:NSClassFromString(@"ITCutWindowEffect")]];
892 if ( [effectClasses containsObject:NSClassFromString([df stringForKey:@"statusWindowVanishEffect"])] ) {
893 [vanishEffectPopup selectItemAtIndex:[effectClasses indexOfObject:NSClassFromString([df stringForKey:@"statusWindowVanishEffect"])]];
895 [vanishEffectPopup selectItemAtIndex:[effectClasses indexOfObject:NSClassFromString(@"ITCutWindowEffect")]];
898 [appearanceSpeedSlider setFloatValue:( -([df floatForKey:@"statusWindowAppearanceSpeed"]) )];
899 [vanishSpeedSlider setFloatValue:( -([df floatForKey:@"statusWindowVanishSpeed"]) )];
900 [vanishDelaySlider setFloatValue:[df floatForKey:@"statusWindowVanishDelay"]];
902 // Setup General Controls
903 selectedBGStyle = [df integerForKey:@"statusWindowBackgroundMode"];
904 [backgroundStylePopup selectItem:[backgroundStylePopup itemAtIndex:[backgroundStylePopup indexOfItemWithTag:selectedBGStyle]]];
906 if ( selectedBGStyle == ITTSWBackgroundColored ) {
907 [backgroundColorWell setEnabled:YES];
908 [backgroundColorPopup setEnabled:YES];
910 [backgroundColorWell setEnabled:NO];
911 [backgroundColorPopup setEnabled:NO];
914 colorData = [df dataForKey:@"statusWindowBackgroundColor"];
917 [backgroundColorWell setColor:(NSColor *)[NSUnarchiver unarchiveObjectWithData:colorData]];
919 [backgroundColorWell setColor:[NSColor blueColor]];
922 [showOnChangeCheckbox setState:([df boolForKey:@"showSongInfoOnChange"] ? NSOnState : NSOffState)];
924 [windowSizingPopup selectItem:[windowSizingPopup itemAtIndex:[windowSizingPopup indexOfItemWithTag:[df integerForKey:@"statusWindowSizing"]]]];
926 // Setup the sharing controls
927 if ([df boolForKey:@"enableSharing"]) {
928 [shareMenuTunesCheckbox setState:NSOnState];
929 [useSharedMenuTunesCheckbox setEnabled:NO];
930 [selectSharedPlayerButton setEnabled:NO];
931 [passwordTextField setEnabled:YES];
932 [nameTextField setEnabled:YES];
933 } else if ([df boolForKey:@"useSharedPlayer"]) {
934 [useSharedMenuTunesCheckbox setState:NSOnState];
935 [shareMenuTunesCheckbox setEnabled:NO];
936 [selectSharedPlayerButton setEnabled:YES];
939 //Setup the Audioscrobbler controls
940 if ([df boolForKey:@"audioscrobblerEnabled"]) {
941 [audioscrobblerEnabledCheckbox setState:NSOnState];
942 [audioscrobblerUserTextField setEnabled:YES];
943 [audioscrobblerPasswordTextField setEnabled:YES];
944 [audioscrobblerUseCacheCheckbox setEnabled:YES];
946 [audioscrobblerUserTextField setEnabled:NO];
947 [audioscrobblerPasswordTextField setEnabled:NO];
948 [audioscrobblerUseCacheCheckbox setEnabled:NO];
950 [audioscrobblerUserTextField setStringValue:[df stringForKey:@"audioscrobblerUser"]];
951 if ([[audioscrobblerUserTextField stringValue] length] > 0) {
952 [audioscrobblerPasswordTextField setStringValue:@"******"];
955 [[NSNotificationCenter defaultCenter] addObserver:sharingTableView selector:@selector(reloadData) name:@"ITMTFoundNetService" object:nil];
957 serverName = [df stringForKey:@"sharedPlayerName"];
958 if (!serverName || [serverName length] == 0) {
959 serverName = @"MenuTunes Shared Player";
961 [nameTextField setStringValue:serverName];
963 [selectPlayerBox setContentView:zeroConfView];
964 if ([[df dataForKey:@"sharedPlayerPassword"] length] > 0) {
965 [passwordTextField setStringValue:@"p4s5w0rdMT1.2"];
967 [passwordTextField setStringValue:@""];
969 if ([df stringForKey:@"sharedPlayerHost"]) {
970 [hostTextField setStringValue:[df stringForKey:@"sharedPlayerHost"]];
973 if ([[NetworkController sharedController] isConnectedToServer]) {
974 [selectedPlayerTextField setStringValue:[[[NetworkController sharedController] networkObject] serverName]];
975 [locationTextField setStringValue:[[NetworkController sharedController] remoteHost]];
977 [selectedPlayerTextField setStringValue:@"No shared player selected."];
978 [locationTextField setStringValue:@"-"];
982 - (void)setupScreenPopup
984 ITDebugLog(@"Setting up screen popup");
985 NSArray *screens = [NSScreen screens];
986 if ([screens count] > 1) {
987 int i, index = [df integerForKey:@"statusWindowScreenIndex"];
988 [screenPopup setEnabled:YES];
989 for (i = 0; i < [screens count]; i++) {
990 NSScreen *screen = [screens objectAtIndex:i];
991 if (![screen isEqual:[NSScreen mainScreen]]) {
992 [screenPopup addItemWithTitle:[NSString stringWithFormat:@"Screen %i", i + 1]];
995 [screenPopup selectItemAtIndex:index];
996 [[StatusWindow sharedWindow] setScreen:[[NSScreen screens] objectAtIndex:index]];
998 while ([screenPopup numberOfItems] > 1) {
999 [screenPopup removeItemAtIndex:1];
1001 [screenPopup setEnabled:NO];
1002 [[StatusWindow sharedWindow] setScreen:[NSScreen mainScreen]];
1006 - (void)setStatusWindowEntryEffect:(Class)effectClass
1008 StatusWindow *sw = [StatusWindow sharedWindow];
1010 float time = ([df floatForKey:@"statusWindowAppearanceSpeed"] ? [df floatForKey:@"statusWindowAppearanceSpeed"] : 0.8);
1011 [df setObject:NSStringFromClass(effectClass) forKey:@"statusWindowAppearanceEffect"];
1013 [sw setEntryEffect:[[[effectClass alloc] initWithWindow:sw] autorelease]];
1014 [[sw entryEffect] setEffectTime:time];
1017 - (void)setStatusWindowExitEffect:(Class)effectClass
1019 StatusWindow *sw = [StatusWindow sharedWindow];
1021 float time = ([df floatForKey:@"statusWindowVanishSpeed"] ? [df floatForKey:@"statusWindowVanishSpeed"] : 0.8);
1022 [df setObject:NSStringFromClass(effectClass) forKey:@"statusWindowVanishEffect"];
1024 [sw setExitEffect:[[[effectClass alloc] initWithWindow:sw] autorelease]];
1025 [[sw exitEffect] setEffectTime:time];
1028 - (void)setCustomColor:(NSColor *)color updateWell:(BOOL)update
1030 [(ITTSWBackgroundView *)[[StatusWindow sharedWindow] contentView] setBackgroundColor:color];
1031 [df setObject:[NSArchiver archivedDataWithRootObject:color] forKey:@"statusWindowBackgroundColor"];
1034 [backgroundColorWell setColor:color];
1038 - (void)repopulateEffectPopupsForVerticalPosition:(ITVerticalWindowPosition)vPos horizontalPosition:(ITHorizontalWindowPosition)hPos
1040 NSEnumerator *effectEnum = [effectClasses objectEnumerator];
1043 [appearanceEffectPopup removeAllItems];
1044 [vanishEffectPopup removeAllItems];
1046 while ( (anItem = [effectEnum nextObject]) ) {
1047 [appearanceEffectPopup addItemWithTitle:[anItem effectName]];
1048 [vanishEffectPopup addItemWithTitle:[anItem effectName]];
1050 [[appearanceEffectPopup lastItem] setRepresentedObject:anItem];
1051 [[vanishEffectPopup lastItem] setRepresentedObject:anItem];
1053 if ( [self effect:anItem supportsVerticalPosition:vPos withHorizontalPosition:hPos] ) {
1054 [[appearanceEffectPopup lastItem] setEnabled:YES];
1055 [[vanishEffectPopup lastItem] setEnabled:YES];
1057 [[appearanceEffectPopup lastItem] setEnabled:NO];
1058 [[vanishEffectPopup lastItem] setEnabled:NO];
1064 - (BOOL)effect:(Class)effectClass supportsVerticalPosition:(ITVerticalWindowPosition)vPos withHorizontalPosition:(ITHorizontalWindowPosition)hPos
1068 if ( vPos == ITWindowPositionTop ) {
1069 if ( hPos == ITWindowPositionLeft ) {
1070 valid = ( [[[[effectClass supportedPositions] objectForKey:@"Top"] objectForKey:@"Left"] boolValue] ) ;
1071 } else if ( hPos == ITWindowPositionCenter ) {
1072 valid = ( [[[[effectClass supportedPositions] objectForKey:@"Top"] objectForKey:@"Center"] boolValue] );
1073 } else if ( hPos == ITWindowPositionRight ) {
1074 valid = ( [[[[effectClass supportedPositions] objectForKey:@"Top"] objectForKey:@"Right"] boolValue] );
1076 } else if ( vPos == ITWindowPositionMiddle ) {
1077 if ( hPos == ITWindowPositionLeft ) {
1078 valid = ( [[[[effectClass supportedPositions] objectForKey:@"Middle"] objectForKey:@"Left"] boolValue] );
1079 } else if ( hPos == ITWindowPositionCenter ) {
1080 valid = ( [[[[effectClass supportedPositions] objectForKey:@"Middle"] objectForKey:@"Center"] boolValue] );
1081 } else if ( hPos == ITWindowPositionRight ) {
1082 valid = ( [[[[effectClass supportedPositions] objectForKey:@"Middle"] objectForKey:@"Right"] boolValue] );
1084 } else if ( vPos == ITWindowPositionBottom ) {
1085 if ( hPos == ITWindowPositionLeft ) {
1086 valid = ( [[[[effectClass supportedPositions] objectForKey:@"Bottom"] objectForKey:@"Left"] boolValue] );
1087 } else if ( hPos == ITWindowPositionCenter ) {
1088 valid = ( [[[[effectClass supportedPositions] objectForKey:@"Bottom"] objectForKey:@"Center"] boolValue] );
1089 } else if ( hPos == ITWindowPositionRight ) {
1090 valid = ( [[[[effectClass supportedPositions] objectForKey:@"Bottom"] objectForKey:@"Right"] boolValue] );
1097 - (IBAction)changeMenus:(id)sender
1099 ITDebugLog(@"Synchronizing menus");
1100 [df setObject:myItems forKey:@"menu"];
1103 [[controller menuController] performSelector:@selector(rebuildSubmenus) withObject:nil afterDelay:0.0];
1105 //If we're connected over a network, refresh the menu immediately
1106 if ([[NetworkController sharedController] isConnectedToServer]) {
1107 [controller timerUpdate];
1112 /*************************************************************************/
1114 #pragma mark NSWindow DELEGATE METHODS
1115 /*************************************************************************/
1117 - (void)windowWillClose:(NSNotification *)note
1119 [(MainController *)controller closePreferences];
1122 /*************************************************************************/
1124 #pragma mark NSTextField DELEGATE METHODS
1125 /*************************************************************************/
1127 - (void)controlTextDidChange:(NSNotification*)note
1129 if ([note object] == hostTextField) {
1130 if ([[hostTextField stringValue] length] == 0) {
1131 [sharingPanelOKButton setEnabled:NO];
1133 [sharingPanelOKButton setEnabled:YES];
1138 /*************************************************************************/
1140 #pragma mark NSTableView DATASOURCE METHODS
1141 /*************************************************************************/
1143 - (int)numberOfRowsInTableView:(NSTableView *)aTableView
1145 if (aTableView == menuTableView) {
1146 return [myItems count];
1147 } else if (aTableView == allTableView) {
1148 return [availableItems count];
1149 } else if (aTableView == hotKeysTableView) {
1150 return [hotKeysArray count];
1152 return [[[NetworkController sharedController] remoteServices] count];
1156 - (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex
1158 if (aTableView == menuTableView) {
1159 NSString *object = [myItems objectAtIndex:rowIndex];
1160 if ([[aTableColumn identifier] isEqualToString:@"name"]) {
1161 if ([object isEqualToString:@"showPlayer"]) {
1162 NSString *string = nil;
1164 string = [NSString stringWithFormat:@"%@ %@", NSLocalizedString(@"show", @"Show"), [[controller currentRemote] playerSimpleName]];
1166 [controller networkError:localException];
1170 return NSLocalizedString(object, @"ERROR");
1172 if ([submenuItems containsObject:object])
1174 return [NSImage imageNamed:@"submenu"];
1179 } else if (aTableView == allTableView) {
1180 NSString *object = [availableItems objectAtIndex:rowIndex];
1181 if ([[aTableColumn identifier] isEqualToString:@"name"]) {
1182 if ([object isEqualToString:@"showPlayer"]) {
1183 NSString *string = nil;
1185 string = [NSString stringWithFormat:@"%@ %@", NSLocalizedString(@"show", @"Show"), [[controller currentRemote] playerSimpleName]];
1187 [controller networkError:localException];
1191 return NSLocalizedString(object, @"ERROR");
1193 if ([submenuItems containsObject:object]) {
1194 return [NSImage imageNamed:@"submenu"];
1199 } else if (aTableView == hotKeysTableView) {
1200 if ([[aTableColumn identifier] isEqualToString:@"name"]) {
1201 return [hotKeyNamesArray objectAtIndex:rowIndex];
1203 return [[hotKeysDictionary objectForKey:[hotKeysArray objectAtIndex:rowIndex]] description];
1206 return [[[[NetworkController sharedController] remoteServices] objectAtIndex:rowIndex] name];
1210 - (BOOL)tableView:(NSTableView *)tableView writeRows:(NSArray*)rows toPasteboard:(NSPasteboard*)pboard
1212 if (tableView == menuTableView) {
1213 [pboard declareTypes:[NSArray arrayWithObjects:@"MenuTableViewPboardType", nil] owner:self];
1214 [pboard setString:[[rows objectAtIndex:0] stringValue] forType:@"MenuTableViewPboardType"];
1218 if (tableView == allTableView) {
1219 [pboard declareTypes:[NSArray arrayWithObjects:@"AllTableViewPboardType", nil] owner:self];
1220 [pboard setString:[[rows objectAtIndex:0] stringValue] forType:@"AllTableViewPboardType"];
1226 - (BOOL)tableView:(NSTableView*)tableView acceptDrop:(id <NSDraggingInfo>)info row:(int)row dropOperation:(NSTableViewDropOperation)operation
1230 NSString *dragData, *temp;
1232 pb = [info draggingPasteboard];
1234 if ([[pb types] containsObject:@"MenuTableViewPboardType"]) {
1235 dragData = [pb stringForType:@"MenuTableViewPboardType"];
1236 dragRow = [dragData intValue];
1237 temp = [myItems objectAtIndex:dragRow];
1239 if (tableView == menuTableView) {
1240 [myItems insertObject:temp atIndex:row];
1241 if (row > dragRow) {
1242 [myItems removeObjectAtIndex:dragRow];
1244 [myItems removeObjectAtIndex:dragRow + 1];
1246 } else if (tableView == allTableView) {
1247 if (![temp isEqualToString:@"separator"]) {
1248 [availableItems addObject:temp];
1250 [myItems removeObjectAtIndex:dragRow];
1252 } else if ([[pb types] containsObject:@"AllTableViewPboardType"]) {
1253 dragData = [pb stringForType:@"AllTableViewPboardType"];
1254 dragRow = [dragData intValue];
1255 temp = [availableItems objectAtIndex:dragRow];
1257 [myItems insertObject:temp atIndex:row];
1259 if (![temp isEqualToString:@"separator"]) {
1260 [availableItems removeObjectAtIndex:dragRow];
1264 [menuTableView reloadData];
1265 [allTableView reloadData];
1266 [self changeMenus:self];
1270 - (NSDragOperation)tableView:(NSTableView*)tableView validateDrop:(id <NSDraggingInfo>)info proposedRow:(int)row proposedDropOperation:(NSTableViewDropOperation)operation
1272 if (tableView == allTableView) {
1273 if ([[[info draggingPasteboard] types] containsObject:@"AllTableViewPboardType"]) {
1274 return NSDragOperationNone;
1277 if ([[[info draggingPasteboard] types] containsObject:@"MenuTableViewPboardType"]) {
1278 NSString *item = [myItems objectAtIndex:[[[info draggingPasteboard] stringForType:@"MenuTableViewPboardType"] intValue]];
1279 if ([item isEqualToString:@"preferences"] || [item isEqualToString:@"quit"]) {
1280 return NSDragOperationNone;
1284 [tableView setDropRow:-1 dropOperation:NSTableViewDropOn];
1285 return NSDragOperationGeneric;
1288 if (operation == NSTableViewDropOn || row == -1)
1290 return NSDragOperationNone;
1292 return NSDragOperationGeneric;
1296 /*************************************************************************/
1298 #pragma mark DEALLOCATION METHODS
1299 /*************************************************************************/
1303 [[NSNotificationCenter defaultCenter] removeObserver:self];
1304 [hotKeysArray release];
1305 [hotKeysDictionary release];
1306 [effectClasses release];
1307 [menuTableView setDataSource:nil];
1308 [allTableView setDataSource:nil];
1309 [controller release];
1310 [availableItems release];
1311 [submenuItems release];