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 <ITFoundation/ITFoundation.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)setStatusWindowEntryEffect:(Class)effectClass;
46 - (void)setStatusWindowExitEffect:(Class)effectClass;
47 - (void)setCustomColor:(NSColor *)color updateWell:(BOOL)update;
48 - (void)repopulateEffectPopupsForVerticalPosition:(ITVerticalWindowPosition)vPos horizontalPosition:(ITHorizontalWindowPosition)hPos;
49 - (BOOL)effect:(Class)effectClass supportsVerticalPosition:(ITVerticalWindowPosition)vPos withHorizontalPosition:(ITHorizontalWindowPosition)hPos;
50 - (IBAction)changeMenus:(id)sender;
51 - (void)setLaunchesAtLogin:(BOOL)flag;
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 hotKeyNamesArray = [[NSArray alloc] initWithObjects:@"Play/Pause",
114 hotKeysDictionary = [[NSMutableDictionary alloc] init];
117 [self setupWindow]; // Load in the nib, and perform any initial setup.
118 [[NSColorPanel sharedColorPanel] setShowsAlpha:YES];
124 /*************************************************************************/
126 #pragma mark ACCESSOR METHODS
127 /*************************************************************************/
134 - (void)setController:(id)object
136 [controller autorelease];
137 controller = [object retain];
141 /*************************************************************************/
143 #pragma mark INSTANCE METHODS
144 /*************************************************************************/
146 - (BOOL)showPasswordPanel
148 [passwordPanel setLevel:NSStatusWindowLevel];
149 [passwordPanelOKButton setTitle:@"Connect"];
150 [passwordPanelTitle setStringValue:@"Password Required"];
151 [passwordPanelMessage setStringValue:[NSString stringWithFormat:@"Please enter a password for access to the MenuTunes player named %@ at %@.", [[[NetworkController sharedController] networkObject] serverName], [[NetworkController sharedController] remoteHost]]];
152 [passwordPanel setLevel:NSStatusWindowLevel];
153 [NSApp activateIgnoringOtherApps:YES];
154 [passwordPanel center];
155 [passwordPanel orderFrontRegardless];
156 [passwordPanel makeKeyWindow];
157 if ([NSApp runModalForWindow:passwordPanel]) {
164 - (BOOL)showInvalidPasswordPanel
166 [passwordPanel setLevel:NSStatusWindowLevel];
167 [passwordPanelOKButton setTitle:@"Retry"];
168 [passwordPanelTitle setStringValue:@"Invalid Password"];
169 [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]]];
170 [passwordPanel setLevel:NSStatusWindowLevel];
171 [NSApp activateIgnoringOtherApps:YES];
172 [passwordPanel center];
173 [passwordPanel orderFrontRegardless];
174 [passwordPanel makeKeyWindow];
175 if ([NSApp runModalForWindow:passwordPanel]) {
182 - (IBAction)showPrefsWindow:(id)sender
184 ITDebugLog(@"Showing preferences window.");
185 if (!myItems) { // If menu array does not exist yet, then the window hasn't been setup.
186 ITDebugLog(@"Window doesn't exist, initial setup.");
187 [self setupCustomizationTables]; // Setup the DnD manu config tables.
188 [self setupMenuItems]; // Setup the arrays of menu items
189 [self setupUI]; // Sets up additional UI
190 [window setDelegate:self];
191 [menuTableView reloadData];
192 [hotKeysTableView setDoubleAction:@selector(hotKeysTableViewDoubleClicked:)];
194 //Change the launch player checkbox to the proper name
196 [launchPlayerAtLaunchCheckbox setTitle:[NSString stringWithFormat:@"Launch %@ when MenuTunes launches", [[controller currentRemote] playerSimpleName]]]; //This isn't localized...
198 [controller networkError:localException];
202 [self resetRemotePlayerTextFields];
203 [launchAtLoginCheckbox becomeFirstResponder];
204 [NSApp activateIgnoringOtherApps:YES];
206 [window orderFrontRegardless];
207 [window makeKeyWindow];
210 - (IBAction)changeGeneralSetting:(id)sender
212 ITDebugLog(@"Changing general setting of tag %i.", [sender tag]);
213 if ( [sender tag] == 1010) {
214 [self setLaunchesAtLogin:SENDER_STATE];
215 } else if ( [sender tag] == 1020) {
216 [df setBool:SENDER_STATE forKey:@"LaunchPlayerWithMT"];
217 } else if ( [sender tag] == 1030) {
218 [df setInteger:[sender intValue] forKey:@"SongsInAdvance"];
219 [[controller menuController] rebuildSubmenus];
220 } else if ( [sender tag] == 1040) {
221 // This will not be executed. Song info always shows the title of the song.
222 // [df setBool:SENDER_STATE forKey:@"showName"];
223 } else if ( [sender tag] == 1050) {
224 [df setBool:SENDER_STATE forKey:@"showArtist"];
225 } else if ( [sender tag] == 1055) {
226 [df setBool:SENDER_STATE forKey:@"showComposer"];
227 } else if ( [sender tag] == 1060) {
228 [df setBool:SENDER_STATE forKey:@"showAlbum"];
229 } else if ( [sender tag] == 1070) {
230 [df setBool:SENDER_STATE forKey:@"showTime"];
231 } else if ( [sender tag] == 1080) {
232 [df setBool:SENDER_STATE forKey:@"showTrackNumber"];
233 } else if ( [sender tag] == 1090) {
234 [df setBool:SENDER_STATE forKey:@"showTrackRating"];
235 } else if ( [sender tag] == 1100) {
236 [df setBool:SENDER_STATE forKey:@"showAlbumArtwork"];
237 } else if ( [sender tag] == 1110) {
238 [df setBool:SENDER_STATE forKey:@"runScripts"];
240 [runScriptsCheckbox setState:NSOnState];
241 [showScriptsButton setEnabled:YES];
243 [showScriptsButton setEnabled:NO];
245 } else if ( [sender tag] == 1120) {
246 mkdir([[NSHomeDirectory() stringByAppendingPathComponent:@"Library/Application Support/MenuTunes/Scripts"] cString], 0744);
247 [[NSWorkspace sharedWorkspace] openFile:[NSHomeDirectory() stringByAppendingPathComponent:@"Library/Application Support/MenuTunes/Scripts"]];
252 - (IBAction)changeSharingSetting:(id)sender
254 ITDebugLog(@"Changing sharing setting of tag %i.", [sender tag]);
255 if ( [sender tag] == 5010 ) {
256 BOOL state = SENDER_STATE;
257 [df setBool:state forKey:@"enableSharing"];
258 //Disable/enable the use of shared player options
259 [useSharedMenuTunesCheckbox setEnabled:!state];
260 [passwordTextField setEnabled:state];
261 [nameTextField setEnabled:state];
262 [selectSharedPlayerButton setEnabled:NO];
263 [controller setServerStatus:state]; //Set server status
264 } else if ( [sender tag] == 5015 ) {
265 [df setObject:[sender stringValue] forKey:@"sharedPlayerName"];
266 [[NetworkController sharedController] resetServerName];
267 } else if ( [sender tag] == 5030 ) {
268 //Set the server password
269 const char *instring = [[sender stringValue] UTF8String];
270 const char *password = "p4s5w0rdMT1.2";
271 unsigned char *result;
272 NSData *hashedPass, *passwordStringHash;
273 if ([[sender stringValue] length] == 0) {
274 [df setObject:[NSData data] forKey:@"sharedPlayerPassword"];
277 result = SHA1(instring, strlen(instring), NULL);
278 hashedPass = [NSData dataWithBytes:result length:strlen(result)];
279 result = SHA1(password, strlen(password), NULL);
280 passwordStringHash = [NSData dataWithBytes:result length:strlen(result)];
281 if (![hashedPass isEqualToData:passwordStringHash]) {
282 [df setObject:hashedPass forKey:@"sharedPlayerPassword"];
283 [sender setStringValue:@"p4s5w0rdMT1.2"];
285 } else if ( [sender tag] == 5040 ) {
286 BOOL state = SENDER_STATE;
287 [df setBool:state forKey:@"useSharedPlayer"];
288 //Disable/enable the use of sharing options
289 [shareMenuTunesCheckbox setEnabled:!state];
290 [passwordTextField setEnabled:NO];
291 [nameTextField setEnabled:NO];
292 [selectSharedPlayerButton setEnabled:state];
294 if (state && ([controller connectToServer] == 1)) {
295 [selectedPlayerTextField setStringValue:[[[NetworkController sharedController] networkObject] serverName]];
296 [locationTextField setStringValue:[[NetworkController sharedController] remoteHost]];
298 [selectedPlayerTextField setStringValue:@"No shared player selected."];
299 [locationTextField setStringValue:@"-"];
300 if ([[NetworkController sharedController] isConnectedToServer]) {
301 [controller disconnectFromServer];
305 } else if ( [sender tag] == 5050 ) {
306 //If no player is selected in the table view, turn off OK button.
307 if ([sender clickedRow] == -1 ) {
308 [sharingPanelOKButton setEnabled:NO];
310 [sharingPanelOKButton setEnabled:YES];
312 } else if ( [sender tag] == 5051 ) {
313 [df setObject:[sender stringValue] forKey:@"sharedPlayerHost"];
314 } else if ( [sender tag] == 5060 ) {
315 //Set OK button state
316 if (([selectPlayerBox contentView] == zeroConfView && [sharingTableView selectedRow] == -1) ||
317 ([selectPlayerBox contentView] == manualView && [[hostTextField stringValue] length] == 0)) {
318 [sharingPanelOKButton setEnabled:NO];
320 [sharingPanelOKButton setEnabled:YES];
322 //Show selection sheet
323 [NSApp beginSheet:selectPlayerSheet modalForWindow:window modalDelegate:self didEndSelector:NULL contextInfo:nil];
324 } else if ( [sender tag] == 5100 ) {
326 if ( ([sender indexOfItem:[sender selectedItem]] == 0) && ([selectPlayerBox contentView] != zeroConfView) ) {
327 NSRect frame = [selectPlayerSheet frame];
328 frame.origin.y -= 58;
329 frame.size.height = 273;
330 if ([sharingTableView selectedRow] == -1) {
331 [sharingPanelOKButton setEnabled:NO];
333 [selectPlayerBox setContentView:zeroConfView];
334 [selectPlayerSheet setFrame:frame display:YES animate:YES];
335 } else if ( ([sender indexOfItem:[sender selectedItem]] == 1) && ([selectPlayerBox contentView] != manualView) ){
336 NSRect frame = [selectPlayerSheet frame];
337 frame.origin.y += 58;
338 frame.size.height = 215;
339 if ([[hostTextField stringValue] length] == 0) {
340 [sharingPanelOKButton setEnabled:NO];
342 [sharingPanelOKButton setEnabled:YES];
344 [selectPlayerBox setContentView:manualView];
345 [selectPlayerSheet setFrame:frame display:YES animate:YES];
346 [hostTextField selectText:nil];
348 } else if ( [sender tag] == 5150 ) {
349 const char *instring = [[sender stringValue] UTF8String];
350 unsigned char *result;
351 result = SHA1(instring, strlen(instring), NULL);
352 [df setObject:[NSData dataWithBytes:result length:strlen(result)] forKey:@"connectPassword"];
353 } else if ( [sender tag] == 5110 ) {
355 [NSApp endSheet:selectPlayerSheet];
356 [selectPlayerSheet orderOut:nil];
357 if ([selectPlayerBox contentView] == manualView) {
358 [hostTextField setStringValue:[df stringForKey:@"sharedPlayerHost"]];
361 } else if ( [sender tag] == 5120 ) {
363 [NSApp endSheet:selectPlayerSheet];
364 [selectPlayerSheet orderOut:nil];
366 [self changeSharingSetting:clientPasswordTextField];
368 if ([selectPlayerBox contentView] == manualView) {
369 [df setObject:[hostTextField stringValue] forKey:@"sharedPlayerHost"];
371 if ([sharingTableView selectedRow] > -1) {
372 [df setObject:[NSString stringWithCString:inet_ntoa((*(struct sockaddr_in*)[[[[[[NetworkController sharedController] remoteServices] objectAtIndex:[sharingTableView selectedRow]] addresses] objectAtIndex:0] bytes]).sin_addr)] forKey:@"sharedPlayerHost"];
376 if ([controller connectToServer] == 1) {
377 [useSharedMenuTunesCheckbox setState:NSOnState];
378 [selectedPlayerTextField setStringValue:[[[NetworkController sharedController] networkObject] serverName]];
379 [locationTextField setStringValue:[[NetworkController sharedController] remoteHost]];
381 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);
383 } else if ( [sender tag] == 6010 ) {
384 //Cancel password entry
385 [passwordPanel orderOut:nil];
386 [NSApp stopModalWithCode:0];
387 } else if ( [sender tag] == 6020 ) {
388 //OK password entry, retry connect
389 const char *instring = [[passwordPanelTextField stringValue] UTF8String];
390 unsigned char *result;
391 result = SHA1(instring, strlen(instring), NULL);
392 [df setObject:[NSData dataWithBytes:result length:strlen(result)] forKey:@"connectPassword"];
393 [passwordPanel orderOut:nil];
394 [NSApp stopModalWithCode:1];
399 - (IBAction)changeStatusWindowSetting:(id)sender
401 StatusWindow *sw = [StatusWindow sharedWindow];
402 ITDebugLog(@"Changing status window setting of tag %i", [sender tag]);
404 if ( [sender tag] == 2010) {
406 BOOL entryEffectValid = YES;
407 BOOL exitEffectValid = YES;
409 [df setInteger:[sender selectedRow] forKey:@"statusWindowVerticalPosition"];
410 [df setInteger:[sender selectedColumn] forKey:@"statusWindowHorizontalPosition"];
411 [sw setVerticalPosition:[sender selectedRow]];
412 [sw setHorizontalPosition:[sender selectedColumn]];
414 // Enable/disable the items in the popups.
415 [self repopulateEffectPopupsForVerticalPosition:[sw verticalPosition]
416 horizontalPosition:[sw horizontalPosition]];
418 // Make sure the effects support the new position.
419 entryEffectValid = ( [self effect:[[sw entryEffect] class]
420 supportsVerticalPosition:[sw verticalPosition]
421 withHorizontalPosition:[sw horizontalPosition]] );
422 exitEffectValid = ( [self effect:[[sw exitEffect] class]
423 supportsVerticalPosition:[sw verticalPosition]
424 withHorizontalPosition:[sw horizontalPosition]] );
426 if ( ! entryEffectValid ) {
427 [appearanceEffectPopup selectItemAtIndex:[[appearanceEffectPopup menu] indexOfItemWithRepresentedObject:NSClassFromString(@"ITCutWindowEffect")]];
428 [self setStatusWindowEntryEffect:NSClassFromString(@"ITCutWindowEffect")];
430 [appearanceEffectPopup selectItemAtIndex:[[appearanceEffectPopup menu] indexOfItemWithRepresentedObject:[[sw entryEffect] class]]];
433 if ( ! exitEffectValid ) {
434 [vanishEffectPopup selectItemAtIndex:[[vanishEffectPopup menu] indexOfItemWithRepresentedObject:NSClassFromString(@"ITDissolveWindowEffect")]];
435 [self setStatusWindowExitEffect:NSClassFromString(@"ITDissolveWindowEffect")];
437 [vanishEffectPopup selectItemAtIndex:[[vanishEffectPopup menu] indexOfItemWithRepresentedObject:[[sw exitEffect] class]]];
440 [(MainController *)controller showCurrentTrackInfo];
442 } else if ( [sender tag] == 2020) {
444 // Update screen selection.
446 } else if ( [sender tag] == 2030) {
448 [self setStatusWindowEntryEffect:[[sender selectedItem] representedObject]];
449 [(MainController *)controller showCurrentTrackInfo];
451 } else if ( [sender tag] == 2040) {
453 [self setStatusWindowExitEffect:[[sender selectedItem] representedObject]];
454 [(MainController *)controller showCurrentTrackInfo];
456 } else if ( [sender tag] == 2050) {
457 float newTime = ( -([sender floatValue]) );
458 [df setFloat:newTime forKey:@"statusWindowAppearanceSpeed"];
459 [[sw entryEffect] setEffectTime:newTime];
460 } else if ( [sender tag] == 2060) {
461 float newTime = ( -([sender floatValue]) );
462 [df setFloat:newTime forKey:@"statusWindowVanishSpeed"];
463 [[sw exitEffect] setEffectTime:newTime];
464 } else if ( [sender tag] == 2070) {
465 [df setFloat:[sender floatValue] forKey:@"statusWindowVanishDelay"];
466 [sw setExitDelay:[sender floatValue]];
467 } else if ( [sender tag] == 2080) {
468 [df setBool:SENDER_STATE forKey:@"showSongInfoOnChange"];
469 } else if ( [sender tag] == 2090) {
471 int setting = [sender indexOfSelectedItem];
473 if ( setting == 0 ) {
474 [(ITTSWBackgroundView *)[sw contentView] setBackgroundMode:ITTSWBackgroundApple];
475 [backgroundColorWell setEnabled:NO];
476 [backgroundColorPopup setEnabled:NO];
477 } else if ( setting == 1 ) {
478 [(ITTSWBackgroundView *)[sw contentView] setBackgroundMode:ITTSWBackgroundReadable];
479 [backgroundColorWell setEnabled:NO];
480 [backgroundColorPopup setEnabled:NO];
481 } else if ( setting == 2 ) {
482 [(ITTSWBackgroundView *)[sw contentView] setBackgroundMode:ITTSWBackgroundColored];
483 [backgroundColorWell setEnabled:YES];
484 [backgroundColorPopup setEnabled:YES];
487 [df setInteger:setting forKey:@"statusWindowBackgroundMode"];
488 [(MainController *)controller showCurrentTrackInfo];
490 } else if ( [sender tag] == 2091) {
491 [self setCustomColor:[sender color] updateWell:NO];
492 [(MainController *)controller showCurrentTrackInfo];
493 } else if ( [sender tag] == 2092) {
495 int selectedItem = [sender indexOfSelectedItem];
497 if ( selectedItem == 1 ) { // An NSPopUpButton in PullDown mode uses item 0 as its title. Its first selectable item is 1.
498 [self setCustomColor:[NSColor colorWithCalibratedRed:0.92549 green:0.686275 blue:0.0 alpha:1.0] updateWell:YES];
499 } else if ( selectedItem == 2 ) {
500 [self setCustomColor:[NSColor colorWithCalibratedRed:0.380392 green:0.670588 blue:0.0 alpha:1.0] updateWell:YES];
501 } else if ( selectedItem == 3 ) {
502 [self setCustomColor:[NSColor colorWithCalibratedRed:0.443137 green:0.231373 blue:0.619608 alpha:1.0] updateWell:YES];
503 } else if ( selectedItem == 4 ) {
504 [self setCustomColor:[NSColor colorWithCalibratedRed:0.831373 green:0.12549 blue:0.509804 alpha:1.0] updateWell:YES];
505 } else if ( selectedItem == 5 ) {
506 [self setCustomColor:[NSColor colorWithCalibratedRed:0.00784314 green:0.611765 blue:0.662745 alpha:1.0] updateWell:YES];
508 [self setCustomColor:[NSColor colorWithCalibratedWhite:0.15 alpha:0.70] updateWell:YES];
510 [(MainController *)controller showCurrentTrackInfo];
512 } else if ( [sender tag] == 2095) {
513 [df setInteger:[sender indexOfSelectedItem] forKey:@"statusWindowSizing"];
514 [(MainController *)controller showCurrentTrackInfo];
520 - (void)registerDefaults
523 NSMutableDictionary *loginWindow;
524 NSMutableArray *loginArray;
525 NSEnumerator *loginEnum;
527 ITDebugLog(@"Registering defaults.");
528 [df setObject:[NSArray arrayWithObjects:
540 nil] forKey:@"menu"];
542 [df setInteger:MT_CURRENT_VERSION forKey:@"appVersion"];
543 [df setInteger:5 forKey:@"SongsInAdvance"];
544 // [df setBool:YES forKey:@"showName"]; // Song info will always show song title.
545 [df setBool:YES forKey:@"showArtist"];
546 [df setBool:YES forKey:@"showAlbumArtwork"];
547 [df setBool:NO forKey:@"showAlbum"];
548 [df setBool:NO forKey:@"showComposer"];
549 [df setBool:NO forKey:@"showTime"];
550 [df setBool:NO forKey:@"showToolTip"];
552 [df setObject:@"ITCutWindowEffect" forKey:@"statusWindowAppearanceEffect"];
553 [df setObject:@"ITDissolveWindowEffect" forKey:@"statusWindowVanishEffect"];
554 [df setFloat:0.8 forKey:@"statusWindowAppearanceSpeed"];
555 [df setFloat:0.8 forKey:@"statusWindowVanishSpeed"];
556 [df setFloat:4.0 forKey:@"statusWindowVanishDelay"];
557 [df setInteger:(int)ITWindowPositionBottom forKey:@"statusWindowVerticalPosition"];
558 [df setInteger:(int)ITWindowPositionLeft forKey:@"statusWindowHorizontalPosition"];
559 [[StatusWindow sharedWindow] setVerticalPosition:(int)ITWindowPositionBottom];
560 [[StatusWindow sharedWindow] setHorizontalPosition:(int)ITWindowPositionLeft];
561 [df setBool:YES forKey:@"showSongInfoOnChange"];
563 [df setObject:[NSArchiver archivedDataWithRootObject:[NSColor blueColor]] forKey:@"statusWindowBackgroundColor"];
567 loginWindow = [[df persistentDomainForName:@"loginwindow"] mutableCopy];
568 loginArray = [loginWindow objectForKey:@"AutoLaunchedApplicationDictionary"];
569 loginEnum = [loginArray objectEnumerator];
571 while ( (anItem = [loginEnum nextObject]) ) {
572 if ( [[[anItem objectForKey:@"Path"] lastPathComponent] isEqualToString:[[[NSBundle mainBundle] bundlePath] lastPathComponent]] ) {
576 [loginWindow release];
579 [[StatusWindowController sharedController] showSetupQueryWindow];
585 [[StatusWindow sharedWindow] setLocked:NO];
586 [[StatusWindow sharedWindow] vanish:self];
587 [[StatusWindow sharedWindow] setIgnoresMouseEvents:YES];
589 [self setLaunchesAtLogin:YES];
592 - (void)autoLaunchCancel
594 [[StatusWindow sharedWindow] setLocked:NO];
595 [[StatusWindow sharedWindow] vanish:self];
596 [[StatusWindow sharedWindow] setIgnoresMouseEvents:YES];
599 - (void)deletePressedInTableView:(NSTableView *)tableView
601 if (tableView == menuTableView) {
602 int selRow = [tableView selectedRow];
603 ITDebugLog(@"Delete pressed in menu table view.");
605 NSString *object = [myItems objectAtIndex:selRow];
607 if ([object isEqualToString:@"preferences"]) {
612 if (![object isEqualToString:@"separator"])
613 [availableItems addObject:object];
614 ITDebugLog(@"Removing object named %@", object);
615 [myItems removeObjectAtIndex:selRow];
616 [menuTableView reloadData];
617 [allTableView reloadData];
619 [self changeMenus:self];
623 - (void)resetRemotePlayerTextFields
625 if ([[NetworkController sharedController] isConnectedToServer]) {
626 [selectedPlayerTextField setStringValue:[[[NetworkController sharedController] networkObject] serverName]];
627 [locationTextField setStringValue:[[NetworkController sharedController] remoteHost]];
629 [selectedPlayerTextField setStringValue:@"No shared player selected."];
630 [locationTextField setStringValue:@"-"];
634 /*************************************************************************/
636 #pragma mark HOTKEY SUPPORT METHODS
637 /*************************************************************************/
639 - (IBAction)clearHotKey:(id)sender
641 [hotKeysDictionary setObject:[ITKeyCombo clearKeyCombo] forKey:[hotKeysArray objectAtIndex:[hotKeysTableView selectedRow]]];
642 [df setObject:[[ITKeyCombo clearKeyCombo] plistRepresentation] forKey:[hotKeysArray objectAtIndex:[hotKeysTableView selectedRow]]];
643 [controller setupHotKeys];
644 [hotKeysTableView reloadData];
647 - (IBAction)editHotKey:(id)sender
649 ITKeyComboPanel *panel = [ITKeyComboPanel sharedPanel];
650 NSString *keyComboKey = [hotKeysArray objectAtIndex:[hotKeysTableView selectedRow]];
651 ITKeyCombo *keyCombo;
653 ITDebugLog(@"Setting key combo on hot key %@.", keyComboKey);
654 [controller clearHotKeys];
655 [panel setKeyCombo:[hotKeysDictionary objectForKey:[hotKeysArray objectAtIndex:[hotKeysTableView selectedRow]]]];
656 [panel setKeyBindingName:[hotKeyNamesArray objectAtIndex:[hotKeysTableView selectedRow]]];
657 if ([panel runModal] == NSOKButton) {
658 NSEnumerator *keyEnumerator = [[hotKeysDictionary allKeys] objectEnumerator];
660 keyCombo = [panel keyCombo];
662 //Check for duplicate key combo
663 while ( (nextKey = [keyEnumerator nextObject]) ) {
664 if ([[hotKeysDictionary objectForKey:nextKey] isEqual:keyCombo] &&
665 ![keyCombo isEqual:[ITKeyCombo clearKeyCombo]]) {
666 [hotKeysDictionary setObject:[ITKeyCombo clearKeyCombo]
668 [df setObject:[[ITKeyCombo clearKeyCombo] plistRepresentation]
673 [hotKeysDictionary setObject:keyCombo forKey:keyComboKey];
674 [df setObject:[keyCombo plistRepresentation] forKey:keyComboKey];
675 [controller setupHotKeys];
676 [hotKeysTableView reloadData];
677 ITDebugLog(@"Set combo %@ on hot key %@.", keyCombo, keyComboKey);
679 ITDebugLog(@"Hot key setting on hot key %@ cancelled.", keyComboKey);
683 - (void)hotKeysTableViewDoubleClicked:(id)sender
685 if ([sender clickedRow] > -1) {
686 [self editHotKey:sender];
690 /*************************************************************************/
692 #pragma mark PRIVATE METHOD IMPLEMENTATIONS
693 /*************************************************************************/
697 ITDebugLog(@"Loading Preferences.nib.");
698 if (![NSBundle loadNibNamed:@"Preferences" owner:self]) {
699 ITDebugLog(@"Failed to load Preferences.nib.");
705 - (void)setupCustomizationTables
707 NSImageCell *imgCell = [[[NSImageCell alloc] initImageCell:nil] autorelease];
708 ITDebugLog(@"Setting up table views.");
709 // Set the table view cells up
710 [imgCell setImageScaling:NSScaleNone];
711 [[menuTableView tableColumnWithIdentifier:@"submenu"] setDataCell:imgCell];
712 [[allTableView tableColumnWithIdentifier:@"submenu"] setDataCell:imgCell];
714 // Register for drag and drop
715 [menuTableView registerForDraggedTypes:[NSArray arrayWithObjects:
716 @"MenuTableViewPboardType",
717 @"AllTableViewPboardType",
719 [allTableView registerForDraggedTypes:[NSArray arrayWithObjects:
720 @"MenuTableViewPboardType",
721 @"AllTableViewPboardType",
725 - (void)setupMenuItems
727 NSEnumerator *itemEnum;
729 ITDebugLog(@"Setting up table view arrays.");
730 // Set the list of items you can have.
731 availableItems = [[NSMutableArray alloc] initWithObjects:
747 // Get our preferred menu
748 myItems = [[df arrayForKey:@"menu"] mutableCopy];
750 // Delete items in the availableItems array that are already part of the menu
751 itemEnum = [myItems objectEnumerator];
752 while ( (anItem = [itemEnum nextObject]) ) {
753 if (![anItem isEqualToString:@"separator"]) {
754 [availableItems removeObject:anItem];
758 // Items that show should a submenu image
759 submenuItems = [[NSArray alloc] initWithObjects:
769 NSMutableDictionary *loginwindow;
770 NSMutableArray *loginarray;
771 NSEnumerator *loginEnum;
772 NSEnumerator *keyArrayEnum;
773 NSString *serverName;
778 [df setInteger:MT_CURRENT_VERSION forKey:@"appVersion"];
780 ITDebugLog(@"Setting up preferences UI.");
781 // Fill in the number of songs in advance to show field
782 [songsInAdvance setIntValue:[df integerForKey:@"SongsInAdvance"]];
784 // Fill hot key array
785 keyArrayEnum = [hotKeysArray objectEnumerator];
787 while ( (anItem = [keyArrayEnum nextObject]) ) {
788 if ([df objectForKey:anItem]) {
789 ITDebugLog(@"Setting up \"%@\" hot key.", anItem);
790 [hotKeysDictionary setObject:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:anItem]] forKey:anItem];
792 [hotKeysDictionary setObject:[ITKeyCombo clearKeyCombo] forKey:anItem];
796 ITDebugLog(@"Setting up track info checkboxes.");
797 // Check current track info buttons
798 [albumCheckbox setState:[df boolForKey:@"showAlbum"] ? NSOnState : NSOffState];
799 [nameCheckbox setState:NSOnState]; // Song info will ALWAYS show song title.
800 [nameCheckbox setEnabled:NO]; // Song info will ALWAYS show song title.
801 [artistCheckbox setState:[df boolForKey:@"showArtist"] ? NSOnState : NSOffState];
802 [composerCheckbox setState:[df boolForKey:@"showComposer"] ? NSOnState : NSOffState];
803 [trackTimeCheckbox setState:[df boolForKey:@"showTime"] ? NSOnState : NSOffState];
804 [trackNumberCheckbox setState:[df boolForKey:@"showTrackNumber"] ? NSOnState : NSOffState];
805 [ratingCheckbox setState:[df boolForKey:@"showTrackRating"] ? NSOnState : NSOffState];
806 [albumArtworkCheckbox setState:[df boolForKey:@"showAlbumArtwork"] ? NSOnState : NSOffState];
808 if ([df boolForKey:@"runScripts"]) {
809 [runScriptsCheckbox setState:NSOnState];
810 [showScriptsButton setEnabled:YES];
812 [showScriptsButton setEnabled:NO];
815 // Set the launch at login checkbox state
816 ITDebugLog(@"Setting launch at login state.");
818 loginwindow = [[df persistentDomainForName:@"loginwindow"] mutableCopy];
819 loginarray = [loginwindow objectForKey:@"AutoLaunchedApplicationDictionary"];
821 loginEnum = [loginarray objectEnumerator];
822 while ( (anItem = [loginEnum nextObject]) ) {
823 if ([[[anItem objectForKey:@"Path"] lastPathComponent] isEqualToString:[[[NSBundle mainBundle] bundlePath] lastPathComponent]]) {
824 [launchAtLoginCheckbox setState:NSOnState];
828 // Set the launch player checkbox state
829 ITDebugLog(@"Setting launch player with MenuTunes state.");
830 [launchPlayerAtLaunchCheckbox setState:[df boolForKey:@"LaunchPlayerWithMT"] ? NSOnState : NSOffState];
832 // Setup the positioning controls
833 [positionMatrix selectCellAtRow:[df integerForKey:@"statusWindowVerticalPosition"]
834 column:[df integerForKey:@"statusWindowHorizontalPosition"]];
836 // Setup effects controls
837 // Populate the effects popups
838 [appearanceEffectPopup setAutoenablesItems:NO];
839 [vanishEffectPopup setAutoenablesItems:NO];
840 [self repopulateEffectPopupsForVerticalPosition:[df integerForKey:@"statusWindowVerticalPosition"]
841 horizontalPosition:[df integerForKey:@"statusWindowHorizontalPosition"]];
843 // Attempt to find the pref'd effect in the list.
844 // If it's not there, use cut/dissolve.
845 if ( [effectClasses containsObject:NSClassFromString([df stringForKey:@"statusWindowAppearanceEffect"])] ) {
846 [appearanceEffectPopup selectItemAtIndex:[effectClasses indexOfObject:NSClassFromString([df stringForKey:@"statusWindowAppearanceEffect"])]];
848 [appearanceEffectPopup selectItemAtIndex:[effectClasses indexOfObject:NSClassFromString(@"ITCutWindowEffect")]];
851 if ( [effectClasses containsObject:NSClassFromString([df stringForKey:@"statusWindowVanishEffect"])] ) {
852 [vanishEffectPopup selectItemAtIndex:[effectClasses indexOfObject:NSClassFromString([df stringForKey:@"statusWindowVanishEffect"])]];
854 [vanishEffectPopup selectItemAtIndex:[effectClasses indexOfObject:NSClassFromString(@"ITCutWindowEffect")]];
857 [appearanceSpeedSlider setFloatValue:( -([df floatForKey:@"statusWindowAppearanceSpeed"]) )];
858 [vanishSpeedSlider setFloatValue:( -([df floatForKey:@"statusWindowVanishSpeed"]) )];
859 [vanishDelaySlider setFloatValue:[df floatForKey:@"statusWindowVanishDelay"]];
861 // Setup General Controls
862 selectedBGStyle = [df integerForKey:@"statusWindowBackgroundMode"];
863 [backgroundStylePopup selectItem:[backgroundStylePopup itemAtIndex:[backgroundStylePopup indexOfItemWithTag:selectedBGStyle]]];
865 if ( selectedBGStyle == ITTSWBackgroundColored ) {
866 [backgroundColorWell setEnabled:YES];
867 [backgroundColorPopup setEnabled:YES];
869 [backgroundColorWell setEnabled:NO];
870 [backgroundColorPopup setEnabled:NO];
873 colorData = [df dataForKey:@"statusWindowBackgroundColor"];
876 [backgroundColorWell setColor:(NSColor *)[NSUnarchiver unarchiveObjectWithData:colorData]];
878 [backgroundColorWell setColor:[NSColor blueColor]];
881 [showOnChangeCheckbox setState:([df boolForKey:@"showSongInfoOnChange"] ? NSOnState : NSOffState)];
883 [windowSizingPopup selectItem:[windowSizingPopup itemAtIndex:[windowSizingPopup indexOfItemWithTag:[df integerForKey:@"statusWindowSizing"]]]];
885 // Setup the sharing controls
886 if ([df boolForKey:@"enableSharing"]) {
887 [shareMenuTunesCheckbox setState:NSOnState];
888 [useSharedMenuTunesCheckbox setEnabled:NO];
889 [selectSharedPlayerButton setEnabled:NO];
890 [passwordTextField setEnabled:YES];
891 [nameTextField setEnabled:YES];
892 } else if ([df boolForKey:@"useSharedPlayer"]) {
893 [useSharedMenuTunesCheckbox setState:NSOnState];
894 [shareMenuTunesCheckbox setEnabled:NO];
895 [selectSharedPlayerButton setEnabled:YES];
898 [[NSNotificationCenter defaultCenter] addObserver:sharingTableView selector:@selector(reloadData) name:@"ITMTFoundNetService" object:nil];
900 serverName = [df stringForKey:@"sharedPlayerName"];
901 if (!serverName || [serverName length] == 0) {
902 serverName = @"MenuTunes Shared Player";
904 [nameTextField setStringValue:serverName];
906 [selectPlayerBox setContentView:zeroConfView];
907 if ([[df dataForKey:@"sharedPlayerPassword"] length] > 0) {
908 [passwordTextField setStringValue:@"p4s5w0rdMT1.2"];
910 [passwordTextField setStringValue:@""];
912 if ([df stringForKey:@"sharedPlayerHost"]) {
913 [hostTextField setStringValue:[df stringForKey:@"sharedPlayerHost"]];
916 if ([[NetworkController sharedController] isConnectedToServer]) {
917 [selectedPlayerTextField setStringValue:[[[NetworkController sharedController] networkObject] serverName]];
918 [locationTextField setStringValue:[[NetworkController sharedController] remoteHost]];
920 [selectedPlayerTextField setStringValue:@"No shared player selected."];
921 [locationTextField setStringValue:@"-"];
925 - (void)setStatusWindowEntryEffect:(Class)effectClass
927 StatusWindow *sw = [StatusWindow sharedWindow];
929 float time = ([df floatForKey:@"statusWindowAppearanceSpeed"] ? [df floatForKey:@"statusWindowAppearanceSpeed"] : 0.8);
930 [df setObject:NSStringFromClass(effectClass) forKey:@"statusWindowAppearanceEffect"];
932 [sw setEntryEffect:[[[effectClass alloc] initWithWindow:sw] autorelease]];
933 [[sw entryEffect] setEffectTime:time];
936 - (void)setStatusWindowExitEffect:(Class)effectClass
938 StatusWindow *sw = [StatusWindow sharedWindow];
940 float time = ([df floatForKey:@"statusWindowVanishSpeed"] ? [df floatForKey:@"statusWindowVanishSpeed"] : 0.8);
941 [df setObject:NSStringFromClass(effectClass) forKey:@"statusWindowVanishEffect"];
943 [sw setExitEffect:[[[effectClass alloc] initWithWindow:sw] autorelease]];
944 [[sw exitEffect] setEffectTime:time];
947 - (void)setCustomColor:(NSColor *)color updateWell:(BOOL)update
949 [(ITTSWBackgroundView *)[[StatusWindow sharedWindow] contentView] setBackgroundColor:color];
950 [df setObject:[NSArchiver archivedDataWithRootObject:color] forKey:@"statusWindowBackgroundColor"];
953 [backgroundColorWell setColor:color];
957 - (void)repopulateEffectPopupsForVerticalPosition:(ITVerticalWindowPosition)vPos horizontalPosition:(ITHorizontalWindowPosition)hPos
959 NSEnumerator *effectEnum = [effectClasses objectEnumerator];
962 [appearanceEffectPopup removeAllItems];
963 [vanishEffectPopup removeAllItems];
965 while ( (anItem = [effectEnum nextObject]) ) {
966 [appearanceEffectPopup addItemWithTitle:[anItem effectName]];
967 [vanishEffectPopup addItemWithTitle:[anItem effectName]];
969 [[appearanceEffectPopup lastItem] setRepresentedObject:anItem];
970 [[vanishEffectPopup lastItem] setRepresentedObject:anItem];
972 if ( [self effect:anItem supportsVerticalPosition:vPos withHorizontalPosition:hPos] ) {
973 [[appearanceEffectPopup lastItem] setEnabled:YES];
974 [[vanishEffectPopup lastItem] setEnabled:YES];
976 [[appearanceEffectPopup lastItem] setEnabled:NO];
977 [[vanishEffectPopup lastItem] setEnabled:NO];
983 - (BOOL)effect:(Class)effectClass supportsVerticalPosition:(ITVerticalWindowPosition)vPos withHorizontalPosition:(ITHorizontalWindowPosition)hPos
987 if ( vPos == ITWindowPositionTop ) {
988 if ( hPos == ITWindowPositionLeft ) {
989 valid = ( [[[[effectClass supportedPositions] objectForKey:@"Top"] objectForKey:@"Left"] boolValue] ) ;
990 } else if ( hPos == ITWindowPositionCenter ) {
991 valid = ( [[[[effectClass supportedPositions] objectForKey:@"Top"] objectForKey:@"Center"] boolValue] );
992 } else if ( hPos == ITWindowPositionRight ) {
993 valid = ( [[[[effectClass supportedPositions] objectForKey:@"Top"] objectForKey:@"Right"] boolValue] );
995 } else if ( vPos == ITWindowPositionMiddle ) {
996 if ( hPos == ITWindowPositionLeft ) {
997 valid = ( [[[[effectClass supportedPositions] objectForKey:@"Middle"] objectForKey:@"Left"] boolValue] );
998 } else if ( hPos == ITWindowPositionCenter ) {
999 valid = ( [[[[effectClass supportedPositions] objectForKey:@"Middle"] objectForKey:@"Center"] boolValue] );
1000 } else if ( hPos == ITWindowPositionRight ) {
1001 valid = ( [[[[effectClass supportedPositions] objectForKey:@"Middle"] objectForKey:@"Right"] boolValue] );
1003 } else if ( vPos == ITWindowPositionBottom ) {
1004 if ( hPos == ITWindowPositionLeft ) {
1005 valid = ( [[[[effectClass supportedPositions] objectForKey:@"Bottom"] objectForKey:@"Left"] boolValue] );
1006 } else if ( hPos == ITWindowPositionCenter ) {
1007 valid = ( [[[[effectClass supportedPositions] objectForKey:@"Bottom"] objectForKey:@"Center"] boolValue] );
1008 } else if ( hPos == ITWindowPositionRight ) {
1009 valid = ( [[[[effectClass supportedPositions] objectForKey:@"Bottom"] objectForKey:@"Right"] boolValue] );
1016 - (IBAction)changeMenus:(id)sender
1018 ITDebugLog(@"Synchronizing menus");
1019 [df setObject:myItems forKey:@"menu"];
1022 //If we're connected over a network, refresh the menu immediately
1023 if ([[NetworkController sharedController] isConnectedToServer]) {
1024 [controller timerUpdate];
1028 - (void)setLaunchesAtLogin:(BOOL)flag
1030 NSMutableDictionary *loginwindow;
1031 NSMutableArray *loginarray;
1032 ITDebugLog(@"Setting launches at login: %i", flag);
1034 loginwindow = [[df persistentDomainForName:@"loginwindow"] mutableCopy];
1035 loginarray = [loginwindow objectForKey:@"AutoLaunchedApplicationDictionary"];
1038 NSDictionary *itemDict = [NSDictionary dictionaryWithObjectsAndKeys:
1039 [[NSBundle mainBundle] bundlePath], @"Path",
1040 [NSNumber numberWithInt:0], @"Hide", nil];
1041 [loginarray addObject:itemDict];
1044 for (i = 0; i < [loginarray count]; i++) {
1045 NSDictionary *tempDict = [loginarray objectAtIndex:i];
1046 if ([[[tempDict objectForKey:@"Path"] lastPathComponent] isEqualToString:[[[NSBundle mainBundle] bundlePath] lastPathComponent]]) {
1047 [loginarray removeObjectAtIndex:i];
1052 [df setPersistentDomain:loginwindow forName:@"loginwindow"];
1054 [loginwindow release];
1055 ITDebugLog(@"Finished setting launches at login.");
1059 /*************************************************************************/
1061 #pragma mark NSWindow DELEGATE METHODS
1062 /*************************************************************************/
1064 - (void)windowWillClose:(NSNotification *)note
1066 [(MainController *)controller closePreferences];
1069 /*************************************************************************/
1071 #pragma mark NSTextField DELEGATE METHODS
1072 /*************************************************************************/
1074 - (void)controlTextDidChange:(NSNotification*)note
1076 if ([note object] == hostTextField) {
1077 if ([[hostTextField stringValue] length] == 0) {
1078 [sharingPanelOKButton setEnabled:NO];
1080 [sharingPanelOKButton setEnabled:YES];
1085 /*************************************************************************/
1087 #pragma mark NSTableView DATASOURCE METHODS
1088 /*************************************************************************/
1090 - (int)numberOfRowsInTableView:(NSTableView *)aTableView
1092 if (aTableView == menuTableView) {
1093 return [myItems count];
1094 } else if (aTableView == allTableView) {
1095 return [availableItems count];
1096 } else if (aTableView == hotKeysTableView) {
1097 return [hotKeysArray count];
1099 return [[[NetworkController sharedController] remoteServices] count];
1103 - (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex
1105 if (aTableView == menuTableView) {
1106 NSString *object = [myItems objectAtIndex:rowIndex];
1107 if ([[aTableColumn identifier] isEqualToString:@"name"]) {
1108 if ([object isEqualToString:@"showPlayer"]) {
1109 NSString *string = nil;
1111 string = [NSString stringWithFormat:@"%@ %@", NSLocalizedString(@"show", @"Show"), [[controller currentRemote] playerSimpleName]];
1113 [controller networkError:localException];
1117 return NSLocalizedString(object, @"ERROR");
1119 if ([submenuItems containsObject:object])
1121 return [NSImage imageNamed:@"submenu"];
1126 } else if (aTableView == allTableView) {
1127 NSString *object = [availableItems objectAtIndex:rowIndex];
1128 if ([[aTableColumn identifier] isEqualToString:@"name"]) {
1129 if ([object isEqualToString:@"showPlayer"]) {
1130 NSString *string = nil;
1132 string = [NSString stringWithFormat:@"%@ %@", NSLocalizedString(@"show", @"Show"), [[controller currentRemote] playerSimpleName]];
1134 [controller networkError:localException];
1138 return NSLocalizedString(object, @"ERROR");
1140 if ([submenuItems containsObject:object]) {
1141 return [NSImage imageNamed:@"submenu"];
1146 } else if (aTableView == hotKeysTableView) {
1147 if ([[aTableColumn identifier] isEqualToString:@"name"]) {
1148 return [hotKeyNamesArray objectAtIndex:rowIndex];
1150 return [[hotKeysDictionary objectForKey:[hotKeysArray objectAtIndex:rowIndex]] description];
1153 return [[[[NetworkController sharedController] remoteServices] objectAtIndex:rowIndex] name];
1157 - (BOOL)tableView:(NSTableView *)tableView writeRows:(NSArray*)rows toPasteboard:(NSPasteboard*)pboard
1159 if (tableView == menuTableView) {
1160 [pboard declareTypes:[NSArray arrayWithObjects:@"MenuTableViewPboardType", nil] owner:self];
1161 [pboard setString:[[rows objectAtIndex:0] stringValue] forType:@"MenuTableViewPboardType"];
1165 if (tableView == allTableView) {
1166 [pboard declareTypes:[NSArray arrayWithObjects:@"AllTableViewPboardType", nil] owner:self];
1167 [pboard setString:[[rows objectAtIndex:0] stringValue] forType:@"AllTableViewPboardType"];
1173 - (BOOL)tableView:(NSTableView*)tableView acceptDrop:(id <NSDraggingInfo>)info row:(int)row dropOperation:(NSTableViewDropOperation)operation
1177 NSString *dragData, *temp;
1179 pb = [info draggingPasteboard];
1181 if ([[pb types] containsObject:@"MenuTableViewPboardType"]) {
1182 dragData = [pb stringForType:@"MenuTableViewPboardType"];
1183 dragRow = [dragData intValue];
1184 temp = [myItems objectAtIndex:dragRow];
1186 if (tableView == menuTableView) {
1187 [myItems insertObject:temp atIndex:row];
1188 if (row > dragRow) {
1189 [myItems removeObjectAtIndex:dragRow];
1191 [myItems removeObjectAtIndex:dragRow + 1];
1193 } else if (tableView == allTableView) {
1194 if (![temp isEqualToString:@"separator"]) {
1195 [availableItems addObject:temp];
1197 [myItems removeObjectAtIndex:dragRow];
1199 } else if ([[pb types] containsObject:@"AllTableViewPboardType"]) {
1200 dragData = [pb stringForType:@"AllTableViewPboardType"];
1201 dragRow = [dragData intValue];
1202 temp = [availableItems objectAtIndex:dragRow];
1204 [myItems insertObject:temp atIndex:row];
1206 if (![temp isEqualToString:@"separator"]) {
1207 [availableItems removeObjectAtIndex:dragRow];
1211 [menuTableView reloadData];
1212 [allTableView reloadData];
1213 [self changeMenus:self];
1217 - (NSDragOperation)tableView:(NSTableView*)tableView validateDrop:(id <NSDraggingInfo>)info proposedRow:(int)row proposedDropOperation:(NSTableViewDropOperation)operation
1219 if (tableView == allTableView) {
1220 if ([[[info draggingPasteboard] types] containsObject:@"AllTableViewPboardType"]) {
1221 return NSDragOperationNone;
1224 if ([[[info draggingPasteboard] types] containsObject:@"MenuTableViewPboardType"]) {
1225 NSString *item = [myItems objectAtIndex:[[[info draggingPasteboard] stringForType:@"MenuTableViewPboardType"] intValue]];
1226 if ([item isEqualToString:@"preferences"] || [item isEqualToString:@"quit"]) {
1227 return NSDragOperationNone;
1231 [tableView setDropRow:-1 dropOperation:NSTableViewDropOn];
1232 return NSDragOperationGeneric;
1235 if (operation == NSTableViewDropOn || row == -1)
1237 return NSDragOperationNone;
1239 return NSDragOperationGeneric;
1243 /*************************************************************************/
1245 #pragma mark DEALLOCATION METHODS
1246 /*************************************************************************/
1250 [hotKeysArray release];
1251 [hotKeysDictionary release];
1252 [effectClasses release];
1253 [menuTableView setDataSource:nil];
1254 [allTableView setDataSource:nil];
1255 [controller release];
1256 [availableItems release];
1257 [submenuItems release];