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:1200 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 ITDebugLog(@"Setting up preferences UI.");
774 // Fill in the number of songs in advance to show field
775 [songsInAdvance setIntValue:[df integerForKey:@"SongsInAdvance"]];
777 // Fill hot key array
778 keyArrayEnum = [hotKeysArray objectEnumerator];
780 while ( (anItem = [keyArrayEnum nextObject]) ) {
781 if ([df objectForKey:anItem]) {
782 ITDebugLog(@"Setting up \"%@\" hot key.", anItem);
783 [hotKeysDictionary setObject:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:anItem]] forKey:anItem];
785 [hotKeysDictionary setObject:[ITKeyCombo clearKeyCombo] forKey:anItem];
789 ITDebugLog(@"Setting up track info checkboxes.");
790 // Check current track info buttons
791 [albumCheckbox setState:[df boolForKey:@"showAlbum"] ? NSOnState : NSOffState];
792 [nameCheckbox setState:NSOnState]; // Song info will ALWAYS show song title.
793 [nameCheckbox setEnabled:NO]; // Song info will ALWAYS show song title.
794 [artistCheckbox setState:[df boolForKey:@"showArtist"] ? NSOnState : NSOffState];
795 [trackTimeCheckbox setState:[df boolForKey:@"showTime"] ? NSOnState : NSOffState];
796 [trackNumberCheckbox setState:[df boolForKey:@"showTrackNumber"] ? NSOnState : NSOffState];
797 [ratingCheckbox setState:[df boolForKey:@"showTrackRating"] ? NSOnState : NSOffState];
798 [albumArtworkCheckbox setState:[df boolForKey:@"showAlbumArtwork"] ? NSOnState : NSOffState];
800 if ([df boolForKey:@"runScripts"]) {
801 [runScriptsCheckbox setState:NSOnState];
802 [showScriptsButton setEnabled:YES];
804 [showScriptsButton setEnabled:NO];
807 // Set the launch at login checkbox state
808 ITDebugLog(@"Setting launch at login state.");
810 loginwindow = [[df persistentDomainForName:@"loginwindow"] mutableCopy];
811 loginarray = [loginwindow objectForKey:@"AutoLaunchedApplicationDictionary"];
813 loginEnum = [loginarray objectEnumerator];
814 while ( (anItem = [loginEnum nextObject]) ) {
815 if ([[[anItem objectForKey:@"Path"] lastPathComponent] isEqualToString:[[[NSBundle mainBundle] bundlePath] lastPathComponent]]) {
816 [launchAtLoginCheckbox setState:NSOnState];
820 // Set the launch player checkbox state
821 ITDebugLog(@"Setting launch player with MenuTunes state.");
822 [launchPlayerAtLaunchCheckbox setState:[df boolForKey:@"LaunchPlayerWithMT"] ? NSOnState : NSOffState];
824 // Setup the positioning controls
825 [positionMatrix selectCellAtRow:[df integerForKey:@"statusWindowVerticalPosition"]
826 column:[df integerForKey:@"statusWindowHorizontalPosition"]];
828 // Setup effects controls
829 // Populate the effects popups
830 [appearanceEffectPopup setAutoenablesItems:NO];
831 [vanishEffectPopup setAutoenablesItems:NO];
832 [self repopulateEffectPopupsForVerticalPosition:[df integerForKey:@"statusWindowVerticalPosition"]
833 horizontalPosition:[df integerForKey:@"statusWindowHorizontalPosition"]];
835 // Attempt to find the pref'd effect in the list.
836 // If it's not there, use cut/dissolve.
837 if ( [effectClasses containsObject:NSClassFromString([df stringForKey:@"statusWindowAppearanceEffect"])] ) {
838 [appearanceEffectPopup selectItemAtIndex:[effectClasses indexOfObject:NSClassFromString([df stringForKey:@"statusWindowAppearanceEffect"])]];
840 [appearanceEffectPopup selectItemAtIndex:[effectClasses indexOfObject:NSClassFromString(@"ITCutWindowEffect")]];
843 if ( [effectClasses containsObject:NSClassFromString([df stringForKey:@"statusWindowVanishEffect"])] ) {
844 [vanishEffectPopup selectItemAtIndex:[effectClasses indexOfObject:NSClassFromString([df stringForKey:@"statusWindowVanishEffect"])]];
846 [vanishEffectPopup selectItemAtIndex:[effectClasses indexOfObject:NSClassFromString(@"ITCutWindowEffect")]];
849 [appearanceSpeedSlider setFloatValue:( -([df floatForKey:@"statusWindowAppearanceSpeed"]) )];
850 [vanishSpeedSlider setFloatValue:( -([df floatForKey:@"statusWindowVanishSpeed"]) )];
851 [vanishDelaySlider setFloatValue:[df floatForKey:@"statusWindowVanishDelay"]];
853 // Setup General Controls
854 selectedBGStyle = [df integerForKey:@"statusWindowBackgroundMode"];
855 [backgroundStylePopup selectItem:[backgroundStylePopup itemAtIndex:[backgroundStylePopup indexOfItemWithTag:selectedBGStyle]]];
857 if ( selectedBGStyle == ITTSWBackgroundColored ) {
858 [backgroundColorWell setEnabled:YES];
859 [backgroundColorPopup setEnabled:YES];
861 [backgroundColorWell setEnabled:NO];
862 [backgroundColorPopup setEnabled:NO];
865 colorData = [df dataForKey:@"statusWindowBackgroundColor"];
868 [backgroundColorWell setColor:(NSColor *)[NSUnarchiver unarchiveObjectWithData:colorData]];
870 [backgroundColorWell setColor:[NSColor blueColor]];
873 [showOnChangeCheckbox setState:([df boolForKey:@"showSongInfoOnChange"] ? NSOnState : NSOffState)];
875 [windowSizingPopup selectItem:[windowSizingPopup itemAtIndex:[windowSizingPopup indexOfItemWithTag:[df integerForKey:@"statusWindowSizing"]]]];
877 // Setup the sharing controls
878 if ([df boolForKey:@"enableSharing"]) {
879 [shareMenuTunesCheckbox setState:NSOnState];
880 [useSharedMenuTunesCheckbox setEnabled:NO];
881 [selectSharedPlayerButton setEnabled:NO];
882 [passwordTextField setEnabled:YES];
883 [nameTextField setEnabled:YES];
884 } else if ([df boolForKey:@"useSharedPlayer"]) {
885 [useSharedMenuTunesCheckbox setState:NSOnState];
886 [shareMenuTunesCheckbox setEnabled:NO];
887 [selectSharedPlayerButton setEnabled:YES];
890 [[NSNotificationCenter defaultCenter] addObserver:sharingTableView selector:@selector(reloadData) name:@"ITMTFoundNetService" object:nil];
892 serverName = [df stringForKey:@"sharedPlayerName"];
893 if (!serverName || [serverName length] == 0) {
894 serverName = @"MenuTunes Shared Player";
896 [nameTextField setStringValue:serverName];
898 [selectPlayerBox setContentView:zeroConfView];
899 if ([[df dataForKey:@"sharedPlayerPassword"] length] > 0) {
900 [passwordTextField setStringValue:@"p4s5w0rdMT1.2"];
902 [passwordTextField setStringValue:@""];
904 if ([df stringForKey:@"sharedPlayerHost"]) {
905 [hostTextField setStringValue:[df stringForKey:@"sharedPlayerHost"]];
908 if ([[NetworkController sharedController] isConnectedToServer]) {
909 [selectedPlayerTextField setStringValue:[[[NetworkController sharedController] networkObject] serverName]];
910 [locationTextField setStringValue:[[NetworkController sharedController] remoteHost]];
912 [selectedPlayerTextField setStringValue:@"No shared player selected."];
913 [locationTextField setStringValue:@"-"];
917 - (void)setStatusWindowEntryEffect:(Class)effectClass
919 StatusWindow *sw = [StatusWindow sharedWindow];
921 float time = ([df floatForKey:@"statusWindowAppearanceSpeed"] ? [df floatForKey:@"statusWindowAppearanceSpeed"] : 0.8);
922 [df setObject:NSStringFromClass(effectClass) forKey:@"statusWindowAppearanceEffect"];
924 [sw setEntryEffect:[[[effectClass alloc] initWithWindow:sw] autorelease]];
925 [[sw entryEffect] setEffectTime:time];
928 - (void)setStatusWindowExitEffect:(Class)effectClass
930 StatusWindow *sw = [StatusWindow sharedWindow];
932 float time = ([df floatForKey:@"statusWindowVanishSpeed"] ? [df floatForKey:@"statusWindowVanishSpeed"] : 0.8);
933 [df setObject:NSStringFromClass(effectClass) forKey:@"statusWindowVanishEffect"];
935 [sw setExitEffect:[[[effectClass alloc] initWithWindow:sw] autorelease]];
936 [[sw exitEffect] setEffectTime:time];
939 - (void)setCustomColor:(NSColor *)color updateWell:(BOOL)update
941 [(ITTSWBackgroundView *)[[StatusWindow sharedWindow] contentView] setBackgroundColor:color];
942 [df setObject:[NSArchiver archivedDataWithRootObject:color] forKey:@"statusWindowBackgroundColor"];
945 [backgroundColorWell setColor:color];
949 - (void)repopulateEffectPopupsForVerticalPosition:(ITVerticalWindowPosition)vPos horizontalPosition:(ITHorizontalWindowPosition)hPos
951 NSEnumerator *effectEnum = [effectClasses objectEnumerator];
954 [appearanceEffectPopup removeAllItems];
955 [vanishEffectPopup removeAllItems];
957 while ( (anItem = [effectEnum nextObject]) ) {
958 [appearanceEffectPopup addItemWithTitle:[anItem effectName]];
959 [vanishEffectPopup addItemWithTitle:[anItem effectName]];
961 [[appearanceEffectPopup lastItem] setRepresentedObject:anItem];
962 [[vanishEffectPopup lastItem] setRepresentedObject:anItem];
964 if ( [self effect:anItem supportsVerticalPosition:vPos withHorizontalPosition:hPos] ) {
965 [[appearanceEffectPopup lastItem] setEnabled:YES];
966 [[vanishEffectPopup lastItem] setEnabled:YES];
968 [[appearanceEffectPopup lastItem] setEnabled:NO];
969 [[vanishEffectPopup lastItem] setEnabled:NO];
975 - (BOOL)effect:(Class)effectClass supportsVerticalPosition:(ITVerticalWindowPosition)vPos withHorizontalPosition:(ITHorizontalWindowPosition)hPos
979 if ( vPos == ITWindowPositionTop ) {
980 if ( hPos == ITWindowPositionLeft ) {
981 valid = ( [[[[effectClass supportedPositions] objectForKey:@"Top"] objectForKey:@"Left"] boolValue] ) ;
982 } else if ( hPos == ITWindowPositionCenter ) {
983 valid = ( [[[[effectClass supportedPositions] objectForKey:@"Top"] objectForKey:@"Center"] boolValue] );
984 } else if ( hPos == ITWindowPositionRight ) {
985 valid = ( [[[[effectClass supportedPositions] objectForKey:@"Top"] objectForKey:@"Right"] boolValue] );
987 } else if ( vPos == ITWindowPositionMiddle ) {
988 if ( hPos == ITWindowPositionLeft ) {
989 valid = ( [[[[effectClass supportedPositions] objectForKey:@"Middle"] objectForKey:@"Left"] boolValue] );
990 } else if ( hPos == ITWindowPositionCenter ) {
991 valid = ( [[[[effectClass supportedPositions] objectForKey:@"Middle"] objectForKey:@"Center"] boolValue] );
992 } else if ( hPos == ITWindowPositionRight ) {
993 valid = ( [[[[effectClass supportedPositions] objectForKey:@"Middle"] objectForKey:@"Right"] boolValue] );
995 } else if ( vPos == ITWindowPositionBottom ) {
996 if ( hPos == ITWindowPositionLeft ) {
997 valid = ( [[[[effectClass supportedPositions] objectForKey:@"Bottom"] objectForKey:@"Left"] boolValue] );
998 } else if ( hPos == ITWindowPositionCenter ) {
999 valid = ( [[[[effectClass supportedPositions] objectForKey:@"Bottom"] objectForKey:@"Center"] boolValue] );
1000 } else if ( hPos == ITWindowPositionRight ) {
1001 valid = ( [[[[effectClass supportedPositions] objectForKey:@"Bottom"] objectForKey:@"Right"] boolValue] );
1008 - (IBAction)changeMenus:(id)sender
1010 ITDebugLog(@"Synchronizing menus");
1011 [df setObject:myItems forKey:@"menu"];
1014 //If we're connected over a network, refresh the menu immediately
1015 if ([[NetworkController sharedController] isConnectedToServer]) {
1016 [controller timerUpdate];
1020 - (void)setLaunchesAtLogin:(BOOL)flag
1022 NSMutableDictionary *loginwindow;
1023 NSMutableArray *loginarray;
1024 ITDebugLog(@"Setting launches at login: %i", flag);
1026 loginwindow = [[df persistentDomainForName:@"loginwindow"] mutableCopy];
1027 loginarray = [loginwindow objectForKey:@"AutoLaunchedApplicationDictionary"];
1030 NSDictionary *itemDict = [NSDictionary dictionaryWithObjectsAndKeys:
1031 [[NSBundle mainBundle] bundlePath], @"Path",
1032 [NSNumber numberWithInt:0], @"Hide", nil];
1033 [loginarray addObject:itemDict];
1036 for (i = 0; i < [loginarray count]; i++) {
1037 NSDictionary *tempDict = [loginarray objectAtIndex:i];
1038 if ([[[tempDict objectForKey:@"Path"] lastPathComponent] isEqualToString:[[[NSBundle mainBundle] bundlePath] lastPathComponent]]) {
1039 [loginarray removeObjectAtIndex:i];
1044 [df setPersistentDomain:loginwindow forName:@"loginwindow"];
1046 [loginwindow release];
1047 ITDebugLog(@"Finished setting launches at login.");
1051 /*************************************************************************/
1053 #pragma mark NSWindow DELEGATE METHODS
1054 /*************************************************************************/
1056 - (void)windowWillClose:(NSNotification *)note
1058 [(MainController *)controller closePreferences];
1061 /*************************************************************************/
1063 #pragma mark NSTextField DELEGATE METHODS
1064 /*************************************************************************/
1066 - (void)controlTextDidChange:(NSNotification*)note
1068 if ([note object] == hostTextField) {
1069 if ([[hostTextField stringValue] length] == 0) {
1070 [sharingPanelOKButton setEnabled:NO];
1072 [sharingPanelOKButton setEnabled:YES];
1077 /*************************************************************************/
1079 #pragma mark NSTableView DATASOURCE METHODS
1080 /*************************************************************************/
1082 - (int)numberOfRowsInTableView:(NSTableView *)aTableView
1084 if (aTableView == menuTableView) {
1085 return [myItems count];
1086 } else if (aTableView == allTableView) {
1087 return [availableItems count];
1088 } else if (aTableView == hotKeysTableView) {
1089 return [hotKeysArray count];
1091 return [[[NetworkController sharedController] remoteServices] count];
1095 - (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex
1097 if (aTableView == menuTableView) {
1098 NSString *object = [myItems objectAtIndex:rowIndex];
1099 if ([[aTableColumn identifier] isEqualToString:@"name"]) {
1100 if ([object isEqualToString:@"showPlayer"]) {
1101 NSString *string = nil;
1103 string = [NSString stringWithFormat:@"%@ %@", NSLocalizedString(@"show", @"Show"), [[controller currentRemote] playerSimpleName]];
1105 [controller networkError:localException];
1109 return NSLocalizedString(object, @"ERROR");
1111 if ([submenuItems containsObject:object])
1113 return [NSImage imageNamed:@"submenu"];
1118 } else if (aTableView == allTableView) {
1119 NSString *object = [availableItems objectAtIndex:rowIndex];
1120 if ([[aTableColumn identifier] isEqualToString:@"name"]) {
1121 if ([object isEqualToString:@"showPlayer"]) {
1122 NSString *string = nil;
1124 string = [NSString stringWithFormat:@"%@ %@", NSLocalizedString(@"show", @"Show"), [[controller currentRemote] playerSimpleName]];
1126 [controller networkError:localException];
1130 return NSLocalizedString(object, @"ERROR");
1132 if ([submenuItems containsObject:object]) {
1133 return [NSImage imageNamed:@"submenu"];
1138 } else if (aTableView == hotKeysTableView) {
1139 if ([[aTableColumn identifier] isEqualToString:@"name"]) {
1140 return [hotKeyNamesArray objectAtIndex:rowIndex];
1142 return [[hotKeysDictionary objectForKey:[hotKeysArray objectAtIndex:rowIndex]] description];
1145 return [[[[NetworkController sharedController] remoteServices] objectAtIndex:rowIndex] name];
1149 - (BOOL)tableView:(NSTableView *)tableView writeRows:(NSArray*)rows toPasteboard:(NSPasteboard*)pboard
1151 if (tableView == menuTableView) {
1152 [pboard declareTypes:[NSArray arrayWithObjects:@"MenuTableViewPboardType", nil] owner:self];
1153 [pboard setString:[[rows objectAtIndex:0] stringValue] forType:@"MenuTableViewPboardType"];
1157 if (tableView == allTableView) {
1158 [pboard declareTypes:[NSArray arrayWithObjects:@"AllTableViewPboardType", nil] owner:self];
1159 [pboard setString:[[rows objectAtIndex:0] stringValue] forType:@"AllTableViewPboardType"];
1165 - (BOOL)tableView:(NSTableView*)tableView acceptDrop:(id <NSDraggingInfo>)info row:(int)row dropOperation:(NSTableViewDropOperation)operation
1169 NSString *dragData, *temp;
1171 pb = [info draggingPasteboard];
1173 if ([[pb types] containsObject:@"MenuTableViewPboardType"]) {
1174 dragData = [pb stringForType:@"MenuTableViewPboardType"];
1175 dragRow = [dragData intValue];
1176 temp = [myItems objectAtIndex:dragRow];
1178 if (tableView == menuTableView) {
1179 [myItems insertObject:temp atIndex:row];
1180 if (row > dragRow) {
1181 [myItems removeObjectAtIndex:dragRow];
1183 [myItems removeObjectAtIndex:dragRow + 1];
1185 } else if (tableView == allTableView) {
1186 if (![temp isEqualToString:@"separator"]) {
1187 [availableItems addObject:temp];
1189 [myItems removeObjectAtIndex:dragRow];
1191 } else if ([[pb types] containsObject:@"AllTableViewPboardType"]) {
1192 dragData = [pb stringForType:@"AllTableViewPboardType"];
1193 dragRow = [dragData intValue];
1194 temp = [availableItems objectAtIndex:dragRow];
1196 [myItems insertObject:temp atIndex:row];
1198 if (![temp isEqualToString:@"separator"]) {
1199 [availableItems removeObjectAtIndex:dragRow];
1203 [menuTableView reloadData];
1204 [allTableView reloadData];
1205 [self changeMenus:self];
1209 - (NSDragOperation)tableView:(NSTableView*)tableView validateDrop:(id <NSDraggingInfo>)info proposedRow:(int)row proposedDropOperation:(NSTableViewDropOperation)operation
1211 if (tableView == allTableView) {
1212 if ([[[info draggingPasteboard] types] containsObject:@"AllTableViewPboardType"]) {
1213 return NSDragOperationNone;
1216 if ([[[info draggingPasteboard] types] containsObject:@"MenuTableViewPboardType"]) {
1217 NSString *item = [myItems objectAtIndex:[[[info draggingPasteboard] stringForType:@"MenuTableViewPboardType"] intValue]];
1218 if ([item isEqualToString:@"preferences"] || [item isEqualToString:@"quit"]) {
1219 return NSDragOperationNone;
1223 [tableView setDropRow:-1 dropOperation:NSTableViewDropOn];
1224 return NSDragOperationGeneric;
1227 if (operation == NSTableViewDropOn || row == -1)
1229 return NSDragOperationNone;
1231 return NSDragOperationGeneric;
1235 /*************************************************************************/
1237 #pragma mark DEALLOCATION METHODS
1238 /*************************************************************************/
1242 [hotKeysArray release];
1243 [hotKeysDictionary release];
1244 [effectClasses release];
1245 [menuTableView setDataSource:nil];
1246 [allTableView setDataSource:nil];
1247 [controller release];
1248 [availableItems release];
1249 [submenuItems release];