home *** CD-ROM | disk | FTP | other *** search
/ Mac Easy 2008 March / MAC_easy_03_08.iso / Software / Shareware / Isolator-3.3.dmg / Docs / Source Code / IsoController.m.safe < prev    next >
Encoding:
Text File  |  2008-06-04  |  19.4 KB  |  722 lines

  1. //
  2. //  IsoController.m
  3. //  Isolator
  4. //
  5. //  Created by Ben Willmore on 08/02/2007.
  6. //  Copyright 2007 __MyCompanyName__. All rights reserved.
  7. //
  8.  
  9. /* FIXME:
  10. * Add option to blacken only main display
  11. * Does hotpluggin monitors work correctly? regardless of the state of hidebackgroundapps??
  12. */
  13.  
  14. #import "Carbon/Carbon.h"
  15. #import <HIToolbox/MacApplication.h>
  16. #import "IsoController.h"
  17.  
  18. @implementation IsoController
  19.  
  20. static kNoReason = 0;
  21. static kAppSwitched = 1;
  22. static kEnteredIsolateMode = 2;
  23.  
  24. static kCentury = 100;
  25.  
  26. static IsoController *me;
  27.  
  28. pascal OSStatus appSwitched (EventHandlerCallRef nextHandler, EventRef theEvent, void* userData)
  29. // handle change in frontmost app
  30. {
  31.     //NSLog(@"app switched");
  32.     [me setReason:kAppSwitched];
  33.     [me isolate];
  34.     return eventNotHandledErr;
  35. }
  36.  
  37. OSStatus hotKeyHandler(EventHandlerCallRef nextHandler,EventRef theEvent, void *userData)
  38. // handle hotkey press
  39. {
  40.     EventHotKeyID hkCom;
  41.     GetEventParameter(theEvent,kEventParamDirectObject,typeEventHotKeyID,NULL,sizeof(hkCom),NULL,&hkCom);
  42.     int l = hkCom.id;
  43.  
  44.     switch (l) {
  45.     case 1: // isolator hotkey
  46.         [me toggle];
  47.         break;
  48.     case 2: // prefs win hotkey
  49.         [me openPrefs];
  50.         break;
  51.     }
  52.     
  53.     return noErr;
  54. }
  55.  
  56. -(id) init
  57. {
  58.     [super init];
  59.     
  60.     // register for Carbon event on app switching
  61.     me = self;
  62.  
  63.     sparkleUpdater = [[SUUpdater alloc] init];
  64.  
  65.     reason = kNoReason;
  66.  
  67.     EventTypeSpec eventType;
  68.     eventType.eventClass = kEventClassApplication;
  69.     eventType.eventKind  = kEventAppFrontSwitched;
  70.     EventHandlerUPP handlerUPP = NewEventHandlerUPP(appSwitched);
  71.     InstallApplicationEventHandler (handlerUPP, 1, &eventType, self, NULL);
  72.     DisposeEventHandlerUPP(appSwitched);
  73.     
  74.     // set up hotkey for prefs window -- remember asynckeys for key codes
  75.     EventHotKeyID gMyHotKeyID;
  76.     //EventTypeSpec eventType;
  77.     eventType.eventClass=kEventClassKeyboard;
  78.     eventType.eventKind=kEventHotKeyPressed;
  79.     
  80.     gMyHotKeyID.signature='htk2';
  81.     gMyHotKeyID.id=2;
  82.     
  83.     // cmd-opt-shift-i for prefs window
  84.     RegisterEventHotKey(34, cmdKey + optionKey + shiftKey, gMyHotKeyID, GetApplicationEventTarget(), 0, &gMyHotKeyRef);
  85.  
  86.     // read preferences
  87.     NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
  88.     if (![defaults objectForKey:@"StartupMode"])
  89.         [defaults setInteger:1 forKey:@"StartupMode"];
  90.     
  91.     if ([defaults integerForKey:@"StartupMode"]==0) 
  92.         active = YES;
  93.     else
  94.         active = NO;
  95.  
  96.     if (![defaults objectForKey:@"HideDesktop"])
  97.         [defaults setBool:YES forKey:@"HideDesktop"];
  98.     
  99.     if (![defaults objectForKey:@"HideBackgroundApps"])
  100.         [defaults setInteger:NO forKey:@"HideBackgroundApps"];
  101.  
  102.     if (![defaults objectForKey:@"HideOnMainScreenOnly"])
  103.         [defaults setInteger:NO forKey:@"HideOnMainScreenOnly"];
  104.  
  105.     if ([defaults integerForKey:@"Hotkey"]==0) {
  106.         [defaults setInteger:34 forKey:@"Hotkey"]; // cmd-shift-i
  107.         [defaults setInteger:NSCommandKeyMask+NSShiftKeyMask forKey:@"HotkeyFlags"];
  108.     }
  109.     
  110.     if (![defaults objectForKey:@"ShowMenubarIcon"])
  111.         [defaults setBool:YES forKey:@"ShowMenubarIcon"];    
  112.  
  113.     if (![defaults objectForKey:@"MouseClickEffect"])
  114.         [defaults setInteger:1 forKey:@"MouseClickEffect"];    
  115.  
  116.     if (![defaults objectForKey:@"NumberOfTimesUsed"])
  117.         [defaults setInteger:0 forKey:@"NumberOfTimesUsed"];    
  118.  
  119.     if (![defaults objectForKey:@"SUScheduledCheckInterval"])
  120.         [defaults setInteger:86400 forKey:@"SUScheduledCheckInterval"];    
  121.  
  122.     shownCenturyMessage = NO;
  123.     if ([defaults integerForKey:@"NumberOfTimesUsed"]>=kCentury)
  124.         shownCenturyMessage = YES;
  125.     
  126.     // startupitem
  127.     startupItemController = [[StartupItemController alloc] init];
  128.     
  129.     // status bar control
  130.     [self setupStatusItem];
  131.     
  132.     gMyHotKeyRef = 0;
  133.  
  134.     // value transformer
  135.     LessThanAboutOne *lessThanAboutOne;
  136.     
  137.     lessThanAboutOne = [[[LessThanAboutOne alloc] init] autorelease];
  138.     [NSValueTransformer setValueTransformer:lessThanAboutOne forName:@"LessThanAboutOne"];
  139.  
  140.     [[NSDistributedNotificationCenter defaultCenter]
  141.                 addObserver:self
  142.                 selector:@selector(applicationDidChangeScreenParameters:)
  143.                 name:NSApplicationDidChangeScreenParametersNotification
  144.                 object:nil];
  145.  
  146.     lastProcessSerial.highLongOfPSN = 0;
  147.     lastProcessSerial.lowLongOfPSN = kNoProcess;
  148.     
  149.     ignorePID = -1;
  150.     
  151.     return self;
  152. }
  153.  
  154. -(void) awakeFromNib
  155. {
  156.     [self setupBlackWindows];
  157.     [self setKeyCombo];
  158. }
  159.  
  160. -(void) setKeyCombo
  161. {
  162.     KeyCombo keyCombo;
  163.     NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
  164.  
  165.     keyCombo.code = [defaults integerForKey:@"Hotkey"];
  166.     keyCombo.flags = [defaults integerForKey:@"HotkeyFlags"];
  167.     [shortcutRecorder setKeyCombo:keyCombo];
  168. }
  169.  
  170. -(void) saveKeyCombo
  171. {
  172.     KeyCombo keyCombo = [shortcutRecorder keyCombo];
  173.     NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
  174.     
  175.     [defaults setInteger:keyCombo.code forKey:@"Hotkey"];
  176.     [defaults setInteger:keyCombo.flags forKey:@"HotkeyFlags"];
  177.     [self syncDefaults:self];
  178. }
  179.  
  180. -(void) setupStatusItem
  181. {
  182.     NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
  183.     if ([defaults boolForKey:@"ShowMenubarIcon"]==NO) {
  184.         if (statusItem) {
  185.             [[NSStatusBar systemStatusBar] removeStatusItem:statusItem];
  186.             [statusItem release];
  187.         }
  188.         statusItem = nil;
  189.         return;
  190.     }
  191.     
  192.     if (statusItem) {
  193.         return;
  194.     }
  195.     
  196.     [self initStatusMenu];
  197.     statusItem = [[[NSStatusBar systemStatusBar] statusItemWithLength:22.] retain];
  198.  
  199.     NSRect theFrame = [[statusItem view] frame];
  200.     [statusItem setView:[[[IsoStatusItemView alloc] initWithFrame:theFrame isoController:self] autorelease]];
  201.     [[statusItem view] setNeedsDisplay:YES];
  202.     [statusItem setHighlightMode:YES];
  203. }
  204.  
  205. -(NSStatusItem*) getStatusItem
  206. {
  207.     return statusItem;
  208. }
  209.  
  210. -(void) initStatusMenu
  211. {
  212.     statusMenu = [[NSMenu alloc] initWithTitle:@"Isolator"];
  213.  
  214.     [statusMenu setDelegate:self];
  215.     
  216.     if (active)
  217.         toggleMenuItem = [[NSMenuItem alloc] initWithTitle:@"Turn Isolator Off" action:@selector(toggle) keyEquivalent:@""];
  218.     else
  219.         toggleMenuItem = [[NSMenuItem alloc] initWithTitle:@"Turn Isolator On" action:@selector(toggle) keyEquivalent:@""];
  220.     
  221.     [statusMenu insertItem:toggleMenuItem atIndex:0];
  222.  
  223.     [statusMenu insertItem:[NSMenuItem separatorItem] atIndex:1];
  224.     
  225.     NSMenuItem* prefsMenuItem = [[[NSMenuItem alloc] initWithTitle:@"Preferences..." action:@selector(openPrefs) keyEquivalent:@""] autorelease];
  226.     [statusMenu insertItem:prefsMenuItem atIndex:2];
  227.     
  228.     [statusMenu insertItem:[NSMenuItem separatorItem] atIndex:3];
  229.  
  230.     NSMenuItem* updateMenuItem = [[[NSMenuItem alloc] initWithTitle:@"Check for updates..." action:@selector(checkForUpdates:) keyEquivalent:@""] autorelease];
  231.     [updateMenuItem setTarget:sparkleUpdater];
  232.     [statusMenu insertItem:updateMenuItem atIndex:4];
  233.  
  234.     NSMenuItem* aboutMenuItem = [[[NSMenuItem alloc] initWithTitle:@"About Isolator" action:@selector(openAboutPanel) keyEquivalent:@""] autorelease];
  235.     [statusMenu insertItem:aboutMenuItem atIndex:5];
  236.     
  237.     NSMenuItem* quitMenuItem = [[[NSMenuItem alloc] initWithTitle:@"Quit" action:@selector(exit) keyEquivalent:@""] autorelease];
  238.     [statusMenu insertItem:quitMenuItem atIndex:6];
  239. }
  240.  
  241. -(NSMenu*)getStatusMenu
  242. {
  243.     return statusMenu;
  244. }
  245.  
  246. -(void)showStatusMenu
  247. {
  248.     [statusItem popUpStatusItemMenu:statusMenu];
  249. }
  250.  
  251. -(void) applicationDidChangeScreenParameters:(id)object
  252. {
  253.     [self setupBlackWindows];
  254. }
  255.  
  256. -(void) setupBlackWindows
  257. {
  258.     NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
  259.     NSArray* screens;
  260.     
  261.     if ([defaults boolForKey:@"HideOnMainScreenOnly"]==YES)
  262.         screens = [[NSArray alloc] initWithObjects: [NSScreen mainScreen], nil];
  263.     else
  264.         screens = [NSScreen screens];
  265.  
  266.     if (blackWindows) {
  267.         NSEnumerator *blackEnumerator = [blackWindows objectEnumerator];
  268.         BlackWindow *blackWindow = nil;
  269.         while (blackWindow = [blackEnumerator nextObject]) {
  270.             [blackWindow close];
  271.         }
  272.         [blackWindows release];
  273.     }
  274.     
  275.     blackWindows = [[NSMutableArray alloc] init];
  276.     NSEnumerator *enumerator = [screens objectEnumerator];
  277.     NSScreen* screen;
  278.     BlackWindow* window;
  279.     while( screen = [enumerator nextObject] ) {
  280.         window = [[BlackWindow alloc] initWithScreen:screen];
  281.         [blackWindows addObject:window];
  282.     }
  283.     
  284.     //[screens release];  // don't understand why this isn't needed
  285.     
  286.     if (active) {
  287.         [self enterIsolateMode];
  288.     }
  289.     else {
  290.         NSEnumerator *enumerator = [blackWindows objectEnumerator];
  291.         BlackWindow* window;
  292.         while( window = [enumerator nextObject] ) {
  293.             [window orderOut:self];
  294.         }
  295.     }
  296. }
  297.  
  298. -(void) isolate
  299. {
  300.     if ( !active )
  301.         return;
  302.  
  303.     NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
  304.  
  305.     NSDictionary* activeApp = [[[[NSWorkspace sharedWorkspace] activeApplication] copy] autorelease];
  306.     NSNumber* activePID = [[[activeApp objectForKey:@"NSApplicationProcessIdentifier"] copy] autorelease];
  307.     NSNumber* myPID = [NSNumber numberWithInt:[[NSProcessInfo processInfo] processIdentifier]];
  308.  
  309.     if ( (reason==kAppSwitched) && [activePID isEqual:myPID] )
  310.         return;
  311.     
  312.     if ( [activePID isEqual:[NSNumber numberWithInt:ignorePID]] )
  313.         return;
  314.         
  315.     ignorePID = [activePID intValue];
  316.     serialToSet.highLongOfPSN = [[activeApp objectForKey:@"NSApplicationProcessSerialNumberHigh"] longValue];
  317.     serialToSet.lowLongOfPSN = [[activeApp objectForKey:@"NSApplicationProcessSerialNumberLow"] longValue];
  318.  
  319.     if ([defaults boolForKey:@"HideBackgroundApps"]) {
  320.         // then hide all the background apps
  321.         
  322.         NSArray* launchedApps = [[[[NSWorkspace sharedWorkspace] launchedApplications] copy] autorelease];
  323.         NSEnumerator* appEnum = [launchedApps objectEnumerator];
  324.         NSDictionary* app = nil;
  325.         NSNumber* pid = nil;
  326.         ProcessSerialNumber psn;
  327.         
  328.         ProcessInfoRec info;
  329.         info.processInfoLength = sizeof(ProcessInfoRec);
  330.         info.processName = nil;
  331.         FSSpec tempFSSpec;
  332.         info.processAppSpec = &tempFSSpec;
  333.  
  334.         while (app = [appEnum nextObject]) {
  335.             pid = [app objectForKey:@"NSApplicationProcessIdentifier"];
  336.         
  337.             if (![pid isEqual:activePID]) {
  338.                 psn.highLongOfPSN = [[app objectForKey:@"NSApplicationProcessSerialNumberHigh"] longValue];
  339.                 psn.lowLongOfPSN = [[app objectForKey:@"NSApplicationProcessSerialNumberLow"] longValue];
  340.  
  341.                 if (GetProcessInformation(&psn, &info) == noErr) {
  342.                     ShowHideProcess(&psn,FALSE);
  343.                 }
  344.             }
  345.         }
  346.             
  347.         // this kludge makes cmd-tabbing work correctly. without it, sometimes the frontmost application doesn't move to 
  348.         // the left of the bar as it should
  349.         [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(setFrontmost:) userInfo:nil repeats:NO];
  350.     }
  351.     else {
  352.         //[NSTimer scheduledTimerWithTimeInterval:0.01 target:self selector:@selector(setFrontmostAndRepositionBlackWindows:) userInfo:nil repeats:NO];
  353.         [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(setFrontmostToApp:) userInfo:nil repeats:NO];
  354.     }
  355. }
  356.  
  357. -(void) setFrontmost:(id)dummy
  358. {
  359.     NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
  360.     if ([defaults boolForKey:@"HideDesktop"] && (reason==kEnteredIsolateMode)) {
  361.         NSEnumerator *enumerator = [blackWindows objectEnumerator];
  362.         BlackWindow* window;
  363.         while( window = [enumerator nextObject] ) {
  364.             [window fadeInOrOut:YES];
  365.         }
  366.     }
  367.  
  368.     ProcessSerialNumber mySerial;
  369.     GetCurrentProcess(&mySerial);
  370.     SetFrontProcess(&mySerial);
  371.     SetFrontProcess(&serialToSet);
  372. }
  373.  
  374. -(void) setFrontmostToMe:(id)dummy
  375. {
  376.     NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
  377.     if ([defaults boolForKey:@"HideDesktop"] && (reason==kEnteredIsolateMode)) {
  378.         NSEnumerator *enumerator = [blackWindows objectEnumerator];
  379.         BlackWindow* window;
  380.         while( window = [enumerator nextObject] ) {
  381.             [window fadeInOrOut:YES];
  382.         }
  383.     }
  384.  
  385.     ProcessSerialNumber mySerial;
  386.     GetCurrentProcess(&mySerial);
  387.     SetFrontProcess(&mySerial);
  388.     [NSTimer scheduledTimerWithTimeInterval:0.01 target:self selector:@selector(setFrontmostToApp:) userInfo:nil repeats:NO];
  389. }
  390.  
  391. -(void) setFrontmostToApp:(id)dummy
  392. {
  393.     SetFrontProcess(&serialToSet);
  394.     
  395.     CGSConnection cid;
  396.     CGSGetConnectionIDForPSN(0, &serialToSet, &cid);
  397.  
  398.     ProcessSerialNumber myPSN;
  399.     CGSConnection myCid;
  400.     GetCurrentProcess(&myPSN);
  401.     CGSGetConnectionIDForPSN(0, &myPSN, &myCid);
  402.  
  403.     int nWindows;
  404.     
  405.     CGSGetOnScreenWindowCount(myCid, cid, &nWindows); 
  406.     CGSWindow list[nWindows];
  407.     CGSGetOnScreenWindowList(myCid, cid, nWindows, list, &nWindows);
  408.     
  409.     // special case for Finder which owns Desktop window
  410.     ProcessInfoRec info;
  411.     unsigned char processName[64];
  412.     info.processInfoLength = sizeof(ProcessInfoRec);
  413.     info.processName = processName;
  414.     FSSpec tempFSSpec;
  415.     info.processAppSpec = &tempFSSpec;
  416.     if (GetProcessInformation(&serialToSet, &info) != noErr)
  417.         return;
  418.  
  419.     NSString *processNameStr = (NSString*) CFStringCreateWithPascalString(kCFAllocatorDefault, processName, kCFStringEncodingASCII);
  420.     if ([processNameStr isEqual:@"Finder"]) {
  421.         NSLog(@"%d",nWindows);
  422.         nWindows = nWindows-1;
  423.     }
  424.     [processNameStr release];
  425.     
  426.     NSEnumerator *enumerator = [blackWindows objectEnumerator];
  427.     BlackWindow* window;
  428.     while( window = [enumerator nextObject] ) {
  429.         if (nWindows==0)
  430.             [window orderFrontRegardless];
  431.         else
  432.             [window orderWindow:NSWindowBelow relativeTo:list[nWindows-1]];
  433.     }
  434. }
  435.  
  436.  
  437. -(void) setFrontmostAndRepositionBlackWindows:(id)dummy
  438. {
  439.     [self setFrontmost:self];
  440.  
  441.     CGSConnection cid;
  442.     CGSGetConnectionIDForPSN(0, &serialToSet, &cid);
  443.  
  444.     ProcessSerialNumber myPSN;
  445.     CGSConnection myCid;
  446.     GetCurrentProcess(&myPSN);
  447.     CGSGetConnectionIDForPSN(0, &myPSN, &myCid);
  448.  
  449.     int nWindows;
  450.     
  451.     CGSGetOnScreenWindowCount(myCid, cid, &nWindows); 
  452.     CGSWindow list[nWindows];
  453.     CGSGetOnScreenWindowList(myCid, cid, nWindows, list, &nWindows);
  454.     
  455.     // special case for Finder which owns Desktop window
  456.     ProcessInfoRec info;
  457.     unsigned char processName[64];
  458.     info.processInfoLength = sizeof(ProcessInfoRec);
  459.     info.processName = processName;
  460.     FSSpec tempFSSpec;
  461.     info.processAppSpec = &tempFSSpec;
  462.     if (GetProcessInformation(&serialToSet, &info) != noErr)
  463.         return;
  464.  
  465.     NSString *processNameStr = (NSString*) CFStringCreateWithPascalString(kCFAllocatorDefault, processName, kCFStringEncodingASCII);
  466.     if ([processNameStr isEqual:@"Finder"]) {
  467.         nWindows = 0;
  468.     }
  469.     [processNameStr release];
  470.     
  471.     NSEnumerator *enumerator = [blackWindows objectEnumerator];
  472.     BlackWindow* window;
  473.     while( window = [enumerator nextObject] ) {
  474.         if (nWindows==0)
  475.             [window orderFrontRegardless];
  476.         else
  477.             [window orderWindow:NSWindowBelow relativeTo:list[nWindows-1]];
  478.     }
  479. }
  480.  
  481. -(void) enterIsolateMode
  482. {    
  483.     if (!shownCenturyMessage) {
  484.         NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
  485.         int numTimes = [defaults integerForKey:@"NumberOfTimesUsed"]+1;
  486.         [defaults setInteger:numTimes forKey:@"NumberOfTimesUsed"];
  487.         if (numTimes==kCentury) {
  488.             [self showCenturyMessage];
  489.             shownCenturyMessage = YES;
  490.             return;
  491.         }
  492.     }
  493.  
  494.     ignorePID = -1;
  495.     active = YES;
  496.     
  497.     [[statusItem view] setNeedsDisplay:YES];
  498.     [toggleMenuItem setTitle:@"Turn Isolator Off"];
  499.  
  500.     // level depends on whether we're hiding other apps or not
  501.     NSEnumerator *enumerator = [blackWindows objectEnumerator];
  502.     BlackWindow* window;
  503.     while( window = [enumerator nextObject] ) {
  504.         [window setLevelAsAppropriate];
  505.     }
  506.  
  507.     reason = kEnteredIsolateMode;
  508.     [self isolate];
  509. }
  510.  
  511. -(void) leaveIsolateMode
  512. {
  513.     ignorePID = -1;
  514.     active = NO;
  515.  
  516.     NSEnumerator *enumerator = [blackWindows objectEnumerator];
  517.     BlackWindow* window;
  518.     while( window = [enumerator nextObject] ) {
  519.         [window fadeInOrOut:NO];
  520.     }
  521.  
  522.     [[statusItem view] setNeedsDisplay:YES];
  523.     [toggleMenuItem setTitle:@"Turn Isolator On"];
  524. }
  525.  
  526. -(void) toggle
  527. {    
  528.     if (active)
  529.         [self leaveIsolateMode];
  530.     else
  531.         [self enterIsolateMode];
  532. }
  533.  
  534. -(void) openPrefs
  535. {
  536.     [self willChangeValueForKey:@"startupItemEnabled"];
  537.     [self didChangeValueForKey:@"startupItemEnabled"];
  538.     [NSApp activateIgnoringOtherApps:YES];
  539.     [prefWindow makeKeyAndOrderFront:self];
  540. }
  541.  
  542. -(IBAction)setBackgroundColor:(id)sender
  543. {    
  544.     
  545.     [self syncDefaults:self];
  546.     NSEnumerator *enumerator = [blackWindows objectEnumerator];
  547.     BlackWindow* window;
  548.     while( window = [enumerator nextObject] ) {
  549.         [window setColor];
  550.     }
  551.     [[statusItem view] setNeedsDisplay:YES];
  552. }
  553.  
  554. -(IBAction)setOpacity:(id)sender
  555. {
  556.     [self syncDefaults:self];
  557.     NSEnumerator *enumerator = [blackWindows objectEnumerator];
  558.     BlackWindow* window;
  559.     while( window = [enumerator nextObject] ) {
  560.         [window setOpacity];
  561.     }
  562.     [[statusItem view] setNeedsDisplay:YES];
  563. }
  564.  
  565. -(IBAction)setClickThrough:(id)sender
  566. {
  567.     [self syncDefaults:self];
  568.     NSEnumerator *enumerator = [blackWindows objectEnumerator];
  569.     BlackWindow* window;
  570.     while( window = [enumerator nextObject] ) {
  571.         [window setClickThrough];
  572.     }
  573. }
  574.  
  575. -(IBAction)setMenuBarIcon:(id)sender
  576. {
  577.     [self syncDefaults:self];
  578.     [self setupStatusItem];
  579. }
  580.  
  581. -(IBAction)setHideBackgroundApps:(id)sender
  582. {
  583.     [self syncDefaults:self];
  584.     [self leaveIsolateMode];
  585. }
  586.  
  587. -(IBAction)setUpdatesIncludeBetaVersions:(id)sender
  588. {
  589.     NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
  590.     if ([defaults boolForKey:@"UpdatesIncludeBetaVersions"])
  591.         [defaults setObject:@"http://willmore.eu/software/isolator/allversions.xml" forKey:@"SUFeedURL"];
  592.     else
  593.         [defaults setObject:@"http://willmore.eu/software/isolator/releases.xml" forKey:@"SUFeedURL"];    
  594.     
  595.     [self syncDefaults:self];
  596. }
  597.  
  598. -(IBAction)setWindow:(id)sender
  599. {
  600.     [self syncDefaults:self];
  601.     if (!active)
  602.         return;
  603.  
  604.     NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
  605.  
  606.     if ([defaults boolForKey:@"HideDesktop"]) {
  607.         NSEnumerator *enumerator = [blackWindows objectEnumerator];
  608.         BlackWindow* window;
  609.         while( window = [enumerator nextObject] ) {
  610.             [window fadeInOrOut:YES];
  611.         }
  612.  
  613.     }
  614.     else {
  615.         NSEnumerator *enumerator = [blackWindows objectEnumerator];
  616.         BlackWindow* window;
  617.         while( window = [enumerator nextObject] ) {
  618.             [window fadeInOrOut:NO];
  619.         }
  620.     }
  621. }
  622.  
  623. -(void) registerHotkey:(KeyCombo)keyCombo
  624. {
  625.     if (gMyHotKeyRef>0) {
  626.         UnregisterEventHotKey(gMyHotKeyRef);
  627.     }
  628.     
  629.     // set up hotkey -- remember asynckeys for key codes
  630.     EventHotKeyID gMyHotKeyID;
  631.     EventTypeSpec eventType;
  632.     eventType.eventClass=kEventClassKeyboard;
  633.     eventType.eventKind=kEventHotKeyPressed;
  634.     
  635.     InstallApplicationEventHandler(&hotKeyHandler,1,&eventType,NULL,NULL);
  636.  
  637.     gMyHotKeyID.signature='htk1';
  638.     gMyHotKeyID.id=1;
  639.     
  640.     RegisterEventHotKey([shortcutRecorder keyCombo].code, [shortcutRecorder cocoaToCarbonFlags:[shortcutRecorder keyCombo].flags], 
  641.         gMyHotKeyID, GetApplicationEventTarget(), 0, &gMyHotKeyRef);
  642. }
  643.  
  644. - (void)shortcutRecorder:(SRRecorderControl *)aRecorder keyComboDidChange:(KeyCombo)newKeyCombo;
  645. {
  646.     [self registerHotkey:newKeyCombo];
  647.     [self saveKeyCombo];
  648. }
  649.  
  650. - (void)syncDefaults:(id)sender
  651. {
  652.     [[NSUserDefaults standardUserDefaults] synchronize];
  653. }
  654.  
  655. -(BOOL)startupItemEnabled
  656. {
  657.     return [startupItemController enabled];
  658. }
  659.  
  660. -(void)setStartupItemEnabled:(BOOL)value
  661. {
  662.     [startupItemController setEnabled:value];
  663. }
  664.  
  665. -(void)openAboutPanel
  666. {
  667.     [NSApp orderFrontStandardAboutPanel:self];
  668.     [NSApp activateIgnoringOtherApps:YES];
  669. }
  670.  
  671. - (void)applicationWillTerminate:(NSNotification *)notification
  672. {
  673.     [[NSUserDefaults standardUserDefaults] synchronize];
  674. }
  675.  
  676. -(BOOL)isActive
  677. {
  678.     return active;
  679. }
  680.  
  681. -(void)setReason:(int)theReason
  682. {
  683.     reason = theReason;
  684. }
  685.  
  686. -(void)showCenturyMessage {
  687.     NSAlert* alert = [[NSAlert alloc] init];
  688.     [alert setMessageText:[NSString stringWithFormat:@"You have used Isolator %d times.",kCentury]];
  689.     [alert setInformativeText:@"Please consider donating a few dollars to support its continued development. Either way, you won't see this message again."];
  690.     [alert addButtonWithTitle:@"Go to Paypal donation page"];
  691.     [alert addButtonWithTitle:@"Don't donate"];
  692.  
  693.     int result = [alert runModal];
  694.     if (result==NSAlertFirstButtonReturn) {
  695.         NSString* urlString = @"https://www.paypal.com/cgi-bin/webscr?cmd=_xclick&business=bdeb%40willmore%2eeu&item_name=Isolator&no_shipping=2&no_note=1&tax=0¤cy_code=GBP&bn=PP%2dDonationsBF&charset=UTF%2d8";
  696.         [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:urlString]];
  697.     }
  698.     [alert release];
  699. }
  700.  
  701. -(void) dealloc
  702. {
  703.     [sparkleUpdater release];
  704.     [startupItemController release];
  705.     [blackWindows release];
  706.     [statusMenu release];
  707.     [toggleMenuItem release];
  708.     [statusItem release];
  709.     
  710.     [mainMenu release];
  711.     [prefWindow release];
  712.     [shortcutRecorder release];
  713.     [super dealloc];
  714. }
  715.  
  716. -(void) exit
  717. {
  718.     [NSApp terminate:self];
  719. }
  720.  
  721. @end
  722.