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] == 1105) {
240 [df setBool:SENDER_STATE forKey:@"showToolTip"];
241 } else if ( [sender tag] == 1110) {
242 [df setBool:SENDER_STATE forKey:@"runScripts"];
244 [runScriptsCheckbox setState:NSOnState];
245 [showScriptsButton setEnabled:YES];
247 [showScriptsButton setEnabled:NO];
249 } else if ( [sender tag] == 1120) {
250 mkdir([[NSHomeDirectory() stringByAppendingPathComponent:@"Library/Application Support/MenuTunes/Scripts"] cString], 0744);
251 [[NSWorkspace sharedWorkspace] openFile:[NSHomeDirectory() stringByAppendingPathComponent:@"Library/Application Support/MenuTunes/Scripts"]];
256 - (IBAction)changeSharingSetting:(id)sender
258 ITDebugLog(@"Changing sharing setting of tag %i.", [sender tag]);
259 if ( [sender tag] == 5010 ) {
260 BOOL state = SENDER_STATE;
261 [df setBool:state forKey:@"enableSharing"];
262 //Disable/enable the use of shared player options
263 [useSharedMenuTunesCheckbox setEnabled:!state];
264 [passwordTextField setEnabled:state];
265 [nameTextField setEnabled:state];
266 [selectSharedPlayerButton setEnabled:NO];
267 [controller setServerStatus:state]; //Set server status
268 } else if ( [sender tag] == 5015 ) {
269 [df setObject:[sender stringValue] forKey:@"sharedPlayerName"];
270 [[NetworkController sharedController] resetServerName];
271 } else if ( [sender tag] == 5030 ) {
272 //Set the server password
273 const char *instring = [[sender stringValue] UTF8String];
274 const char *password = "p4s5w0rdMT1.2";
275 unsigned char *result;
276 NSData *hashedPass, *passwordStringHash;
277 if ([[sender stringValue] length] == 0) {
278 [df setObject:[NSData data] forKey:@"sharedPlayerPassword"];
281 result = SHA1(instring, strlen(instring), NULL);
282 hashedPass = [NSData dataWithBytes:result length:strlen(result)];
283 result = SHA1(password, strlen(password), NULL);
284 passwordStringHash = [NSData dataWithBytes:result length:strlen(result)];
285 if (![hashedPass isEqualToData:passwordStringHash]) {
286 [df setObject:hashedPass forKey:@"sharedPlayerPassword"];
287 [sender setStringValue:@"p4s5w0rdMT1.2"];
289 } else if ( [sender tag] == 5040 ) {
290 BOOL state = SENDER_STATE;
291 [df setBool:state forKey:@"useSharedPlayer"];
292 //Disable/enable the use of sharing options
293 [shareMenuTunesCheckbox setEnabled:!state];
294 [passwordTextField setEnabled:NO];
295 [nameTextField setEnabled:NO];
296 [selectSharedPlayerButton setEnabled:state];
298 if (state && ([controller connectToServer] == 1)) {
299 [selectedPlayerTextField setStringValue:[[[NetworkController sharedController] networkObject] serverName]];
300 [locationTextField setStringValue:[[NetworkController sharedController] remoteHost]];
302 [selectedPlayerTextField setStringValue:@"No shared player selected."];
303 [locationTextField setStringValue:@"-"];
304 if ([[NetworkController sharedController] isConnectedToServer]) {
305 [controller disconnectFromServer];
309 } else if ( [sender tag] == 5050 ) {
310 //If no player is selected in the table view, turn off OK button.
311 if ([sender clickedRow] == -1 ) {
312 [sharingPanelOKButton setEnabled:NO];
314 [sharingPanelOKButton setEnabled:YES];
316 } else if ( [sender tag] == 5051 ) {
317 [df setObject:[sender stringValue] forKey:@"sharedPlayerHost"];
318 } else if ( [sender tag] == 5060 ) {
319 //Set OK button state
320 if (([selectPlayerBox contentView] == zeroConfView && [sharingTableView selectedRow] == -1) ||
321 ([selectPlayerBox contentView] == manualView && [[hostTextField stringValue] length] == 0)) {
322 [sharingPanelOKButton setEnabled:NO];
324 [sharingPanelOKButton setEnabled:YES];
326 //Show selection sheet
327 [NSApp beginSheet:selectPlayerSheet modalForWindow:window modalDelegate:self didEndSelector:NULL contextInfo:nil];
328 } else if ( [sender tag] == 5100 ) {
330 if ( ([sender indexOfItem:[sender selectedItem]] == 0) && ([selectPlayerBox contentView] != zeroConfView) ) {
331 NSRect frame = [selectPlayerSheet frame];
332 frame.origin.y -= 58;
333 frame.size.height = 273;
334 if ([sharingTableView selectedRow] == -1) {
335 [sharingPanelOKButton setEnabled:NO];
337 [selectPlayerBox setContentView:zeroConfView];
338 [selectPlayerSheet setFrame:frame display:YES animate:YES];
339 } else if ( ([sender indexOfItem:[sender selectedItem]] == 1) && ([selectPlayerBox contentView] != manualView) ){
340 NSRect frame = [selectPlayerSheet frame];
341 frame.origin.y += 58;
342 frame.size.height = 215;
343 if ([[hostTextField stringValue] length] == 0) {
344 [sharingPanelOKButton setEnabled:NO];
346 [sharingPanelOKButton setEnabled:YES];
348 [selectPlayerBox setContentView:manualView];
349 [selectPlayerSheet setFrame:frame display:YES animate:YES];
350 [hostTextField selectText:nil];
352 } else if ( [sender tag] == 5150 ) {
353 const char *instring = [[sender stringValue] UTF8String];
354 unsigned char *result;
355 result = SHA1(instring, strlen(instring), NULL);
356 [df setObject:[NSData dataWithBytes:result length:strlen(result)] forKey:@"connectPassword"];
357 } else if ( [sender tag] == 5110 ) {
359 [NSApp endSheet:selectPlayerSheet];
360 [selectPlayerSheet orderOut:nil];
361 if ([selectPlayerBox contentView] == manualView) {
362 [hostTextField setStringValue:[df stringForKey:@"sharedPlayerHost"]];
365 } else if ( [sender tag] == 5120 ) {
367 [NSApp endSheet:selectPlayerSheet];
368 [selectPlayerSheet orderOut:nil];
370 [self changeSharingSetting:clientPasswordTextField];
372 if ([selectPlayerBox contentView] == manualView) {
373 [df setObject:[hostTextField stringValue] forKey:@"sharedPlayerHost"];
375 if ([sharingTableView selectedRow] > -1) {
376 [df setObject:[NSString stringWithCString:inet_ntoa((*(struct sockaddr_in*)[[[[[[NetworkController sharedController] remoteServices] objectAtIndex:[sharingTableView selectedRow]] addresses] objectAtIndex:0] bytes]).sin_addr)] forKey:@"sharedPlayerHost"];
380 if ([controller connectToServer] == 1) {
381 [useSharedMenuTunesCheckbox setState:NSOnState];
382 [selectedPlayerTextField setStringValue:[[[NetworkController sharedController] networkObject] serverName]];
383 [locationTextField setStringValue:[[NetworkController sharedController] remoteHost]];
385 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);
387 } else if ( [sender tag] == 6010 ) {
388 //Cancel password entry
389 [passwordPanel orderOut:nil];
390 [NSApp stopModalWithCode:0];
391 } else if ( [sender tag] == 6020 ) {
392 //OK password entry, retry connect
393 const char *instring = [[passwordPanelTextField stringValue] UTF8String];
394 unsigned char *result;
395 result = SHA1(instring, strlen(instring), NULL);
396 [df setObject:[NSData dataWithBytes:result length:strlen(result)] forKey:@"connectPassword"];
397 [passwordPanel orderOut:nil];
398 [NSApp stopModalWithCode:1];
403 - (IBAction)changeStatusWindowSetting:(id)sender
405 StatusWindow *sw = [StatusWindow sharedWindow];
406 ITDebugLog(@"Changing status window setting of tag %i", [sender tag]);
408 if ( [sender tag] == 2010) {
410 BOOL entryEffectValid = YES;
411 BOOL exitEffectValid = YES;
413 [df setInteger:[sender selectedRow] forKey:@"statusWindowVerticalPosition"];
414 [df setInteger:[sender selectedColumn] forKey:@"statusWindowHorizontalPosition"];
415 [sw setVerticalPosition:[sender selectedRow]];
416 [sw setHorizontalPosition:[sender selectedColumn]];
418 // Enable/disable the items in the popups.
419 [self repopulateEffectPopupsForVerticalPosition:[sw verticalPosition]
420 horizontalPosition:[sw horizontalPosition]];
422 // Make sure the effects support the new position.
423 entryEffectValid = ( [self effect:[[sw entryEffect] class]
424 supportsVerticalPosition:[sw verticalPosition]
425 withHorizontalPosition:[sw horizontalPosition]] );
426 exitEffectValid = ( [self effect:[[sw exitEffect] class]
427 supportsVerticalPosition:[sw verticalPosition]
428 withHorizontalPosition:[sw horizontalPosition]] );
430 if ( ! entryEffectValid ) {
431 [appearanceEffectPopup selectItemAtIndex:[[appearanceEffectPopup menu] indexOfItemWithRepresentedObject:NSClassFromString(@"ITCutWindowEffect")]];
432 [self setStatusWindowEntryEffect:NSClassFromString(@"ITCutWindowEffect")];
434 [appearanceEffectPopup selectItemAtIndex:[[appearanceEffectPopup menu] indexOfItemWithRepresentedObject:[[sw entryEffect] class]]];
437 if ( ! exitEffectValid ) {
438 [vanishEffectPopup selectItemAtIndex:[[vanishEffectPopup menu] indexOfItemWithRepresentedObject:NSClassFromString(@"ITDissolveWindowEffect")]];
439 [self setStatusWindowExitEffect:NSClassFromString(@"ITDissolveWindowEffect")];
441 [vanishEffectPopup selectItemAtIndex:[[vanishEffectPopup menu] indexOfItemWithRepresentedObject:[[sw exitEffect] class]]];
444 [(MainController *)controller showCurrentTrackInfo];
446 } else if ( [sender tag] == 2020) {
448 // Update screen selection.
450 } else if ( [sender tag] == 2030) {
452 [self setStatusWindowEntryEffect:[[sender selectedItem] representedObject]];
454 } else if ( [sender tag] == 2040) {
456 [self setStatusWindowExitEffect:[[sender selectedItem] representedObject]];
458 } else if ( [sender tag] == 2050) {
459 float newTime = ( -([sender floatValue]) );
460 [df setFloat:newTime forKey:@"statusWindowAppearanceSpeed"];
461 [[sw entryEffect] setEffectTime:newTime];
462 } else if ( [sender tag] == 2060) {
463 float newTime = ( -([sender floatValue]) );
464 [df setFloat:newTime forKey:@"statusWindowVanishSpeed"];
465 [[sw exitEffect] setEffectTime:newTime];
466 } else if ( [sender tag] == 2070) {
467 [df setFloat:[sender floatValue] forKey:@"statusWindowVanishDelay"];
468 [sw setExitDelay:[sender floatValue]];
469 } else if ( [sender tag] == 2080) {
470 [df setBool:SENDER_STATE forKey:@"showSongInfoOnChange"];
471 } else if ( [sender tag] == 2090) {
473 int setting = [sender indexOfSelectedItem];
475 if ( setting == 0 ) {
476 [(ITTSWBackgroundView *)[sw contentView] setBackgroundMode:ITTSWBackgroundApple];
477 [backgroundColorWell setEnabled:NO];
478 [backgroundColorPopup setEnabled:NO];
479 } else if ( setting == 1 ) {
480 [(ITTSWBackgroundView *)[sw contentView] setBackgroundMode:ITTSWBackgroundReadable];
481 [backgroundColorWell setEnabled:NO];
482 [backgroundColorPopup setEnabled:NO];
483 } else if ( setting == 2 ) {
484 [(ITTSWBackgroundView *)[sw contentView] setBackgroundMode:ITTSWBackgroundColored];
485 [backgroundColorWell setEnabled:YES];
486 [backgroundColorPopup setEnabled:YES];
489 [df setInteger:setting forKey:@"statusWindowBackgroundMode"];
491 } else if ( [sender tag] == 2091) {
492 [self setCustomColor:[sender color] updateWell:NO];
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];
511 } else if ( [sender tag] == 2095) {
512 [df setInteger:[sender indexOfSelectedItem] forKey:@"statusWindowSizing"];
513 [(MainController *)controller showCurrentTrackInfo];
519 - (void)registerDefaults
522 NSMutableDictionary *loginWindow;
523 NSMutableArray *loginArray;
524 NSEnumerator *loginEnum;
526 ITDebugLog(@"Registering defaults.");
527 [df setObject:[NSArray arrayWithObjects:
539 nil] forKey:@"menu"];
541 [df setInteger:MT_CURRENT_VERSION forKey:@"appVersion"];
542 [df setInteger:5 forKey:@"SongsInAdvance"];
543 // [df setBool:YES forKey:@"showName"]; // Song info will always show song title.
544 [df setBool:YES forKey:@"showArtist"];
545 [df setBool:YES forKey:@"showAlbumArtwork"];
546 [df setBool:NO forKey:@"showAlbum"];
547 [df setBool:NO forKey:@"showTime"];
549 [df setObject:@"ITCutWindowEffect" forKey:@"statusWindowAppearanceEffect"];
550 [df setObject:@"ITDissolveWindowEffect" forKey:@"statusWindowVanishEffect"];
551 [df setFloat:0.8 forKey:@"statusWindowAppearanceSpeed"];
552 [df setFloat:0.8 forKey:@"statusWindowVanishSpeed"];
553 [df setFloat:4.0 forKey:@"statusWindowVanishDelay"];
554 [df setInteger:(int)ITWindowPositionBottom forKey:@"statusWindowVerticalPosition"];
555 [df setInteger:(int)ITWindowPositionLeft forKey:@"statusWindowHorizontalPosition"];
556 [[StatusWindow sharedWindow] setVerticalPosition:(int)ITWindowPositionBottom];
557 [[StatusWindow sharedWindow] setHorizontalPosition:(int)ITWindowPositionLeft];
558 [df setBool:YES forKey:@"showSongInfoOnChange"];
560 [df setObject:[NSArchiver archivedDataWithRootObject:[NSColor blueColor]] forKey:@"statusWindowBackgroundColor"];
564 loginWindow = [[df persistentDomainForName:@"loginwindow"] mutableCopy];
565 loginArray = [loginWindow objectForKey:@"AutoLaunchedApplicationDictionary"];
566 loginEnum = [loginArray objectEnumerator];
568 while ( (anItem = [loginEnum nextObject]) ) {
569 if ( [[[anItem objectForKey:@"Path"] lastPathComponent] isEqualToString:[[[NSBundle mainBundle] bundlePath] lastPathComponent]] ) {
573 [loginWindow release];
576 [[StatusWindowController sharedController] showSetupQueryWindow];
582 [[StatusWindow sharedWindow] setLocked:NO];
583 [[StatusWindow sharedWindow] vanish:self];
584 [[StatusWindow sharedWindow] setIgnoresMouseEvents:YES];
586 [self setLaunchesAtLogin:YES];
589 - (void)autoLaunchCancel
591 [[StatusWindow sharedWindow] setLocked:NO];
592 [[StatusWindow sharedWindow] vanish:self];
593 [[StatusWindow sharedWindow] setIgnoresMouseEvents:YES];
596 - (void)deletePressedInTableView:(NSTableView *)tableView
598 if (tableView == menuTableView) {
599 int selRow = [tableView selectedRow];
600 ITDebugLog(@"Delete pressed in menu table view.");
602 NSString *object = [myItems objectAtIndex:selRow];
604 if ([object isEqualToString:@"preferences"]) {
609 if (![object isEqualToString:@"separator"])
610 [availableItems addObject:object];
611 ITDebugLog(@"Removing object named %@", object);
612 [myItems removeObjectAtIndex:selRow];
613 [menuTableView reloadData];
614 [allTableView reloadData];
616 [self changeMenus:self];
620 - (void)resetRemotePlayerTextFields
622 if ([[NetworkController sharedController] isConnectedToServer]) {
623 [selectedPlayerTextField setStringValue:[[[NetworkController sharedController] networkObject] serverName]];
624 [locationTextField setStringValue:[[NetworkController sharedController] remoteHost]];
626 [selectedPlayerTextField setStringValue:@"No shared player selected."];
627 [locationTextField setStringValue:@"-"];
631 /*************************************************************************/
633 #pragma mark HOTKEY SUPPORT METHODS
634 /*************************************************************************/
636 - (IBAction)clearHotKey:(id)sender
638 [hotKeysDictionary setObject:[ITKeyCombo clearKeyCombo] forKey:[hotKeysArray objectAtIndex:[hotKeysTableView selectedRow]]];
639 [df setObject:[[ITKeyCombo clearKeyCombo] plistRepresentation] forKey:[hotKeysArray objectAtIndex:[hotKeysTableView selectedRow]]];
640 [controller setupHotKeys];
641 [hotKeysTableView reloadData];
644 - (IBAction)editHotKey:(id)sender
646 ITKeyComboPanel *panel = [ITKeyComboPanel sharedPanel];
647 NSString *keyComboKey = [hotKeysArray objectAtIndex:[hotKeysTableView selectedRow]];
648 ITKeyCombo *keyCombo;
650 ITDebugLog(@"Setting key combo on hot key %@.", keyComboKey);
651 [controller clearHotKeys];
652 [panel setKeyCombo:[hotKeysDictionary objectForKey:[hotKeysArray objectAtIndex:[hotKeysTableView selectedRow]]]];
653 [panel setKeyBindingName:[hotKeyNamesArray objectAtIndex:[hotKeysTableView selectedRow]]];
654 if ([panel runModal] == NSOKButton) {
655 NSEnumerator *keyEnumerator = [[hotKeysDictionary allKeys] objectEnumerator];
657 keyCombo = [panel keyCombo];
659 //Check for duplicate key combo
660 while ( (nextKey = [keyEnumerator nextObject]) ) {
661 if ([[hotKeysDictionary objectForKey:nextKey] isEqual:keyCombo] &&
662 ![keyCombo isEqual:[ITKeyCombo clearKeyCombo]]) {
663 [hotKeysDictionary setObject:[ITKeyCombo clearKeyCombo]
665 [df setObject:[[ITKeyCombo clearKeyCombo] plistRepresentation]
670 [hotKeysDictionary setObject:keyCombo forKey:keyComboKey];
671 [df setObject:[keyCombo plistRepresentation] forKey:keyComboKey];
672 [controller setupHotKeys];
673 [hotKeysTableView reloadData];
674 ITDebugLog(@"Set combo %@ on hot key %@.", keyCombo, keyComboKey);
676 ITDebugLog(@"Hot key setting on hot key %@ cancelled.", keyComboKey);
680 - (void)hotKeysTableViewDoubleClicked:(id)sender
682 if ([sender clickedRow] > -1) {
683 [self editHotKey:sender];
687 /*************************************************************************/
689 #pragma mark PRIVATE METHOD IMPLEMENTATIONS
690 /*************************************************************************/
694 ITDebugLog(@"Loading Preferences.nib.");
695 if (![NSBundle loadNibNamed:@"Preferences" owner:self]) {
696 ITDebugLog(@"Failed to load Preferences.nib.");
702 - (void)setupCustomizationTables
704 NSImageCell *imgCell = [[[NSImageCell alloc] initImageCell:nil] autorelease];
705 ITDebugLog(@"Setting up table views.");
706 // Set the table view cells up
707 [imgCell setImageScaling:NSScaleNone];
708 [[menuTableView tableColumnWithIdentifier:@"submenu"] setDataCell:imgCell];
709 [[allTableView tableColumnWithIdentifier:@"submenu"] setDataCell:imgCell];
711 // Register for drag and drop
712 [menuTableView registerForDraggedTypes:[NSArray arrayWithObjects:
713 @"MenuTableViewPboardType",
714 @"AllTableViewPboardType",
716 [allTableView registerForDraggedTypes:[NSArray arrayWithObjects:
717 @"MenuTableViewPboardType",
718 @"AllTableViewPboardType",
722 - (void)setupMenuItems
724 NSEnumerator *itemEnum;
726 ITDebugLog(@"Setting up table view arrays.");
727 // Set the list of items you can have.
728 availableItems = [[NSMutableArray alloc] initWithObjects:
744 // Get our preferred menu
745 myItems = [[df arrayForKey:@"menu"] mutableCopy];
747 // Delete items in the availableItems array that are already part of the menu
748 itemEnum = [myItems objectEnumerator];
749 while ( (anItem = [itemEnum nextObject]) ) {
750 if (![anItem isEqualToString:@"separator"]) {
751 [availableItems removeObject:anItem];
755 // Items that show should a submenu image
756 submenuItems = [[NSArray alloc] initWithObjects:
766 NSMutableDictionary *loginwindow;
767 NSMutableArray *loginarray;
768 NSEnumerator *loginEnum;
769 NSEnumerator *keyArrayEnum;
770 NSString *serverName;
775 [df setInteger:MT_CURRENT_VERSION forKey:@"appVersion"];
777 ITDebugLog(@"Setting up preferences UI.");
778 // Fill in the number of songs in advance to show field
779 [songsInAdvance setIntValue:[df integerForKey:@"SongsInAdvance"]];
781 // Fill hot key array
782 keyArrayEnum = [hotKeysArray objectEnumerator];
784 while ( (anItem = [keyArrayEnum nextObject]) ) {
785 if ([df objectForKey:anItem]) {
786 ITDebugLog(@"Setting up \"%@\" hot key.", anItem);
787 [hotKeysDictionary setObject:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:anItem]] forKey:anItem];
789 [hotKeysDictionary setObject:[ITKeyCombo clearKeyCombo] forKey:anItem];
793 ITDebugLog(@"Setting up track info checkboxes.");
794 // Check current track info buttons
795 [albumCheckbox setState:[df boolForKey:@"showAlbum"] ? NSOnState : NSOffState];
796 [nameCheckbox setState:NSOnState]; // Song info will ALWAYS show song title.
797 [nameCheckbox setEnabled:NO]; // Song info will ALWAYS show song title.
798 [artistCheckbox setState:[df boolForKey:@"showArtist"] ? NSOnState : NSOffState];
799 [trackTimeCheckbox setState:[df boolForKey:@"showTime"] ? NSOnState : NSOffState];
800 [trackNumberCheckbox setState:[df boolForKey:@"showTrackNumber"] ? NSOnState : NSOffState];
801 [ratingCheckbox setState:[df boolForKey:@"showTrackRating"] ? NSOnState : NSOffState];
802 [albumArtworkCheckbox setState:[df boolForKey:@"showAlbumArtwork"] ? NSOnState : NSOffState];
804 [showToolTipCheckbox setState:[df boolForKey:@"showToolTip"] ? NSOnState : NSOffState];
806 if ([df boolForKey:@"runScripts"]) {
807 [runScriptsCheckbox setState:NSOnState];
808 [showScriptsButton setEnabled:YES];
810 [showScriptsButton setEnabled:NO];
813 // Set the launch at login checkbox state
814 ITDebugLog(@"Setting launch at login state.");
816 loginwindow = [[df persistentDomainForName:@"loginwindow"] mutableCopy];
817 loginarray = [loginwindow objectForKey:@"AutoLaunchedApplicationDictionary"];
819 loginEnum = [loginarray objectEnumerator];
820 while ( (anItem = [loginEnum nextObject]) ) {
821 if ([[[anItem objectForKey:@"Path"] lastPathComponent] isEqualToString:[[[NSBundle mainBundle] bundlePath] lastPathComponent]]) {
822 [launchAtLoginCheckbox setState:NSOnState];
826 // Set the launch player checkbox state
827 ITDebugLog(@"Setting launch player with MenuTunes state.");
828 [launchPlayerAtLaunchCheckbox setState:[df boolForKey:@"LaunchPlayerWithMT"] ? NSOnState : NSOffState];
830 // Setup the positioning controls
831 [positionMatrix selectCellAtRow:[df integerForKey:@"statusWindowVerticalPosition"]
832 column:[df integerForKey:@"statusWindowHorizontalPosition"]];
834 // Setup effects controls
835 // Populate the effects popups
836 [appearanceEffectPopup setAutoenablesItems:NO];
837 [vanishEffectPopup setAutoenablesItems:NO];
838 [self repopulateEffectPopupsForVerticalPosition:[df integerForKey:@"statusWindowVerticalPosition"]
839 horizontalPosition:[df integerForKey:@"statusWindowHorizontalPosition"]];
841 // Attempt to find the pref'd effect in the list.
842 // If it's not there, use cut/dissolve.
843 if ( [effectClasses containsObject:NSClassFromString([df stringForKey:@"statusWindowAppearanceEffect"])] ) {
844 [appearanceEffectPopup selectItemAtIndex:[effectClasses indexOfObject:NSClassFromString([df stringForKey:@"statusWindowAppearanceEffect"])]];
846 [appearanceEffectPopup selectItemAtIndex:[effectClasses indexOfObject:NSClassFromString(@"ITCutWindowEffect")]];
849 if ( [effectClasses containsObject:NSClassFromString([df stringForKey:@"statusWindowVanishEffect"])] ) {
850 [vanishEffectPopup selectItemAtIndex:[effectClasses indexOfObject:NSClassFromString([df stringForKey:@"statusWindowVanishEffect"])]];
852 [vanishEffectPopup selectItemAtIndex:[effectClasses indexOfObject:NSClassFromString(@"ITCutWindowEffect")]];
855 [appearanceSpeedSlider setFloatValue:( -([df floatForKey:@"statusWindowAppearanceSpeed"]) )];
856 [vanishSpeedSlider setFloatValue:( -([df floatForKey:@"statusWindowVanishSpeed"]) )];
857 [vanishDelaySlider setFloatValue:[df floatForKey:@"statusWindowVanishDelay"]];
859 // Setup General Controls
860 selectedBGStyle = [df integerForKey:@"statusWindowBackgroundMode"];
861 [backgroundStylePopup selectItem:[backgroundStylePopup itemAtIndex:[backgroundStylePopup indexOfItemWithTag:selectedBGStyle]]];
863 if ( selectedBGStyle == ITTSWBackgroundColored ) {
864 [backgroundColorWell setEnabled:YES];
865 [backgroundColorPopup setEnabled:YES];
867 [backgroundColorWell setEnabled:NO];
868 [backgroundColorPopup setEnabled:NO];
871 colorData = [df dataForKey:@"statusWindowBackgroundColor"];
874 [backgroundColorWell setColor:(NSColor *)[NSUnarchiver unarchiveObjectWithData:colorData]];
876 [backgroundColorWell setColor:[NSColor blueColor]];
879 [showOnChangeCheckbox setState:([df boolForKey:@"showSongInfoOnChange"] ? NSOnState : NSOffState)];
881 [windowSizingPopup selectItem:[windowSizingPopup itemAtIndex:[windowSizingPopup indexOfItemWithTag:[df integerForKey:@"statusWindowSizing"]]]];
883 // Setup the sharing controls
884 if ([df boolForKey:@"enableSharing"]) {
885 [shareMenuTunesCheckbox setState:NSOnState];
886 [useSharedMenuTunesCheckbox setEnabled:NO];
887 [selectSharedPlayerButton setEnabled:NO];
888 [passwordTextField setEnabled:YES];
889 [nameTextField setEnabled:YES];
890 } else if ([df boolForKey:@"useSharedPlayer"]) {
891 [useSharedMenuTunesCheckbox setState:NSOnState];
892 [shareMenuTunesCheckbox setEnabled:NO];
893 [selectSharedPlayerButton setEnabled:YES];
896 [[NSNotificationCenter defaultCenter] addObserver:sharingTableView selector:@selector(reloadData) name:@"ITMTFoundNetService" object:nil];
898 serverName = [df stringForKey:@"sharedPlayerName"];
899 if (!serverName || [serverName length] == 0) {
900 serverName = @"MenuTunes Shared Player";
902 [nameTextField setStringValue:serverName];
904 [selectPlayerBox setContentView:zeroConfView];
905 if ([[df dataForKey:@"sharedPlayerPassword"] length] > 0) {
906 [passwordTextField setStringValue:@"p4s5w0rdMT1.2"];
908 [passwordTextField setStringValue:@""];
910 if ([df stringForKey:@"sharedPlayerHost"]) {
911 [hostTextField setStringValue:[df stringForKey:@"sharedPlayerHost"]];
914 if ([[NetworkController sharedController] isConnectedToServer]) {
915 [selectedPlayerTextField setStringValue:[[[NetworkController sharedController] networkObject] serverName]];
916 [locationTextField setStringValue:[[NetworkController sharedController] remoteHost]];
918 [selectedPlayerTextField setStringValue:@"No shared player selected."];
919 [locationTextField setStringValue:@"-"];
923 - (void)setStatusWindowEntryEffect:(Class)effectClass
925 StatusWindow *sw = [StatusWindow sharedWindow];
927 float time = ([df floatForKey:@"statusWindowAppearanceSpeed"] ? [df floatForKey:@"statusWindowAppearanceSpeed"] : 0.8);
928 [df setObject:NSStringFromClass(effectClass) forKey:@"statusWindowAppearanceEffect"];
930 [sw setEntryEffect:[[[effectClass alloc] initWithWindow:sw] autorelease]];
931 [[sw entryEffect] setEffectTime:time];
934 - (void)setStatusWindowExitEffect:(Class)effectClass
936 StatusWindow *sw = [StatusWindow sharedWindow];
938 float time = ([df floatForKey:@"statusWindowVanishSpeed"] ? [df floatForKey:@"statusWindowVanishSpeed"] : 0.8);
939 [df setObject:NSStringFromClass(effectClass) forKey:@"statusWindowVanishEffect"];
941 [sw setExitEffect:[[[effectClass alloc] initWithWindow:sw] autorelease]];
942 [[sw exitEffect] setEffectTime:time];
945 - (void)setCustomColor:(NSColor *)color updateWell:(BOOL)update
947 [(ITTSWBackgroundView *)[[StatusWindow sharedWindow] contentView] setBackgroundColor:color];
948 [df setObject:[NSArchiver archivedDataWithRootObject:color] forKey:@"statusWindowBackgroundColor"];
951 [backgroundColorWell setColor:color];
955 - (void)repopulateEffectPopupsForVerticalPosition:(ITVerticalWindowPosition)vPos horizontalPosition:(ITHorizontalWindowPosition)hPos
957 NSEnumerator *effectEnum = [effectClasses objectEnumerator];
960 [appearanceEffectPopup removeAllItems];
961 [vanishEffectPopup removeAllItems];
963 while ( (anItem = [effectEnum nextObject]) ) {
964 [appearanceEffectPopup addItemWithTitle:[anItem effectName]];
965 [vanishEffectPopup addItemWithTitle:[anItem effectName]];
967 [[appearanceEffectPopup lastItem] setRepresentedObject:anItem];
968 [[vanishEffectPopup lastItem] setRepresentedObject:anItem];
970 if ( [self effect:anItem supportsVerticalPosition:vPos withHorizontalPosition:hPos] ) {
971 [[appearanceEffectPopup lastItem] setEnabled:YES];
972 [[vanishEffectPopup lastItem] setEnabled:YES];
974 [[appearanceEffectPopup lastItem] setEnabled:NO];
975 [[vanishEffectPopup lastItem] setEnabled:NO];
981 - (BOOL)effect:(Class)effectClass supportsVerticalPosition:(ITVerticalWindowPosition)vPos withHorizontalPosition:(ITHorizontalWindowPosition)hPos
985 if ( vPos == ITWindowPositionTop ) {
986 if ( hPos == ITWindowPositionLeft ) {
987 valid = ( [[[[effectClass supportedPositions] objectForKey:@"Top"] objectForKey:@"Left"] boolValue] ) ;
988 } else if ( hPos == ITWindowPositionCenter ) {
989 valid = ( [[[[effectClass supportedPositions] objectForKey:@"Top"] objectForKey:@"Center"] boolValue] );
990 } else if ( hPos == ITWindowPositionRight ) {
991 valid = ( [[[[effectClass supportedPositions] objectForKey:@"Top"] objectForKey:@"Right"] boolValue] );
993 } else if ( vPos == ITWindowPositionMiddle ) {
994 if ( hPos == ITWindowPositionLeft ) {
995 valid = ( [[[[effectClass supportedPositions] objectForKey:@"Middle"] objectForKey:@"Left"] boolValue] );
996 } else if ( hPos == ITWindowPositionCenter ) {
997 valid = ( [[[[effectClass supportedPositions] objectForKey:@"Middle"] objectForKey:@"Center"] boolValue] );
998 } else if ( hPos == ITWindowPositionRight ) {
999 valid = ( [[[[effectClass supportedPositions] objectForKey:@"Middle"] objectForKey:@"Right"] boolValue] );
1001 } else if ( vPos == ITWindowPositionBottom ) {
1002 if ( hPos == ITWindowPositionLeft ) {
1003 valid = ( [[[[effectClass supportedPositions] objectForKey:@"Bottom"] objectForKey:@"Left"] boolValue] );
1004 } else if ( hPos == ITWindowPositionCenter ) {
1005 valid = ( [[[[effectClass supportedPositions] objectForKey:@"Bottom"] objectForKey:@"Center"] boolValue] );
1006 } else if ( hPos == ITWindowPositionRight ) {
1007 valid = ( [[[[effectClass supportedPositions] objectForKey:@"Bottom"] objectForKey:@"Right"] boolValue] );
1014 - (IBAction)changeMenus:(id)sender
1016 ITDebugLog(@"Synchronizing menus");
1017 [df setObject:myItems forKey:@"menu"];
1020 //If we're connected over a network, refresh the menu immediately
1021 if ([[NetworkController sharedController] isConnectedToServer]) {
1022 [controller timerUpdate];
1026 - (void)setLaunchesAtLogin:(BOOL)flag
1028 NSMutableDictionary *loginwindow;
1029 NSMutableArray *loginarray;
1030 ITDebugLog(@"Setting launches at login: %i", flag);
1032 loginwindow = [[df persistentDomainForName:@"loginwindow"] mutableCopy];
1033 loginarray = [loginwindow objectForKey:@"AutoLaunchedApplicationDictionary"];
1036 NSDictionary *itemDict = [NSDictionary dictionaryWithObjectsAndKeys:
1037 [[NSBundle mainBundle] bundlePath], @"Path",
1038 [NSNumber numberWithInt:0], @"Hide", nil];
1039 [loginarray addObject:itemDict];
1042 for (i = 0; i < [loginarray count]; i++) {
1043 NSDictionary *tempDict = [loginarray objectAtIndex:i];
1044 if ([[[tempDict objectForKey:@"Path"] lastPathComponent] isEqualToString:[[[NSBundle mainBundle] bundlePath] lastPathComponent]]) {
1045 [loginarray removeObjectAtIndex:i];
1050 [df setPersistentDomain:loginwindow forName:@"loginwindow"];
1052 [loginwindow release];
1053 ITDebugLog(@"Finished setting launches at login.");
1057 /*************************************************************************/
1059 #pragma mark NSWindow DELEGATE METHODS
1060 /*************************************************************************/
1062 - (void)windowWillClose:(NSNotification *)note
1064 [(MainController *)controller closePreferences];
1067 /*************************************************************************/
1069 #pragma mark NSTextField DELEGATE METHODS
1070 /*************************************************************************/
1072 - (void)controlTextDidChange:(NSNotification*)note
1074 if ([note object] == hostTextField) {
1075 if ([[hostTextField stringValue] length] == 0) {
1076 [sharingPanelOKButton setEnabled:NO];
1078 [sharingPanelOKButton setEnabled:YES];
1083 /*************************************************************************/
1085 #pragma mark NSTableView DATASOURCE METHODS
1086 /*************************************************************************/
1088 - (int)numberOfRowsInTableView:(NSTableView *)aTableView
1090 if (aTableView == menuTableView) {
1091 return [myItems count];
1092 } else if (aTableView == allTableView) {
1093 return [availableItems count];
1094 } else if (aTableView == hotKeysTableView) {
1095 return [hotKeysArray count];
1097 return [[[NetworkController sharedController] remoteServices] count];
1101 - (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex
1103 if (aTableView == menuTableView) {
1104 NSString *object = [myItems objectAtIndex:rowIndex];
1105 if ([[aTableColumn identifier] isEqualToString:@"name"]) {
1106 if ([object isEqualToString:@"showPlayer"]) {
1107 NSString *string = nil;
1109 string = [NSString stringWithFormat:@"%@ %@", NSLocalizedString(@"show", @"Show"), [[controller currentRemote] playerSimpleName]];
1111 [controller networkError:localException];
1115 return NSLocalizedString(object, @"ERROR");
1117 if ([submenuItems containsObject:object])
1119 return [NSImage imageNamed:@"submenu"];
1124 } else if (aTableView == allTableView) {
1125 NSString *object = [availableItems objectAtIndex:rowIndex];
1126 if ([[aTableColumn identifier] isEqualToString:@"name"]) {
1127 if ([object isEqualToString:@"showPlayer"]) {
1128 NSString *string = nil;
1130 string = [NSString stringWithFormat:@"%@ %@", NSLocalizedString(@"show", @"Show"), [[controller currentRemote] playerSimpleName]];
1132 [controller networkError:localException];
1136 return NSLocalizedString(object, @"ERROR");
1138 if ([submenuItems containsObject:object]) {
1139 return [NSImage imageNamed:@"submenu"];
1144 } else if (aTableView == hotKeysTableView) {
1145 if ([[aTableColumn identifier] isEqualToString:@"name"]) {
1146 return [hotKeyNamesArray objectAtIndex:rowIndex];
1148 return [[hotKeysDictionary objectForKey:[hotKeysArray objectAtIndex:rowIndex]] description];
1151 return [[[[NetworkController sharedController] remoteServices] objectAtIndex:rowIndex] name];
1155 - (BOOL)tableView:(NSTableView *)tableView writeRows:(NSArray*)rows toPasteboard:(NSPasteboard*)pboard
1157 if (tableView == menuTableView) {
1158 [pboard declareTypes:[NSArray arrayWithObjects:@"MenuTableViewPboardType", nil] owner:self];
1159 [pboard setString:[[rows objectAtIndex:0] stringValue] forType:@"MenuTableViewPboardType"];
1163 if (tableView == allTableView) {
1164 [pboard declareTypes:[NSArray arrayWithObjects:@"AllTableViewPboardType", nil] owner:self];
1165 [pboard setString:[[rows objectAtIndex:0] stringValue] forType:@"AllTableViewPboardType"];
1171 - (BOOL)tableView:(NSTableView*)tableView acceptDrop:(id <NSDraggingInfo>)info row:(int)row dropOperation:(NSTableViewDropOperation)operation
1175 NSString *dragData, *temp;
1177 pb = [info draggingPasteboard];
1179 if ([[pb types] containsObject:@"MenuTableViewPboardType"]) {
1180 dragData = [pb stringForType:@"MenuTableViewPboardType"];
1181 dragRow = [dragData intValue];
1182 temp = [myItems objectAtIndex:dragRow];
1184 if (tableView == menuTableView) {
1185 [myItems insertObject:temp atIndex:row];
1186 if (row > dragRow) {
1187 [myItems removeObjectAtIndex:dragRow];
1189 [myItems removeObjectAtIndex:dragRow + 1];
1191 } else if (tableView == allTableView) {
1192 if (![temp isEqualToString:@"separator"]) {
1193 [availableItems addObject:temp];
1195 [myItems removeObjectAtIndex:dragRow];
1197 } else if ([[pb types] containsObject:@"AllTableViewPboardType"]) {
1198 dragData = [pb stringForType:@"AllTableViewPboardType"];
1199 dragRow = [dragData intValue];
1200 temp = [availableItems objectAtIndex:dragRow];
1202 [myItems insertObject:temp atIndex:row];
1204 if (![temp isEqualToString:@"separator"]) {
1205 [availableItems removeObjectAtIndex:dragRow];
1209 [menuTableView reloadData];
1210 [allTableView reloadData];
1211 [self changeMenus:self];
1215 - (NSDragOperation)tableView:(NSTableView*)tableView validateDrop:(id <NSDraggingInfo>)info proposedRow:(int)row proposedDropOperation:(NSTableViewDropOperation)operation
1217 if (tableView == allTableView) {
1218 if ([[[info draggingPasteboard] types] containsObject:@"AllTableViewPboardType"]) {
1219 return NSDragOperationNone;
1222 if ([[[info draggingPasteboard] types] containsObject:@"MenuTableViewPboardType"]) {
1223 NSString *item = [myItems objectAtIndex:[[[info draggingPasteboard] stringForType:@"MenuTableViewPboardType"] intValue]];
1224 if ([item isEqualToString:@"preferences"] || [item isEqualToString:@"quit"]) {
1225 return NSDragOperationNone;
1229 [tableView setDropRow:-1 dropOperation:NSTableViewDropOn];
1230 return NSDragOperationGeneric;
1233 if (operation == NSTableViewDropOn || row == -1)
1235 return NSDragOperationNone;
1237 return NSDragOperationGeneric;
1241 /*************************************************************************/
1243 #pragma mark DEALLOCATION METHODS
1244 /*************************************************************************/
1248 [hotKeysArray release];
1249 [hotKeysDictionary release];
1250 [effectClasses release];
1251 [menuTableView setDataSource:nil];
1252 [allTableView setDataSource:nil];
1253 [controller release];
1254 [availableItems release];
1255 [submenuItems release];