1 #import "PreferencesController.h"
2 #import "MainController.h"
3 #import "MenuController.h"
4 #import "NetworkController.h"
5 #import "NetworkObject.h"
6 #import "StatusWindow.h"
7 #import "StatusWindowController.h"
8 #import "CustomMenuTableView.h"
10 #import <netinet/in.h>
12 #import <openssl/sha.h>
16 #import <ITFoundation/ITLoginItem.h>
18 #import <ITKit/ITHotKeyCenter.h>
19 #import <ITKit/ITKeyCombo.h>
20 #import <ITKit/ITKeyComboPanel.h>
21 #import <ITKit/ITWindowPositioning.h>
22 #import <ITKit/ITKeyBroadcaster.h>
24 #import <ITKit/ITTSWBackgroundView.h>
25 #import <ITKit/ITWindowEffect.h>
26 #import <ITKit/ITCutWindowEffect.h>
27 #import <ITKit/ITDissolveWindowEffect.h>
28 #import <ITKit/ITSlideHorizontallyWindowEffect.h>
29 #import <ITKit/ITSlideVerticallyWindowEffect.h>
30 #import <ITKit/ITPivotWindowEffect.h>
33 #define SENDER_STATE (([sender state] == NSOnState) ? YES : NO)
35 /*************************************************************************/
37 #pragma mark PRIVATE INTERFACE
38 /*************************************************************************/
40 @interface PreferencesController (Private)
42 - (void)setupCustomizationTables;
43 - (void)setupMenuItems;
45 - (void)setStatusWindowEntryEffect:(Class)effectClass;
46 - (void)setStatusWindowExitEffect:(Class)effectClass;
47 - (void)setCustomColor:(NSColor *)color updateWell:(BOOL)update;
48 - (void)repopulateEffectPopupsForVerticalPosition:(ITVerticalWindowPosition)vPos horizontalPosition:(ITHorizontalWindowPosition)hPos;
49 - (BOOL)effect:(Class)effectClass supportsVerticalPosition:(ITVerticalWindowPosition)vPos withHorizontalPosition:(ITHorizontalWindowPosition)hPos;
50 - (IBAction)changeMenus:(id)sender;
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",
109 hotKeyNamesArray = [[NSArray alloc] initWithObjects:@"Play/Pause",
123 @"Pop-up status menu",
124 [NSString stringWithUTF8String:"Set Rating: ☆☆☆☆☆"],
125 [NSString stringWithUTF8String:"Set Rating: ★☆☆☆☆"],
126 [NSString stringWithUTF8String:"Set Rating: ★★☆☆☆"],
127 [NSString stringWithUTF8String:"Set Rating: ★★★☆☆"],
128 [NSString stringWithUTF8String:"Set Rating: ★★★★☆"],
129 [NSString stringWithUTF8String:"Set Rating: ★★★★★"],
131 hotKeysDictionary = [[NSMutableDictionary alloc] init];
134 [self setupWindow]; // Load in the nib, and perform any initial setup.
135 [[NSColorPanel sharedColorPanel] setShowsAlpha:YES];
141 /*************************************************************************/
143 #pragma mark ACCESSOR METHODS
144 /*************************************************************************/
151 - (void)setController:(id)object
153 [controller autorelease];
154 controller = [object retain];
158 /*************************************************************************/
160 #pragma mark INSTANCE METHODS
161 /*************************************************************************/
163 - (BOOL)showPasswordPanel
165 [passwordPanel setLevel:NSStatusWindowLevel];
166 [passwordPanelOKButton setTitle:@"Connect"];
167 [passwordPanelTitle setStringValue:@"Password Required"];
168 [passwordPanelMessage setStringValue:[NSString stringWithFormat:@"Please enter a password for access to the MenuTunes player named %@ at %@.", [[[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 - (BOOL)showInvalidPasswordPanel
183 [passwordPanel setLevel:NSStatusWindowLevel];
184 [passwordPanelOKButton setTitle:@"Retry"];
185 [passwordPanelTitle setStringValue:@"Invalid Password"];
186 [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]]];
187 [passwordPanel setLevel:NSStatusWindowLevel];
188 [NSApp activateIgnoringOtherApps:YES];
189 [passwordPanel center];
190 [passwordPanel orderFrontRegardless];
191 [passwordPanel makeKeyWindow];
192 if ([NSApp runModalForWindow:passwordPanel]) {
199 - (IBAction)showPrefsWindow:(id)sender
201 ITDebugLog(@"Showing preferences window.");
202 if (!myItems) { // If menu array does not exist yet, then the window hasn't been setup.
203 ITDebugLog(@"Window doesn't exist, initial setup.");
204 [self setupCustomizationTables]; // Setup the DnD manu config tables.
205 [self setupMenuItems]; // Setup the arrays of menu items
206 [self setupUI]; // Sets up additional UI
207 [window setDelegate:self];
208 [menuTableView reloadData];
209 [hotKeysTableView reloadData];
210 [hotKeysTableView setDoubleAction:@selector(hotKeysTableViewDoubleClicked:)];
212 //Change the launch player checkbox to the proper name
214 [launchPlayerAtLaunchCheckbox setTitle:[NSString stringWithFormat:@"Launch %@ when MenuTunes launches", [[controller currentRemote] playerSimpleName]]]; //This isn't localized...
216 [controller networkError:localException];
220 [self resetRemotePlayerTextFields];
221 [launchAtLoginCheckbox becomeFirstResponder];
222 [NSApp activateIgnoringOtherApps:YES];
224 [window orderFrontRegardless];
225 [window makeKeyWindow];
228 - (IBAction)changeGeneralSetting:(id)sender
230 ITDebugLog(@"Changing general setting of tag %i.", [sender tag]);
231 if ( [sender tag] == 1010) {
232 ITSetApplicationLaunchOnLogin([[NSBundle mainBundle] bundlePath], SENDER_STATE);
233 } else if ( [sender tag] == 1020) {
234 [df setBool:SENDER_STATE forKey:@"LaunchPlayerWithMT"];
235 } else if ( [sender tag] == 1030) {
236 [df setInteger:[sender intValue] forKey:@"SongsInAdvance"];
237 if ([[controller currentRemote] playerRunningState] == ITMTRemotePlayerRunning) {
238 [[controller menuController] performSelector:@selector(rebuildSubmenus) withObject:nil afterDelay:0];
240 } else if ( [sender tag] == 1040) {
241 // This will not be executed. Song info always shows the title of the song.
242 // [df setBool:SENDER_STATE forKey:@"showName"];
243 } else if ( [sender tag] == 1050) {
244 [df setBool:SENDER_STATE forKey:@"showArtist"];
245 } else if ( [sender tag] == 1055) {
246 [df setBool:SENDER_STATE forKey:@"showComposer"];
247 } else if ( [sender tag] == 1060) {
248 [df setBool:SENDER_STATE forKey:@"showAlbum"];
249 } else if ( [sender tag] == 1070) {
250 [df setBool:SENDER_STATE forKey:@"showTime"];
251 } else if ( [sender tag] == 1080) {
252 [df setBool:SENDER_STATE forKey:@"showTrackNumber"];
253 } else if ( [sender tag] == 1085) {
254 [df setBool:SENDER_STATE forKey:@"showPlayCount"];
255 } else if ( [sender tag] == 1090) {
256 [df setBool:SENDER_STATE forKey:@"showTrackRating"];
257 } else if ( [sender tag] == 1100) {
258 [df setBool:SENDER_STATE forKey:@"showAlbumArtwork"];
259 } else if ( [sender tag] == 1110) {
260 [df setBool:SENDER_STATE forKey:@"runScripts"];
262 [runScriptsCheckbox setState:NSOnState];
263 [showScriptsButton setEnabled:YES];
265 [showScriptsButton setEnabled:NO];
267 } else if ( [sender tag] == 1120) {
268 mkdir([[NSHomeDirectory() stringByAppendingPathComponent:@"Library/Application Support/MenuTunes/Scripts"] cString], 0744);
269 [[NSWorkspace sharedWorkspace] openFile:[NSHomeDirectory() stringByAppendingPathComponent:@"Library/Application Support/MenuTunes/Scripts"]];
274 - (IBAction)changeSharingSetting:(id)sender
276 ITDebugLog(@"Changing sharing setting of tag %i.", [sender tag]);
277 if ( [sender tag] == 5010 ) {
278 BOOL state = SENDER_STATE;
279 [df setBool:state forKey:@"enableSharing"];
280 //Disable/enable the use of shared player options
281 [useSharedMenuTunesCheckbox setEnabled:!state];
282 [passwordTextField setEnabled:state];
283 [nameTextField setEnabled:state];
284 [selectSharedPlayerButton setEnabled:NO];
285 [controller setServerStatus:state]; //Set server status
286 } else if ( [sender tag] == 5015 ) {
287 [df setObject:[sender stringValue] forKey:@"sharedPlayerName"];
288 [[NetworkController sharedController] resetServerName];
289 } else if ( [sender tag] == 5030 ) {
290 //Set the server password
291 const char *instring = [[sender stringValue] UTF8String];
292 const char *password = "p4s5w0rdMT1.2";
293 unsigned char *result;
294 NSData *hashedPass, *passwordStringHash;
295 if ([[sender stringValue] length] == 0) {
296 [df setObject:[NSData data] forKey:@"sharedPlayerPassword"];
299 result = SHA1(instring, strlen(instring), NULL);
300 hashedPass = [NSData dataWithBytes:result length:strlen(result)];
301 result = SHA1(password, strlen(password), NULL);
302 passwordStringHash = [NSData dataWithBytes:result length:strlen(result)];
303 if (![hashedPass isEqualToData:passwordStringHash]) {
304 [df setObject:hashedPass forKey:@"sharedPlayerPassword"];
305 [sender setStringValue:@"p4s5w0rdMT1.2"];
307 } else if ( [sender tag] == 5040 ) {
308 BOOL state = SENDER_STATE;
309 [df setBool:state forKey:@"useSharedPlayer"];
310 //Disable/enable the use of sharing options
311 [shareMenuTunesCheckbox setEnabled:!state];
312 [passwordTextField setEnabled:NO];
313 [nameTextField setEnabled:NO];
314 [selectSharedPlayerButton setEnabled:state];
316 if (state && ([controller connectToServer] == 1)) {
317 [selectedPlayerTextField setStringValue:[[[NetworkController sharedController] networkObject] serverName]];
318 [locationTextField setStringValue:[[NetworkController sharedController] remoteHost]];
320 [selectedPlayerTextField setStringValue:@"No shared player selected."];
321 [locationTextField setStringValue:@"-"];
322 if ([[NetworkController sharedController] isConnectedToServer]) {
323 [controller disconnectFromServer];
327 } else if ( [sender tag] == 5050 ) {
328 //If no player is selected in the table view, turn off OK button.
329 if ([sender clickedRow] == -1 ) {
330 [sharingPanelOKButton setEnabled:NO];
332 [sharingPanelOKButton setEnabled:YES];
334 } else if ( [sender tag] == 5051 ) {
335 [df setObject:[sender stringValue] forKey:@"sharedPlayerHost"];
336 } else if ( [sender tag] == 5060 ) {
337 //Set OK button state
338 if (([selectPlayerBox contentView] == zeroConfView && [sharingTableView selectedRow] == -1) ||
339 ([selectPlayerBox contentView] == manualView && [[hostTextField stringValue] length] == 0)) {
340 [sharingPanelOKButton setEnabled:NO];
342 [sharingPanelOKButton setEnabled:YES];
344 //Show selection sheet
345 [NSApp beginSheet:selectPlayerSheet modalForWindow:window modalDelegate:self didEndSelector:NULL contextInfo:nil];
346 } else if ( [sender tag] == 5100 ) {
348 if ( ([sender indexOfItem:[sender selectedItem]] == 0) && ([selectPlayerBox contentView] != zeroConfView) ) {
349 NSRect frame = [selectPlayerSheet frame];
350 frame.origin.y -= 58;
351 frame.size.height = 273;
352 if ([sharingTableView selectedRow] == -1) {
353 [sharingPanelOKButton setEnabled:NO];
355 [selectPlayerBox setContentView:zeroConfView];
356 [selectPlayerSheet setFrame:frame display:YES animate:YES];
357 } else if ( ([sender indexOfItem:[sender selectedItem]] == 1) && ([selectPlayerBox contentView] != manualView) ){
358 NSRect frame = [selectPlayerSheet frame];
359 frame.origin.y += 58;
360 frame.size.height = 215;
361 if ([[hostTextField stringValue] length] == 0) {
362 [sharingPanelOKButton setEnabled:NO];
364 [sharingPanelOKButton setEnabled:YES];
366 [selectPlayerBox setContentView:manualView];
367 [selectPlayerSheet setFrame:frame display:YES animate:YES];
368 [hostTextField selectText:nil];
370 } else if ( [sender tag] == 5150 ) {
371 const char *instring = [[sender stringValue] UTF8String];
372 unsigned char *result;
373 result = SHA1(instring, strlen(instring), NULL);
374 [df setObject:[NSData dataWithBytes:result length:strlen(result)] forKey:@"connectPassword"];
375 } else if ( [sender tag] == 5110 ) {
377 [NSApp endSheet:selectPlayerSheet];
378 [selectPlayerSheet orderOut:nil];
379 if ([selectPlayerBox contentView] == manualView) {
380 [hostTextField setStringValue:[df stringForKey:@"sharedPlayerHost"]];
383 } else if ( [sender tag] == 5120 ) {
385 [NSApp endSheet:selectPlayerSheet];
386 [selectPlayerSheet orderOut:nil];
388 [self changeSharingSetting:clientPasswordTextField];
390 if ([selectPlayerBox contentView] == manualView) {
391 [df setObject:[hostTextField stringValue] forKey:@"sharedPlayerHost"];
393 if ([sharingTableView selectedRow] > -1) {
394 [df setObject:[NSString stringWithCString:inet_ntoa((*(struct sockaddr_in*)[[[[[[NetworkController sharedController] remoteServices] objectAtIndex:[sharingTableView selectedRow]] addresses] objectAtIndex:0] bytes]).sin_addr)] forKey:@"sharedPlayerHost"];
398 if ([controller connectToServer] == 1) {
399 [useSharedMenuTunesCheckbox setState:NSOnState];
400 [selectedPlayerTextField setStringValue:[[[NetworkController sharedController] networkObject] serverName]];
401 [locationTextField setStringValue:[[NetworkController sharedController] remoteHost]];
403 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);
405 } else if ( [sender tag] == 6010 ) {
406 //Cancel password entry
407 [passwordPanel orderOut:nil];
408 [NSApp stopModalWithCode:0];
409 } else if ( [sender tag] == 6020 ) {
410 //OK password entry, retry connect
411 const char *instring = [[passwordPanelTextField stringValue] UTF8String];
412 unsigned char *result;
413 result = SHA1(instring, strlen(instring), NULL);
414 [df setObject:[NSData dataWithBytes:result length:strlen(result)] forKey:@"connectPassword"];
415 [passwordPanel orderOut:nil];
416 [NSApp stopModalWithCode:1];
421 - (IBAction)changeStatusWindowSetting:(id)sender
423 StatusWindow *sw = [StatusWindow sharedWindow];
424 ITDebugLog(@"Changing status window setting of tag %i", [sender tag]);
426 if ( [sender tag] == 2010) {
428 BOOL entryEffectValid = YES;
429 BOOL exitEffectValid = YES;
431 [df setInteger:[sender selectedRow] forKey:@"statusWindowVerticalPosition"];
432 [df setInteger:[sender selectedColumn] forKey:@"statusWindowHorizontalPosition"];
433 [sw setVerticalPosition:[sender selectedRow]];
434 [sw setHorizontalPosition:[sender selectedColumn]];
436 // Enable/disable the items in the popups.
437 [self repopulateEffectPopupsForVerticalPosition:[sw verticalPosition]
438 horizontalPosition:[sw horizontalPosition]];
440 // Make sure the effects support the new position.
441 entryEffectValid = ( [self effect:[[sw entryEffect] class]
442 supportsVerticalPosition:[sw verticalPosition]
443 withHorizontalPosition:[sw horizontalPosition]] );
444 exitEffectValid = ( [self effect:[[sw exitEffect] class]
445 supportsVerticalPosition:[sw verticalPosition]
446 withHorizontalPosition:[sw horizontalPosition]] );
448 if ( ! entryEffectValid ) {
449 [appearanceEffectPopup selectItemAtIndex:[[appearanceEffectPopup menu] indexOfItemWithRepresentedObject:NSClassFromString(@"ITCutWindowEffect")]];
450 [self setStatusWindowEntryEffect:NSClassFromString(@"ITCutWindowEffect")];
452 [appearanceEffectPopup selectItemAtIndex:[[appearanceEffectPopup menu] indexOfItemWithRepresentedObject:[[sw entryEffect] class]]];
455 if ( ! exitEffectValid ) {
456 [vanishEffectPopup selectItemAtIndex:[[vanishEffectPopup menu] indexOfItemWithRepresentedObject:NSClassFromString(@"ITDissolveWindowEffect")]];
457 [self setStatusWindowExitEffect:NSClassFromString(@"ITDissolveWindowEffect")];
459 [vanishEffectPopup selectItemAtIndex:[[vanishEffectPopup menu] indexOfItemWithRepresentedObject:[[sw exitEffect] class]]];
462 [(MainController *)controller showCurrentTrackInfo];
464 } else if ( [sender tag] == 2020) {
466 // Update screen selection.
467 [[StatusWindow sharedWindow] setScreen:[[NSScreen screens] objectAtIndex:[sender indexOfSelectedItem]]];
468 [(MainController *)controller showCurrentTrackInfo];
470 } else if ( [sender tag] == 2030) {
472 [self setStatusWindowEntryEffect:[[sender selectedItem] representedObject]];
473 [(MainController *)controller showCurrentTrackInfo];
475 } else if ( [sender tag] == 2040) {
477 [self setStatusWindowExitEffect:[[sender selectedItem] representedObject]];
478 [(MainController *)controller showCurrentTrackInfo];
480 } else if ( [sender tag] == 2050) {
481 float newTime = ( -([sender floatValue]) );
482 [df setFloat:newTime forKey:@"statusWindowAppearanceSpeed"];
483 [[sw entryEffect] setEffectTime:newTime];
484 } else if ( [sender tag] == 2060) {
485 float newTime = ( -([sender floatValue]) );
486 [df setFloat:newTime forKey:@"statusWindowVanishSpeed"];
487 [[sw exitEffect] setEffectTime:newTime];
488 } else if ( [sender tag] == 2070) {
489 [df setFloat:[sender floatValue] forKey:@"statusWindowVanishDelay"];
490 [sw setExitDelay:[sender floatValue]];
491 } else if ( [sender tag] == 2080) {
492 [df setBool:SENDER_STATE forKey:@"showSongInfoOnChange"];
493 } else if ( [sender tag] == 2090) {
495 int setting = [sender indexOfSelectedItem];
497 if ( setting == 0 ) {
498 [(ITTSWBackgroundView *)[sw contentView] setBackgroundMode:ITTSWBackgroundApple];
499 [backgroundColorWell setEnabled:NO];
500 [backgroundColorPopup setEnabled:NO];
501 } else if ( setting == 1 ) {
502 [(ITTSWBackgroundView *)[sw contentView] setBackgroundMode:ITTSWBackgroundReadable];
503 [backgroundColorWell setEnabled:NO];
504 [backgroundColorPopup setEnabled:NO];
505 } else if ( setting == 2 ) {
506 [(ITTSWBackgroundView *)[sw contentView] setBackgroundMode:ITTSWBackgroundColored];
507 [backgroundColorWell setEnabled:YES];
508 [backgroundColorPopup setEnabled:YES];
511 [df setInteger:setting forKey:@"statusWindowBackgroundMode"];
512 [(MainController *)controller showCurrentTrackInfo];
514 } else if ( [sender tag] == 2091) {
515 [self setCustomColor:[sender color] updateWell:NO];
516 [(MainController *)controller showCurrentTrackInfo];
517 } else if ( [sender tag] == 2092) {
519 int selectedItem = [sender indexOfSelectedItem];
521 if ( selectedItem == 1 ) { // An NSPopUpButton in PullDown mode uses item 0 as its title. Its first selectable item is 1.
522 [self setCustomColor:[NSColor colorWithCalibratedRed:0.92549 green:0.686275 blue:0.0 alpha:1.0] updateWell:YES];
523 } else if ( selectedItem == 2 ) {
524 [self setCustomColor:[NSColor colorWithCalibratedRed:0.380392 green:0.670588 blue:0.0 alpha:1.0] updateWell:YES];
525 } else if ( selectedItem == 3 ) {
526 [self setCustomColor:[NSColor colorWithCalibratedRed:0.443137 green:0.231373 blue:0.619608 alpha:1.0] updateWell:YES];
527 } else if ( selectedItem == 4 ) {
528 [self setCustomColor:[NSColor colorWithCalibratedRed:0.831373 green:0.12549 blue:0.509804 alpha:1.0] updateWell:YES];
529 } else if ( selectedItem == 5 ) {
530 [self setCustomColor:[NSColor colorWithCalibratedRed:0.00784314 green:0.611765 blue:0.662745 alpha:1.0] updateWell:YES];
532 [self setCustomColor:[NSColor colorWithCalibratedWhite:0.15 alpha:0.70] updateWell:YES];
534 [(MainController *)controller showCurrentTrackInfo];
536 } else if ( [sender tag] == 2095) {
537 [df setInteger:[sender indexOfSelectedItem] forKey:@"statusWindowSizing"];
538 [(MainController *)controller showCurrentTrackInfo];
544 - (void)registerDefaults
546 ITDebugLog(@"Registering defaults.");
547 [df setObject:[NSArray arrayWithObjects:
559 nil] forKey:@"menu"];
561 [df setInteger:MT_CURRENT_VERSION forKey:@"appVersion"];
562 [df setInteger:5 forKey:@"SongsInAdvance"];
563 // [df setBool:YES forKey:@"showName"]; // Song info will always show song title.
564 [df setBool:YES forKey:@"showArtist"];
565 [df setBool:YES forKey:@"showAlbumArtwork"];
566 [df setBool:NO forKey:@"showAlbum"];
567 [df setBool:NO forKey:@"showComposer"];
568 [df setBool:NO forKey:@"showTime"];
569 [df setBool:NO forKey:@"showToolTip"];
571 [df setObject:@"ITCutWindowEffect" forKey:@"statusWindowAppearanceEffect"];
572 [df setObject:@"ITDissolveWindowEffect" forKey:@"statusWindowVanishEffect"];
573 [df setFloat:0.8 forKey:@"statusWindowAppearanceSpeed"];
574 [df setFloat:0.8 forKey:@"statusWindowVanishSpeed"];
575 [df setFloat:4.0 forKey:@"statusWindowVanishDelay"];
576 [df setInteger:(int)ITWindowPositionBottom forKey:@"statusWindowVerticalPosition"];
577 [df setInteger:(int)ITWindowPositionLeft forKey:@"statusWindowHorizontalPosition"];
578 [[StatusWindow sharedWindow] setVerticalPosition:(int)ITWindowPositionBottom];
579 [[StatusWindow sharedWindow] setHorizontalPosition:(int)ITWindowPositionLeft];
580 [df setBool:YES forKey:@"showSongInfoOnChange"];
582 [df setObject:[NSArchiver archivedDataWithRootObject:[NSColor blueColor]] forKey:@"statusWindowBackgroundColor"];
586 if (ITDoesApplicationLaunchOnLogin([[NSBundle mainBundle] bundlePath])) {
587 [[StatusWindowController sharedController] showSetupQueryWindow];
593 [[StatusWindow sharedWindow] setLocked:NO];
594 [[StatusWindow sharedWindow] vanish:self];
595 [[StatusWindow sharedWindow] setIgnoresMouseEvents:YES];
597 ITSetApplicationLaunchOnLogin([[NSBundle mainBundle] bundlePath], YES);
600 - (void)autoLaunchCancel
602 [[StatusWindow sharedWindow] setLocked:NO];
603 [[StatusWindow sharedWindow] vanish:self];
604 [[StatusWindow sharedWindow] setIgnoresMouseEvents:YES];
607 - (void)deletePressedInTableView:(NSTableView *)tableView
609 if (tableView == menuTableView) {
610 int selRow = [tableView selectedRow];
611 ITDebugLog(@"Delete pressed in menu table view.");
613 NSString *object = [myItems objectAtIndex:selRow];
615 if ([object isEqualToString:@"preferences"]) {
620 if (![object isEqualToString:@"separator"])
621 [availableItems addObject:object];
622 ITDebugLog(@"Removing object named %@", object);
623 [myItems removeObjectAtIndex:selRow];
624 [menuTableView reloadData];
625 [allTableView reloadData];
627 [self changeMenus:self];
631 - (void)resetRemotePlayerTextFields
633 if ([[NetworkController sharedController] isConnectedToServer]) {
634 [selectedPlayerTextField setStringValue:[[[NetworkController sharedController] networkObject] serverName]];
635 [locationTextField setStringValue:[[NetworkController sharedController] remoteHost]];
637 [selectedPlayerTextField setStringValue:@"No shared player selected."];
638 [locationTextField setStringValue:@"-"];
642 /*************************************************************************/
644 #pragma mark HOTKEY SUPPORT METHODS
645 /*************************************************************************/
647 - (IBAction)clearHotKey:(id)sender
649 [hotKeysDictionary setObject:[ITKeyCombo clearKeyCombo] forKey:[hotKeysArray objectAtIndex:[hotKeysTableView selectedRow]]];
650 [df setObject:[[ITKeyCombo clearKeyCombo] plistRepresentation] forKey:[hotKeysArray objectAtIndex:[hotKeysTableView selectedRow]]];
651 [controller setupHotKeys];
652 [hotKeysTableView reloadData];
655 - (IBAction)editHotKey:(id)sender
657 ITKeyComboPanel *panel = [ITKeyComboPanel sharedPanel];
658 NSString *keyComboKey = [hotKeysArray objectAtIndex:[hotKeysTableView selectedRow]];
659 ITKeyCombo *keyCombo;
661 ITDebugLog(@"Setting key combo on hot key %@.", keyComboKey);
662 [controller clearHotKeys];
663 [panel setKeyCombo:[hotKeysDictionary objectForKey:[hotKeysArray objectAtIndex:[hotKeysTableView selectedRow]]]];
664 [panel setKeyBindingName:[hotKeyNamesArray objectAtIndex:[hotKeysTableView selectedRow]]];
665 if ([panel runModal] == NSOKButton) {
666 NSEnumerator *keyEnumerator = [[hotKeysDictionary allKeys] objectEnumerator];
668 keyCombo = [panel keyCombo];
670 //Check for duplicate key combo
671 while ( (nextKey = [keyEnumerator nextObject]) ) {
672 if ([[hotKeysDictionary objectForKey:nextKey] isEqual:keyCombo] &&
673 ![keyCombo isEqual:[ITKeyCombo clearKeyCombo]]) {
674 [hotKeysDictionary setObject:[ITKeyCombo clearKeyCombo]
676 [df setObject:[[ITKeyCombo clearKeyCombo] plistRepresentation]
681 [hotKeysDictionary setObject:keyCombo forKey:keyComboKey];
682 [df setObject:[keyCombo plistRepresentation] forKey:keyComboKey];
683 [controller setupHotKeys];
684 [hotKeysTableView reloadData];
685 ITDebugLog(@"Set combo %@ on hot key %@.", keyCombo, keyComboKey);
687 ITDebugLog(@"Hot key setting on hot key %@ cancelled.", keyComboKey);
691 - (void)hotKeysTableViewDoubleClicked:(id)sender
693 if ([sender clickedRow] > -1) {
694 [self editHotKey:sender];
698 /*************************************************************************/
700 #pragma mark PRIVATE METHOD IMPLEMENTATIONS
701 /*************************************************************************/
705 ITDebugLog(@"Loading Preferences.nib.");
706 if (![NSBundle loadNibNamed:@"Preferences" owner:self]) {
707 ITDebugLog(@"Failed to load Preferences.nib.");
713 - (void)setupCustomizationTables
715 NSImageCell *imgCell = [[[NSImageCell alloc] initImageCell:nil] autorelease];
716 ITDebugLog(@"Setting up table views.");
717 // Set the table view cells up
718 [imgCell setImageScaling:NSScaleNone];
719 [[menuTableView tableColumnWithIdentifier:@"submenu"] setDataCell:imgCell];
720 [[allTableView tableColumnWithIdentifier:@"submenu"] setDataCell:imgCell];
722 // Register for drag and drop
723 [menuTableView registerForDraggedTypes:[NSArray arrayWithObjects:
724 @"MenuTableViewPboardType",
725 @"AllTableViewPboardType",
727 [allTableView registerForDraggedTypes:[NSArray arrayWithObjects:
728 @"MenuTableViewPboardType",
729 @"AllTableViewPboardType",
733 - (void)setupMenuItems
735 NSEnumerator *itemEnum;
737 ITDebugLog(@"Setting up table view arrays.");
738 // Set the list of items you can have.
739 availableItems = [[NSMutableArray alloc] initWithObjects:
757 // Get our preferred menu
758 myItems = [[df arrayForKey:@"menu"] mutableCopy];
760 // Delete items in the availableItems array that are already part of the menu
761 itemEnum = [myItems objectEnumerator];
762 while ( (anItem = [itemEnum nextObject]) ) {
763 if (![anItem isEqualToString:@"separator"]) {
764 [availableItems removeObject:anItem];
768 // Items that show should a submenu image
769 submenuItems = [[NSArray alloc] initWithObjects:
781 NSEnumerator *keyArrayEnum;
782 NSString *serverName;
786 NSArray *screens = [NSScreen screens];
788 [df setInteger:MT_CURRENT_VERSION forKey:@"appVersion"];
790 ITDebugLog(@"Setting up preferences UI.");
791 // Fill in the number of songs in advance to show field
792 [songsInAdvance setIntValue:[df integerForKey:@"SongsInAdvance"]];
794 // Fill hot key array
795 keyArrayEnum = [hotKeysArray objectEnumerator];
797 while ( (anItem = [keyArrayEnum nextObject]) ) {
798 if ([df objectForKey:anItem]) {
799 ITDebugLog(@"Setting up \"%@\" hot key.", anItem);
800 [hotKeysDictionary setObject:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:anItem]] forKey:anItem];
802 [hotKeysDictionary setObject:[ITKeyCombo clearKeyCombo] forKey:anItem];
806 ITDebugLog(@"Setting up screen popup");
807 if ([screens count] > 1) {
809 [screenPopup setEnabled:YES];
810 for (i = 0; i < [screens count]; i++) {
811 NSScreen *screen = [screens objectAtIndex:i];
812 if (![screen isEqual:[NSScreen mainScreen]]) {
813 [screenPopup addItemWithTitle:[NSString stringWithFormat:@"Screen %i", i + 1]];
818 ITDebugLog(@"Setting up track info checkboxes.");
819 // Check current track info buttons
820 [albumCheckbox setState:[df boolForKey:@"showAlbum"] ? NSOnState : NSOffState];
821 [nameCheckbox setState:NSOnState]; // Song info will ALWAYS show song title.
822 [nameCheckbox setEnabled:NO]; // Song info will ALWAYS show song title.
823 [artistCheckbox setState:[df boolForKey:@"showArtist"] ? NSOnState : NSOffState];
824 [composerCheckbox setState:[df boolForKey:@"showComposer"] ? NSOnState : NSOffState];
825 [trackTimeCheckbox setState:[df boolForKey:@"showTime"] ? NSOnState : NSOffState];
826 [trackNumberCheckbox setState:[df boolForKey:@"showTrackNumber"] ? NSOnState : NSOffState];
827 [playCountCheckbox setState:[df boolForKey:@"showPlayCount"] ? NSOnState : NSOffState];
828 [ratingCheckbox setState:[df boolForKey:@"showTrackRating"] ? NSOnState : NSOffState];
829 [albumArtworkCheckbox setState:[df boolForKey:@"showAlbumArtwork"] ? NSOnState : NSOffState];
831 if ([df boolForKey:@"runScripts"]) {
832 [runScriptsCheckbox setState:NSOnState];
833 [showScriptsButton setEnabled:YES];
835 [showScriptsButton setEnabled:NO];
838 // Set the launch at login checkbox state
839 ITDebugLog(@"Setting launch at login state.");
840 if (ITDoesApplicationLaunchOnLogin([[NSBundle mainBundle] bundlePath])) {
841 [launchAtLoginCheckbox setState:NSOnState];
844 // Set the launch player checkbox state
845 ITDebugLog(@"Setting launch player with MenuTunes state.");
846 [launchPlayerAtLaunchCheckbox setState:[df boolForKey:@"LaunchPlayerWithMT"] ? NSOnState : NSOffState];
848 // Setup the positioning controls
849 [positionMatrix selectCellAtRow:[df integerForKey:@"statusWindowVerticalPosition"]
850 column:[df integerForKey:@"statusWindowHorizontalPosition"]];
852 // Setup effects controls
853 // Populate the effects popups
854 [appearanceEffectPopup setAutoenablesItems:NO];
855 [vanishEffectPopup setAutoenablesItems:NO];
856 [self repopulateEffectPopupsForVerticalPosition:[df integerForKey:@"statusWindowVerticalPosition"]
857 horizontalPosition:[df integerForKey:@"statusWindowHorizontalPosition"]];
859 // Attempt to find the pref'd effect in the list.
860 // If it's not there, use cut/dissolve.
861 if ( [effectClasses containsObject:NSClassFromString([df stringForKey:@"statusWindowAppearanceEffect"])] ) {
862 [appearanceEffectPopup selectItemAtIndex:[effectClasses indexOfObject:NSClassFromString([df stringForKey:@"statusWindowAppearanceEffect"])]];
864 [appearanceEffectPopup selectItemAtIndex:[effectClasses indexOfObject:NSClassFromString(@"ITCutWindowEffect")]];
867 if ( [effectClasses containsObject:NSClassFromString([df stringForKey:@"statusWindowVanishEffect"])] ) {
868 [vanishEffectPopup selectItemAtIndex:[effectClasses indexOfObject:NSClassFromString([df stringForKey:@"statusWindowVanishEffect"])]];
870 [vanishEffectPopup selectItemAtIndex:[effectClasses indexOfObject:NSClassFromString(@"ITCutWindowEffect")]];
873 [appearanceSpeedSlider setFloatValue:( -([df floatForKey:@"statusWindowAppearanceSpeed"]) )];
874 [vanishSpeedSlider setFloatValue:( -([df floatForKey:@"statusWindowVanishSpeed"]) )];
875 [vanishDelaySlider setFloatValue:[df floatForKey:@"statusWindowVanishDelay"]];
877 // Setup General Controls
878 selectedBGStyle = [df integerForKey:@"statusWindowBackgroundMode"];
879 [backgroundStylePopup selectItem:[backgroundStylePopup itemAtIndex:[backgroundStylePopup indexOfItemWithTag:selectedBGStyle]]];
881 if ( selectedBGStyle == ITTSWBackgroundColored ) {
882 [backgroundColorWell setEnabled:YES];
883 [backgroundColorPopup setEnabled:YES];
885 [backgroundColorWell setEnabled:NO];
886 [backgroundColorPopup setEnabled:NO];
889 colorData = [df dataForKey:@"statusWindowBackgroundColor"];
892 [backgroundColorWell setColor:(NSColor *)[NSUnarchiver unarchiveObjectWithData:colorData]];
894 [backgroundColorWell setColor:[NSColor blueColor]];
897 [showOnChangeCheckbox setState:([df boolForKey:@"showSongInfoOnChange"] ? NSOnState : NSOffState)];
899 [windowSizingPopup selectItem:[windowSizingPopup itemAtIndex:[windowSizingPopup indexOfItemWithTag:[df integerForKey:@"statusWindowSizing"]]]];
901 // Setup the sharing controls
902 if ([df boolForKey:@"enableSharing"]) {
903 [shareMenuTunesCheckbox setState:NSOnState];
904 [useSharedMenuTunesCheckbox setEnabled:NO];
905 [selectSharedPlayerButton setEnabled:NO];
906 [passwordTextField setEnabled:YES];
907 [nameTextField setEnabled:YES];
908 } else if ([df boolForKey:@"useSharedPlayer"]) {
909 [useSharedMenuTunesCheckbox setState:NSOnState];
910 [shareMenuTunesCheckbox setEnabled:NO];
911 [selectSharedPlayerButton setEnabled:YES];
914 [[NSNotificationCenter defaultCenter] addObserver:sharingTableView selector:@selector(reloadData) name:@"ITMTFoundNetService" object:nil];
916 serverName = [df stringForKey:@"sharedPlayerName"];
917 if (!serverName || [serverName length] == 0) {
918 serverName = @"MenuTunes Shared Player";
920 [nameTextField setStringValue:serverName];
922 [selectPlayerBox setContentView:zeroConfView];
923 if ([[df dataForKey:@"sharedPlayerPassword"] length] > 0) {
924 [passwordTextField setStringValue:@"p4s5w0rdMT1.2"];
926 [passwordTextField setStringValue:@""];
928 if ([df stringForKey:@"sharedPlayerHost"]) {
929 [hostTextField setStringValue:[df stringForKey:@"sharedPlayerHost"]];
932 if ([[NetworkController sharedController] isConnectedToServer]) {
933 [selectedPlayerTextField setStringValue:[[[NetworkController sharedController] networkObject] serverName]];
934 [locationTextField setStringValue:[[NetworkController sharedController] remoteHost]];
936 [selectedPlayerTextField setStringValue:@"No shared player selected."];
937 [locationTextField setStringValue:@"-"];
941 - (void)setStatusWindowEntryEffect:(Class)effectClass
943 StatusWindow *sw = [StatusWindow sharedWindow];
945 float time = ([df floatForKey:@"statusWindowAppearanceSpeed"] ? [df floatForKey:@"statusWindowAppearanceSpeed"] : 0.8);
946 [df setObject:NSStringFromClass(effectClass) forKey:@"statusWindowAppearanceEffect"];
948 [sw setEntryEffect:[[[effectClass alloc] initWithWindow:sw] autorelease]];
949 [[sw entryEffect] setEffectTime:time];
952 - (void)setStatusWindowExitEffect:(Class)effectClass
954 StatusWindow *sw = [StatusWindow sharedWindow];
956 float time = ([df floatForKey:@"statusWindowVanishSpeed"] ? [df floatForKey:@"statusWindowVanishSpeed"] : 0.8);
957 [df setObject:NSStringFromClass(effectClass) forKey:@"statusWindowVanishEffect"];
959 [sw setExitEffect:[[[effectClass alloc] initWithWindow:sw] autorelease]];
960 [[sw exitEffect] setEffectTime:time];
963 - (void)setCustomColor:(NSColor *)color updateWell:(BOOL)update
965 [(ITTSWBackgroundView *)[[StatusWindow sharedWindow] contentView] setBackgroundColor:color];
966 [df setObject:[NSArchiver archivedDataWithRootObject:color] forKey:@"statusWindowBackgroundColor"];
969 [backgroundColorWell setColor:color];
973 - (void)repopulateEffectPopupsForVerticalPosition:(ITVerticalWindowPosition)vPos horizontalPosition:(ITHorizontalWindowPosition)hPos
975 NSEnumerator *effectEnum = [effectClasses objectEnumerator];
978 [appearanceEffectPopup removeAllItems];
979 [vanishEffectPopup removeAllItems];
981 while ( (anItem = [effectEnum nextObject]) ) {
982 [appearanceEffectPopup addItemWithTitle:[anItem effectName]];
983 [vanishEffectPopup addItemWithTitle:[anItem effectName]];
985 [[appearanceEffectPopup lastItem] setRepresentedObject:anItem];
986 [[vanishEffectPopup lastItem] setRepresentedObject:anItem];
988 if ( [self effect:anItem supportsVerticalPosition:vPos withHorizontalPosition:hPos] ) {
989 [[appearanceEffectPopup lastItem] setEnabled:YES];
990 [[vanishEffectPopup lastItem] setEnabled:YES];
992 [[appearanceEffectPopup lastItem] setEnabled:NO];
993 [[vanishEffectPopup lastItem] setEnabled:NO];
999 - (BOOL)effect:(Class)effectClass supportsVerticalPosition:(ITVerticalWindowPosition)vPos withHorizontalPosition:(ITHorizontalWindowPosition)hPos
1003 if ( vPos == ITWindowPositionTop ) {
1004 if ( hPos == ITWindowPositionLeft ) {
1005 valid = ( [[[[effectClass supportedPositions] objectForKey:@"Top"] objectForKey:@"Left"] boolValue] ) ;
1006 } else if ( hPos == ITWindowPositionCenter ) {
1007 valid = ( [[[[effectClass supportedPositions] objectForKey:@"Top"] objectForKey:@"Center"] boolValue] );
1008 } else if ( hPos == ITWindowPositionRight ) {
1009 valid = ( [[[[effectClass supportedPositions] objectForKey:@"Top"] objectForKey:@"Right"] boolValue] );
1011 } else if ( vPos == ITWindowPositionMiddle ) {
1012 if ( hPos == ITWindowPositionLeft ) {
1013 valid = ( [[[[effectClass supportedPositions] objectForKey:@"Middle"] objectForKey:@"Left"] boolValue] );
1014 } else if ( hPos == ITWindowPositionCenter ) {
1015 valid = ( [[[[effectClass supportedPositions] objectForKey:@"Middle"] objectForKey:@"Center"] boolValue] );
1016 } else if ( hPos == ITWindowPositionRight ) {
1017 valid = ( [[[[effectClass supportedPositions] objectForKey:@"Middle"] objectForKey:@"Right"] boolValue] );
1019 } else if ( vPos == ITWindowPositionBottom ) {
1020 if ( hPos == ITWindowPositionLeft ) {
1021 valid = ( [[[[effectClass supportedPositions] objectForKey:@"Bottom"] objectForKey:@"Left"] boolValue] );
1022 } else if ( hPos == ITWindowPositionCenter ) {
1023 valid = ( [[[[effectClass supportedPositions] objectForKey:@"Bottom"] objectForKey:@"Center"] boolValue] );
1024 } else if ( hPos == ITWindowPositionRight ) {
1025 valid = ( [[[[effectClass supportedPositions] objectForKey:@"Bottom"] objectForKey:@"Right"] boolValue] );
1032 - (IBAction)changeMenus:(id)sender
1034 ITDebugLog(@"Synchronizing menus");
1035 [df setObject:myItems forKey:@"menu"];
1038 [[controller menuController] performSelector:@selector(rebuildSubmenus) withObject:nil afterDelay:0.0];
1040 //If we're connected over a network, refresh the menu immediately
1041 if ([[NetworkController sharedController] isConnectedToServer]) {
1042 [controller timerUpdate];
1047 /*************************************************************************/
1049 #pragma mark NSWindow DELEGATE METHODS
1050 /*************************************************************************/
1052 - (void)windowWillClose:(NSNotification *)note
1054 [(MainController *)controller closePreferences];
1057 /*************************************************************************/
1059 #pragma mark NSTextField DELEGATE METHODS
1060 /*************************************************************************/
1062 - (void)controlTextDidChange:(NSNotification*)note
1064 if ([note object] == hostTextField) {
1065 if ([[hostTextField stringValue] length] == 0) {
1066 [sharingPanelOKButton setEnabled:NO];
1068 [sharingPanelOKButton setEnabled:YES];
1073 /*************************************************************************/
1075 #pragma mark NSTableView DATASOURCE METHODS
1076 /*************************************************************************/
1078 - (int)numberOfRowsInTableView:(NSTableView *)aTableView
1080 if (aTableView == menuTableView) {
1081 return [myItems count];
1082 } else if (aTableView == allTableView) {
1083 return [availableItems count];
1084 } else if (aTableView == hotKeysTableView) {
1085 return [hotKeysArray count];
1087 return [[[NetworkController sharedController] remoteServices] count];
1091 - (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex
1093 if (aTableView == menuTableView) {
1094 NSString *object = [myItems objectAtIndex:rowIndex];
1095 if ([[aTableColumn identifier] isEqualToString:@"name"]) {
1096 if ([object isEqualToString:@"showPlayer"]) {
1097 NSString *string = nil;
1099 string = [NSString stringWithFormat:@"%@ %@", NSLocalizedString(@"show", @"Show"), [[controller currentRemote] playerSimpleName]];
1101 [controller networkError:localException];
1105 return NSLocalizedString(object, @"ERROR");
1107 if ([submenuItems containsObject:object])
1109 return [NSImage imageNamed:@"submenu"];
1114 } else if (aTableView == allTableView) {
1115 NSString *object = [availableItems objectAtIndex:rowIndex];
1116 if ([[aTableColumn identifier] isEqualToString:@"name"]) {
1117 if ([object isEqualToString:@"showPlayer"]) {
1118 NSString *string = nil;
1120 string = [NSString stringWithFormat:@"%@ %@", NSLocalizedString(@"show", @"Show"), [[controller currentRemote] playerSimpleName]];
1122 [controller networkError:localException];
1126 return NSLocalizedString(object, @"ERROR");
1128 if ([submenuItems containsObject:object]) {
1129 return [NSImage imageNamed:@"submenu"];
1134 } else if (aTableView == hotKeysTableView) {
1135 if ([[aTableColumn identifier] isEqualToString:@"name"]) {
1136 return [hotKeyNamesArray objectAtIndex:rowIndex];
1138 return [[hotKeysDictionary objectForKey:[hotKeysArray objectAtIndex:rowIndex]] description];
1141 return [[[[NetworkController sharedController] remoteServices] objectAtIndex:rowIndex] name];
1145 - (BOOL)tableView:(NSTableView *)tableView writeRows:(NSArray*)rows toPasteboard:(NSPasteboard*)pboard
1147 if (tableView == menuTableView) {
1148 [pboard declareTypes:[NSArray arrayWithObjects:@"MenuTableViewPboardType", nil] owner:self];
1149 [pboard setString:[[rows objectAtIndex:0] stringValue] forType:@"MenuTableViewPboardType"];
1153 if (tableView == allTableView) {
1154 [pboard declareTypes:[NSArray arrayWithObjects:@"AllTableViewPboardType", nil] owner:self];
1155 [pboard setString:[[rows objectAtIndex:0] stringValue] forType:@"AllTableViewPboardType"];
1161 - (BOOL)tableView:(NSTableView*)tableView acceptDrop:(id <NSDraggingInfo>)info row:(int)row dropOperation:(NSTableViewDropOperation)operation
1165 NSString *dragData, *temp;
1167 pb = [info draggingPasteboard];
1169 if ([[pb types] containsObject:@"MenuTableViewPboardType"]) {
1170 dragData = [pb stringForType:@"MenuTableViewPboardType"];
1171 dragRow = [dragData intValue];
1172 temp = [myItems objectAtIndex:dragRow];
1174 if (tableView == menuTableView) {
1175 [myItems insertObject:temp atIndex:row];
1176 if (row > dragRow) {
1177 [myItems removeObjectAtIndex:dragRow];
1179 [myItems removeObjectAtIndex:dragRow + 1];
1181 } else if (tableView == allTableView) {
1182 if (![temp isEqualToString:@"separator"]) {
1183 [availableItems addObject:temp];
1185 [myItems removeObjectAtIndex:dragRow];
1187 } else if ([[pb types] containsObject:@"AllTableViewPboardType"]) {
1188 dragData = [pb stringForType:@"AllTableViewPboardType"];
1189 dragRow = [dragData intValue];
1190 temp = [availableItems objectAtIndex:dragRow];
1192 [myItems insertObject:temp atIndex:row];
1194 if (![temp isEqualToString:@"separator"]) {
1195 [availableItems removeObjectAtIndex:dragRow];
1199 [menuTableView reloadData];
1200 [allTableView reloadData];
1201 [self changeMenus:self];
1205 - (NSDragOperation)tableView:(NSTableView*)tableView validateDrop:(id <NSDraggingInfo>)info proposedRow:(int)row proposedDropOperation:(NSTableViewDropOperation)operation
1207 if (tableView == allTableView) {
1208 if ([[[info draggingPasteboard] types] containsObject:@"AllTableViewPboardType"]) {
1209 return NSDragOperationNone;
1212 if ([[[info draggingPasteboard] types] containsObject:@"MenuTableViewPboardType"]) {
1213 NSString *item = [myItems objectAtIndex:[[[info draggingPasteboard] stringForType:@"MenuTableViewPboardType"] intValue]];
1214 if ([item isEqualToString:@"preferences"] || [item isEqualToString:@"quit"]) {
1215 return NSDragOperationNone;
1219 [tableView setDropRow:-1 dropOperation:NSTableViewDropOn];
1220 return NSDragOperationGeneric;
1223 if (operation == NSTableViewDropOn || row == -1)
1225 return NSDragOperationNone;
1227 return NSDragOperationGeneric;
1231 /*************************************************************************/
1233 #pragma mark DEALLOCATION METHODS
1234 /*************************************************************************/
1238 [hotKeysArray release];
1239 [hotKeysDictionary release];
1240 [effectClasses release];
1241 [menuTableView setDataSource:nil];
1242 [allTableView setDataSource:nil];
1243 [controller release];
1244 [availableItems release];
1245 [submenuItems release];