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)setupScreenPopup;
46 - (void)setStatusWindowEntryEffect:(Class)effectClass;
47 - (void)setStatusWindowExitEffect:(Class)effectClass;
48 - (void)setCustomColor:(NSColor *)color updateWell:(BOOL)update;
49 - (void)repopulateEffectPopupsForVerticalPosition:(ITVerticalWindowPosition)vPos horizontalPosition:(ITHorizontalWindowPosition)hPos;
50 - (BOOL)effect:(Class)effectClass supportsVerticalPosition:(ITVerticalWindowPosition)vPos withHorizontalPosition:(ITHorizontalWindowPosition)hPos;
51 - (IBAction)changeMenus:(id)sender;
55 @implementation PreferencesController
58 /*************************************************************************/
60 #pragma mark STATIC VARIABLES
61 /*************************************************************************/
63 static PreferencesController *prefs = nil;
66 /*************************************************************************/
68 #pragma mark INITIALIZATION METHODS
69 /*************************************************************************/
71 + (PreferencesController *)sharedPrefs;
74 prefs = [[self alloc] init];
81 if ( (self = [super init]) ) {
82 ITDebugLog(@"Preferences initialized.");
83 df = [[NSUserDefaults standardUserDefaults] retain];
85 effectClasses = [[ITWindowEffect effectClasses] retain];
87 hotKeysArray = [[NSArray alloc] initWithObjects:@"PlayPause",
110 hotKeyNamesArray = [[NSArray alloc] initWithObjects:@"Play/Pause",
124 @"Pop-up status menu",
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: ★★★★☆"],
130 [NSString stringWithUTF8String:"Set Rating: ★★★★★"],
132 hotKeysDictionary = [[NSMutableDictionary alloc] init];
135 [self setupWindow]; // Load in the nib, and perform any initial setup.
136 [[NSColorPanel sharedColorPanel] setShowsAlpha:YES];
142 /*************************************************************************/
144 #pragma mark ACCESSOR METHODS
145 /*************************************************************************/
152 - (void)setController:(id)object
154 [controller autorelease];
155 controller = [object retain];
159 /*************************************************************************/
161 #pragma mark INSTANCE METHODS
162 /*************************************************************************/
164 - (BOOL)showPasswordPanel
166 [passwordPanel setLevel:NSStatusWindowLevel];
167 [passwordPanelOKButton setTitle:@"Connect"];
168 [passwordPanelTitle setStringValue:@"Password Required"];
169 [passwordPanelMessage setStringValue:[NSString stringWithFormat:@"Please enter a password for access to the MenuTunes player named %@ at %@.", [[[NetworkController sharedController] networkObject] serverName], [[NetworkController sharedController] remoteHost]]];
170 [passwordPanel setLevel:NSStatusWindowLevel];
171 [NSApp activateIgnoringOtherApps:YES];
172 [passwordPanel center];
173 [passwordPanel orderFrontRegardless];
174 [passwordPanel makeKeyWindow];
175 if ([NSApp runModalForWindow:passwordPanel]) {
182 - (BOOL)showInvalidPasswordPanel
184 [passwordPanel setLevel:NSStatusWindowLevel];
185 [passwordPanelOKButton setTitle:@"Retry"];
186 [passwordPanelTitle setStringValue:@"Invalid Password"];
187 [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]]];
188 [passwordPanel setLevel:NSStatusWindowLevel];
189 [NSApp activateIgnoringOtherApps:YES];
190 [passwordPanel center];
191 [passwordPanel orderFrontRegardless];
192 [passwordPanel makeKeyWindow];
193 if ([NSApp runModalForWindow:passwordPanel]) {
200 - (IBAction)showPrefsWindow:(id)sender
202 ITDebugLog(@"Showing preferences window.");
203 if (!myItems) { // If menu array does not exist yet, then the window hasn't been setup.
204 ITDebugLog(@"Window doesn't exist, initial setup.");
205 [self setupCustomizationTables]; // Setup the DnD manu config tables.
206 [self setupMenuItems]; // Setup the arrays of menu items
207 [self setupUI]; // Sets up additional UI
208 [window setDelegate:self];
209 [menuTableView reloadData];
210 [hotKeysTableView reloadData];
211 [hotKeysTableView setDoubleAction:@selector(hotKeysTableViewDoubleClicked:)];
213 //Change the launch player checkbox to the proper name
215 [launchPlayerAtLaunchCheckbox setTitle:[NSString stringWithFormat:@"Launch %@ when MenuTunes launches", [[controller currentRemote] playerSimpleName]]]; //This isn't localized...
217 [controller networkError:localException];
221 [self resetRemotePlayerTextFields];
222 [launchAtLoginCheckbox becomeFirstResponder];
223 [NSApp activateIgnoringOtherApps:YES];
224 if (![window isVisible]) {
227 [window orderFrontRegardless];
228 [window makeKeyWindow];
231 - (IBAction)changeGeneralSetting:(id)sender
233 ITDebugLog(@"Changing general setting of tag %i.", [sender tag]);
234 if ( [sender tag] == 1010) {
235 ITSetApplicationLaunchOnLogin([[NSBundle mainBundle] bundlePath], SENDER_STATE);
236 } else if ( [sender tag] == 1020) {
237 [df setBool:SENDER_STATE forKey:@"LaunchPlayerWithMT"];
238 } else if ( [sender tag] == 1030) {
239 [df setInteger:[sender intValue] forKey:@"SongsInAdvance"];
240 if ([[controller currentRemote] playerRunningState] == ITMTRemotePlayerRunning) {
241 [[controller menuController] performSelector:@selector(rebuildSubmenus) withObject:nil afterDelay:0];
243 } else if ( [sender tag] == 1040) {
244 // This will not be executed. Song info always shows the title of the song.
245 // [df setBool:SENDER_STATE forKey:@"showName"];
246 } else if ( [sender tag] == 1050) {
247 [df setBool:SENDER_STATE forKey:@"showArtist"];
248 } else if ( [sender tag] == 1055) {
249 [df setBool:SENDER_STATE forKey:@"showComposer"];
250 } else if ( [sender tag] == 1060) {
251 [df setBool:SENDER_STATE forKey:@"showAlbum"];
252 } else if ( [sender tag] == 1070) {
253 [df setBool:SENDER_STATE forKey:@"showTime"];
254 } else if ( [sender tag] == 1080) {
255 [df setBool:SENDER_STATE forKey:@"showTrackNumber"];
256 } else if ( [sender tag] == 1085) {
257 [df setBool:SENDER_STATE forKey:@"showPlayCount"];
258 } else if ( [sender tag] == 1090) {
259 [df setBool:SENDER_STATE forKey:@"showTrackRating"];
260 } else if ( [sender tag] == 1100) {
261 [df setBool:SENDER_STATE forKey:@"showAlbumArtwork"];
262 } else if ( [sender tag] == 1110) {
263 [df setBool:SENDER_STATE forKey:@"runScripts"];
265 [runScriptsCheckbox setState:NSOnState];
266 [showScriptsButton setEnabled:YES];
268 [showScriptsButton setEnabled:NO];
270 } else if ( [sender tag] == 1120) {
271 mkdir([[NSHomeDirectory() stringByAppendingPathComponent:@"Library/Application Support/MenuTunes/Scripts"] cString], 0744);
272 [[NSWorkspace sharedWorkspace] openFile:[NSHomeDirectory() stringByAppendingPathComponent:@"Library/Application Support/MenuTunes/Scripts"]];
277 - (IBAction)changeSharingSetting:(id)sender
279 ITDebugLog(@"Changing sharing setting of tag %i.", [sender tag]);
280 if ( [sender tag] == 5010 ) {
281 BOOL state = SENDER_STATE;
282 [df setBool:state forKey:@"enableSharing"];
283 //Disable/enable the use of shared player options
284 [useSharedMenuTunesCheckbox setEnabled:!state];
285 [passwordTextField setEnabled:state];
286 [nameTextField setEnabled:state];
287 [selectSharedPlayerButton setEnabled:NO];
288 [controller setServerStatus:state]; //Set server status
289 } else if ( [sender tag] == 5015 ) {
290 [df setObject:[sender stringValue] forKey:@"sharedPlayerName"];
291 [[NetworkController sharedController] resetServerName];
292 } else if ( [sender tag] == 5030 ) {
293 //Set the server password
294 const char *instring = [[sender stringValue] UTF8String];
295 const char *password = "p4s5w0rdMT1.2";
296 unsigned char *result;
297 NSData *hashedPass, *passwordStringHash;
298 if ([[sender stringValue] length] == 0) {
299 [df setObject:[NSData data] forKey:@"sharedPlayerPassword"];
302 result = SHA1(instring, strlen(instring), NULL);
303 hashedPass = [NSData dataWithBytes:result length:strlen(result)];
304 result = SHA1(password, strlen(password), NULL);
305 passwordStringHash = [NSData dataWithBytes:result length:strlen(result)];
306 if (![hashedPass isEqualToData:passwordStringHash]) {
307 [df setObject:hashedPass forKey:@"sharedPlayerPassword"];
308 [sender setStringValue:@"p4s5w0rdMT1.2"];
310 } else if ( [sender tag] == 5040 ) {
311 BOOL state = SENDER_STATE;
312 [df setBool:state forKey:@"useSharedPlayer"];
313 //Disable/enable the use of sharing options
314 [shareMenuTunesCheckbox setEnabled:!state];
315 [passwordTextField setEnabled:NO];
316 [nameTextField setEnabled:NO];
317 [selectSharedPlayerButton setEnabled:state];
319 if (state && ([controller connectToServer] == 1)) {
320 [selectedPlayerTextField setStringValue:[[[NetworkController sharedController] networkObject] serverName]];
321 [locationTextField setStringValue:[[NetworkController sharedController] remoteHost]];
323 [selectedPlayerTextField setStringValue:@"No shared player selected."];
324 [locationTextField setStringValue:@"-"];
325 if ([[NetworkController sharedController] isConnectedToServer]) {
326 [controller disconnectFromServer];
330 } else if ( [sender tag] == 5050 ) {
331 //If no player is selected in the table view, turn off OK button.
332 if ([sender clickedRow] == -1 ) {
333 [sharingPanelOKButton setEnabled:NO];
335 [sharingPanelOKButton setEnabled:YES];
337 } else if ( [sender tag] == 5051 ) {
338 [df setObject:[sender stringValue] forKey:@"sharedPlayerHost"];
339 } else if ( [sender tag] == 5060 ) {
340 //Set OK button state
341 if (([selectPlayerBox contentView] == zeroConfView && [sharingTableView selectedRow] == -1) ||
342 ([selectPlayerBox contentView] == manualView && [[hostTextField stringValue] length] == 0)) {
343 [sharingPanelOKButton setEnabled:NO];
345 [sharingPanelOKButton setEnabled:YES];
347 //Show selection sheet
348 [NSApp beginSheet:selectPlayerSheet modalForWindow:window modalDelegate:self didEndSelector:NULL contextInfo:nil];
349 } else if ( [sender tag] == 5100 ) {
351 if ( ([sender indexOfItem:[sender selectedItem]] == 0) && ([selectPlayerBox contentView] != zeroConfView) ) {
352 NSRect frame = [selectPlayerSheet frame];
353 frame.origin.y -= 58;
354 frame.size.height = 273;
355 if ([sharingTableView selectedRow] == -1) {
356 [sharingPanelOKButton setEnabled:NO];
358 [selectPlayerBox setContentView:zeroConfView];
359 [selectPlayerSheet setFrame:frame display:YES animate:YES];
360 } else if ( ([sender indexOfItem:[sender selectedItem]] == 1) && ([selectPlayerBox contentView] != manualView) ){
361 NSRect frame = [selectPlayerSheet frame];
362 frame.origin.y += 58;
363 frame.size.height = 215;
364 if ([[hostTextField stringValue] length] == 0) {
365 [sharingPanelOKButton setEnabled:NO];
367 [sharingPanelOKButton setEnabled:YES];
369 [selectPlayerBox setContentView:manualView];
370 [selectPlayerSheet setFrame:frame display:YES animate:YES];
371 [hostTextField selectText:nil];
373 } else if ( [sender tag] == 5150 ) {
374 const char *instring = [[sender stringValue] UTF8String];
375 unsigned char *result;
376 result = SHA1(instring, strlen(instring), NULL);
377 [df setObject:[NSData dataWithBytes:result length:strlen(result)] forKey:@"connectPassword"];
378 } else if ( [sender tag] == 5110 ) {
380 [NSApp endSheet:selectPlayerSheet];
381 [selectPlayerSheet orderOut:nil];
382 if ([selectPlayerBox contentView] == manualView) {
383 [hostTextField setStringValue:[df stringForKey:@"sharedPlayerHost"]];
386 } else if ( [sender tag] == 5120 ) {
388 [NSApp endSheet:selectPlayerSheet];
389 [selectPlayerSheet orderOut:nil];
391 [self changeSharingSetting:clientPasswordTextField];
393 if ([selectPlayerBox contentView] == manualView) {
394 [df setObject:[hostTextField stringValue] forKey:@"sharedPlayerHost"];
396 if ([sharingTableView selectedRow] > -1) {
397 [df setObject:[NSString stringWithCString:inet_ntoa((*(struct sockaddr_in*)[[[[[[NetworkController sharedController] remoteServices] objectAtIndex:[sharingTableView selectedRow]] addresses] objectAtIndex:0] bytes]).sin_addr)] forKey:@"sharedPlayerHost"];
401 if ([controller connectToServer] == 1) {
402 [useSharedMenuTunesCheckbox setState:NSOnState];
403 [selectedPlayerTextField setStringValue:[[[NetworkController sharedController] networkObject] serverName]];
404 [locationTextField setStringValue:[[NetworkController sharedController] remoteHost]];
406 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);
408 } else if ( [sender tag] == 6010 ) {
409 //Cancel password entry
410 [passwordPanel orderOut:nil];
411 [NSApp stopModalWithCode:0];
412 } else if ( [sender tag] == 6020 ) {
413 //OK password entry, retry connect
414 const char *instring = [[passwordPanelTextField stringValue] UTF8String];
415 unsigned char *result;
416 result = SHA1(instring, strlen(instring), NULL);
417 [df setObject:[NSData dataWithBytes:result length:strlen(result)] forKey:@"connectPassword"];
418 [passwordPanel orderOut:nil];
419 [NSApp stopModalWithCode:1];
424 - (IBAction)changeStatusWindowSetting:(id)sender
426 StatusWindow *sw = [StatusWindow sharedWindow];
427 ITDebugLog(@"Changing status window setting of tag %i", [sender tag]);
429 if ( [sender tag] == 2010) {
431 BOOL entryEffectValid = YES;
432 BOOL exitEffectValid = YES;
434 [df setInteger:[sender selectedRow] forKey:@"statusWindowVerticalPosition"];
435 [df setInteger:[sender selectedColumn] forKey:@"statusWindowHorizontalPosition"];
436 [sw setVerticalPosition:[sender selectedRow]];
437 [sw setHorizontalPosition:[sender selectedColumn]];
439 // Enable/disable the items in the popups.
440 [self repopulateEffectPopupsForVerticalPosition:[sw verticalPosition]
441 horizontalPosition:[sw horizontalPosition]];
443 // Make sure the effects support the new position.
444 entryEffectValid = ( [self effect:[[sw entryEffect] class]
445 supportsVerticalPosition:[sw verticalPosition]
446 withHorizontalPosition:[sw horizontalPosition]] );
447 exitEffectValid = ( [self effect:[[sw exitEffect] class]
448 supportsVerticalPosition:[sw verticalPosition]
449 withHorizontalPosition:[sw horizontalPosition]] );
451 if ( ! entryEffectValid ) {
452 [appearanceEffectPopup selectItemAtIndex:[[appearanceEffectPopup menu] indexOfItemWithRepresentedObject:NSClassFromString(@"ITCutWindowEffect")]];
453 [self setStatusWindowEntryEffect:NSClassFromString(@"ITCutWindowEffect")];
455 [appearanceEffectPopup selectItemAtIndex:[[appearanceEffectPopup menu] indexOfItemWithRepresentedObject:[[sw entryEffect] class]]];
458 if ( ! exitEffectValid ) {
459 [vanishEffectPopup selectItemAtIndex:[[vanishEffectPopup menu] indexOfItemWithRepresentedObject:NSClassFromString(@"ITDissolveWindowEffect")]];
460 [self setStatusWindowExitEffect:NSClassFromString(@"ITDissolveWindowEffect")];
462 [vanishEffectPopup selectItemAtIndex:[[vanishEffectPopup menu] indexOfItemWithRepresentedObject:[[sw exitEffect] class]]];
465 [(MainController *)controller showCurrentTrackInfo];
467 } else if ( [sender tag] == 2020) {
469 // Update screen selection.
470 [[StatusWindow sharedWindow] setScreen:[[NSScreen screens] objectAtIndex:[sender indexOfSelectedItem]]];
471 [df setInteger:[sender indexOfSelectedItem] forKey:@"statusWindowScreenIndex"];
472 [(MainController *)controller showCurrentTrackInfo];
474 } else if ( [sender tag] == 2030) {
476 [self setStatusWindowEntryEffect:[[sender selectedItem] representedObject]];
477 [(MainController *)controller showCurrentTrackInfo];
479 } else if ( [sender tag] == 2040) {
481 [self setStatusWindowExitEffect:[[sender selectedItem] representedObject]];
482 [(MainController *)controller showCurrentTrackInfo];
484 } else if ( [sender tag] == 2050) {
485 float newTime = ( -([sender floatValue]) );
486 [df setFloat:newTime forKey:@"statusWindowAppearanceSpeed"];
487 [[sw entryEffect] setEffectTime:newTime];
488 } else if ( [sender tag] == 2060) {
489 float newTime = ( -([sender floatValue]) );
490 [df setFloat:newTime forKey:@"statusWindowVanishSpeed"];
491 [[sw exitEffect] setEffectTime:newTime];
492 } else if ( [sender tag] == 2070) {
493 [df setFloat:[sender floatValue] forKey:@"statusWindowVanishDelay"];
494 [sw setExitDelay:[sender floatValue]];
495 } else if ( [sender tag] == 2080) {
496 [df setBool:SENDER_STATE forKey:@"showSongInfoOnChange"];
497 } else if ( [sender tag] == 2090) {
499 int setting = [sender indexOfSelectedItem];
501 if ( setting == 0 ) {
502 [(ITTSWBackgroundView *)[sw contentView] setBackgroundMode:ITTSWBackgroundApple];
503 [backgroundColorWell setEnabled:NO];
504 [backgroundColorPopup setEnabled:NO];
505 } else if ( setting == 1 ) {
506 [(ITTSWBackgroundView *)[sw contentView] setBackgroundMode:ITTSWBackgroundReadable];
507 [backgroundColorWell setEnabled:NO];
508 [backgroundColorPopup setEnabled:NO];
509 } else if ( setting == 2 ) {
510 [(ITTSWBackgroundView *)[sw contentView] setBackgroundMode:ITTSWBackgroundColored];
511 [backgroundColorWell setEnabled:YES];
512 [backgroundColorPopup setEnabled:YES];
515 [df setInteger:setting forKey:@"statusWindowBackgroundMode"];
516 [(MainController *)controller showCurrentTrackInfo];
518 } else if ( [sender tag] == 2091) {
519 [self setCustomColor:[sender color] updateWell:NO];
520 [(MainController *)controller showCurrentTrackInfo];
521 } else if ( [sender tag] == 2092) {
523 int selectedItem = [sender indexOfSelectedItem];
525 if ( selectedItem == 1 ) { // An NSPopUpButton in PullDown mode uses item 0 as its title. Its first selectable item is 1.
526 [self setCustomColor:[NSColor colorWithCalibratedRed:0.92549 green:0.686275 blue:0.0 alpha:1.0] updateWell:YES];
527 } else if ( selectedItem == 2 ) {
528 [self setCustomColor:[NSColor colorWithCalibratedRed:0.380392 green:0.670588 blue:0.0 alpha:1.0] updateWell:YES];
529 } else if ( selectedItem == 3 ) {
530 [self setCustomColor:[NSColor colorWithCalibratedRed:0.443137 green:0.231373 blue:0.619608 alpha:1.0] updateWell:YES];
531 } else if ( selectedItem == 4 ) {
532 [self setCustomColor:[NSColor colorWithCalibratedRed:0.831373 green:0.12549 blue:0.509804 alpha:1.0] updateWell:YES];
533 } else if ( selectedItem == 5 ) {
534 [self setCustomColor:[NSColor colorWithCalibratedRed:0.00784314 green:0.611765 blue:0.662745 alpha:1.0] updateWell:YES];
536 [self setCustomColor:[NSColor colorWithCalibratedWhite:0.15 alpha:0.70] updateWell:YES];
538 [(MainController *)controller showCurrentTrackInfo];
540 } else if ( [sender tag] == 2095) {
541 [df setInteger:[sender indexOfSelectedItem] forKey:@"statusWindowSizing"];
542 [(MainController *)controller showCurrentTrackInfo];
548 - (void)registerDefaults
550 ITDebugLog(@"Registering defaults.");
551 [df setObject:[NSArray arrayWithObjects:
563 nil] forKey:@"menu"];
565 [df setInteger:MT_CURRENT_VERSION forKey:@"appVersion"];
566 [df setInteger:5 forKey:@"SongsInAdvance"];
567 // [df setBool:YES forKey:@"showName"]; // Song info will always show song title.
568 [df setBool:YES forKey:@"showArtist"];
569 [df setBool:YES forKey:@"showAlbumArtwork"];
570 [df setBool:NO forKey:@"showAlbum"];
571 [df setBool:NO forKey:@"showComposer"];
572 [df setBool:NO forKey:@"showTime"];
573 [df setBool:NO forKey:@"showToolTip"];
575 [df setObject:@"ITCutWindowEffect" forKey:@"statusWindowAppearanceEffect"];
576 [df setObject:@"ITDissolveWindowEffect" forKey:@"statusWindowVanishEffect"];
577 [df setFloat:0.8 forKey:@"statusWindowAppearanceSpeed"];
578 [df setFloat:0.8 forKey:@"statusWindowVanishSpeed"];
579 [df setFloat:4.0 forKey:@"statusWindowVanishDelay"];
580 [df setInteger:(int)ITWindowPositionBottom forKey:@"statusWindowVerticalPosition"];
581 [df setInteger:(int)ITWindowPositionLeft forKey:@"statusWindowHorizontalPosition"];
582 [df setInteger:0 forKey:@"statusWindowScreenIndex"];
583 [[StatusWindow sharedWindow] setVerticalPosition:(int)ITWindowPositionBottom];
584 [[StatusWindow sharedWindow] setHorizontalPosition:(int)ITWindowPositionLeft];
585 [df setBool:YES forKey:@"showSongInfoOnChange"];
587 [df setObject:[NSArchiver archivedDataWithRootObject:[NSColor blueColor]] forKey:@"statusWindowBackgroundColor"];
591 if (ITDoesApplicationLaunchOnLogin([[NSBundle mainBundle] bundlePath])) {
592 [[StatusWindowController sharedController] showSetupQueryWindow];
598 [[StatusWindow sharedWindow] setLocked:NO];
599 [[StatusWindow sharedWindow] vanish:self];
600 [[StatusWindow sharedWindow] setIgnoresMouseEvents:YES];
602 ITSetApplicationLaunchOnLogin([[NSBundle mainBundle] bundlePath], YES);
605 - (void)autoLaunchCancel
607 [[StatusWindow sharedWindow] setLocked:NO];
608 [[StatusWindow sharedWindow] vanish:self];
609 [[StatusWindow sharedWindow] setIgnoresMouseEvents:YES];
612 - (void)deletePressedInTableView:(NSTableView *)tableView
614 if (tableView == menuTableView) {
615 int selRow = [tableView selectedRow];
616 ITDebugLog(@"Delete pressed in menu table view.");
618 NSString *object = [myItems objectAtIndex:selRow];
620 if ([object isEqualToString:@"preferences"]) {
625 if (![object isEqualToString:@"separator"])
626 [availableItems addObject:object];
627 ITDebugLog(@"Removing object named %@", object);
628 [myItems removeObjectAtIndex:selRow];
629 [menuTableView reloadData];
630 [allTableView reloadData];
632 [self changeMenus:self];
636 - (void)resetRemotePlayerTextFields
638 if ([[NetworkController sharedController] isConnectedToServer]) {
639 [selectedPlayerTextField setStringValue:[[[NetworkController sharedController] networkObject] serverName]];
640 [locationTextField setStringValue:[[NetworkController sharedController] remoteHost]];
642 [selectedPlayerTextField setStringValue:@"No shared player selected."];
643 [locationTextField setStringValue:@"-"];
647 /*************************************************************************/
649 #pragma mark HOTKEY SUPPORT METHODS
650 /*************************************************************************/
652 - (IBAction)clearHotKey:(id)sender
654 [hotKeysDictionary setObject:[ITKeyCombo clearKeyCombo] forKey:[hotKeysArray objectAtIndex:[hotKeysTableView selectedRow]]];
655 [df setObject:[[ITKeyCombo clearKeyCombo] plistRepresentation] forKey:[hotKeysArray objectAtIndex:[hotKeysTableView selectedRow]]];
656 [controller setupHotKeys];
657 [hotKeysTableView reloadData];
660 - (IBAction)editHotKey:(id)sender
662 ITKeyComboPanel *panel = [ITKeyComboPanel sharedPanel];
663 NSString *keyComboKey = [hotKeysArray objectAtIndex:[hotKeysTableView selectedRow]];
664 ITKeyCombo *keyCombo;
666 ITDebugLog(@"Setting key combo on hot key %@.", keyComboKey);
667 [controller clearHotKeys];
668 [panel setKeyCombo:[hotKeysDictionary objectForKey:[hotKeysArray objectAtIndex:[hotKeysTableView selectedRow]]]];
669 [panel setKeyBindingName:[hotKeyNamesArray objectAtIndex:[hotKeysTableView selectedRow]]];
670 if ([panel runModal] == NSOKButton) {
671 NSEnumerator *keyEnumerator = [[hotKeysDictionary allKeys] objectEnumerator];
673 keyCombo = [panel keyCombo];
675 //Check for duplicate key combo
676 while ( (nextKey = [keyEnumerator nextObject]) ) {
677 if ([[hotKeysDictionary objectForKey:nextKey] isEqual:keyCombo] &&
678 ![keyCombo isEqual:[ITKeyCombo clearKeyCombo]]) {
679 [hotKeysDictionary setObject:[ITKeyCombo clearKeyCombo]
681 [df setObject:[[ITKeyCombo clearKeyCombo] plistRepresentation]
686 [hotKeysDictionary setObject:keyCombo forKey:keyComboKey];
687 [df setObject:[keyCombo plistRepresentation] forKey:keyComboKey];
688 [controller setupHotKeys];
689 [hotKeysTableView reloadData];
690 ITDebugLog(@"Set combo %@ on hot key %@.", keyCombo, keyComboKey);
692 ITDebugLog(@"Hot key setting on hot key %@ cancelled.", keyComboKey);
696 - (void)hotKeysTableViewDoubleClicked:(id)sender
698 if ([sender clickedRow] > -1) {
699 [self editHotKey:sender];
703 /*************************************************************************/
705 #pragma mark PRIVATE METHOD IMPLEMENTATIONS
706 /*************************************************************************/
710 ITDebugLog(@"Loading Preferences.nib.");
711 if (![NSBundle loadNibNamed:@"Preferences" owner:self]) {
712 ITDebugLog(@"Failed to load Preferences.nib.");
718 - (void)setupCustomizationTables
720 NSImageCell *imgCell = [[[NSImageCell alloc] initImageCell:nil] autorelease];
721 ITDebugLog(@"Setting up table views.");
722 // Set the table view cells up
723 [imgCell setImageScaling:NSScaleNone];
724 [[menuTableView tableColumnWithIdentifier:@"submenu"] setDataCell:imgCell];
725 [[allTableView tableColumnWithIdentifier:@"submenu"] setDataCell:imgCell];
727 // Register for drag and drop
728 [menuTableView registerForDraggedTypes:[NSArray arrayWithObjects:
729 @"MenuTableViewPboardType",
730 @"AllTableViewPboardType",
732 [allTableView registerForDraggedTypes:[NSArray arrayWithObjects:
733 @"MenuTableViewPboardType",
734 @"AllTableViewPboardType",
738 - (void)setupMenuItems
740 NSEnumerator *itemEnum;
742 ITDebugLog(@"Setting up table view arrays.");
743 // Set the list of items you can have.
744 availableItems = [[NSMutableArray alloc] initWithObjects:
762 // Get our preferred menu
763 myItems = [[df arrayForKey:@"menu"] mutableCopy];
765 // Delete items in the availableItems array that are already part of the menu
766 itemEnum = [myItems objectEnumerator];
767 while ( (anItem = [itemEnum nextObject]) ) {
768 if (![anItem isEqualToString:@"separator"]) {
769 [availableItems removeObject:anItem];
773 // Items that show should a submenu image
774 submenuItems = [[NSArray alloc] initWithObjects:
786 NSEnumerator *keyArrayEnum;
787 NSString *serverName;
792 [df setInteger:MT_CURRENT_VERSION forKey:@"appVersion"];
794 ITDebugLog(@"Setting up preferences UI.");
795 // Fill in the number of songs in advance to show field
796 [songsInAdvance setIntValue:[df integerForKey:@"SongsInAdvance"]];
798 // Fill hot key array
799 keyArrayEnum = [hotKeysArray objectEnumerator];
801 while ( (anItem = [keyArrayEnum nextObject]) ) {
802 if ([df objectForKey:anItem]) {
803 ITDebugLog(@"Setting up \"%@\" hot key.", anItem);
804 [hotKeysDictionary setObject:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:anItem]] forKey:anItem];
806 [hotKeysDictionary setObject:[ITKeyCombo clearKeyCombo] forKey:anItem];
810 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(setupScreenPopup) name:NSApplicationDidChangeScreenParametersNotification object:nil];
811 [self setupScreenPopup];
813 ITDebugLog(@"Setting up track info checkboxes.");
814 // Check current track info buttons
815 [albumCheckbox setState:[df boolForKey:@"showAlbum"] ? NSOnState : NSOffState];
816 [nameCheckbox setState:NSOnState]; // Song info will ALWAYS show song title.
817 [nameCheckbox setEnabled:NO]; // Song info will ALWAYS show song title.
818 [artistCheckbox setState:[df boolForKey:@"showArtist"] ? NSOnState : NSOffState];
819 [composerCheckbox setState:[df boolForKey:@"showComposer"] ? NSOnState : NSOffState];
820 [trackTimeCheckbox setState:[df boolForKey:@"showTime"] ? NSOnState : NSOffState];
821 [trackNumberCheckbox setState:[df boolForKey:@"showTrackNumber"] ? NSOnState : NSOffState];
822 [playCountCheckbox setState:[df boolForKey:@"showPlayCount"] ? NSOnState : NSOffState];
823 [ratingCheckbox setState:[df boolForKey:@"showTrackRating"] ? NSOnState : NSOffState];
824 [albumArtworkCheckbox setState:[df boolForKey:@"showAlbumArtwork"] ? NSOnState : NSOffState];
826 if ([df boolForKey:@"runScripts"]) {
827 [runScriptsCheckbox setState:NSOnState];
828 [showScriptsButton setEnabled:YES];
830 [showScriptsButton setEnabled:NO];
833 // Set the launch at login checkbox state
834 ITDebugLog(@"Setting launch at login state.");
835 if (ITDoesApplicationLaunchOnLogin([[NSBundle mainBundle] bundlePath])) {
836 [launchAtLoginCheckbox setState:NSOnState];
839 // Set the launch player checkbox state
840 ITDebugLog(@"Setting launch player with MenuTunes state.");
841 [launchPlayerAtLaunchCheckbox setState:[df boolForKey:@"LaunchPlayerWithMT"] ? NSOnState : NSOffState];
843 // Setup the positioning controls
844 [positionMatrix selectCellAtRow:[df integerForKey:@"statusWindowVerticalPosition"]
845 column:[df integerForKey:@"statusWindowHorizontalPosition"]];
847 // Setup effects controls
848 // Populate the effects popups
849 [appearanceEffectPopup setAutoenablesItems:NO];
850 [vanishEffectPopup setAutoenablesItems:NO];
851 [self repopulateEffectPopupsForVerticalPosition:[df integerForKey:@"statusWindowVerticalPosition"]
852 horizontalPosition:[df integerForKey:@"statusWindowHorizontalPosition"]];
854 // Attempt to find the pref'd effect in the list.
855 // If it's not there, use cut/dissolve.
856 if ( [effectClasses containsObject:NSClassFromString([df stringForKey:@"statusWindowAppearanceEffect"])] ) {
857 [appearanceEffectPopup selectItemAtIndex:[effectClasses indexOfObject:NSClassFromString([df stringForKey:@"statusWindowAppearanceEffect"])]];
859 [appearanceEffectPopup selectItemAtIndex:[effectClasses indexOfObject:NSClassFromString(@"ITCutWindowEffect")]];
862 if ( [effectClasses containsObject:NSClassFromString([df stringForKey:@"statusWindowVanishEffect"])] ) {
863 [vanishEffectPopup selectItemAtIndex:[effectClasses indexOfObject:NSClassFromString([df stringForKey:@"statusWindowVanishEffect"])]];
865 [vanishEffectPopup selectItemAtIndex:[effectClasses indexOfObject:NSClassFromString(@"ITCutWindowEffect")]];
868 [appearanceSpeedSlider setFloatValue:( -([df floatForKey:@"statusWindowAppearanceSpeed"]) )];
869 [vanishSpeedSlider setFloatValue:( -([df floatForKey:@"statusWindowVanishSpeed"]) )];
870 [vanishDelaySlider setFloatValue:[df floatForKey:@"statusWindowVanishDelay"]];
872 // Setup General Controls
873 selectedBGStyle = [df integerForKey:@"statusWindowBackgroundMode"];
874 [backgroundStylePopup selectItem:[backgroundStylePopup itemAtIndex:[backgroundStylePopup indexOfItemWithTag:selectedBGStyle]]];
876 if ( selectedBGStyle == ITTSWBackgroundColored ) {
877 [backgroundColorWell setEnabled:YES];
878 [backgroundColorPopup setEnabled:YES];
880 [backgroundColorWell setEnabled:NO];
881 [backgroundColorPopup setEnabled:NO];
884 colorData = [df dataForKey:@"statusWindowBackgroundColor"];
887 [backgroundColorWell setColor:(NSColor *)[NSUnarchiver unarchiveObjectWithData:colorData]];
889 [backgroundColorWell setColor:[NSColor blueColor]];
892 [showOnChangeCheckbox setState:([df boolForKey:@"showSongInfoOnChange"] ? NSOnState : NSOffState)];
894 [windowSizingPopup selectItem:[windowSizingPopup itemAtIndex:[windowSizingPopup indexOfItemWithTag:[df integerForKey:@"statusWindowSizing"]]]];
896 // Setup the sharing controls
897 if ([df boolForKey:@"enableSharing"]) {
898 [shareMenuTunesCheckbox setState:NSOnState];
899 [useSharedMenuTunesCheckbox setEnabled:NO];
900 [selectSharedPlayerButton setEnabled:NO];
901 [passwordTextField setEnabled:YES];
902 [nameTextField setEnabled:YES];
903 } else if ([df boolForKey:@"useSharedPlayer"]) {
904 [useSharedMenuTunesCheckbox setState:NSOnState];
905 [shareMenuTunesCheckbox setEnabled:NO];
906 [selectSharedPlayerButton setEnabled:YES];
909 [[NSNotificationCenter defaultCenter] addObserver:sharingTableView selector:@selector(reloadData) name:@"ITMTFoundNetService" object:nil];
911 serverName = [df stringForKey:@"sharedPlayerName"];
912 if (!serverName || [serverName length] == 0) {
913 serverName = @"MenuTunes Shared Player";
915 [nameTextField setStringValue:serverName];
917 [selectPlayerBox setContentView:zeroConfView];
918 if ([[df dataForKey:@"sharedPlayerPassword"] length] > 0) {
919 [passwordTextField setStringValue:@"p4s5w0rdMT1.2"];
921 [passwordTextField setStringValue:@""];
923 if ([df stringForKey:@"sharedPlayerHost"]) {
924 [hostTextField setStringValue:[df stringForKey:@"sharedPlayerHost"]];
927 if ([[NetworkController sharedController] isConnectedToServer]) {
928 [selectedPlayerTextField setStringValue:[[[NetworkController sharedController] networkObject] serverName]];
929 [locationTextField setStringValue:[[NetworkController sharedController] remoteHost]];
931 [selectedPlayerTextField setStringValue:@"No shared player selected."];
932 [locationTextField setStringValue:@"-"];
936 - (void)setupScreenPopup
938 ITDebugLog(@"Setting up screen popup");
939 NSArray *screens = [NSScreen screens];
940 if ([screens count] > 1) {
941 int i, index = [df integerForKey:@"statusWindowScreenIndex"];
942 [screenPopup setEnabled:YES];
943 for (i = 0; i < [screens count]; i++) {
944 NSScreen *screen = [screens objectAtIndex:i];
945 if (![screen isEqual:[NSScreen mainScreen]]) {
946 [screenPopup addItemWithTitle:[NSString stringWithFormat:@"Screen %i", i + 1]];
949 [screenPopup selectItemAtIndex:index];
950 [[StatusWindow sharedWindow] setScreen:[[NSScreen screens] objectAtIndex:index]];
952 while ([screenPopup numberOfItems] > 1) {
953 [screenPopup removeItemAtIndex:1];
955 [screenPopup setEnabled:NO];
956 [[StatusWindow sharedWindow] setScreen:[NSScreen mainScreen]];
960 - (void)setStatusWindowEntryEffect:(Class)effectClass
962 StatusWindow *sw = [StatusWindow sharedWindow];
964 float time = ([df floatForKey:@"statusWindowAppearanceSpeed"] ? [df floatForKey:@"statusWindowAppearanceSpeed"] : 0.8);
965 [df setObject:NSStringFromClass(effectClass) forKey:@"statusWindowAppearanceEffect"];
967 [sw setEntryEffect:[[[effectClass alloc] initWithWindow:sw] autorelease]];
968 [[sw entryEffect] setEffectTime:time];
971 - (void)setStatusWindowExitEffect:(Class)effectClass
973 StatusWindow *sw = [StatusWindow sharedWindow];
975 float time = ([df floatForKey:@"statusWindowVanishSpeed"] ? [df floatForKey:@"statusWindowVanishSpeed"] : 0.8);
976 [df setObject:NSStringFromClass(effectClass) forKey:@"statusWindowVanishEffect"];
978 [sw setExitEffect:[[[effectClass alloc] initWithWindow:sw] autorelease]];
979 [[sw exitEffect] setEffectTime:time];
982 - (void)setCustomColor:(NSColor *)color updateWell:(BOOL)update
984 [(ITTSWBackgroundView *)[[StatusWindow sharedWindow] contentView] setBackgroundColor:color];
985 [df setObject:[NSArchiver archivedDataWithRootObject:color] forKey:@"statusWindowBackgroundColor"];
988 [backgroundColorWell setColor:color];
992 - (void)repopulateEffectPopupsForVerticalPosition:(ITVerticalWindowPosition)vPos horizontalPosition:(ITHorizontalWindowPosition)hPos
994 NSEnumerator *effectEnum = [effectClasses objectEnumerator];
997 [appearanceEffectPopup removeAllItems];
998 [vanishEffectPopup removeAllItems];
1000 while ( (anItem = [effectEnum nextObject]) ) {
1001 [appearanceEffectPopup addItemWithTitle:[anItem effectName]];
1002 [vanishEffectPopup addItemWithTitle:[anItem effectName]];
1004 [[appearanceEffectPopup lastItem] setRepresentedObject:anItem];
1005 [[vanishEffectPopup lastItem] setRepresentedObject:anItem];
1007 if ( [self effect:anItem supportsVerticalPosition:vPos withHorizontalPosition:hPos] ) {
1008 [[appearanceEffectPopup lastItem] setEnabled:YES];
1009 [[vanishEffectPopup lastItem] setEnabled:YES];
1011 [[appearanceEffectPopup lastItem] setEnabled:NO];
1012 [[vanishEffectPopup lastItem] setEnabled:NO];
1018 - (BOOL)effect:(Class)effectClass supportsVerticalPosition:(ITVerticalWindowPosition)vPos withHorizontalPosition:(ITHorizontalWindowPosition)hPos
1022 if ( vPos == ITWindowPositionTop ) {
1023 if ( hPos == ITWindowPositionLeft ) {
1024 valid = ( [[[[effectClass supportedPositions] objectForKey:@"Top"] objectForKey:@"Left"] boolValue] ) ;
1025 } else if ( hPos == ITWindowPositionCenter ) {
1026 valid = ( [[[[effectClass supportedPositions] objectForKey:@"Top"] objectForKey:@"Center"] boolValue] );
1027 } else if ( hPos == ITWindowPositionRight ) {
1028 valid = ( [[[[effectClass supportedPositions] objectForKey:@"Top"] objectForKey:@"Right"] boolValue] );
1030 } else if ( vPos == ITWindowPositionMiddle ) {
1031 if ( hPos == ITWindowPositionLeft ) {
1032 valid = ( [[[[effectClass supportedPositions] objectForKey:@"Middle"] objectForKey:@"Left"] boolValue] );
1033 } else if ( hPos == ITWindowPositionCenter ) {
1034 valid = ( [[[[effectClass supportedPositions] objectForKey:@"Middle"] objectForKey:@"Center"] boolValue] );
1035 } else if ( hPos == ITWindowPositionRight ) {
1036 valid = ( [[[[effectClass supportedPositions] objectForKey:@"Middle"] objectForKey:@"Right"] boolValue] );
1038 } else if ( vPos == ITWindowPositionBottom ) {
1039 if ( hPos == ITWindowPositionLeft ) {
1040 valid = ( [[[[effectClass supportedPositions] objectForKey:@"Bottom"] objectForKey:@"Left"] boolValue] );
1041 } else if ( hPos == ITWindowPositionCenter ) {
1042 valid = ( [[[[effectClass supportedPositions] objectForKey:@"Bottom"] objectForKey:@"Center"] boolValue] );
1043 } else if ( hPos == ITWindowPositionRight ) {
1044 valid = ( [[[[effectClass supportedPositions] objectForKey:@"Bottom"] objectForKey:@"Right"] boolValue] );
1051 - (IBAction)changeMenus:(id)sender
1053 ITDebugLog(@"Synchronizing menus");
1054 [df setObject:myItems forKey:@"menu"];
1057 [[controller menuController] performSelector:@selector(rebuildSubmenus) withObject:nil afterDelay:0.0];
1059 //If we're connected over a network, refresh the menu immediately
1060 if ([[NetworkController sharedController] isConnectedToServer]) {
1061 [controller timerUpdate];
1066 /*************************************************************************/
1068 #pragma mark NSWindow DELEGATE METHODS
1069 /*************************************************************************/
1071 - (void)windowWillClose:(NSNotification *)note
1073 [(MainController *)controller closePreferences];
1076 /*************************************************************************/
1078 #pragma mark NSTextField DELEGATE METHODS
1079 /*************************************************************************/
1081 - (void)controlTextDidChange:(NSNotification*)note
1083 if ([note object] == hostTextField) {
1084 if ([[hostTextField stringValue] length] == 0) {
1085 [sharingPanelOKButton setEnabled:NO];
1087 [sharingPanelOKButton setEnabled:YES];
1092 /*************************************************************************/
1094 #pragma mark NSTableView DATASOURCE METHODS
1095 /*************************************************************************/
1097 - (int)numberOfRowsInTableView:(NSTableView *)aTableView
1099 if (aTableView == menuTableView) {
1100 return [myItems count];
1101 } else if (aTableView == allTableView) {
1102 return [availableItems count];
1103 } else if (aTableView == hotKeysTableView) {
1104 return [hotKeysArray count];
1106 return [[[NetworkController sharedController] remoteServices] count];
1110 - (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex
1112 if (aTableView == menuTableView) {
1113 NSString *object = [myItems objectAtIndex:rowIndex];
1114 if ([[aTableColumn identifier] isEqualToString:@"name"]) {
1115 if ([object isEqualToString:@"showPlayer"]) {
1116 NSString *string = nil;
1118 string = [NSString stringWithFormat:@"%@ %@", NSLocalizedString(@"show", @"Show"), [[controller currentRemote] playerSimpleName]];
1120 [controller networkError:localException];
1124 return NSLocalizedString(object, @"ERROR");
1126 if ([submenuItems containsObject:object])
1128 return [NSImage imageNamed:@"submenu"];
1133 } else if (aTableView == allTableView) {
1134 NSString *object = [availableItems objectAtIndex:rowIndex];
1135 if ([[aTableColumn identifier] isEqualToString:@"name"]) {
1136 if ([object isEqualToString:@"showPlayer"]) {
1137 NSString *string = nil;
1139 string = [NSString stringWithFormat:@"%@ %@", NSLocalizedString(@"show", @"Show"), [[controller currentRemote] playerSimpleName]];
1141 [controller networkError:localException];
1145 return NSLocalizedString(object, @"ERROR");
1147 if ([submenuItems containsObject:object]) {
1148 return [NSImage imageNamed:@"submenu"];
1153 } else if (aTableView == hotKeysTableView) {
1154 if ([[aTableColumn identifier] isEqualToString:@"name"]) {
1155 return [hotKeyNamesArray objectAtIndex:rowIndex];
1157 return [[hotKeysDictionary objectForKey:[hotKeysArray objectAtIndex:rowIndex]] description];
1160 return [[[[NetworkController sharedController] remoteServices] objectAtIndex:rowIndex] name];
1164 - (BOOL)tableView:(NSTableView *)tableView writeRows:(NSArray*)rows toPasteboard:(NSPasteboard*)pboard
1166 if (tableView == menuTableView) {
1167 [pboard declareTypes:[NSArray arrayWithObjects:@"MenuTableViewPboardType", nil] owner:self];
1168 [pboard setString:[[rows objectAtIndex:0] stringValue] forType:@"MenuTableViewPboardType"];
1172 if (tableView == allTableView) {
1173 [pboard declareTypes:[NSArray arrayWithObjects:@"AllTableViewPboardType", nil] owner:self];
1174 [pboard setString:[[rows objectAtIndex:0] stringValue] forType:@"AllTableViewPboardType"];
1180 - (BOOL)tableView:(NSTableView*)tableView acceptDrop:(id <NSDraggingInfo>)info row:(int)row dropOperation:(NSTableViewDropOperation)operation
1184 NSString *dragData, *temp;
1186 pb = [info draggingPasteboard];
1188 if ([[pb types] containsObject:@"MenuTableViewPboardType"]) {
1189 dragData = [pb stringForType:@"MenuTableViewPboardType"];
1190 dragRow = [dragData intValue];
1191 temp = [myItems objectAtIndex:dragRow];
1193 if (tableView == menuTableView) {
1194 [myItems insertObject:temp atIndex:row];
1195 if (row > dragRow) {
1196 [myItems removeObjectAtIndex:dragRow];
1198 [myItems removeObjectAtIndex:dragRow + 1];
1200 } else if (tableView == allTableView) {
1201 if (![temp isEqualToString:@"separator"]) {
1202 [availableItems addObject:temp];
1204 [myItems removeObjectAtIndex:dragRow];
1206 } else if ([[pb types] containsObject:@"AllTableViewPboardType"]) {
1207 dragData = [pb stringForType:@"AllTableViewPboardType"];
1208 dragRow = [dragData intValue];
1209 temp = [availableItems objectAtIndex:dragRow];
1211 [myItems insertObject:temp atIndex:row];
1213 if (![temp isEqualToString:@"separator"]) {
1214 [availableItems removeObjectAtIndex:dragRow];
1218 [menuTableView reloadData];
1219 [allTableView reloadData];
1220 [self changeMenus:self];
1224 - (NSDragOperation)tableView:(NSTableView*)tableView validateDrop:(id <NSDraggingInfo>)info proposedRow:(int)row proposedDropOperation:(NSTableViewDropOperation)operation
1226 if (tableView == allTableView) {
1227 if ([[[info draggingPasteboard] types] containsObject:@"AllTableViewPboardType"]) {
1228 return NSDragOperationNone;
1231 if ([[[info draggingPasteboard] types] containsObject:@"MenuTableViewPboardType"]) {
1232 NSString *item = [myItems objectAtIndex:[[[info draggingPasteboard] stringForType:@"MenuTableViewPboardType"] intValue]];
1233 if ([item isEqualToString:@"preferences"] || [item isEqualToString:@"quit"]) {
1234 return NSDragOperationNone;
1238 [tableView setDropRow:-1 dropOperation:NSTableViewDropOn];
1239 return NSDragOperationGeneric;
1242 if (operation == NSTableViewDropOn || row == -1)
1244 return NSDragOperationNone;
1246 return NSDragOperationGeneric;
1250 /*************************************************************************/
1252 #pragma mark DEALLOCATION METHODS
1253 /*************************************************************************/
1257 [[NSNotificationCenter defaultCenter] removeObserver:self];
1258 [hotKeysArray release];
1259 [hotKeysDictionary release];
1260 [effectClasses release];
1261 [menuTableView setDataSource:nil];
1262 [allTableView setDataSource:nil];
1263 [controller release];
1264 [availableItems release];
1265 [submenuItems release];