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 <netinet/in.h>
12 #import <openssl/sha.h>
16 #import <ITKit/ITLoginItem.h>
18 #import <ITKit/ITHotKeyCenter.h>
19 #import <ITKit/ITKeyCombo.h>
20 #import <ITKit/ITKeyComboPanel.h>
21 #import <ITKit/ITWindowPositioning.h>
22 #import <ITKit/ITKeyBroadcaster.h>
24 #import <ITKit/ITTSWBackgroundView.h>
25 #import <ITKit/ITWindowEffect.h>
26 #import <ITKit/ITCutWindowEffect.h>
27 #import <ITKit/ITDissolveWindowEffect.h>
28 #import <ITKit/ITSlideHorizontallyWindowEffect.h>
29 #import <ITKit/ITSlideVerticallyWindowEffect.h>
30 #import <ITKit/ITPivotWindowEffect.h>
33 #define SENDER_STATE (([sender state] == NSOnState) ? YES : NO)
35 /*************************************************************************/
37 #pragma mark PRIVATE INTERFACE
38 /*************************************************************************/
40 @interface PreferencesController (Private)
42 - (void)setupCustomizationTables;
43 - (void)setupMenuItems;
45 - (void)setupScreenPopup;
46 - (void)setStatusWindowEntryEffect:(Class)effectClass;
47 - (void)setStatusWindowExitEffect:(Class)effectClass;
48 - (void)setCustomColor:(NSColor *)color updateWell:(BOOL)update;
49 - (void)repopulateEffectPopupsForVerticalPosition:(ITVerticalWindowPosition)vPos horizontalPosition:(ITHorizontalWindowPosition)hPos;
50 - (BOOL)effect:(Class)effectClass supportsVerticalPosition:(ITVerticalWindowPosition)vPos withHorizontalPosition:(ITHorizontalWindowPosition)hPos;
51 - (IBAction)changeMenus:(id)sender;
55 @implementation PreferencesController
58 /*************************************************************************/
60 #pragma mark STATIC VARIABLES
61 /*************************************************************************/
63 static PreferencesController *prefs = nil;
66 /*************************************************************************/
68 #pragma mark INITIALIZATION METHODS
69 /*************************************************************************/
71 + (PreferencesController *)sharedPrefs;
74 prefs = [[self alloc] init];
81 if ( (self = [super init]) ) {
82 ITDebugLog(@"Preferences initialized.");
83 df = [[NSUserDefaults standardUserDefaults] retain];
85 effectClasses = [[ITWindowEffect effectClasses] retain];
87 hotKeysArray = [[NSArray alloc] initWithObjects:@"PlayPause",
101 @"ToggleShufflability",
111 hotKeyNamesArray = [[NSArray alloc] initWithObjects:@"Play/Pause",
125 @"Toggle Song Included In Shuffle",
126 @"Pop-up status menu",
127 [NSString stringWithUTF8String:"Set Rating: ☆☆☆☆☆"],
128 [NSString stringWithUTF8String:"Set Rating: ★☆☆☆☆"],
129 [NSString stringWithUTF8String:"Set Rating: ★★☆☆☆"],
130 [NSString stringWithUTF8String:"Set Rating: ★★★☆☆"],
131 [NSString stringWithUTF8String:"Set Rating: ★★★★☆"],
132 [NSString stringWithUTF8String:"Set Rating: ★★★★★"],
134 hotKeysDictionary = [[NSMutableDictionary alloc] init];
137 [self setupWindow]; // Load in the nib, and perform any initial setup.
138 [[NSColorPanel sharedColorPanel] setShowsAlpha:YES];
144 /*************************************************************************/
146 #pragma mark ACCESSOR METHODS
147 /*************************************************************************/
154 - (void)setController:(id)object
156 [controller autorelease];
157 controller = [object retain];
161 /*************************************************************************/
163 #pragma mark INSTANCE METHODS
164 /*************************************************************************/
166 - (BOOL)showPasswordPanel
168 [passwordPanel setLevel:NSStatusWindowLevel];
169 [passwordPanelOKButton setTitle:@"Connect"];
170 [passwordPanelTitle setStringValue:@"Password Required"];
171 [passwordPanelMessage setStringValue:[NSString stringWithFormat:@"Please enter a password for access to the MenuTunes player named %@ at %@.", [[[NetworkController sharedController] networkObject] serverName], [[NetworkController sharedController] remoteHost]]];
172 [passwordPanel setLevel:NSStatusWindowLevel];
173 [NSApp activateIgnoringOtherApps:YES];
174 [passwordPanel center];
175 [passwordPanel orderFrontRegardless];
176 [passwordPanel makeKeyWindow];
177 if ([NSApp runModalForWindow:passwordPanel]) {
184 - (BOOL)showInvalidPasswordPanel
186 [passwordPanel setLevel:NSStatusWindowLevel];
187 [passwordPanelOKButton setTitle:@"Retry"];
188 [passwordPanelTitle setStringValue:@"Invalid Password"];
189 [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]]];
190 [passwordPanel setLevel:NSStatusWindowLevel];
191 [NSApp activateIgnoringOtherApps:YES];
192 [passwordPanel center];
193 [passwordPanel orderFrontRegardless];
194 [passwordPanel makeKeyWindow];
195 if ([NSApp runModalForWindow:passwordPanel]) {
202 - (IBAction)showPrefsWindow:(id)sender
204 ITDebugLog(@"Showing preferences window.");
205 if (!myItems) { // If menu array does not exist yet, then the window hasn't been setup.
206 ITDebugLog(@"Window doesn't exist, initial setup.");
207 [self setupCustomizationTables]; // Setup the DnD manu config tables.
208 [self setupMenuItems]; // Setup the arrays of menu items
209 [self setupUI]; // Sets up additional UI
210 [window setDelegate:self];
211 [menuTableView reloadData];
212 [hotKeysTableView reloadData];
213 [hotKeysTableView setDoubleAction:@selector(hotKeysTableViewDoubleClicked:)];
215 //Change the launch player checkbox to the proper name
217 [launchPlayerAtLaunchCheckbox setTitle:[NSString stringWithFormat:@"Launch %@ when MenuTunes launches", [[controller currentRemote] playerSimpleName]]]; //This isn't localized...
219 [controller networkError:localException];
223 [self resetRemotePlayerTextFields];
224 [launchAtLoginCheckbox becomeFirstResponder];
225 [NSApp activateIgnoringOtherApps:YES];
226 if (![window isVisible]) {
229 [window orderFrontRegardless];
230 [window makeKeyWindow];
233 - (IBAction)changeGeneralSetting:(id)sender
235 ITDebugLog(@"Changing general setting of tag %i.", [sender tag]);
236 if ( [sender tag] == 1010) {
237 ITSetApplicationLaunchOnLogin([[NSBundle mainBundle] bundlePath], SENDER_STATE);
238 } else if ( [sender tag] == 1020) {
239 [df setBool:SENDER_STATE forKey:@"LaunchPlayerWithMT"];
240 } else if ( [sender tag] == 1030) {
241 [df setInteger:[sender intValue] forKey:@"SongsInAdvance"];
242 if ([[controller currentRemote] playerRunningState] == ITMTRemotePlayerRunning) {
243 [[controller menuController] performSelector:@selector(rebuildSubmenus) withObject:nil afterDelay:0];
245 } else if ( [sender tag] == 1040) {
246 // This will not be executed. Song info always shows the title of the song.
247 // [df setBool:SENDER_STATE forKey:@"showName"];
248 } else if ( [sender tag] == 1050) {
249 [df setBool:SENDER_STATE forKey:@"showArtist"];
250 } else if ( [sender tag] == 1055) {
251 [df setBool:SENDER_STATE forKey:@"showComposer"];
252 } else if ( [sender tag] == 1060) {
253 [df setBool:SENDER_STATE forKey:@"showAlbum"];
254 } else if ( [sender tag] == 1070) {
255 [df setBool:SENDER_STATE forKey:@"showTime"];
256 } else if ( [sender tag] == 1080) {
257 [df setBool:SENDER_STATE forKey:@"showTrackNumber"];
258 } else if ( [sender tag] == 1085) {
259 [df setBool:SENDER_STATE forKey:@"showPlayCount"];
260 } else if ( [sender tag] == 1090) {
261 [df setBool:SENDER_STATE forKey:@"showTrackRating"];
262 } else if ( [sender tag] == 1100) {
263 [df setBool:SENDER_STATE forKey:@"showAlbumArtwork"];
264 } else if ( [sender tag] == 1110) {
265 [df setBool:SENDER_STATE forKey:@"runScripts"];
267 [runScriptsCheckbox setState:NSOnState];
268 [showScriptsButton setEnabled:YES];
270 [showScriptsButton setEnabled:NO];
272 } else if ( [sender tag] == 1120) {
273 mkdir([[NSHomeDirectory() stringByAppendingPathComponent:@"Library/Application Support/MenuTunes/Scripts"] cString], 0744);
274 [[NSWorkspace sharedWorkspace] openFile:[NSHomeDirectory() stringByAppendingPathComponent:@"Library/Application Support/MenuTunes/Scripts"]];
279 - (IBAction)changeSharingSetting:(id)sender
281 ITDebugLog(@"Changing sharing setting of tag %i.", [sender tag]);
282 if ( [sender tag] == 5010 ) {
283 BOOL state = SENDER_STATE;
284 [df setBool:state forKey:@"enableSharing"];
285 //Disable/enable the use of shared player options
286 [useSharedMenuTunesCheckbox setEnabled:!state];
287 [passwordTextField setEnabled:state];
288 [nameTextField setEnabled:state];
289 [selectSharedPlayerButton setEnabled:NO];
290 [controller setServerStatus:state]; //Set server status
291 } else if ( [sender tag] == 5015 ) {
292 [df setObject:[sender stringValue] forKey:@"sharedPlayerName"];
293 [[NetworkController sharedController] resetServerName];
294 } else if ( [sender tag] == 5030 ) {
295 //Set the server password
296 const char *instring = [[sender stringValue] UTF8String];
297 const char *password = "p4s5w0rdMT1.2";
298 unsigned char *result;
299 NSData *hashedPass, *passwordStringHash;
300 if ([[sender stringValue] length] == 0) {
301 [df setObject:[NSData data] forKey:@"sharedPlayerPassword"];
304 result = SHA1(instring, strlen(instring), NULL);
305 hashedPass = [NSData dataWithBytes:result length:strlen(result)];
306 result = SHA1(password, strlen(password), NULL);
307 passwordStringHash = [NSData dataWithBytes:result length:strlen(result)];
308 if (![hashedPass isEqualToData:passwordStringHash]) {
309 [df setObject:hashedPass forKey:@"sharedPlayerPassword"];
310 [sender setStringValue:@"p4s5w0rdMT1.2"];
312 } else if ( [sender tag] == 5040 ) {
313 BOOL state = SENDER_STATE;
314 [df setBool:state forKey:@"useSharedPlayer"];
315 //Disable/enable the use of sharing options
316 [shareMenuTunesCheckbox setEnabled:!state];
317 [passwordTextField setEnabled:NO];
318 [nameTextField setEnabled:NO];
319 [selectSharedPlayerButton setEnabled:state];
321 if (state && ([controller connectToServer] == 1)) {
322 [selectedPlayerTextField setStringValue:[[[NetworkController sharedController] networkObject] serverName]];
323 [locationTextField setStringValue:[[NetworkController sharedController] remoteHost]];
325 [selectedPlayerTextField setStringValue:@"No shared player selected."];
326 [locationTextField setStringValue:@"-"];
327 if ([[NetworkController sharedController] isConnectedToServer]) {
328 [controller disconnectFromServer];
332 } else if ( [sender tag] == 5050 ) {
333 //If no player is selected in the table view, turn off OK button.
334 if ([sender clickedRow] == -1 ) {
335 [sharingPanelOKButton setEnabled:NO];
337 [sharingPanelOKButton setEnabled:YES];
339 } else if ( [sender tag] == 5051 ) {
340 [df setObject:[sender stringValue] forKey:@"sharedPlayerHost"];
341 } else if ( [sender tag] == 5060 ) {
342 //Set OK button state
343 if (([selectPlayerBox contentView] == zeroConfView && [sharingTableView selectedRow] == -1) ||
344 ([selectPlayerBox contentView] == manualView && [[hostTextField stringValue] length] == 0)) {
345 [sharingPanelOKButton setEnabled:NO];
347 [sharingPanelOKButton setEnabled:YES];
349 //Show selection sheet
350 [NSApp beginSheet:selectPlayerSheet modalForWindow:window modalDelegate:self didEndSelector:NULL contextInfo:nil];
351 } else if ( [sender tag] == 5100 ) {
353 if ( ([sender indexOfItem:[sender selectedItem]] == 0) && ([selectPlayerBox contentView] != zeroConfView) ) {
354 NSRect frame = [selectPlayerSheet frame];
355 frame.origin.y -= 58;
356 frame.size.height = 273;
357 if ([sharingTableView selectedRow] == -1) {
358 [sharingPanelOKButton setEnabled:NO];
360 [selectPlayerBox setContentView:zeroConfView];
361 [selectPlayerSheet setFrame:frame display:YES animate:YES];
362 } else if ( ([sender indexOfItem:[sender selectedItem]] == 1) && ([selectPlayerBox contentView] != manualView) ){
363 NSRect frame = [selectPlayerSheet frame];
364 frame.origin.y += 58;
365 frame.size.height = 215;
366 if ([[hostTextField stringValue] length] == 0) {
367 [sharingPanelOKButton setEnabled:NO];
369 [sharingPanelOKButton setEnabled:YES];
371 [selectPlayerBox setContentView:manualView];
372 [selectPlayerSheet setFrame:frame display:YES animate:YES];
373 [hostTextField selectText:nil];
375 } else if ( [sender tag] == 5150 ) {
376 const char *instring = [[sender stringValue] UTF8String];
377 unsigned char *result;
378 result = SHA1(instring, strlen(instring), NULL);
379 [df setObject:[NSData dataWithBytes:result length:strlen(result)] forKey:@"connectPassword"];
380 } else if ( [sender tag] == 5110 ) {
382 [NSApp endSheet:selectPlayerSheet];
383 [selectPlayerSheet orderOut:nil];
384 if ([selectPlayerBox contentView] == manualView) {
385 [hostTextField setStringValue:[df stringForKey:@"sharedPlayerHost"]];
388 } else if ( [sender tag] == 5120 ) {
390 [NSApp endSheet:selectPlayerSheet];
391 [selectPlayerSheet orderOut:nil];
393 [self changeSharingSetting:clientPasswordTextField];
395 if ([selectPlayerBox contentView] == manualView) {
396 [df setObject:[hostTextField stringValue] forKey:@"sharedPlayerHost"];
398 if ([sharingTableView selectedRow] > -1) {
399 [df setObject:[NSString stringWithCString:inet_ntoa((*(struct sockaddr_in*)[[[[[[NetworkController sharedController] remoteServices] objectAtIndex:[sharingTableView selectedRow]] addresses] objectAtIndex:0] bytes]).sin_addr)] forKey:@"sharedPlayerHost"];
403 if ([controller connectToServer] == 1) {
404 [useSharedMenuTunesCheckbox setState:NSOnState];
405 [selectedPlayerTextField setStringValue:[[[NetworkController sharedController] networkObject] serverName]];
406 [locationTextField setStringValue:[[NetworkController sharedController] remoteHost]];
408 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);
410 } else if ( [sender tag] == 6010 ) {
411 //Cancel password entry
412 [passwordPanel orderOut:nil];
413 [NSApp stopModalWithCode:0];
414 } else if ( [sender tag] == 6020 ) {
415 //OK password entry, retry connect
416 const char *instring = [[passwordPanelTextField stringValue] UTF8String];
417 unsigned char *result;
418 result = SHA1(instring, strlen(instring), NULL);
419 [df setObject:[NSData dataWithBytes:result length:strlen(result)] forKey:@"connectPassword"];
420 [passwordPanel orderOut:nil];
421 [NSApp stopModalWithCode:1];
426 - (IBAction)changeStatusWindowSetting:(id)sender
428 StatusWindow *sw = [StatusWindow sharedWindow];
429 ITDebugLog(@"Changing status window setting of tag %i", [sender tag]);
431 if ( [sender tag] == 2010) {
433 BOOL entryEffectValid = YES;
434 BOOL exitEffectValid = YES;
436 [df setInteger:[sender selectedRow] forKey:@"statusWindowVerticalPosition"];
437 [df setInteger:[sender selectedColumn] forKey:@"statusWindowHorizontalPosition"];
438 [sw setVerticalPosition:[sender selectedRow]];
439 [sw setHorizontalPosition:[sender selectedColumn]];
441 // Enable/disable the items in the popups.
442 [self repopulateEffectPopupsForVerticalPosition:[sw verticalPosition]
443 horizontalPosition:[sw horizontalPosition]];
445 // Make sure the effects support the new position.
446 entryEffectValid = ( [self effect:[[sw entryEffect] class]
447 supportsVerticalPosition:[sw verticalPosition]
448 withHorizontalPosition:[sw horizontalPosition]] );
449 exitEffectValid = ( [self effect:[[sw exitEffect] class]
450 supportsVerticalPosition:[sw verticalPosition]
451 withHorizontalPosition:[sw horizontalPosition]] );
453 if ( ! entryEffectValid ) {
454 [appearanceEffectPopup selectItemAtIndex:[[appearanceEffectPopup menu] indexOfItemWithRepresentedObject:NSClassFromString(@"ITCutWindowEffect")]];
455 [self setStatusWindowEntryEffect:NSClassFromString(@"ITCutWindowEffect")];
457 [appearanceEffectPopup selectItemAtIndex:[[appearanceEffectPopup menu] indexOfItemWithRepresentedObject:[[sw entryEffect] class]]];
460 if ( ! exitEffectValid ) {
461 [vanishEffectPopup selectItemAtIndex:[[vanishEffectPopup menu] indexOfItemWithRepresentedObject:NSClassFromString(@"ITDissolveWindowEffect")]];
462 [self setStatusWindowExitEffect:NSClassFromString(@"ITDissolveWindowEffect")];
464 [vanishEffectPopup selectItemAtIndex:[[vanishEffectPopup menu] indexOfItemWithRepresentedObject:[[sw exitEffect] class]]];
467 [(MainController *)controller showCurrentTrackInfo];
469 } else if ( [sender tag] == 2020) {
471 // Update screen selection.
472 [[StatusWindow sharedWindow] setScreen:[[NSScreen screens] objectAtIndex:[sender indexOfSelectedItem]]];
473 [df setInteger:[sender indexOfSelectedItem] forKey:@"statusWindowScreenIndex"];
474 [(MainController *)controller showCurrentTrackInfo];
476 } else if ( [sender tag] == 2030) {
478 [self setStatusWindowEntryEffect:[[sender selectedItem] representedObject]];
479 [(MainController *)controller showCurrentTrackInfo];
481 } else if ( [sender tag] == 2040) {
483 [self setStatusWindowExitEffect:[[sender selectedItem] representedObject]];
484 [(MainController *)controller showCurrentTrackInfo];
486 } else if ( [sender tag] == 2050) {
487 float newTime = ( -([sender floatValue]) );
488 [df setFloat:newTime forKey:@"statusWindowAppearanceSpeed"];
489 [[sw entryEffect] setEffectTime:newTime];
490 } else if ( [sender tag] == 2060) {
491 float newTime = ( -([sender floatValue]) );
492 [df setFloat:newTime forKey:@"statusWindowVanishSpeed"];
493 [[sw exitEffect] setEffectTime:newTime];
494 } else if ( [sender tag] == 2070) {
495 [df setFloat:[sender floatValue] forKey:@"statusWindowVanishDelay"];
496 [sw setExitDelay:[sender floatValue]];
497 } else if ( [sender tag] == 2080) {
498 [df setBool:SENDER_STATE forKey:@"showSongInfoOnChange"];
499 } else if ( [sender tag] == 2090) {
501 int setting = [sender indexOfSelectedItem];
503 if ( setting == 0 ) {
504 [(ITTSWBackgroundView *)[sw contentView] setBackgroundMode:ITTSWBackgroundApple];
505 [backgroundColorWell setEnabled:NO];
506 [backgroundColorPopup setEnabled:NO];
507 } else if ( setting == 1 ) {
508 [(ITTSWBackgroundView *)[sw contentView] setBackgroundMode:ITTSWBackgroundReadable];
509 [backgroundColorWell setEnabled:NO];
510 [backgroundColorPopup setEnabled:NO];
511 } else if ( setting == 2 ) {
512 [(ITTSWBackgroundView *)[sw contentView] setBackgroundMode:ITTSWBackgroundColored];
513 [backgroundColorWell setEnabled:YES];
514 [backgroundColorPopup setEnabled:YES];
517 [df setInteger:setting forKey:@"statusWindowBackgroundMode"];
518 [(MainController *)controller showCurrentTrackInfo];
520 } else if ( [sender tag] == 2091) {
521 [self setCustomColor:[sender color] updateWell:NO];
522 [(MainController *)controller showCurrentTrackInfo];
523 } else if ( [sender tag] == 2092) {
525 int selectedItem = [sender indexOfSelectedItem];
527 if ( selectedItem == 1 ) { // An NSPopUpButton in PullDown mode uses item 0 as its title. Its first selectable item is 1.
528 [self setCustomColor:[NSColor colorWithCalibratedRed:0.92549 green:0.686275 blue:0.0 alpha:1.0] updateWell:YES];
529 } else if ( selectedItem == 2 ) {
530 [self setCustomColor:[NSColor colorWithCalibratedRed:0.380392 green:0.670588 blue:0.0 alpha:1.0] updateWell:YES];
531 } else if ( selectedItem == 3 ) {
532 [self setCustomColor:[NSColor colorWithCalibratedRed:0.443137 green:0.231373 blue:0.619608 alpha:1.0] updateWell:YES];
533 } else if ( selectedItem == 4 ) {
534 [self setCustomColor:[NSColor colorWithCalibratedRed:0.831373 green:0.12549 blue:0.509804 alpha:1.0] updateWell:YES];
535 } else if ( selectedItem == 5 ) {
536 [self setCustomColor:[NSColor colorWithCalibratedRed:0.00784314 green:0.611765 blue:0.662745 alpha:1.0] updateWell:YES];
538 [self setCustomColor:[NSColor colorWithCalibratedWhite:0.15 alpha:0.70] updateWell:YES];
540 [(MainController *)controller showCurrentTrackInfo];
542 } else if ( [sender tag] == 2095) {
543 [df setInteger:[sender indexOfSelectedItem] forKey:@"statusWindowSizing"];
544 [(MainController *)controller showCurrentTrackInfo];
550 - (void)registerDefaults
552 ITDebugLog(@"Registering defaults.");
553 [df setObject:[NSArray arrayWithObjects:
566 nil] forKey:@"menu"];
568 [df setInteger:MT_CURRENT_VERSION forKey:@"appVersion"];
569 [df setInteger:5 forKey:@"SongsInAdvance"];
570 // [df setBool:YES forKey:@"showName"]; // Song info will always show song title.
571 [df setBool:YES forKey:@"showArtist"];
572 [df setBool:YES forKey:@"showAlbumArtwork"];
573 [df setBool:NO forKey:@"showAlbum"];
574 [df setBool:NO forKey:@"showComposer"];
575 [df setBool:NO forKey:@"showTime"];
576 [df setBool:NO forKey:@"showToolTip"];
578 [df setObject:@"ITCutWindowEffect" forKey:@"statusWindowAppearanceEffect"];
579 [df setObject:@"ITDissolveWindowEffect" forKey:@"statusWindowVanishEffect"];
580 [df setFloat:0.8 forKey:@"statusWindowAppearanceSpeed"];
581 [df setFloat:0.8 forKey:@"statusWindowVanishSpeed"];
582 [df setFloat:4.0 forKey:@"statusWindowVanishDelay"];
583 [df setInteger:(int)ITWindowPositionBottom forKey:@"statusWindowVerticalPosition"];
584 [df setInteger:(int)ITWindowPositionLeft forKey:@"statusWindowHorizontalPosition"];
585 [df setInteger:0 forKey:@"statusWindowScreenIndex"];
586 [[StatusWindow sharedWindow] setVerticalPosition:(int)ITWindowPositionBottom];
587 [[StatusWindow sharedWindow] setHorizontalPosition:(int)ITWindowPositionLeft];
588 [df setBool:YES forKey:@"showSongInfoOnChange"];
590 [df setObject:[NSArchiver archivedDataWithRootObject:[NSColor blueColor]] forKey:@"statusWindowBackgroundColor"];
594 if (ITDoesApplicationLaunchOnLogin([[NSBundle mainBundle] bundlePath])) {
595 [[StatusWindowController sharedController] showSetupQueryWindow];
601 [[StatusWindow sharedWindow] setLocked:NO];
602 [[StatusWindow sharedWindow] vanish:self];
603 [[StatusWindow sharedWindow] setIgnoresMouseEvents:YES];
605 ITSetApplicationLaunchOnLogin([[NSBundle mainBundle] bundlePath], YES);
608 - (void)autoLaunchCancel
610 [[StatusWindow sharedWindow] setLocked:NO];
611 [[StatusWindow sharedWindow] vanish:self];
612 [[StatusWindow sharedWindow] setIgnoresMouseEvents:YES];
615 - (void)deletePressedInTableView:(NSTableView *)tableView
617 if (tableView == menuTableView) {
618 int selRow = [tableView selectedRow];
619 ITDebugLog(@"Delete pressed in menu table view.");
621 NSString *object = [myItems objectAtIndex:selRow];
623 if ([object isEqualToString:@"preferences"]) {
628 if (![object isEqualToString:@"separator"])
629 [availableItems addObject:object];
630 ITDebugLog(@"Removing object named %@", object);
631 [myItems removeObjectAtIndex:selRow];
632 [menuTableView reloadData];
633 [allTableView reloadData];
635 [self changeMenus:self];
639 - (void)resetRemotePlayerTextFields
641 if ([[NetworkController sharedController] isConnectedToServer]) {
642 [selectedPlayerTextField setStringValue:[[[NetworkController sharedController] networkObject] serverName]];
643 [locationTextField setStringValue:[[NetworkController sharedController] remoteHost]];
645 [selectedPlayerTextField setStringValue:@"No shared player selected."];
646 [locationTextField setStringValue:@"-"];
650 /*************************************************************************/
652 #pragma mark HOTKEY SUPPORT METHODS
653 /*************************************************************************/
655 - (IBAction)clearHotKey:(id)sender
657 [hotKeysDictionary setObject:[ITKeyCombo clearKeyCombo] forKey:[hotKeysArray objectAtIndex:[hotKeysTableView selectedRow]]];
658 [df setObject:[[ITKeyCombo clearKeyCombo] plistRepresentation] forKey:[hotKeysArray objectAtIndex:[hotKeysTableView selectedRow]]];
659 [controller setupHotKeys];
660 [hotKeysTableView reloadData];
663 - (IBAction)editHotKey:(id)sender
665 ITKeyComboPanel *panel = [ITKeyComboPanel sharedPanel];
666 NSString *keyComboKey = [hotKeysArray objectAtIndex:[hotKeysTableView selectedRow]];
667 ITKeyCombo *keyCombo;
669 ITDebugLog(@"Setting key combo on hot key %@.", keyComboKey);
670 [controller clearHotKeys];
671 [panel setKeyCombo:[hotKeysDictionary objectForKey:[hotKeysArray objectAtIndex:[hotKeysTableView selectedRow]]]];
672 [panel setKeyBindingName:[hotKeyNamesArray objectAtIndex:[hotKeysTableView selectedRow]]];
673 if ([panel runModal] == NSOKButton) {
674 NSEnumerator *keyEnumerator = [[hotKeysDictionary allKeys] objectEnumerator];
676 keyCombo = [panel keyCombo];
678 //Check for duplicate key combo
679 while ( (nextKey = [keyEnumerator nextObject]) ) {
680 if ([[hotKeysDictionary objectForKey:nextKey] isEqual:keyCombo] &&
681 ![keyCombo isEqual:[ITKeyCombo clearKeyCombo]]) {
682 [hotKeysDictionary setObject:[ITKeyCombo clearKeyCombo]
684 [df setObject:[[ITKeyCombo clearKeyCombo] plistRepresentation]
689 [hotKeysDictionary setObject:keyCombo forKey:keyComboKey];
690 [df setObject:[keyCombo plistRepresentation] forKey:keyComboKey];
691 [controller setupHotKeys];
692 [hotKeysTableView reloadData];
693 ITDebugLog(@"Set combo %@ on hot key %@.", keyCombo, keyComboKey);
695 ITDebugLog(@"Hot key setting on hot key %@ cancelled.", keyComboKey);
699 - (void)hotKeysTableViewDoubleClicked:(id)sender
701 if ([sender clickedRow] > -1) {
702 [self editHotKey:sender];
706 /*************************************************************************/
708 #pragma mark PRIVATE METHOD IMPLEMENTATIONS
709 /*************************************************************************/
713 ITDebugLog(@"Loading Preferences.nib.");
714 if (![NSBundle loadNibNamed:@"Preferences" owner:self]) {
715 ITDebugLog(@"Failed to load Preferences.nib.");
721 - (void)setupCustomizationTables
723 NSImageCell *imgCell = [[[NSImageCell alloc] initImageCell:nil] autorelease];
724 ITDebugLog(@"Setting up table views.");
725 // Set the table view cells up
726 [imgCell setImageScaling:NSScaleNone];
727 [[menuTableView tableColumnWithIdentifier:@"submenu"] setDataCell:imgCell];
728 [[allTableView tableColumnWithIdentifier:@"submenu"] setDataCell:imgCell];
730 // Register for drag and drop
731 [menuTableView registerForDraggedTypes:[NSArray arrayWithObjects:
732 @"MenuTableViewPboardType",
733 @"AllTableViewPboardType",
735 [allTableView registerForDraggedTypes:[NSArray arrayWithObjects:
736 @"MenuTableViewPboardType",
737 @"AllTableViewPboardType",
741 - (void)setupMenuItems
743 NSEnumerator *itemEnum;
745 ITDebugLog(@"Setting up table view arrays.");
746 // Set the list of items you can have.
747 availableItems = [[NSMutableArray alloc] initWithObjects:
766 // Get our preferred menu
767 myItems = [[df arrayForKey:@"menu"] mutableCopy];
769 // Delete items in the availableItems array that are already part of the menu
770 itemEnum = [myItems objectEnumerator];
771 while ( (anItem = [itemEnum nextObject]) ) {
772 if (![anItem isEqualToString:@"separator"]) {
773 [availableItems removeObject:anItem];
777 // Items that show should a submenu image
778 submenuItems = [[NSArray alloc] initWithObjects:
790 NSEnumerator *keyArrayEnum;
791 NSString *serverName;
796 [df setInteger:MT_CURRENT_VERSION forKey:@"appVersion"];
798 ITDebugLog(@"Setting up preferences UI.");
799 // Fill in the number of songs in advance to show field
800 [songsInAdvance setIntValue:[df integerForKey:@"SongsInAdvance"]];
802 // Fill hot key array
803 keyArrayEnum = [hotKeysArray objectEnumerator];
805 while ( (anItem = [keyArrayEnum nextObject]) ) {
806 if ([df objectForKey:anItem]) {
807 ITDebugLog(@"Setting up \"%@\" hot key.", anItem);
808 [hotKeysDictionary setObject:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:anItem]] forKey:anItem];
810 [hotKeysDictionary setObject:[ITKeyCombo clearKeyCombo] forKey:anItem];
814 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(setupScreenPopup) name:NSApplicationDidChangeScreenParametersNotification object:nil];
815 [self setupScreenPopup];
817 ITDebugLog(@"Setting up track info checkboxes.");
818 // Check current track info buttons
819 [albumCheckbox setState:[df boolForKey:@"showAlbum"] ? NSOnState : NSOffState];
820 [nameCheckbox setState:NSOnState]; // Song info will ALWAYS show song title.
821 [nameCheckbox setEnabled:NO]; // Song info will ALWAYS show song title.
822 [artistCheckbox setState:[df boolForKey:@"showArtist"] ? NSOnState : NSOffState];
823 [composerCheckbox setState:[df boolForKey:@"showComposer"] ? NSOnState : NSOffState];
824 [trackTimeCheckbox setState:[df boolForKey:@"showTime"] ? NSOnState : NSOffState];
825 [trackNumberCheckbox setState:[df boolForKey:@"showTrackNumber"] ? NSOnState : NSOffState];
826 [playCountCheckbox setState:[df boolForKey:@"showPlayCount"] ? NSOnState : NSOffState];
827 [ratingCheckbox setState:[df boolForKey:@"showTrackRating"] ? NSOnState : NSOffState];
828 [albumArtworkCheckbox setState:[df boolForKey:@"showAlbumArtwork"] ? NSOnState : NSOffState];
830 if ([df boolForKey:@"runScripts"]) {
831 [runScriptsCheckbox setState:NSOnState];
832 [showScriptsButton setEnabled:YES];
834 [showScriptsButton setEnabled:NO];
837 // Set the launch at login checkbox state
838 ITDebugLog(@"Setting launch at login state.");
839 if (ITDoesApplicationLaunchOnLogin([[NSBundle mainBundle] bundlePath])) {
840 [launchAtLoginCheckbox setState:NSOnState];
843 // Set the launch player checkbox state
844 ITDebugLog(@"Setting launch player with MenuTunes state.");
845 [launchPlayerAtLaunchCheckbox setState:[df boolForKey:@"LaunchPlayerWithMT"] ? NSOnState : NSOffState];
847 // Setup the positioning controls
848 [positionMatrix selectCellAtRow:[df integerForKey:@"statusWindowVerticalPosition"]
849 column:[df integerForKey:@"statusWindowHorizontalPosition"]];
851 // Setup effects controls
852 // Populate the effects popups
853 [appearanceEffectPopup setAutoenablesItems:NO];
854 [vanishEffectPopup setAutoenablesItems:NO];
855 [self repopulateEffectPopupsForVerticalPosition:[df integerForKey:@"statusWindowVerticalPosition"]
856 horizontalPosition:[df integerForKey:@"statusWindowHorizontalPosition"]];
858 // Attempt to find the pref'd effect in the list.
859 // If it's not there, use cut/dissolve.
860 if ( [effectClasses containsObject:NSClassFromString([df stringForKey:@"statusWindowAppearanceEffect"])] ) {
861 [appearanceEffectPopup selectItemAtIndex:[effectClasses indexOfObject:NSClassFromString([df stringForKey:@"statusWindowAppearanceEffect"])]];
863 [appearanceEffectPopup selectItemAtIndex:[effectClasses indexOfObject:NSClassFromString(@"ITCutWindowEffect")]];
866 if ( [effectClasses containsObject:NSClassFromString([df stringForKey:@"statusWindowVanishEffect"])] ) {
867 [vanishEffectPopup selectItemAtIndex:[effectClasses indexOfObject:NSClassFromString([df stringForKey:@"statusWindowVanishEffect"])]];
869 [vanishEffectPopup selectItemAtIndex:[effectClasses indexOfObject:NSClassFromString(@"ITCutWindowEffect")]];
872 [appearanceSpeedSlider setFloatValue:( -([df floatForKey:@"statusWindowAppearanceSpeed"]) )];
873 [vanishSpeedSlider setFloatValue:( -([df floatForKey:@"statusWindowVanishSpeed"]) )];
874 [vanishDelaySlider setFloatValue:[df floatForKey:@"statusWindowVanishDelay"]];
876 // Setup General Controls
877 selectedBGStyle = [df integerForKey:@"statusWindowBackgroundMode"];
878 [backgroundStylePopup selectItem:[backgroundStylePopup itemAtIndex:[backgroundStylePopup indexOfItemWithTag:selectedBGStyle]]];
880 if ( selectedBGStyle == ITTSWBackgroundColored ) {
881 [backgroundColorWell setEnabled:YES];
882 [backgroundColorPopup setEnabled:YES];
884 [backgroundColorWell setEnabled:NO];
885 [backgroundColorPopup setEnabled:NO];
888 colorData = [df dataForKey:@"statusWindowBackgroundColor"];
891 [backgroundColorWell setColor:(NSColor *)[NSUnarchiver unarchiveObjectWithData:colorData]];
893 [backgroundColorWell setColor:[NSColor blueColor]];
896 [showOnChangeCheckbox setState:([df boolForKey:@"showSongInfoOnChange"] ? NSOnState : NSOffState)];
898 [windowSizingPopup selectItem:[windowSizingPopup itemAtIndex:[windowSizingPopup indexOfItemWithTag:[df integerForKey:@"statusWindowSizing"]]]];
900 // Setup the sharing controls
901 if ([df boolForKey:@"enableSharing"]) {
902 [shareMenuTunesCheckbox setState:NSOnState];
903 [useSharedMenuTunesCheckbox setEnabled:NO];
904 [selectSharedPlayerButton setEnabled:NO];
905 [passwordTextField setEnabled:YES];
906 [nameTextField setEnabled:YES];
907 } else if ([df boolForKey:@"useSharedPlayer"]) {
908 [useSharedMenuTunesCheckbox setState:NSOnState];
909 [shareMenuTunesCheckbox setEnabled:NO];
910 [selectSharedPlayerButton setEnabled:YES];
913 [[NSNotificationCenter defaultCenter] addObserver:sharingTableView selector:@selector(reloadData) name:@"ITMTFoundNetService" object:nil];
915 serverName = [df stringForKey:@"sharedPlayerName"];
916 if (!serverName || [serverName length] == 0) {
917 serverName = @"MenuTunes Shared Player";
919 [nameTextField setStringValue:serverName];
921 [selectPlayerBox setContentView:zeroConfView];
922 if ([[df dataForKey:@"sharedPlayerPassword"] length] > 0) {
923 [passwordTextField setStringValue:@"p4s5w0rdMT1.2"];
925 [passwordTextField setStringValue:@""];
927 if ([df stringForKey:@"sharedPlayerHost"]) {
928 [hostTextField setStringValue:[df stringForKey:@"sharedPlayerHost"]];
931 if ([[NetworkController sharedController] isConnectedToServer]) {
932 [selectedPlayerTextField setStringValue:[[[NetworkController sharedController] networkObject] serverName]];
933 [locationTextField setStringValue:[[NetworkController sharedController] remoteHost]];
935 [selectedPlayerTextField setStringValue:@"No shared player selected."];
936 [locationTextField setStringValue:@"-"];
940 - (void)setupScreenPopup
942 ITDebugLog(@"Setting up screen popup");
943 NSArray *screens = [NSScreen screens];
944 if ([screens count] > 1) {
945 int i, index = [df integerForKey:@"statusWindowScreenIndex"];
946 [screenPopup setEnabled:YES];
947 for (i = 0; i < [screens count]; i++) {
948 NSScreen *screen = [screens objectAtIndex:i];
949 if (![screen isEqual:[NSScreen mainScreen]]) {
950 [screenPopup addItemWithTitle:[NSString stringWithFormat:@"Screen %i", i + 1]];
953 [screenPopup selectItemAtIndex:index];
954 [[StatusWindow sharedWindow] setScreen:[[NSScreen screens] objectAtIndex:index]];
956 while ([screenPopup numberOfItems] > 1) {
957 [screenPopup removeItemAtIndex:1];
959 [screenPopup setEnabled:NO];
960 [[StatusWindow sharedWindow] setScreen:[NSScreen mainScreen]];
964 - (void)setStatusWindowEntryEffect:(Class)effectClass
966 StatusWindow *sw = [StatusWindow sharedWindow];
968 float time = ([df floatForKey:@"statusWindowAppearanceSpeed"] ? [df floatForKey:@"statusWindowAppearanceSpeed"] : 0.8);
969 [df setObject:NSStringFromClass(effectClass) forKey:@"statusWindowAppearanceEffect"];
971 [sw setEntryEffect:[[[effectClass alloc] initWithWindow:sw] autorelease]];
972 [[sw entryEffect] setEffectTime:time];
975 - (void)setStatusWindowExitEffect:(Class)effectClass
977 StatusWindow *sw = [StatusWindow sharedWindow];
979 float time = ([df floatForKey:@"statusWindowVanishSpeed"] ? [df floatForKey:@"statusWindowVanishSpeed"] : 0.8);
980 [df setObject:NSStringFromClass(effectClass) forKey:@"statusWindowVanishEffect"];
982 [sw setExitEffect:[[[effectClass alloc] initWithWindow:sw] autorelease]];
983 [[sw exitEffect] setEffectTime:time];
986 - (void)setCustomColor:(NSColor *)color updateWell:(BOOL)update
988 [(ITTSWBackgroundView *)[[StatusWindow sharedWindow] contentView] setBackgroundColor:color];
989 [df setObject:[NSArchiver archivedDataWithRootObject:color] forKey:@"statusWindowBackgroundColor"];
992 [backgroundColorWell setColor:color];
996 - (void)repopulateEffectPopupsForVerticalPosition:(ITVerticalWindowPosition)vPos horizontalPosition:(ITHorizontalWindowPosition)hPos
998 NSEnumerator *effectEnum = [effectClasses objectEnumerator];
1001 [appearanceEffectPopup removeAllItems];
1002 [vanishEffectPopup removeAllItems];
1004 while ( (anItem = [effectEnum nextObject]) ) {
1005 [appearanceEffectPopup addItemWithTitle:[anItem effectName]];
1006 [vanishEffectPopup addItemWithTitle:[anItem effectName]];
1008 [[appearanceEffectPopup lastItem] setRepresentedObject:anItem];
1009 [[vanishEffectPopup lastItem] setRepresentedObject:anItem];
1011 if ( [self effect:anItem supportsVerticalPosition:vPos withHorizontalPosition:hPos] ) {
1012 [[appearanceEffectPopup lastItem] setEnabled:YES];
1013 [[vanishEffectPopup lastItem] setEnabled:YES];
1015 [[appearanceEffectPopup lastItem] setEnabled:NO];
1016 [[vanishEffectPopup lastItem] setEnabled:NO];
1022 - (BOOL)effect:(Class)effectClass supportsVerticalPosition:(ITVerticalWindowPosition)vPos withHorizontalPosition:(ITHorizontalWindowPosition)hPos
1026 if ( vPos == ITWindowPositionTop ) {
1027 if ( hPos == ITWindowPositionLeft ) {
1028 valid = ( [[[[effectClass supportedPositions] objectForKey:@"Top"] objectForKey:@"Left"] boolValue] ) ;
1029 } else if ( hPos == ITWindowPositionCenter ) {
1030 valid = ( [[[[effectClass supportedPositions] objectForKey:@"Top"] objectForKey:@"Center"] boolValue] );
1031 } else if ( hPos == ITWindowPositionRight ) {
1032 valid = ( [[[[effectClass supportedPositions] objectForKey:@"Top"] objectForKey:@"Right"] boolValue] );
1034 } else if ( vPos == ITWindowPositionMiddle ) {
1035 if ( hPos == ITWindowPositionLeft ) {
1036 valid = ( [[[[effectClass supportedPositions] objectForKey:@"Middle"] objectForKey:@"Left"] boolValue] );
1037 } else if ( hPos == ITWindowPositionCenter ) {
1038 valid = ( [[[[effectClass supportedPositions] objectForKey:@"Middle"] objectForKey:@"Center"] boolValue] );
1039 } else if ( hPos == ITWindowPositionRight ) {
1040 valid = ( [[[[effectClass supportedPositions] objectForKey:@"Middle"] objectForKey:@"Right"] boolValue] );
1042 } else if ( vPos == ITWindowPositionBottom ) {
1043 if ( hPos == ITWindowPositionLeft ) {
1044 valid = ( [[[[effectClass supportedPositions] objectForKey:@"Bottom"] objectForKey:@"Left"] boolValue] );
1045 } else if ( hPos == ITWindowPositionCenter ) {
1046 valid = ( [[[[effectClass supportedPositions] objectForKey:@"Bottom"] objectForKey:@"Center"] boolValue] );
1047 } else if ( hPos == ITWindowPositionRight ) {
1048 valid = ( [[[[effectClass supportedPositions] objectForKey:@"Bottom"] objectForKey:@"Right"] boolValue] );
1055 - (IBAction)changeMenus:(id)sender
1057 ITDebugLog(@"Synchronizing menus");
1058 [df setObject:myItems forKey:@"menu"];
1061 [[controller menuController] performSelector:@selector(rebuildSubmenus) withObject:nil afterDelay:0.0];
1063 //If we're connected over a network, refresh the menu immediately
1064 if ([[NetworkController sharedController] isConnectedToServer]) {
1065 [controller timerUpdate];
1070 /*************************************************************************/
1072 #pragma mark NSWindow DELEGATE METHODS
1073 /*************************************************************************/
1075 - (void)windowWillClose:(NSNotification *)note
1077 [(MainController *)controller closePreferences];
1080 /*************************************************************************/
1082 #pragma mark NSTextField DELEGATE METHODS
1083 /*************************************************************************/
1085 - (void)controlTextDidChange:(NSNotification*)note
1087 if ([note object] == hostTextField) {
1088 if ([[hostTextField stringValue] length] == 0) {
1089 [sharingPanelOKButton setEnabled:NO];
1091 [sharingPanelOKButton setEnabled:YES];
1096 /*************************************************************************/
1098 #pragma mark NSTableView DATASOURCE METHODS
1099 /*************************************************************************/
1101 - (int)numberOfRowsInTableView:(NSTableView *)aTableView
1103 if (aTableView == menuTableView) {
1104 return [myItems count];
1105 } else if (aTableView == allTableView) {
1106 return [availableItems count];
1107 } else if (aTableView == hotKeysTableView) {
1108 return [hotKeysArray count];
1110 return [[[NetworkController sharedController] remoteServices] count];
1114 - (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex
1116 if (aTableView == menuTableView) {
1117 NSString *object = [myItems objectAtIndex:rowIndex];
1118 if ([[aTableColumn identifier] isEqualToString:@"name"]) {
1119 if ([object isEqualToString:@"showPlayer"]) {
1120 NSString *string = nil;
1122 string = [NSString stringWithFormat:@"%@ %@", NSLocalizedString(@"show", @"Show"), [[controller currentRemote] playerSimpleName]];
1124 [controller networkError:localException];
1128 return NSLocalizedString(object, @"ERROR");
1130 if ([submenuItems containsObject:object])
1132 return [NSImage imageNamed:@"submenu"];
1137 } else if (aTableView == allTableView) {
1138 NSString *object = [availableItems objectAtIndex:rowIndex];
1139 if ([[aTableColumn identifier] isEqualToString:@"name"]) {
1140 if ([object isEqualToString:@"showPlayer"]) {
1141 NSString *string = nil;
1143 string = [NSString stringWithFormat:@"%@ %@", NSLocalizedString(@"show", @"Show"), [[controller currentRemote] playerSimpleName]];
1145 [controller networkError:localException];
1149 return NSLocalizedString(object, @"ERROR");
1151 if ([submenuItems containsObject:object]) {
1152 return [NSImage imageNamed:@"submenu"];
1157 } else if (aTableView == hotKeysTableView) {
1158 if ([[aTableColumn identifier] isEqualToString:@"name"]) {
1159 return [hotKeyNamesArray objectAtIndex:rowIndex];
1161 return [[hotKeysDictionary objectForKey:[hotKeysArray objectAtIndex:rowIndex]] description];
1164 return [[[[NetworkController sharedController] remoteServices] objectAtIndex:rowIndex] name];
1168 - (BOOL)tableView:(NSTableView *)tableView writeRows:(NSArray*)rows toPasteboard:(NSPasteboard*)pboard
1170 if (tableView == menuTableView) {
1171 [pboard declareTypes:[NSArray arrayWithObjects:@"MenuTableViewPboardType", nil] owner:self];
1172 [pboard setString:[[rows objectAtIndex:0] stringValue] forType:@"MenuTableViewPboardType"];
1176 if (tableView == allTableView) {
1177 [pboard declareTypes:[NSArray arrayWithObjects:@"AllTableViewPboardType", nil] owner:self];
1178 [pboard setString:[[rows objectAtIndex:0] stringValue] forType:@"AllTableViewPboardType"];
1184 - (BOOL)tableView:(NSTableView*)tableView acceptDrop:(id <NSDraggingInfo>)info row:(int)row dropOperation:(NSTableViewDropOperation)operation
1188 NSString *dragData, *temp;
1190 pb = [info draggingPasteboard];
1192 if ([[pb types] containsObject:@"MenuTableViewPboardType"]) {
1193 dragData = [pb stringForType:@"MenuTableViewPboardType"];
1194 dragRow = [dragData intValue];
1195 temp = [myItems objectAtIndex:dragRow];
1197 if (tableView == menuTableView) {
1198 [myItems insertObject:temp atIndex:row];
1199 if (row > dragRow) {
1200 [myItems removeObjectAtIndex:dragRow];
1202 [myItems removeObjectAtIndex:dragRow + 1];
1204 } else if (tableView == allTableView) {
1205 if (![temp isEqualToString:@"separator"]) {
1206 [availableItems addObject:temp];
1208 [myItems removeObjectAtIndex:dragRow];
1210 } else if ([[pb types] containsObject:@"AllTableViewPboardType"]) {
1211 dragData = [pb stringForType:@"AllTableViewPboardType"];
1212 dragRow = [dragData intValue];
1213 temp = [availableItems objectAtIndex:dragRow];
1215 [myItems insertObject:temp atIndex:row];
1217 if (![temp isEqualToString:@"separator"]) {
1218 [availableItems removeObjectAtIndex:dragRow];
1222 [menuTableView reloadData];
1223 [allTableView reloadData];
1224 [self changeMenus:self];
1228 - (NSDragOperation)tableView:(NSTableView*)tableView validateDrop:(id <NSDraggingInfo>)info proposedRow:(int)row proposedDropOperation:(NSTableViewDropOperation)operation
1230 if (tableView == allTableView) {
1231 if ([[[info draggingPasteboard] types] containsObject:@"AllTableViewPboardType"]) {
1232 return NSDragOperationNone;
1235 if ([[[info draggingPasteboard] types] containsObject:@"MenuTableViewPboardType"]) {
1236 NSString *item = [myItems objectAtIndex:[[[info draggingPasteboard] stringForType:@"MenuTableViewPboardType"] intValue]];
1237 if ([item isEqualToString:@"preferences"] || [item isEqualToString:@"quit"]) {
1238 return NSDragOperationNone;
1242 [tableView setDropRow:-1 dropOperation:NSTableViewDropOn];
1243 return NSDragOperationGeneric;
1246 if (operation == NSTableViewDropOn || row == -1)
1248 return NSDragOperationNone;
1250 return NSDragOperationGeneric;
1254 /*************************************************************************/
1256 #pragma mark DEALLOCATION METHODS
1257 /*************************************************************************/
1261 [[NSNotificationCenter defaultCenter] removeObserver:self];
1262 [hotKeysArray release];
1263 [hotKeysDictionary release];
1264 [effectClasses release];
1265 [menuTableView setDataSource:nil];
1266 [allTableView setDataSource:nil];
1267 [controller release];
1268 [availableItems release];
1269 [submenuItems release];