1 #import "PreferencesController.h"
2 #import "MainController.h"
3 #import "NetworkController.h"
4 #import "NetworkObject.h"
5 #import "StatusWindow.h"
6 #import "StatusWindowController.h"
7 #import "CustomMenuTableView.h"
11 #import <openssl/sha.h>
15 #import <ITFoundation/ITFoundation.h>
17 #import <ITKit/ITHotKeyCenter.h>
18 #import <ITKit/ITKeyCombo.h>
19 #import <ITKit/ITKeyComboPanel.h>
20 #import <ITKit/ITWindowPositioning.h>
21 #import <ITKit/ITKeyBroadcaster.h>
23 #import <ITKit/ITTSWBackgroundView.h>
24 #import <ITKit/ITWindowEffect.h>
25 #import <ITKit/ITCutWindowEffect.h>
26 #import <ITKit/ITDissolveWindowEffect.h>
27 #import <ITKit/ITSlideHorizontallyWindowEffect.h>
28 #import <ITKit/ITSlideVerticallyWindowEffect.h>
29 #import <ITKit/ITPivotWindowEffect.h>
32 #define SENDER_STATE (([sender state] == NSOnState) ? YES : NO)
34 /*************************************************************************/
36 #pragma mark PRIVATE INTERFACE
37 /*************************************************************************/
39 @interface PreferencesController (Private)
41 - (void)setupCustomizationTables;
42 - (void)setupMenuItems;
44 - (void)setStatusWindowEntryEffect:(Class)effectClass;
45 - (void)setStatusWindowExitEffect:(Class)effectClass;
46 - (void)setCustomColor:(NSColor *)color updateWell:(BOOL)update;
47 - (void)repopulateEffectPopupsForVerticalPosition:(ITVerticalWindowPosition)vPos horizontalPosition:(ITHorizontalWindowPosition)hPos;
48 - (BOOL)effect:(Class)effectClass supportsVerticalPosition:(ITVerticalWindowPosition)vPos withHorizontalPosition:(ITHorizontalWindowPosition)hPos;
49 - (IBAction)changeMenus:(id)sender;
50 - (void)setLaunchesAtLogin:(BOOL)flag;
54 @implementation PreferencesController
57 /*************************************************************************/
59 #pragma mark STATIC VARIABLES
60 /*************************************************************************/
62 static PreferencesController *prefs = nil;
65 /*************************************************************************/
67 #pragma mark INITIALIZATION METHODS
68 /*************************************************************************/
70 + (PreferencesController *)sharedPrefs;
73 prefs = [[self alloc] init];
80 if ( (self = [super init]) ) {
81 ITDebugLog(@"Preferences initialized.");
82 df = [[NSUserDefaults standardUserDefaults] retain];
84 effectClasses = [[ITWindowEffect effectClasses] retain];
86 hotKeysArray = [[NSArray alloc] initWithObjects:@"PlayPause",
100 hotKeyNamesArray = [[NSArray alloc] initWithObjects:@"Play/Pause",
113 hotKeysDictionary = [[NSMutableDictionary alloc] init];
116 [self setupWindow]; // Load in the nib, and perform any initial setup.
117 [[NSColorPanel sharedColorPanel] setShowsAlpha:YES];
123 /*************************************************************************/
125 #pragma mark ACCESSOR METHODS
126 /*************************************************************************/
133 - (void)setController:(id)object
135 [controller autorelease];
136 controller = [object retain];
140 /*************************************************************************/
142 #pragma mark INSTANCE METHODS
143 /*************************************************************************/
145 - (BOOL)showPasswordPanel
147 [passwordPanel setLevel:NSStatusWindowLevel];
148 [passwordPanelOKButton setTitle:@"Connect"];
149 [passwordPanelTitle setStringValue:@"Password Required"];
150 [passwordPanelMessage setStringValue:[NSString stringWithFormat:@"Please enter a password for access to the MenuTunes player named %@ at %@.", [[[NetworkController sharedController] networkObject] serverName], [[NetworkController sharedController] remoteHost]]];
151 [passwordPanel setLevel:NSStatusWindowLevel];
152 [NSApp activateIgnoringOtherApps:YES];
153 [passwordPanel center];
154 [passwordPanel orderFrontRegardless];
155 [passwordPanel makeKeyWindow];
156 if ([NSApp runModalForWindow:passwordPanel]) {
163 - (BOOL)showInvalidPasswordPanel
165 [passwordPanel setLevel:NSStatusWindowLevel];
166 [passwordPanelOKButton setTitle:@"Retry"];
167 [passwordPanelTitle setStringValue:@"Invalid Password"];
168 [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]]];
169 [passwordPanel setLevel:NSStatusWindowLevel];
170 [NSApp activateIgnoringOtherApps:YES];
171 [passwordPanel center];
172 [passwordPanel orderFrontRegardless];
173 [passwordPanel makeKeyWindow];
174 if ([NSApp runModalForWindow:passwordPanel]) {
181 - (IBAction)showPrefsWindow:(id)sender
183 ITDebugLog(@"Showing preferences window.");
184 if (!myItems) { // If menu array does not exist yet, then the window hasn't been setup.
185 ITDebugLog(@"Window doesn't exist, initial setup.");
186 [self setupCustomizationTables]; // Setup the DnD manu config tables.
187 [self setupMenuItems]; // Setup the arrays of menu items
188 [self setupUI]; // Sets up additional UI
189 [window setDelegate:self];
190 [menuTableView reloadData];
191 [hotKeysTableView setDoubleAction:@selector(hotKeysTableViewDoubleClicked:)];
193 //Change the launch player checkbox to the proper name
195 [launchPlayerAtLaunchCheckbox setTitle:[NSString stringWithFormat:@"Launch %@ when MenuTunes launches", [[controller currentRemote] playerSimpleName]]]; //This isn't localized...
197 [controller networkError:localException];
201 [self resetRemotePlayerTextFields];
202 [launchAtLoginCheckbox becomeFirstResponder];
203 [NSApp activateIgnoringOtherApps:YES];
205 [window orderFrontRegardless];
206 [window makeKeyWindow];
209 - (IBAction)showTestWindow:(id)sender
211 [controller showTestWindow];
214 - (IBAction)changeGeneralSetting:(id)sender
216 ITDebugLog(@"Changing general setting of tag %i.", [sender tag]);
217 if ( [sender tag] == 1010) {
218 [self setLaunchesAtLogin:SENDER_STATE];
219 } else if ( [sender tag] == 1020) {
220 [df setBool:SENDER_STATE forKey:@"LaunchPlayerWithMT"];
221 } else if ( [sender tag] == 1030) {
222 [df setInteger:[sender intValue] forKey:@"SongsInAdvance"];
224 } else if ( [sender tag] == 1040) {
225 // This will not be executed. Song info always shows the title of the song.
226 // [df setBool:SENDER_STATE forKey:@"showName"];
227 } else if ( [sender tag] == 1050) {
228 [df setBool:SENDER_STATE forKey:@"showArtist"];
229 } else if ( [sender tag] == 1060) {
230 [df setBool:SENDER_STATE forKey:@"showAlbum"];
231 } else if ( [sender tag] == 1070) {
232 [df setBool:SENDER_STATE forKey:@"showTime"];
233 } else if ( [sender tag] == 1080) {
234 [df setBool:SENDER_STATE forKey:@"showTrackNumber"];
235 } else if ( [sender tag] == 1090) {
236 [df setBool:SENDER_STATE forKey:@"showTrackRating"];
237 } else if ( [sender tag] == 1100) {
238 [df setBool:SENDER_STATE forKey:@"showAlbumArtwork"];
239 } else if ( [sender tag] == 1110) {
240 [df setBool:SENDER_STATE forKey:@"runScripts"];
242 [runScriptsCheckbox setState:NSOnState];
243 [showScriptsButton setEnabled:YES];
245 [showScriptsButton setEnabled:NO];
247 } else if ( [sender tag] == 1120) {
248 mkdir([[NSHomeDirectory() stringByAppendingPathComponent:@"Library/Application Support/MenuTunes/Scripts"] cString], 0744);
249 [[NSWorkspace sharedWorkspace] openFile:[NSHomeDirectory() stringByAppendingPathComponent:@"Library/Application Support/MenuTunes/Scripts"]];
254 - (IBAction)changeSharingSetting:(id)sender
256 ITDebugLog(@"Changing sharing setting of tag %i.", [sender tag]);
257 if ( [sender tag] == 5010 ) {
258 BOOL state = SENDER_STATE;
259 [df setBool:state forKey:@"enableSharing"];
260 //Disable/enable the use of shared player options
261 [useSharedMenuTunesCheckbox setEnabled:!state];
262 [passwordTextField setEnabled:state];
263 [nameTextField setEnabled:state];
264 [selectSharedPlayerButton setEnabled:NO];
265 [controller setServerStatus:state]; //Set server status
266 } else if ( [sender tag] == 5015 ) {
267 [df setObject:[sender stringValue] forKey:@"sharedPlayerName"];
268 [[NetworkController sharedController] resetServerName];
269 } else if ( [sender tag] == 5030 ) {
270 //Set the server password
271 const char *instring = [[sender stringValue] UTF8String];
272 const char *password = "p4s5w0rdMT1.2";
273 unsigned char *result;
274 NSData *hashedPass, *passwordStringHash;
275 if ([[sender stringValue] length] == 0) {
276 [df setObject:[NSData data] forKey:@"sharedPlayerPassword"];
279 result = SHA1(instring, strlen(instring), NULL);
280 hashedPass = [NSData dataWithBytes:result length:strlen(result)];
281 result = SHA1(password, strlen(password), NULL);
282 passwordStringHash = [NSData dataWithBytes:result length:strlen(result)];
283 if (![hashedPass isEqualToData:passwordStringHash]) {
284 [df setObject:hashedPass forKey:@"sharedPlayerPassword"];
285 [sender setStringValue:@"p4s5w0rdMT1.2"];
287 } else if ( [sender tag] == 5040 ) {
288 BOOL state = SENDER_STATE;
289 [df setBool:state forKey:@"useSharedPlayer"];
290 //Disable/enable the use of sharing options
291 [shareMenuTunesCheckbox setEnabled:!state];
292 [passwordTextField setEnabled:NO];
293 [nameTextField setEnabled:NO];
294 [selectSharedPlayerButton setEnabled:state];
296 if (state && ([controller connectToServer] == 1)) {
297 [selectedPlayerTextField setStringValue:[[[NetworkController sharedController] networkObject] serverName]];
298 [locationTextField setStringValue:[[NetworkController sharedController] remoteHost]];
300 [selectedPlayerTextField setStringValue:@"No shared player selected."];
301 [locationTextField setStringValue:@"-"];
302 if ([[NetworkController sharedController] isConnectedToServer]) {
303 [controller disconnectFromServer];
307 } else if ( [sender tag] == 5050 ) {
308 //If no player is selected in the table view, turn off OK button.
309 if ([sender clickedRow] == -1 ) {
310 [sharingPanelOKButton setEnabled:NO];
312 [sharingPanelOKButton setEnabled:YES];
314 } else if ( [sender tag] == 5051 ) {
315 [df setObject:[sender stringValue] forKey:@"sharedPlayerHost"];
316 } else if ( [sender tag] == 5060 ) {
317 //Set OK button state
318 if (([selectPlayerBox contentView] == zeroConfView && [sharingTableView selectedRow] == -1) ||
319 ([selectPlayerBox contentView] == manualView && [[hostTextField stringValue] length] == 0)) {
320 [sharingPanelOKButton setEnabled:NO];
322 [sharingPanelOKButton setEnabled:YES];
324 //Show selection sheet
325 [NSApp beginSheet:selectPlayerSheet modalForWindow:window modalDelegate:self didEndSelector:NULL contextInfo:nil];
326 } else if ( [sender tag] == 5100 ) {
328 if ( ([sender indexOfItem:[sender selectedItem]] == 0) && ([selectPlayerBox contentView] != zeroConfView) ) {
329 NSRect frame = [selectPlayerSheet frame];
330 frame.origin.y -= 58;
331 frame.size.height = 273;
332 if ([sharingTableView selectedRow] == -1) {
333 [sharingPanelOKButton setEnabled:NO];
335 [selectPlayerBox setContentView:zeroConfView];
336 [selectPlayerSheet setFrame:frame display:YES animate:YES];
337 } else if ( ([sender indexOfItem:[sender selectedItem]] == 1) && ([selectPlayerBox contentView] != manualView) ){
338 NSRect frame = [selectPlayerSheet frame];
339 frame.origin.y += 58;
340 frame.size.height = 215;
341 if ([[hostTextField stringValue] length] == 0) {
342 [sharingPanelOKButton setEnabled:NO];
344 [sharingPanelOKButton setEnabled:YES];
346 [selectPlayerBox setContentView:manualView];
347 [selectPlayerSheet setFrame:frame display:YES animate:YES];
348 [hostTextField selectText:nil];
350 } else if ( [sender tag] == 5150 ) {
351 const char *instring = [[sender stringValue] UTF8String];
352 unsigned char *result;
353 result = SHA1(instring, strlen(instring), NULL);
354 [df setObject:[NSData dataWithBytes:result length:strlen(result)] forKey:@"connectPassword"];
355 } else if ( [sender tag] == 5110 ) {
357 [NSApp endSheet:selectPlayerSheet];
358 [selectPlayerSheet orderOut:nil];
359 if ([selectPlayerBox contentView] == manualView) {
360 [hostTextField setStringValue:[df stringForKey:@"sharedPlayerHost"]];
363 } else if ( [sender tag] == 5120 ) {
365 [NSApp endSheet:selectPlayerSheet];
366 [selectPlayerSheet orderOut:nil];
368 [self changeSharingSetting:clientPasswordTextField];
370 if ([selectPlayerBox contentView] == manualView) {
371 [df setObject:[hostTextField stringValue] forKey:@"sharedPlayerHost"];
373 if ([sharingTableView selectedRow] > -1) {
374 [df setObject:[NSString stringWithCString:inet_ntoa((*(struct sockaddr_in*)[[[[[[NetworkController sharedController] remoteServices] objectAtIndex:[sharingTableView selectedRow]] addresses] objectAtIndex:0] bytes]).sin_addr)] forKey:@"sharedPlayerHost"];
378 if ([controller connectToServer] == 1) {
379 [useSharedMenuTunesCheckbox setState:NSOnState];
380 [selectedPlayerTextField setStringValue:[[[NetworkController sharedController] networkObject] serverName]];
381 [locationTextField setStringValue:[[NetworkController sharedController] remoteHost]];
383 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);
385 } else if ( [sender tag] == 6010 ) {
386 //Cancel password entry
387 [passwordPanel orderOut:nil];
388 [NSApp stopModalWithCode:0];
389 } else if ( [sender tag] == 6020 ) {
390 //OK password entry, retry connect
391 const char *instring = [[passwordPanelTextField stringValue] UTF8String];
392 unsigned char *result;
393 result = SHA1(instring, strlen(instring), NULL);
394 [df setObject:[NSData dataWithBytes:result length:strlen(result)] forKey:@"connectPassword"];
395 [passwordPanel orderOut:nil];
396 [NSApp stopModalWithCode:1];
401 - (IBAction)changeStatusWindowSetting:(id)sender
403 StatusWindow *sw = [StatusWindow sharedWindow];
404 ITDebugLog(@"Changing status window setting of tag %i", [sender tag]);
406 if ( [sender tag] == 2010) {
408 BOOL entryEffectValid = YES;
409 BOOL exitEffectValid = YES;
411 [df setInteger:[sender selectedRow] forKey:@"statusWindowVerticalPosition"];
412 [df setInteger:[sender selectedColumn] forKey:@"statusWindowHorizontalPosition"];
413 [sw setVerticalPosition:[sender selectedRow]];
414 [sw setHorizontalPosition:[sender selectedColumn]];
416 // Enable/disable the items in the popups.
417 [self repopulateEffectPopupsForVerticalPosition:[sw verticalPosition]
418 horizontalPosition:[sw horizontalPosition]];
420 // Make sure the effects support the new position.
421 entryEffectValid = ( [self effect:[[sw entryEffect] class]
422 supportsVerticalPosition:[sw verticalPosition]
423 withHorizontalPosition:[sw horizontalPosition]] );
424 exitEffectValid = ( [self effect:[[sw exitEffect] class]
425 supportsVerticalPosition:[sw verticalPosition]
426 withHorizontalPosition:[sw horizontalPosition]] );
428 if ( ! entryEffectValid ) {
429 [appearanceEffectPopup selectItemAtIndex:[[appearanceEffectPopup menu] indexOfItemWithRepresentedObject:NSClassFromString(@"ITCutWindowEffect")]];
430 [self setStatusWindowEntryEffect:NSClassFromString(@"ITCutWindowEffect")];
432 [appearanceEffectPopup selectItemAtIndex:[[appearanceEffectPopup menu] indexOfItemWithRepresentedObject:[[sw entryEffect] class]]];
435 if ( ! exitEffectValid ) {
436 [vanishEffectPopup selectItemAtIndex:[[vanishEffectPopup menu] indexOfItemWithRepresentedObject:NSClassFromString(@"ITDissolveWindowEffect")]];
437 [self setStatusWindowExitEffect:NSClassFromString(@"ITDissolveWindowEffect")];
439 [vanishEffectPopup selectItemAtIndex:[[vanishEffectPopup menu] indexOfItemWithRepresentedObject:[[sw exitEffect] class]]];
442 [(MainController *)controller showCurrentTrackInfo];
444 } else if ( [sender tag] == 2020) {
446 // Update screen selection.
448 } else if ( [sender tag] == 2030) {
450 [self setStatusWindowEntryEffect:[[sender selectedItem] representedObject]];
452 } else if ( [sender tag] == 2040) {
454 [self setStatusWindowExitEffect:[[sender selectedItem] representedObject]];
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"];
489 } else if ( [sender tag] == 2091) {
490 [self setCustomColor:[sender color] updateWell:NO];
491 } else if ( [sender tag] == 2092) {
493 int selectedItem = [sender indexOfSelectedItem];
495 if ( selectedItem == 1 ) { // An NSPopUpButton in PullDown mode uses item 0 as its title. Its first selectable item is 1.
496 [self setCustomColor:[NSColor colorWithCalibratedRed:0.92549 green:0.686275 blue:0.0 alpha:1.0] updateWell:YES];
497 } else if ( selectedItem == 2 ) {
498 [self setCustomColor:[NSColor colorWithCalibratedRed:0.380392 green:0.670588 blue:0.0 alpha:1.0] updateWell:YES];
499 } else if ( selectedItem == 3 ) {
500 [self setCustomColor:[NSColor colorWithCalibratedRed:0.443137 green:0.231373 blue:0.619608 alpha:1.0] updateWell:YES];
501 } else if ( selectedItem == 4 ) {
502 [self setCustomColor:[NSColor colorWithCalibratedRed:0.831373 green:0.12549 blue:0.509804 alpha:1.0] updateWell:YES];
503 } else if ( selectedItem == 5 ) {
504 [self setCustomColor:[NSColor colorWithCalibratedRed:0.00784314 green:0.611765 blue:0.662745 alpha:1.0] updateWell:YES];
506 [self setCustomColor:[NSColor colorWithCalibratedWhite:0.15 alpha:0.70] updateWell:YES];
509 } else if ( [sender tag] == 2095) {
510 [df setInteger:[sender indexOfSelectedItem] forKey:@"statusWindowSizing"];
511 [(MainController *)controller showCurrentTrackInfo];
517 - (void)registerDefaults
520 NSMutableDictionary *loginWindow;
521 NSMutableArray *loginArray;
522 NSEnumerator *loginEnum;
524 ITDebugLog(@"Registering defaults.");
525 [df setObject:[NSArray arrayWithObjects:
537 nil] forKey:@"menu"];
539 [df setInteger:MT_CURRENT_VERSION forKey:@"appVersion"];
540 [df setInteger:5 forKey:@"SongsInAdvance"];
541 // [df setBool:YES forKey:@"showName"]; // Song info will always show song title.
542 [df setBool:YES forKey:@"showArtist"];
543 [df setBool:YES forKey:@"showAlbumArtwork"];
544 [df setBool:NO forKey:@"showAlbum"];
545 [df setBool:NO forKey:@"showTime"];
547 [df setObject:@"ITCutWindowEffect" forKey:@"statusWindowAppearanceEffect"];
548 [df setObject:@"ITDissolveWindowEffect" forKey:@"statusWindowVanishEffect"];
549 [df setFloat:0.8 forKey:@"statusWindowAppearanceSpeed"];
550 [df setFloat:0.8 forKey:@"statusWindowVanishSpeed"];
551 [df setFloat:4.0 forKey:@"statusWindowVanishDelay"];
552 [df setInteger:(int)ITWindowPositionBottom forKey:@"statusWindowVerticalPosition"];
553 [df setInteger:(int)ITWindowPositionLeft forKey:@"statusWindowHorizontalPosition"];
554 [[StatusWindow sharedWindow] setVerticalPosition:(int)ITWindowPositionBottom];
555 [[StatusWindow sharedWindow] setHorizontalPosition:(int)ITWindowPositionLeft];
556 [df setBool:YES forKey:@"showSongInfoOnChange"];
558 [df setObject:[NSArchiver archivedDataWithRootObject:[NSColor blueColor]] forKey:@"statusWindowBackgroundColor"];
562 loginWindow = [[df persistentDomainForName:@"loginwindow"] mutableCopy];
563 loginArray = [loginWindow objectForKey:@"AutoLaunchedApplicationDictionary"];
564 loginEnum = [loginArray objectEnumerator];
566 while ( (anItem = [loginEnum nextObject]) ) {
567 if ( [[[anItem objectForKey:@"Path"] lastPathComponent] isEqualToString:[[[NSBundle mainBundle] bundlePath] lastPathComponent]] ) {
571 [loginWindow release];
574 [[StatusWindowController sharedController] showSetupQueryWindow];
580 [[StatusWindow sharedWindow] setLocked:NO];
581 [[StatusWindow sharedWindow] vanish:self];
582 [[StatusWindow sharedWindow] setIgnoresMouseEvents:YES];
584 [self setLaunchesAtLogin:YES];
587 - (void)autoLaunchCancel
589 [[StatusWindow sharedWindow] setLocked:NO];
590 [[StatusWindow sharedWindow] vanish:self];
591 [[StatusWindow sharedWindow] setIgnoresMouseEvents:YES];
594 - (void)deletePressedInTableView:(NSTableView *)tableView
596 if (tableView == menuTableView) {
597 int selRow = [tableView selectedRow];
598 ITDebugLog(@"Delete pressed in menu table view.");
600 NSString *object = [myItems objectAtIndex:selRow];
602 if ([object isEqualToString:@"preferences"]) {
607 if (![object isEqualToString:@"separator"])
608 [availableItems addObject:object];
609 ITDebugLog(@"Removing object named %@", object);
610 [myItems removeObjectAtIndex:selRow];
611 [menuTableView reloadData];
612 [allTableView reloadData];
614 [self changeMenus:self];
618 - (void)resetRemotePlayerTextFields
620 if ([[NetworkController sharedController] isConnectedToServer]) {
621 [selectedPlayerTextField setStringValue:[[[NetworkController sharedController] networkObject] serverName]];
622 [locationTextField setStringValue:[[NetworkController sharedController] remoteHost]];
624 [selectedPlayerTextField setStringValue:@"No shared player selected."];
625 [locationTextField setStringValue:@"-"];
629 /*************************************************************************/
631 #pragma mark HOTKEY SUPPORT METHODS
632 /*************************************************************************/
634 - (IBAction)clearHotKey:(id)sender
636 [hotKeysDictionary setObject:[ITKeyCombo clearKeyCombo] forKey:[hotKeysArray objectAtIndex:[hotKeysTableView selectedRow]]];
637 [df setObject:[[ITKeyCombo clearKeyCombo] plistRepresentation] forKey:[hotKeysArray objectAtIndex:[hotKeysTableView selectedRow]]];
638 [controller setupHotKeys];
639 [hotKeysTableView reloadData];
642 - (IBAction)editHotKey:(id)sender
644 ITKeyComboPanel *panel = [ITKeyComboPanel sharedPanel];
645 NSString *keyComboKey = [hotKeysArray objectAtIndex:[hotKeysTableView selectedRow]];
646 ITKeyCombo *keyCombo;
648 ITDebugLog(@"Setting key combo on hot key %@.", keyComboKey);
649 [controller clearHotKeys];
650 [panel setKeyCombo:[hotKeysDictionary objectForKey:[hotKeysArray objectAtIndex:[hotKeysTableView selectedRow]]]];
651 [panel setKeyBindingName:[hotKeyNamesArray objectAtIndex:[hotKeysTableView selectedRow]]];
652 if ([panel runModal] == NSOKButton) {
653 NSEnumerator *keyEnumerator = [[hotKeysDictionary allKeys] objectEnumerator];
655 keyCombo = [panel keyCombo];
657 //Check for duplicate key combo
658 while ( (nextKey = [keyEnumerator nextObject]) ) {
659 if ([[hotKeysDictionary objectForKey:nextKey] isEqual:keyCombo] &&
660 ![keyCombo isEqual:[ITKeyCombo clearKeyCombo]]) {
661 [hotKeysDictionary setObject:[ITKeyCombo clearKeyCombo]
663 [df setObject:[[ITKeyCombo clearKeyCombo] plistRepresentation]
668 [hotKeysDictionary setObject:keyCombo forKey:keyComboKey];
669 [df setObject:[keyCombo plistRepresentation] forKey:keyComboKey];
670 [controller setupHotKeys];
671 [hotKeysTableView reloadData];
672 ITDebugLog(@"Set combo %@ on hot key %@.", keyCombo, keyComboKey);
674 ITDebugLog(@"Hot key setting on hot key %@ cancelled.", keyComboKey);
678 - (void)hotKeysTableViewDoubleClicked:(id)sender
680 if ([sender clickedRow] > -1) {
681 [self editHotKey:sender];
685 /*************************************************************************/
687 #pragma mark PRIVATE METHOD IMPLEMENTATIONS
688 /*************************************************************************/
692 ITDebugLog(@"Loading Preferences.nib.");
693 if (![NSBundle loadNibNamed:@"Preferences" owner:self]) {
694 ITDebugLog(@"Failed to load Preferences.nib.");
700 - (void)setupCustomizationTables
702 NSImageCell *imgCell = [[[NSImageCell alloc] initImageCell:nil] autorelease];
703 ITDebugLog(@"Setting up table views.");
704 // Set the table view cells up
705 [imgCell setImageScaling:NSScaleNone];
706 [[menuTableView tableColumnWithIdentifier:@"submenu"] setDataCell:imgCell];
707 [[allTableView tableColumnWithIdentifier:@"submenu"] setDataCell:imgCell];
709 // Register for drag and drop
710 [menuTableView registerForDraggedTypes:[NSArray arrayWithObjects:
711 @"MenuTableViewPboardType",
712 @"AllTableViewPboardType",
714 [allTableView registerForDraggedTypes:[NSArray arrayWithObjects:
715 @"MenuTableViewPboardType",
716 @"AllTableViewPboardType",
720 - (void)setupMenuItems
722 NSEnumerator *itemEnum;
724 ITDebugLog(@"Setting up table view arrays.");
725 // Set the list of items you can have.
726 availableItems = [[NSMutableArray alloc] initWithObjects:
742 // Get our preferred menu
743 myItems = [[df arrayForKey:@"menu"] mutableCopy];
745 // Delete items in the availableItems array that are already part of the menu
746 itemEnum = [myItems objectEnumerator];
747 while ( (anItem = [itemEnum nextObject]) ) {
748 if (![anItem isEqualToString:@"separator"]) {
749 [availableItems removeObject:anItem];
753 // Items that show should a submenu image
754 submenuItems = [[NSArray alloc] initWithObjects:
764 NSMutableDictionary *loginwindow;
765 NSMutableArray *loginarray;
766 NSEnumerator *loginEnum;
767 NSEnumerator *keyArrayEnum;
768 NSString *serverName;
773 [df setInteger:MT_CURRENT_VERSION forKey:@"appVersion"];
775 ITDebugLog(@"Setting up preferences UI.");
776 // Fill in the number of songs in advance to show field
777 [songsInAdvance setIntValue:[df integerForKey:@"SongsInAdvance"]];
779 // Fill hot key array
780 keyArrayEnum = [hotKeysArray objectEnumerator];
782 while ( (anItem = [keyArrayEnum nextObject]) ) {
783 if ([df objectForKey:anItem]) {
784 ITDebugLog(@"Setting up \"%@\" hot key.", anItem);
785 [hotKeysDictionary setObject:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:anItem]] forKey:anItem];
787 [hotKeysDictionary setObject:[ITKeyCombo clearKeyCombo] forKey:anItem];
791 ITDebugLog(@"Setting up track info checkboxes.");
792 // Check current track info buttons
793 [albumCheckbox setState:[df boolForKey:@"showAlbum"] ? NSOnState : NSOffState];
794 [nameCheckbox setState:NSOnState]; // Song info will ALWAYS show song title.
795 [nameCheckbox setEnabled:NO]; // Song info will ALWAYS show song title.
796 [artistCheckbox setState:[df boolForKey:@"showArtist"] ? NSOnState : NSOffState];
797 [trackTimeCheckbox setState:[df boolForKey:@"showTime"] ? NSOnState : NSOffState];
798 [trackNumberCheckbox setState:[df boolForKey:@"showTrackNumber"] ? NSOnState : NSOffState];
799 [ratingCheckbox setState:[df boolForKey:@"showTrackRating"] ? NSOnState : NSOffState];
800 [albumArtworkCheckbox setState:[df boolForKey:@"showAlbumArtwork"] ? NSOnState : NSOffState];
802 if ([df boolForKey:@"runScripts"]) {
803 [runScriptsCheckbox setState:NSOnState];
804 [showScriptsButton setEnabled:YES];
806 [showScriptsButton setEnabled:NO];
809 // Set the launch at login checkbox state
810 ITDebugLog(@"Setting launch at login state.");
812 loginwindow = [[df persistentDomainForName:@"loginwindow"] mutableCopy];
813 loginarray = [loginwindow objectForKey:@"AutoLaunchedApplicationDictionary"];
815 loginEnum = [loginarray objectEnumerator];
816 while ( (anItem = [loginEnum nextObject]) ) {
817 if ([[[anItem objectForKey:@"Path"] lastPathComponent] isEqualToString:[[[NSBundle mainBundle] bundlePath] lastPathComponent]]) {
818 [launchAtLoginCheckbox setState:NSOnState];
822 // Set the launch player checkbox state
823 ITDebugLog(@"Setting launch player with MenuTunes state.");
824 [launchPlayerAtLaunchCheckbox setState:[df boolForKey:@"LaunchPlayerWithMT"] ? NSOnState : NSOffState];
826 // Setup the positioning controls
827 [positionMatrix selectCellAtRow:[df integerForKey:@"statusWindowVerticalPosition"]
828 column:[df integerForKey:@"statusWindowHorizontalPosition"]];
830 // Setup effects controls
831 // Populate the effects popups
832 [appearanceEffectPopup setAutoenablesItems:NO];
833 [vanishEffectPopup setAutoenablesItems:NO];
834 [self repopulateEffectPopupsForVerticalPosition:[df integerForKey:@"statusWindowVerticalPosition"]
835 horizontalPosition:[df integerForKey:@"statusWindowHorizontalPosition"]];
837 // Attempt to find the pref'd effect in the list.
838 // If it's not there, use cut/dissolve.
839 if ( [effectClasses containsObject:NSClassFromString([df stringForKey:@"statusWindowAppearanceEffect"])] ) {
840 [appearanceEffectPopup selectItemAtIndex:[effectClasses indexOfObject:NSClassFromString([df stringForKey:@"statusWindowAppearanceEffect"])]];
842 [appearanceEffectPopup selectItemAtIndex:[effectClasses indexOfObject:NSClassFromString(@"ITCutWindowEffect")]];
845 if ( [effectClasses containsObject:NSClassFromString([df stringForKey:@"statusWindowVanishEffect"])] ) {
846 [vanishEffectPopup selectItemAtIndex:[effectClasses indexOfObject:NSClassFromString([df stringForKey:@"statusWindowVanishEffect"])]];
848 [vanishEffectPopup selectItemAtIndex:[effectClasses indexOfObject:NSClassFromString(@"ITCutWindowEffect")]];
851 [appearanceSpeedSlider setFloatValue:( -([df floatForKey:@"statusWindowAppearanceSpeed"]) )];
852 [vanishSpeedSlider setFloatValue:( -([df floatForKey:@"statusWindowVanishSpeed"]) )];
853 [vanishDelaySlider setFloatValue:[df floatForKey:@"statusWindowVanishDelay"]];
855 // Setup General Controls
856 selectedBGStyle = [df integerForKey:@"statusWindowBackgroundMode"];
857 [backgroundStylePopup selectItem:[backgroundStylePopup itemAtIndex:[backgroundStylePopup indexOfItemWithTag:selectedBGStyle]]];
859 if ( selectedBGStyle == ITTSWBackgroundColored ) {
860 [backgroundColorWell setEnabled:YES];
861 [backgroundColorPopup setEnabled:YES];
863 [backgroundColorWell setEnabled:NO];
864 [backgroundColorPopup setEnabled:NO];
867 colorData = [df dataForKey:@"statusWindowBackgroundColor"];
870 [backgroundColorWell setColor:(NSColor *)[NSUnarchiver unarchiveObjectWithData:colorData]];
872 [backgroundColorWell setColor:[NSColor blueColor]];
875 [showOnChangeCheckbox setState:([df boolForKey:@"showSongInfoOnChange"] ? NSOnState : NSOffState)];
877 [windowSizingPopup selectItem:[windowSizingPopup itemAtIndex:[windowSizingPopup indexOfItemWithTag:[df integerForKey:@"statusWindowSizing"]]]];
879 // Setup the sharing controls
880 if ([df boolForKey:@"enableSharing"]) {
881 [shareMenuTunesCheckbox setState:NSOnState];
882 [useSharedMenuTunesCheckbox setEnabled:NO];
883 [selectSharedPlayerButton setEnabled:NO];
884 [passwordTextField setEnabled:YES];
885 [nameTextField setEnabled:YES];
886 } else if ([df boolForKey:@"useSharedPlayer"]) {
887 [useSharedMenuTunesCheckbox setState:NSOnState];
888 [shareMenuTunesCheckbox setEnabled:NO];
889 [selectSharedPlayerButton setEnabled:YES];
892 [[NSNotificationCenter defaultCenter] addObserver:sharingTableView selector:@selector(reloadData) name:@"ITMTFoundNetService" object:nil];
894 serverName = [df stringForKey:@"sharedPlayerName"];
895 if (!serverName || [serverName length] == 0) {
896 serverName = @"MenuTunes Shared Player";
898 [nameTextField setStringValue:serverName];
900 [selectPlayerBox setContentView:zeroConfView];
901 if ([[df dataForKey:@"sharedPlayerPassword"] length] > 0) {
902 [passwordTextField setStringValue:@"p4s5w0rdMT1.2"];
904 [passwordTextField setStringValue:@""];
906 if ([df stringForKey:@"sharedPlayerHost"]) {
907 [hostTextField setStringValue:[df stringForKey:@"sharedPlayerHost"]];
910 if ([[NetworkController sharedController] isConnectedToServer]) {
911 [selectedPlayerTextField setStringValue:[[[NetworkController sharedController] networkObject] serverName]];
912 [locationTextField setStringValue:[[NetworkController sharedController] remoteHost]];
914 [selectedPlayerTextField setStringValue:@"No shared player selected."];
915 [locationTextField setStringValue:@"-"];
919 - (void)setStatusWindowEntryEffect:(Class)effectClass
921 StatusWindow *sw = [StatusWindow sharedWindow];
923 float time = ([df floatForKey:@"statusWindowAppearanceSpeed"] ? [df floatForKey:@"statusWindowAppearanceSpeed"] : 0.8);
924 [df setObject:NSStringFromClass(effectClass) forKey:@"statusWindowAppearanceEffect"];
926 [sw setEntryEffect:[[[effectClass alloc] initWithWindow:sw] autorelease]];
927 [[sw entryEffect] setEffectTime:time];
930 - (void)setStatusWindowExitEffect:(Class)effectClass
932 StatusWindow *sw = [StatusWindow sharedWindow];
934 float time = ([df floatForKey:@"statusWindowVanishSpeed"] ? [df floatForKey:@"statusWindowVanishSpeed"] : 0.8);
935 [df setObject:NSStringFromClass(effectClass) forKey:@"statusWindowVanishEffect"];
937 [sw setExitEffect:[[[effectClass alloc] initWithWindow:sw] autorelease]];
938 [[sw exitEffect] setEffectTime:time];
941 - (void)setCustomColor:(NSColor *)color updateWell:(BOOL)update
943 [(ITTSWBackgroundView *)[[StatusWindow sharedWindow] contentView] setBackgroundColor:color];
944 [df setObject:[NSArchiver archivedDataWithRootObject:color] forKey:@"statusWindowBackgroundColor"];
947 [backgroundColorWell setColor:color];
951 - (void)repopulateEffectPopupsForVerticalPosition:(ITVerticalWindowPosition)vPos horizontalPosition:(ITHorizontalWindowPosition)hPos
953 NSEnumerator *effectEnum = [effectClasses objectEnumerator];
956 [appearanceEffectPopup removeAllItems];
957 [vanishEffectPopup removeAllItems];
959 while ( (anItem = [effectEnum nextObject]) ) {
960 [appearanceEffectPopup addItemWithTitle:[anItem effectName]];
961 [vanishEffectPopup addItemWithTitle:[anItem effectName]];
963 [[appearanceEffectPopup lastItem] setRepresentedObject:anItem];
964 [[vanishEffectPopup lastItem] setRepresentedObject:anItem];
966 if ( [self effect:anItem supportsVerticalPosition:vPos withHorizontalPosition:hPos] ) {
967 [[appearanceEffectPopup lastItem] setEnabled:YES];
968 [[vanishEffectPopup lastItem] setEnabled:YES];
970 [[appearanceEffectPopup lastItem] setEnabled:NO];
971 [[vanishEffectPopup lastItem] setEnabled:NO];
977 - (BOOL)effect:(Class)effectClass supportsVerticalPosition:(ITVerticalWindowPosition)vPos withHorizontalPosition:(ITHorizontalWindowPosition)hPos
981 if ( vPos == ITWindowPositionTop ) {
982 if ( hPos == ITWindowPositionLeft ) {
983 valid = ( [[[[effectClass supportedPositions] objectForKey:@"Top"] objectForKey:@"Left"] boolValue] ) ;
984 } else if ( hPos == ITWindowPositionCenter ) {
985 valid = ( [[[[effectClass supportedPositions] objectForKey:@"Top"] objectForKey:@"Center"] boolValue] );
986 } else if ( hPos == ITWindowPositionRight ) {
987 valid = ( [[[[effectClass supportedPositions] objectForKey:@"Top"] objectForKey:@"Right"] boolValue] );
989 } else if ( vPos == ITWindowPositionMiddle ) {
990 if ( hPos == ITWindowPositionLeft ) {
991 valid = ( [[[[effectClass supportedPositions] objectForKey:@"Middle"] objectForKey:@"Left"] boolValue] );
992 } else if ( hPos == ITWindowPositionCenter ) {
993 valid = ( [[[[effectClass supportedPositions] objectForKey:@"Middle"] objectForKey:@"Center"] boolValue] );
994 } else if ( hPos == ITWindowPositionRight ) {
995 valid = ( [[[[effectClass supportedPositions] objectForKey:@"Middle"] objectForKey:@"Right"] boolValue] );
997 } else if ( vPos == ITWindowPositionBottom ) {
998 if ( hPos == ITWindowPositionLeft ) {
999 valid = ( [[[[effectClass supportedPositions] objectForKey:@"Bottom"] objectForKey:@"Left"] boolValue] );
1000 } else if ( hPos == ITWindowPositionCenter ) {
1001 valid = ( [[[[effectClass supportedPositions] objectForKey:@"Bottom"] objectForKey:@"Center"] boolValue] );
1002 } else if ( hPos == ITWindowPositionRight ) {
1003 valid = ( [[[[effectClass supportedPositions] objectForKey:@"Bottom"] objectForKey:@"Right"] boolValue] );
1010 - (IBAction)changeMenus:(id)sender
1012 ITDebugLog(@"Synchronizing menus");
1013 [df setObject:myItems forKey:@"menu"];
1016 //If we're connected over a network, refresh the menu immediately
1017 if ([[NetworkController sharedController] isConnectedToServer]) {
1018 [controller timerUpdate];
1022 - (void)setLaunchesAtLogin:(BOOL)flag
1024 NSMutableDictionary *loginwindow;
1025 NSMutableArray *loginarray;
1026 ITDebugLog(@"Setting launches at login: %i", flag);
1028 loginwindow = [[df persistentDomainForName:@"loginwindow"] mutableCopy];
1029 loginarray = [loginwindow objectForKey:@"AutoLaunchedApplicationDictionary"];
1032 NSDictionary *itemDict = [NSDictionary dictionaryWithObjectsAndKeys:
1033 [[NSBundle mainBundle] bundlePath], @"Path",
1034 [NSNumber numberWithInt:0], @"Hide", nil];
1035 [loginarray addObject:itemDict];
1038 for (i = 0; i < [loginarray count]; i++) {
1039 NSDictionary *tempDict = [loginarray objectAtIndex:i];
1040 if ([[[tempDict objectForKey:@"Path"] lastPathComponent] isEqualToString:[[[NSBundle mainBundle] bundlePath] lastPathComponent]]) {
1041 [loginarray removeObjectAtIndex:i];
1046 [df setPersistentDomain:loginwindow forName:@"loginwindow"];
1048 [loginwindow release];
1049 ITDebugLog(@"Finished setting launches at login.");
1053 /*************************************************************************/
1055 #pragma mark NSWindow DELEGATE METHODS
1056 /*************************************************************************/
1058 - (void)windowWillClose:(NSNotification *)note
1060 [(MainController *)controller closePreferences];
1063 /*************************************************************************/
1065 #pragma mark NSTextField DELEGATE METHODS
1066 /*************************************************************************/
1068 - (void)controlTextDidChange:(NSNotification*)note
1070 if ([note object] == hostTextField) {
1071 if ([[hostTextField stringValue] length] == 0) {
1072 [sharingPanelOKButton setEnabled:NO];
1074 [sharingPanelOKButton setEnabled:YES];
1079 /*************************************************************************/
1081 #pragma mark NSTableView DATASOURCE METHODS
1082 /*************************************************************************/
1084 - (int)numberOfRowsInTableView:(NSTableView *)aTableView
1086 if (aTableView == menuTableView) {
1087 return [myItems count];
1088 } else if (aTableView == allTableView) {
1089 return [availableItems count];
1090 } else if (aTableView == hotKeysTableView) {
1091 return [hotKeysArray count];
1093 return [[[NetworkController sharedController] remoteServices] count];
1097 - (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex
1099 if (aTableView == menuTableView) {
1100 NSString *object = [myItems objectAtIndex:rowIndex];
1101 if ([[aTableColumn identifier] isEqualToString:@"name"]) {
1102 if ([object isEqualToString:@"showPlayer"]) {
1103 NSString *string = nil;
1105 string = [NSString stringWithFormat:@"%@ %@", NSLocalizedString(@"show", @"Show"), [[controller currentRemote] playerSimpleName]];
1107 [controller networkError:localException];
1111 return NSLocalizedString(object, @"ERROR");
1113 if ([submenuItems containsObject:object])
1115 return [NSImage imageNamed:@"submenu"];
1120 } else if (aTableView == allTableView) {
1121 NSString *object = [availableItems objectAtIndex:rowIndex];
1122 if ([[aTableColumn identifier] isEqualToString:@"name"]) {
1123 if ([object isEqualToString:@"showPlayer"]) {
1124 NSString *string = nil;
1126 string = [NSString stringWithFormat:@"%@ %@", NSLocalizedString(@"show", @"Show"), [[controller currentRemote] playerSimpleName]];
1128 [controller networkError:localException];
1132 return NSLocalizedString(object, @"ERROR");
1134 if ([submenuItems containsObject:object]) {
1135 return [NSImage imageNamed:@"submenu"];
1140 } else if (aTableView == hotKeysTableView) {
1141 if ([[aTableColumn identifier] isEqualToString:@"name"]) {
1142 return [hotKeyNamesArray objectAtIndex:rowIndex];
1144 return [[hotKeysDictionary objectForKey:[hotKeysArray objectAtIndex:rowIndex]] description];
1147 return [[[[NetworkController sharedController] remoteServices] objectAtIndex:rowIndex] name];
1151 - (BOOL)tableView:(NSTableView *)tableView writeRows:(NSArray*)rows toPasteboard:(NSPasteboard*)pboard
1153 if (tableView == menuTableView) {
1154 [pboard declareTypes:[NSArray arrayWithObjects:@"MenuTableViewPboardType", nil] owner:self];
1155 [pboard setString:[[rows objectAtIndex:0] stringValue] forType:@"MenuTableViewPboardType"];
1159 if (tableView == allTableView) {
1160 [pboard declareTypes:[NSArray arrayWithObjects:@"AllTableViewPboardType", nil] owner:self];
1161 [pboard setString:[[rows objectAtIndex:0] stringValue] forType:@"AllTableViewPboardType"];
1167 - (BOOL)tableView:(NSTableView*)tableView acceptDrop:(id <NSDraggingInfo>)info row:(int)row dropOperation:(NSTableViewDropOperation)operation
1171 NSString *dragData, *temp;
1173 pb = [info draggingPasteboard];
1175 if ([[pb types] containsObject:@"MenuTableViewPboardType"]) {
1176 dragData = [pb stringForType:@"MenuTableViewPboardType"];
1177 dragRow = [dragData intValue];
1178 temp = [myItems objectAtIndex:dragRow];
1180 if (tableView == menuTableView) {
1181 [myItems insertObject:temp atIndex:row];
1182 if (row > dragRow) {
1183 [myItems removeObjectAtIndex:dragRow];
1185 [myItems removeObjectAtIndex:dragRow + 1];
1187 } else if (tableView == allTableView) {
1188 if (![temp isEqualToString:@"separator"]) {
1189 [availableItems addObject:temp];
1191 [myItems removeObjectAtIndex:dragRow];
1193 } else if ([[pb types] containsObject:@"AllTableViewPboardType"]) {
1194 dragData = [pb stringForType:@"AllTableViewPboardType"];
1195 dragRow = [dragData intValue];
1196 temp = [availableItems objectAtIndex:dragRow];
1198 [myItems insertObject:temp atIndex:row];
1200 if (![temp isEqualToString:@"separator"]) {
1201 [availableItems removeObjectAtIndex:dragRow];
1205 [menuTableView reloadData];
1206 [allTableView reloadData];
1207 [self changeMenus:self];
1211 - (NSDragOperation)tableView:(NSTableView*)tableView validateDrop:(id <NSDraggingInfo>)info proposedRow:(int)row proposedDropOperation:(NSTableViewDropOperation)operation
1213 if (tableView == allTableView) {
1214 if ([[[info draggingPasteboard] types] containsObject:@"AllTableViewPboardType"]) {
1215 return NSDragOperationNone;
1218 if ([[[info draggingPasteboard] types] containsObject:@"MenuTableViewPboardType"]) {
1219 NSString *item = [myItems objectAtIndex:[[[info draggingPasteboard] stringForType:@"MenuTableViewPboardType"] intValue]];
1220 if ([item isEqualToString:@"preferences"] || [item isEqualToString:@"quit"]) {
1221 return NSDragOperationNone;
1225 [tableView setDropRow:-1 dropOperation:NSTableViewDropOn];
1226 return NSDragOperationGeneric;
1229 if (operation == NSTableViewDropOn || row == -1)
1231 return NSDragOperationNone;
1233 return NSDragOperationGeneric;
1237 /*************************************************************************/
1239 #pragma mark DEALLOCATION METHODS
1240 /*************************************************************************/
1244 [hotKeysArray release];
1245 [hotKeysDictionary release];
1246 [effectClasses release];
1247 [menuTableView setDataSource:nil];
1248 [allTableView setDataSource:nil];
1249 [controller release];
1250 [availableItems release];
1251 [submenuItems release];